Place limit order in two sides base on current inventory quantity
In this project, we utilize inventory quantities to simultaneously place both bid and ask orders. The prices of these orders are adjusted dynamically in response to changes in the matched market price. Forced sale scenarios and asset expiration dates are accounted for by incorporating additional fees into the asset's valuation.
In market making, one common approach to liquidity provision involves simultaneously placing bid and ask orders based on the current inventory levels held by the market maker. This strategy dynamically adjusts order prices in response to changes in the matched market price, allowing the market maker to maintain balanced exposure while capturing the bid-ask spread. The positions are held overnight.
We place bid and ask price with our formula:
$$bid = price - step * (max(inventory, 0) * 0.02 + 1)$$ $$ask = price - step * (min(inventory, 0) * 0.02 - 1)$$
The step size should exceed the sum of the transaction fee and slippage. Bid and ask prices are updated either every 15 seconds or upon the execution of a position.
- Data source: Algotrade database
- Data period: from 2022-01-01 to 2025-04-29
- Each sell or buy side will be charge 0.4 / 2 fee.
- The daily close price, bid, ask and tick price are collected from Algotrade database using SQL queries.
- The data is collected using the script
data_loader.py
- The data is stored in the
data/is/
anddata/os/
folders.
- Set up python virtual environment
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.txt
- (OPTIONAL) Create
.env
file in the root directory of the project and fill in the required information. The.env
file is used to store environment variables that are used in the project. The following is an example of a.env
file:
DB_NAME=<database name>
DB_USER=<database user name>
DB_PASSWORD=<database password>
DB_HOST=<host name or IP address>
DB_PORT=<database port>
Data can be download directly from Google Drive. The data files are stored in the data
folder with the following folder structure:
data
├── is
│ ├── VN30F1M_data.csv
│ └── VN30F2M_data.csv
└── os
├── VN30F1M_data.csv
└── VN30F2M_data.csv
You should place this folder to the current PYTHONPATH
for the following steps.
To collect data from database, run this command below in the root directory:
python data_loader.py
The result will be stored in the data/is/
and data/os/
Specify period and parameters in parameter/backtesting_parameter.json
file.
python backtesting.py
The results are stored in the result/backtest/
folder.
To run the optimization, execute the command in the root folder:
python optimization.py
The optimization parameter are store in parameter/optimization_parameter.json
. After optimizing, the optimized parameters are stored in parameter/optimized_parameter.json
.
To run the out-of-sample backtesting results, execute this command
python evaluation.py
The script will get value from parameter/optimized_parameter.json
to execute. The results are stored in the result/optimization
folder.
Running the in-sample backtesting by execute the command:
python backtesting.py
- Backtesting results are stored in the
result/backtest/
folder. - 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 backtesting results are constructuted from 2022-01-01 to 2023-01-01.
| Metric | Value |
|------------------------|------------------------------------|
| Sharpe Ratio | 1.5619 |
| Sortino Ratio | 2.3335 |
| Maximum Drawdown (MDD) | -0.1891 |
- The NAV chart. The chart is located at:
result/backtest/nav.png
- Drawdown chart. The chart is located at
result/backtest/drawdown.png
- Daily inventory. The chart is located at
result/backtest/inventory.png
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.py
The currently found optimized parameters with the seed 2025
are:
{
"step": 2.940955612440289
}
- Specify the out-sample period and parameters in
parameter/backtesting_parameter.json
file. - 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 constructuted from 2024-01-02 to 2025-04-29.
| Metric | Value |
|------------------------|------------------------------------|
| Sharpe Ratio | -0.0536 |
| Sortino Ratio | -0.0673 |
| Maximum Drawdown (MDD) | -0.2137 |
- The NAV chart. The chart is located at
result/optimization/nav.png
. - Drawdown chart. The chart is located at
result/optimization/drawdown.png
. - Daily inventory. The chart is located at
result/optimization/inventory.png
[1] ALGOTRADE, Algorithmic Trading Theory and Practice - A Practical Guide with Applications on the Vietnamese Stock Market, 1st ed. DIMI BOOK, 2023, pp. 52–53. Accessed: May 12, 2025. [Online]. Available: Link