Place buy and sell orders at fixed price intervals to capture profits from price oscillations
In this project, we implement a Grid Trading strategy on VN30 Index Futures (VN30F1M). The strategy places a series of buy orders below the current price and sell orders above the current price at fixed intervals. When the price oscillates within a range, the strategy systematically buys low and sells high, capturing small profits from each price movement. This approach works best in sideways or range-bound markets.
Grid trading is a structured trading method that creates a "grid" of orders at predetermined price levels. Unlike trend-following strategies, grid trading does not attempt to predict market direction. Instead, it profits from the natural oscillation of prices within a trading range.
The core concept is simple:
- When price drops, buy orders are triggered, accumulating positions
- When price rises, sell orders are triggered, taking profits
- The process repeats as price moves up and down through the grid levels
This strategy is particularly suitable for:
- Range-bound or sideways markets
- Assets with high volatility but no clear trend
- Automated trading systems that can manage multiple orders
We place buy and sell orders at fixed price intervals (grid levels) around a reference price:
Grid Level Formulas:
- Buy levels:
reference_price - i × grid_stepfor i = 1, 2, ..., num_grids - Sell levels:
reference_price + i × grid_stepfor i = 1, 2, ..., num_grids
Strategy Parameters:
grid_step: The price interval between adjacent grid levelsnum_grids: Number of grid levels above and below the reference priceposition_size: Number of contracts to trade at each grid level
The reference price is reset at the start of each trading day to adapt to overnight price changes.
- Data source: Algotrade database
- Data period:
- In-sample: 2022-01-01 to 2023-01-01
- Out-of-sample: 2024-01-02 to 2025-04-29
- Instrument: VN30F1M (VN30 Index Futures, Front Month)
- Transaction fee: 0.4 per contract per side
- The tick-level matched prices, bid-ask spreads, and daily close prices are collected from Algotrade database
- The data is collected using the script
data_loader.py - The data is stored in the
data/is/anddata/os/folders
Recommendation: We recommend using Python 3.12 for this project. While other versions may work, Python 3.12 has been tested and verified to work smoothly with all dependencies.
- Set up python virtual environment using conda py312:
python -m venv venv
source venv/bin/activate # for Linux/MacOS
.\venv\Scripts\activate.bat # for Windows command line
.\venv\Scripts\Activate.ps1 # for Windows PowerShell- Install the required packages:
pip install -r requirements.txtData can be downloaded directly from the shared Google Drive folder. The data files should be placed in the data folder with the following structure:
data
├── is
│ ├── VN30F1M_data.csv
│ └── vnindex.csv
└── os
├── VN30F1M_data.csv
└── vnindex.csv
Specify period and parameters in parameter/backtesting_parameter.json file.
python backtesting.pyThe results are stored in the result/backtest/ folder.
- The backtesting results are constructed from 2022-01-01 to 2023-01-01.
- Parameters: grid_step=2.0, num_grids=5, position_size=1
| Metric | Value |
|------------------------|------------------------------------|
| Sharpe Ratio | 1.0777 |
| Sortino Ratio | 1.6530 |
| Maximum Drawdown (MDD) | -0.7329 |
| HPR (%) | 83.39 |
| Monthly return (%) | 11.06 |
| Annual return (%) | 17.25 |
-
The HPR chart. The chart is located at:
result/backtest/hpr.svg -
Drawdown chart. The chart is located at
result/backtest/drawdown.svg -
Daily inventory. The chart is located at
result/backtest/inventory.svg
- Used metrics:
- Sharpe ratio (SR)
- Sortino ratio (SoR)
- Maximum drawdown (MDD)
- We use a risk-free rate of 6% per annum, equivalent to approximately 0.023% per day, as a benchmark for evaluating the Sharpe Ratio (SR) and Sortino Ratio (SoR).
The configuration of optimization is stored in parameter/optimization_parameter.json. You can adjust the range of parameters. Random seed is used for reconstructing the optimization process. The optimized parameter is stored in parameter/optimized_parameter.json.
The optimization process can be reproduced by executing the command:
python optimization.pygrid_step: Range [1.0, 5.0] - Price interval between grid levelsnum_grids: Range [3, 10] - Number of grid levels above and below referenceposition_size: Range [1, 5] - Number of contracts per grid level
{
"random_seed": 2025,
"no_trials": 20,
"grid_step": [1.0, 5.0],
"num_grids": [3, 10],
"position_size": [1, 5]
}The currently found optimized parameters are:
{
"grid_step": 4.1,
"num_grids": 3,
"position_size": 3
}- Specify the out-sample period and parameters in
parameter/backtesting_parameter.jsonfile. - The out-sample data is loaded on the previous step. Refer to section Data for more information.
- To evaluate the out-sample data run the command below:
python evaluation.py- The out-sample backtesting results are constructed from 2024-01-02 to 2025-04-29.
- Parameters: grid_step=4.1, num_grids=3, position_size=3
| Metric | Value |
|------------------------|------------------------------------|
| Sharpe Ratio | -1.5840 |
| Sortino Ratio | -2.0163 |
| Maximum Drawdown (MDD) | -0.8990 |
| HPR (%) | -89.67 |
| Monthly return (%) | -11.08 |
| Annual return (%) | -80.19 |
-
The HPR chart. The chart is located at
result/optimization/hpr.svg. -
Drawdown chart. The chart is located at
result/optimization/drawdown.svg. -
Daily inventory. The chart is located at
result/optimization/inventory.svg
The Grid Trading strategy showed promising in-sample results with a Sharpe ratio of 1.08 and 83% HPR. However, the out-of-sample performance was significantly negative, highlighting key limitations of grid trading:
-
Trending Markets: Grid trading performs poorly in strongly trending markets. The out-of-sample period (2024-2025) experienced significant directional moves, particularly in April 2025, causing severe losses.
-
Position Accumulation: In trending markets, the strategy continuously accumulates positions against the trend, leading to large drawdowns.
-
Range-Bound Assumption: The strategy's core assumption of mean-reverting price behavior does not hold during trending periods.
- Add trend detection filters to pause trading during strong trends
- Implement dynamic grid spacing based on volatility
- Add position limits to cap maximum exposure
- Consider asymmetric grids based on market conditions
ProtoGrid/
├── .gitignore
├── README.md
├── requirements.txt
├── config/
│ └── config.py
├── data/
│ ├── is/
│ │ ├── VN30F1M_data.csv
│ │ └── vnindex.csv
│ └── os/
│ ├── VN30F1M_data.csv
│ └── vnindex.csv
├── metrics/
│ └── metric.py
├── parameter/
│ ├── backtesting_parameter.json
│ ├── optimization_parameter.json
│ └── optimized_parameter.json
├── result/
│ ├── backtest/
│ │ ├── hpr.svg
│ │ ├── drawdown.svg
│ │ └── inventory.svg
│ └── optimization/
│ ├── hpr.svg
│ ├── drawdown.svg
│ ├── inventory.svg
│ └── optimization.log.csv
├── backtesting.py
├── data_loader.py
├── evaluation.py
├── optimization.py
└── utils.py
[1] ALGOTRADE, Algorithmic Trading Theory and Practice - A Practical Guide with Applications on the Vietnamese Stock Market, 1st ed. DIMI BOOK, 2023, Chapter 3. Accessed: Dec. 8, 2025. [Online]. Available: Link