QuantBack is a Python backtesting project for building and testing ETF pairs-trading ideas with realistic execution assumptions and train/validation/test evaluation. The data layer supports both CSV and Parquet-backed price stores.
The project now also supports an optional C++ stats extension for the pair-trading math path, so the strategy can keep Python research ergonomics while offloading repeated numerical work to compiled code. That extension now covers both single formulas and full rolling spread-window summaries.
The current project supports:
- multi-symbol daily-bar backtests from a combined price table
- long/short pair trades with multi-leg order handling
- spread trading on log prices
- optional compiled math backend for hedge-ratio and rolling z-score calculations
- two hedge modes:
unit: assumes a 1:1 relationshipstaticBeta: estimates a fixed hedge ratio from the warmup window
- execution realism:
- slippage in basis points
- per-order commissions
- basic risk controls:
- max package capital fraction
- max dollars per leg
- stop-loss
- max holding period
- signal diagnostics:
- spread
- rolling mean/std
- z-score
- hedge ratio
- package PnL / return
- experiment sweeps across:
- train / validation / test windows
- candidate ETF pairs
- hedge modes
- strategy parameters
The main strategy in strats/pairStrat.py works like this:
- Pick two ETFs, such as
IVVandVOO. - Transform prices into log prices.
- Build a spread:
unit:log(A) - log(B)staticBeta:log(A) - beta * log(B)
- Compute a rolling z-score of that spread.
- Enter when the spread is far from its rolling mean:
- low z-score: buy
A, sellB - high z-score: sell
A, buyB
- low z-score: buy
- Exit when:
- the spread mean reverts,
- the trade loses too much,
- or the trade stays open too long.
main.py: runs one configured backtestengine/: data feed, execution, portfolio accounting, performance evaluation, backtest loopstrats/: strategy interfaces and strategy implementationsanalytics/: helpers for saving run outputsconfig/: runtime configurationdata/: combined ETF price dataexperiments/: multi-run research scriptslogs/: generated backtest and experiment outputs
Run one configured backtest:
python3 main.pyBuild the optional C++ extension:
bash cpp/buildFastStats.shValidate and benchmark the extension:
python3 cpp/checkFastStats.py
python3 cpp/benchFastStats.pyRun the train/validation/test sweep:
python3 experiments/tvtSweep.pyBenchmark storage format and compiled math:
python3 experiments/benchStorage.py
python3 cpp/benchFastStats.pyThe current ETF universe in data/prices.csv and data/prices.parquet includes:
SPYIVVVOOQQQXLK
The latest validation winner from logs/experiments/bestValChoice.txt was:
- pair:
IVV / VOO - hedge mode:
staticBeta - lookback:
30 - entry z-score:
2.0 - exit z-score:
0.2
Important caveat:
- this looked best on validation, but the broader train/validation/test results still show that the strategy is fragile after costs
- the project is currently stronger as a research/infrastructure artifact than as proof of a durable edge
Measured on this machine with the current 5-symbol 2023 dataset using the reproducible benchmark in experiments/benchStorage.py:
- Parquet vs CSV data loads:
11.284 msvs14.212 msper load, about20.6%faster - End-to-end backtest runs:
92.491 msvs94.166 msper run, about1.8%faster - C++ vs Python
calcBeta:0.050640 svs4.035666 sover10,000calls, about79.7xfaster - C++ vs Python
rollZScore:0.030045 svs2.385154 s, about79.4xfaster - C++ vs Python spread-window stats:
0.052351 svs2.491074 s, about47.6xfaster
These improvements matter differently:
- Parquet is a useful systems improvement because it cuts repeated research and data-loading time without changing behavior
- The C++ extension is the larger numerical speedup and is the better resume talking point for quant-dev style engineering
Good resume bullet direction for this project:
- built a Python ETF pairs-trading backtester with train/validation/test evaluation, realistic execution costs, risk controls, and multi-symbol portfolio accounting
- added a Parquet-backed market-data path that reduced repeated data-load time by about
21%versus CSV on the current dataset - implemented a C++ extension for hedge-ratio and rolling spread-stat calculations, accelerating core numerical kernels by about
48-80xversus Python
Keep the wording honest:
- say these are benchmarked engineering improvements, not trading alpha
- say the strategy is a mean-reversion ETF pairs strategy using log spreads and rolling z-scores
- do not imply the backtest shows a production-ready edge, because right now it does not