Backtesing framework written in C++17 to simulate trading strategy for BTC/USD pair.
Warning
THIS FRAMEWORK IS NOT INTENDED FOR ANY REAL-WORLD INVESTMENT DECISIONS. THE USE OF THIS FRAMEWORK AND THE DATA PROVIDED IS AT YOUR OWN RISK. I DO NOT TAKE ANY RESPONSIBILITY FOR THE CORRECTNESS OF THE DATA OR ANY OUTPUTS GENERATED BY THIS FRAMEWORK WHEN APPLIED IN REAL-WORLD SCENARIOS. THIS FRAMEWORK IS DESIGNED SOLELY FOR LEARNING AND EDUCATIONAL PURPOSES.
- Dependency
- Project Structure
- Data Download
- Compiling & Building
- Tick Data Generation
- Strategy Example And Other Terms
- How to Run
- How To Add New Strategy
- Performance Details
- Memory Allocation Test
- Task & Improvements
- License
common-util added as git submodule.
gnu plot On mac easy installation would be brew install gnuplot
googletest for unit test cases.
C++17,
CMake,
Clang,
βββ backtesting
βΒ Β βββ base (This compiles as static lib which get used in both data generation and simulation)
βΒ Β βΒ Β βββ account (Keep track of account balance/simulate buy and sell)
βΒ Β βΒ Β βββ common_interface
βΒ Β βΒ Β βββ price_history (OHLC data clean up)
βΒ Β βΒ Β βββ trade_simulator (Interface to add new trade strategy)
βΒ Β βΒ Β βββ util (Read/Write into binary file, getting cmd line argument)
βΒ Β βββ execution (actually running the simulation)
βΒ Β βββ logs (log generation which get used to plot a graph)
βΒ Β βββ simulators (Trading strategy and Trade Simulator logic)
βΒ Β βββ strategy
βββ data (Create this folder to hold ohlc data)
βββ data_generator (Logic to convert TPV to OHLC and change frequency of OHLC)
βββ external (Added dependancy as git submodule, memory map file, logger, time, command line argument and string formate util)
βββ quick_run (bash script to quickly running the project)
βββ result_plot (logic to plot a graph after running simulation)Time Price Volume (TPV) data is hosted on kaggle. It can be downloaded directly or with use of wget
link to download.
Create a folder named data and unzip data there with the name bitstamp_tick_data.csv

- clone the repo
git clone https://github.com/xpd54/backtesting - update submodule
git submodule update --init --recursive - create a build folder and build
mkdir build && cd build && cmake .. && make
This generate 3 exicutables
- ohlc_generator (To convert TPV to binary form of OHLC data formate)
- trade_simulator (Execute trade simulation)
- plot (plot graph with evaluation log)
Any trade simulation we would like to run on different frequency of tick data. ohlc_generator does this to do it quickly with [start_time - end_time) (time range to consider for ohlc (open high low close))
cd quick_run && chmod +x quick_ohlc_generation.sh
./quick_ohlc_generation.sh (might take a minute or two as tpv data is close to 3.2gb). I suggest checking quick_ohlc_generation.sh before running it, naming convention is self explanatory.
Rebalancing Trade Strategy:- The basic idea here is to keep the portfolio value (BTC * BTC price + USD) in a way where cryptocurrency weight is defined by alpha and it's allowed to go up and down by epsilon. Example:- alpha 0.7 means that we would like to keep 70% of the total portfolio value in the base (crypto) currency, and 30% in the quote currency (USD) for whole execution time. So Current quick run between [start_time='2017-01-01' - end_time='2024-01-01'] we would maintain 70% in BTC and 30% in USD for the whole 7 years while looking to make a profit.
epsilon 0.05f means the maximum allowed deviation from the desired alpha allocation. We allow the actual base (crypto) currency allocation to be within the interval: [alpha x (1 - epsilon) - alpha x (1 + epsilon)].
So [0.735 - 0.665] which means it's allowed to allocate 73.5% to 66.5% in BTC and the rest corresponding in USD. codebase is self-explanatory but will add more clarity in words soon!!
use quick_run/quick_simulation.sh to run trade simulation over 5 min frequency data.
Example commands are HERE
./trade_simulator \
--input_price_history_binary_file="../data/bitstamp_tick_data_5min.mov" \ (change it to 30min.mov and 1h.mov for other frequency)
--output_account_log_file="../data/account.log" \
--output_simulator_log_file="../data/simulator.log" \
--start_time="2017-01-01" \ (start execution from jan 2017 to jan 2024)
--end_time="2024-01-01" \
--start_base_balance=1.0 \ (we have 1 btc to start with)
--start_quote_balance=0.0 \ (we have 0 usd to start with)Here we can see with the rebalancing trade strategy we have a gain of 2043.60%. But if we would have just bought and hold it would have been 4272.85%. Well HODL strategy is the best in the crypto market do time travel and buy in 2011 and sell in 2024 that's HODL.
[2025-02-03 20:47:00.665] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.7000|0.0500] evaluation
[2025-02-03 20:47:05.889] [0x7ff8599feb40] [INFO] --------------- Time Period --------------- Strategy gain | Base gain(HODL) | Score | volatility
[2025-02-03 20:47:05.890] [0x7ff8599feb40] [INFO] [2017-01-01 00:00:00 - 2024-01-01 00:00:00): 2043.6094% | 4272.8594% | 0.4902 | 0.0000 | 0.0000
[2025-02-03 20:47:05.895] [0x7ff8599feb40] [INFO] Evaluated in 0:0:5sec
if we run the same strategy over diff frequencies it should be close enough (2055% gain) on 1h frequency with a similar 0.49 score.
[2025-02-03 20:55:00.884] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.7000|0.0500] evaluation
[2025-02-03 20:55:01.650] [0x7ff8599feb40] [INFO] --------------- Time Period --------------- Strategy gain | Base gain(HODL) | Score | volatility
[2025-02-03 20:55:01.650] [0x7ff8599feb40] [INFO] [2017-01-01 00:00:00 - 2024-01-01 00:00:00): 2055.8738% | 4271.8188% | 0.4931 | 0.0000 | 0.0000
[2025-02-03 20:55:01.657] [0x7ff8599feb40] [INFO] Evaluated in 0:0:1sec
We should look into our evaluation log which shows we are selling our 0.3 BTC immediately where our alpha was set for 0.7 (70% in BTC) we achieved that balance.
1483228800,966.3400,966.9900,964.6000,966.6000,102.4848,1.0000,0.0000,0.0000,,,,,
1483232400,966.6000,966.6000,962.5400,963.8700,149.0256,1.0000,0.0000,0.0000,,,,,
1483232400,966.6000,966.6000,962.5400,963.8700,149.0256,0.7000,287.9200,1.4500,MARKET,SELL,0.300000,,
1483236000,964.3500,965.7500,961.9900,963.9700,94.2674,0.7000,287.9200,1.4500,,,,,
If we plot the portfolio value we see it started with 1 BTC and goes down (get sold and allocate in USD) asap after that it trades in the range of [0.735 - 0.665] (73.5% to 66.5%) of allocation.
./plot --output_account_log_file="../data/account.log"
(The number of data points that I am using is limited to 30000 as higher data point plot generation becomes slower)

