This project presents the design, implementation, and evaluation of a hybrid trading strategy tailored for the VN30F1M futures index. The strategy combines two widely recognized technical indicators: the 100-period Simple Moving Average (SMA-100) and the 14-period Relative Strength Index (RSI-14). By integrating the concepts of trend-following and mean-reversion, the strategy seeks to exploit temporary mispricings in the market. Specifically, it generates trading signals when the RSI reaches extreme levelsโindicating overbought or oversold conditionsโrelative to the direction of the SMA-defined trend. These entry signals are then evaluated for profitability within the broader context of the prevailing market regime. We utilize historical price data provided by Algotrade to backtest the strategy across multiple timeframes and market conditions. Key performance metrics, including cumulative returns, Sharpe ratio, and maximum drawdown, are used to assess robustness and risk-adjusted performance. The results indicate that the hybrid approach holds potential for consistent profitability, particularly during trending but volatile market phases, underscoring the value of blending momentum and reversal signals in systematic trading.
In recent years, algorithmic trading has revolutionized financial markets by enabling systematic, rule-based decision-making that minimizes human emotion and error. These algorithms can process vast amounts of market data in real time and execute trades based on predefined strategies, significantly improving efficiency and consistency in trading operations. Two of the most commonly used strategies in this domain are momentum strategies, which follow the direction of existing trends, and mean-reversion strategies, which capitalize on the tendency of prices to revert to their historical average after reaching extreme levels.
This project explores a hybrid approach that synthesizes both strategy types, focusing on the VN30F1M futures contractโa key derivative instrument tied to the VN30 Index, which tracks the 30 largest and most liquid stocks on the Ho Chi Minh Stock Exchange. The VN30F1M is known for its high trading volume and price volatility, making it an ideal candidate for technical-based trading strategies.
The foundation of the hybrid strategy lies in the use of two technical indicators:
- The Simple Moving Average (SMA) with a 100-period window serves as a proxy for long-term market trend direction, helping to identify whether the overall sentiment is bullish or bearish.
- The Relative Strength Index (RSI) with a 14-period window is used to detect short-term momentum extremes. When RSI values cross certain thresholdsโtypically above 70 for overbought and below 30 for oversold conditionsโthey may signal potential turning points in price action.
By aligning RSI signals with the direction suggested by the SMA, the strategy aims to enter trades with higher probability setups, favoring mean-reversion trades that are in harmony with the broader trend. This hybrid structure is designed to reduce false signals and improve trade timing, which are common challenges in purely mean-reversion or momentum-based systems.
This section introduces foundational concepts in technical analysis that our strategy builds upon, specifically the Simple Moving Average (SMA) and the Relative Strength Index (RSI).
The Simple Moving Average (SMA) is a widely used trend-following indicator that smooths price data by averaging closing prices over a fixed time window. It helps identify the general direction of market momentum and filters out short-term noise.
Formula:
SMAโ = (Pโ + Pโโโ + ... + Pโโโโโ) / n
Where:
SMA_t
is the SMA at timet
n
is the window size (e.g., 100)P_t
is the closing price at timet
Usage in our strategy:
- Go long only when price is above the SMA (bullish trend)
- Go short only when price is below the SMA (bearish trend)
The Relative Strength Index (RSI) is a momentum oscillator that measures the magnitude of recent price changes to evaluate overbought or oversold conditions in the price of an asset.
Basic formula:
RS = Average Gain / Average Loss
RSI = 100 - (100 / (1 + RS))
RSI values range from 0 to 100:
- RSI > 70 โ Overbought (potential for downward reversal)
- RSI < 30 โ Oversold (potential for upward reversal)
In our strategy:
- Long entry when RSI is low (e.g., below 30) and price is above SMA
- Short entry when RSI is high (e.g., above 70) and price is below SMA
By combining SMA and RSI:
- SMA acts as a trend filter
- RSI provides entry timing for mean-reversion opportunities
This hybrid approach blends momentum and mean-reversion principles to improve signal quality and reduce noise in trade decisions.
In volatile markets like the VN30F1M futures index, price movements often exhibit both trend-following characteristics during strong momentum phases and mean-reverting behavior during overbought or oversold corrections. Our trading hypothesis aims to exploit market inefficiencies by combining these two regimes into a single hybrid strategy:
When the Relative Strength Index (RSI) indicates oversold or overbought conditions and aligns with the broader trend confirmed by a slower-moving average, prices are >likely to revert to their mean. This creates opportunities to trade counter to short-term extremes while respecting the dominant trend.
The core premise behind this hypothesis is that short-term overreactions in price โ often captured by RSI โ have a higher likelihood of mean-reverting when the trade is aligned with the underlying trend direction, measured by the Simple Moving Average (SMA).
We combine both trend-following and mean-reversion signals to form a hybrid strategy.
RSI < RSI_lower threshold
(e.g., < 30) โ oversold conditionPrice > SMA
โ confirms bullish trendSufficient capital
to meet margin requirement
RSI > RSI_upper threshold
(e.g., > 70) โ overbought conditionPrice < SMA
โ confirms bearish trendSufficient capital
to meet margin requirement.
This logic ensures that positions are only opened when a potential reversal is aligned with the dominant trend regime.
RSI > 40
โ market no longer oversold- Price crosses below SMA โ trend may be reversing
- Stop-loss triggered: Price drops more than 1.5% from entry
RSI < 60
โ market no longer overbought- Price crosses above SMA โ trend may be reversing
- Stop-loss triggered: Price rises more than 1.5% from entry.
After closing a position, realized P&L is added to capital, and the system becomes eligible to open a new position the next day (if entry conditions are met)
This project is organized in a modular and extensible format. Below is an overview of the key files and folders and their roles within the system.
Group9/
โโโ config/
โโโ data/
โ โโโ in_sample_data.csv # Preprocessed historical data for training
โ โโโ out_sample_data.csv # Preprocessed historical data for evaluation
โโโ graph/
โ โโโ ... # Charts and figures generated during backtest/optimization
โโโ src/
โ โโโ backtest.py # Main script for in-sample and out-sample backtesting
โ โโโ optimize.py # Parameter tuning script using Optuna
โ โโโ strategy.py # Core trading strategy logic (entry/exit rules)
โ โโโ evaluate.py # Utility functions to compute Sharpe ratio, drawdown, return
โ โโโ data.py # Data loading and preprocessing functions
โ โโโ utils.py # (Optional) visualization helpers
โโโ best_params.json # Saved best parameters from optimization
โโโ requirements.txt # Python dependencies
โโโ README.md # Project documentation
-
src/strategy.py
Implements the main trading strategy. Contains logic for SMA/RSI signal generation, trade execution, and asset value tracking. -
src/backtest.py
Used to evaluate strategy performance on historical data. Calculates key metrics and visualizes asset growth over time. -
src/optimize.py
Performs hyperparameter optimization using Optuna to identify the most profitable SMA/RSI values. -
src/data.py
Handles data collection, formatting, and cleaning. Can fetch data from local or online sources. -
src/evaluate.py
Calculate Metrics: Sharpe ratio, drawdown, return calculations. -
graph/
Stores all backtesting/optimization charts for reporting or analysis.
You can easily extend the project with additional strategies, evaluation metrics, or data sources by adding new modules under the src/
folder.
- Source: [Algotrade Internship Database]
- Format: CSV
- Instrument: VN30F1M futures
- Timeframe:
- In-Sample: 2021-02-08 โ 2023-12-22
- Out-of-Sample: 2023-12-22 โ 2025-03-19
You can obtain the required datasets in either of the following ways:
- Download from Google Drive: ๐ Data Folder
- After downloading, extract the contents into the projectโs
data/
folder.
Run the following command from the project root to fetch and process the data:
python -m src.data
This will automatically generate the following files in the data/
folder:
- in_sample_data.csv
- out_sample_data.csv
Please be patient ^^ the script may take a few minutes to complete.
- Raw price data of VN30F1M was extracted using below SQL queries from the Algotrade database:
SELECT m.datetime, m.tickersymbol, m.price
FROM "quote"."matched" m
LEFT JOIN "quote"."total" v
ON m.tickersymbol = v.tickersymbol
AND m.datetime = v.datetime
WHERE m.datetime BETWEEN '2021-02-08' AND '2025-03-20'
AND m.tickersymbol LIKE 'VN30F%'
- Preprocessing steps included:
- Filtering trading hours: Only data from 09:00 AM onward was retained.
- Minute aggregation: Only the first trade per minute was kept to reduce noise.
- Standardization: Column names were unified; only relevant columns were retained:
timestamp
,ticker
,close_price
- Data split:
- 70% allocated to
in-sample
- 30% allocated to
out-of-sample
No timestamp overlap between the two sets. This clean and well-structured dataset serves as the foundation for both backtesting and strategy optimization.
- 70% allocated to
Follow the steps below to set up the project locally.
Start by cloning the repository to your local machine:
git clone "https://github.com/algotrade-course/SMAxRSI.git"
cd SMAxRSI
Create an isolated Python environment to manage dependencies cleanly.
python -m venv venv
.\venv\Scripts\activate
python3 -m venv venv
source venv/bin/activate
๐ก Tip: Always activate your virtual environment before running the project.
Install all required libraries specified in requirements.txt
:
pip install -r requirements.txt
Once the setup is complete, you can proceed to data preparation or run the strategy by using instructions in the sections below.
To begin in-sample backtesting, run the following command:
python -m src.backtest
Parameter | Description | Default Value |
---|---|---|
sma_window |
Window size for Simple Moving Average | 100 |
rsi_lower |
Buy threshold (RSI lower bound) | 25 |
rsi_upper |
Sell threshold (RSI upper bound) | 75 |
Parameter | Value |
---|---|
Final Asset Value | 55,475,000.00 VND |
Sharpe Ratio | 0.7217 |
Maximum Drawdown | -9.66% |
Accumulated return rate | 0.39% |
The optimization process uses Optuna's Tree-structured Parzen Estimator (TPE) to maximize the final asset value by tuning strategy parameters automatically.
This process explores a wide range of parameter combinations and selects the one that yields the highest final asset.
Parameter | Description | Search Space |
---|---|---|
sma_window |
Window size for Simple Moving Average | Integer: 50 โ 200 |
rsi_lower |
Buy threshold | Float: 20.0 โ 35.0 |
rsi_upper |
Sell threshold | Float: 65.0 โ 80.0 |
Hyperparameter | Description | Value |
---|---|---|
n_trials |
Number of optimization runs | 80 |
seed |
For reproducibility | 710 |
sampler |
Sampling algorithm | TPESampler |
To start the optimization process:
python -m src.optimize
Parameter | Description | Optimized Value |
---|---|---|
sma_window |
Simple Moving Average window | 200 |
rsi_lower |
Buy threshold (RSI lower bound) | 29.14 |
rsi_upper |
Sell threshold (RSI upper bound) | 65.00 |
Parameter | Value |
---|---|
Sharpe Ratio | 2.5603 |
Maximum Drawdown | -9.25% |
Accumulated return rate | 2.31% |
To test performance on unseen data using the best parameters:
python -m src.backtest --use-optimized
- Evaluation:
Parameter | Value |
---|---|
Final Asset Value | 27990000.0 VND |
Sharpe Ratio | -1.7126 |
Maximum Drawdown | -31.14% |
Accumulated return rate | -0.30% |
๐ Note: Optional additional optimization for out-of-sample data can be run similarly.
The backtesting experiments yield important insights into the robustness and generalizability of the trading strategy.
-
In-sample vs. Out-of-sample Performance:
While the strategy demonstrated promising results during the in-sample phaseโwith consistent profit growth and controlled riskโthe out-of-sample evaluation presented a stark contrast. The strategy struggled with negative returns and higher drawdowns, suggesting that the performance was likely influenced by overfitting to historical data rather than generalizable signal strength. -
Observations from Baseline Parameters:
When tested with conventional technical indicator thresholds (SMA window = 100, RSI = 30/70), the strategy's asset curve remained largely flat for extended durations. This behavior implies that the current signal conditions are either overly restrictive or misaligned with broader market dynamics, leading to periods of inactivity and missed opportunities. -
Strategy Fragility and Sensitivity:
The high sensitivity of the strategy to parameter tuning, along with poor adaptability across different market regimes, indicates a structurally fragile design. Such issues are critical for real-world deployment, especially in volatile or non-stationary environments.
To enhance the strategy's robustness and readiness for live trading environments, we suggest the following improvements:
- Reassess entry and exit logic to minimize "dead zones" where no trades are triggered.
- Introduce adaptive or dynamic thresholding mechanisms instead of fixed RSI/SMA values.
- Incorporate market regime detection (e.g., trending vs. ranging markets) to adjust behavior dynamically.
- Apply walk-forward validation or stability tests to ensure parameter reliability across time.
By addressing these points, the strategy could evolve toward a more resilient and adaptable trading system capable of performing consistently across varied market conditions.