Let's run it with multiple combinations of alpha and epsilon [alpha|epsilon]. Again if we look int the score we can see highest score suggest allocating most of the assets in BTC which is HODL (buy and hold).
./trade_simulator \
--input_price_history_binary_file="../data/bitstamp_tick_data_1h.mov" \
--output_account_log_file="../data/account.log" \
--output_simulator_log_file="../data/simulator.log" \
--evaluation_period_months=6 \
--start_time="2017-01-01" \
--end_time="2024-01-01" \
--start_base_balance=1.0 \
--start_quote_balance=0.0 \
--evaluate_combination=1[2025-02-03 21:05:18.790] [0x7ff8599feb40] [INFO] Evaluation Combination of simulators
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.9000|0.2000]: 1.0000
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.9000|0.1000]: 0.9948
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.9000|0.0500]: 0.9905
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.9000|0.0100]: 0.9812
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.7000|0.2000]: 0.9586
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.7000|0.0500]: 0.9509
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.7000|0.1000]: 0.9483
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.7000|0.0100]: 0.9421
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.5000|0.1000]: 0.9132
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.5000|0.2000]: 0.9126
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.5000|0.0500]: 0.9085
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.5000|0.0100]: 0.8923
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.3000|0.2000]: 0.8647
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.3000|0.1000]: 0.8625
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.3000|0.0500]: 0.8556
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.3000|0.0100]: 0.8429
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.1000|0.2000]: 0.8064
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.1000|0.1000]: 0.8048
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.1000|0.0500]: 0.8024
[2025-02-03 21:05:18.886] [0x7ff8599feb40] [INFO] rebalancing_trade_simulator[0.1000|0.0100]: 0.7963
TODO:- Add more details on how rebalancing trade strategy works
By implementation of an interface provided into backtesting/base/trade_simulator/trade_simulator.hpp
And updating backtesting/simulators/simulator_factory.hpp and backtesting/simulators/strategy/common_strategy_config.hpp
it's easily can be achieved.
I am using mmap to read and write into the file. Which map the disk file directly to memory. I wrote a common-util which have easy to use header-only lib.
Memory Leaks Output
Process: trade_simulator [71547]
Path: /Users/USER/*/trade_simulator
Load Address: 0x102783000
Identifier: trade_simulator
Version: 0
Code Type: X86-64
Platform: macOS
Parent Process: leaks [71546]
Date/Time: 2025-02-09 17:44:26.653 +0800
Launch Time: 2025-02-09 17:44:15.726 +0800
OS Version: macOS 15.1.1 (24B91)
Report Version: 7
Analysis Tool: /usr/bin/leaks
Physical footprint: 1840K
Physical footprint (peak): 46.6M
Idle exit: untracked
----
leaks Report Version: 4.0, multi-line stacks
Process 71547: 224 nodes malloced for 55 KB
Process 71547: 0 leaks for 0 total leaked bytes.
- Single instance Logger for both console and logfile.
- Use CRTP to avoid using virtual.
- Improve read/write with memory mapped file.
- Write OHLC data to a binary file.
- Run account logging on different processes.
- Rebalancing trade strategy doc.
- Financial indicators
- Integrate google test
- Unit Test cases
- Check and Fix memory leaks.