quantbt-engine 1.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantbt/__init__.py +862 -0
- quantbt/adapters/__init__.py +5 -0
- quantbt/adapters/nautilus/__init__.py +42 -0
- quantbt/adapters/nautilus/_dependency.py +53 -0
- quantbt/adapters/nautilus/backend.py +639 -0
- quantbt/adapters/nautilus/instruments.py +275 -0
- quantbt/adapters/nautilus/options.py +465 -0
- quantbt/adapters/nautilus/reports.py +231 -0
- quantbt/backends/__init__.py +29 -0
- quantbt/backends/_native_event_rust.py +2168 -0
- quantbt/backends/native_event.py +5048 -0
- quantbt/backends/native_option.py +796 -0
- quantbt/backends/native_portfolio.py +928 -0
- quantbt/backends/native_vectorized.py +1299 -0
- quantbt/backtester.py +485 -0
- quantbt/benchmarks/__init__.py +1 -0
- quantbt/benchmarks/compare_phase9_parity.py +302 -0
- quantbt/benchmarks/gamma_scalping_backtestsample.py +802 -0
- quantbt/benchmarks/profile_phase7.py +415 -0
- quantbt/benchmarks/run_arbitrage_phase_e.py +160 -0
- quantbt/benchmarks/run_optimization_overhead.py +195 -0
- quantbt/benchmarks/run_options_engine.py +272 -0
- quantbt/benchmarks/run_pct_equity_nautilus_smoke.py +227 -0
- quantbt/benchmarks/run_phase12_arbitrage_cert.py +402 -0
- quantbt/benchmarks/run_phase12_benchmark_nautilus_cert.py +430 -0
- quantbt/benchmarks/run_phase13_portfolio_report.py +97 -0
- quantbt/benchmarks/run_phase13_wfo_cache.py +162 -0
- quantbt/benchmarks/run_phase14_service_loop.py +694 -0
- quantbt/benchmarks/run_phase15a_nautilus_certification.py +514 -0
- quantbt/benchmarks/run_phase15b_synthetic_depth.py +175 -0
- quantbt/benchmarks/run_phase16_performance_debt.py +389 -0
- quantbt/benchmarks/run_phase30e_reactive_runner.py +161 -0
- quantbt/benchmarks/run_phase31_intrabar.py +441 -0
- quantbt/benchmarks/run_phase34a_native_event_memory.py +179 -0
- quantbt/benchmarks/run_phase34b_native_event_prepared_score.py +173 -0
- quantbt/benchmarks/run_phase34c_native_event_single_pass.py +176 -0
- quantbt/benchmarks/run_phase7.py +654 -0
- quantbt/benchmarks/run_portfolio_real_parity.py +537 -0
- quantbt/core/__init__.py +323 -0
- quantbt/core/arbitrage.py +767 -0
- quantbt/core/basket.py +234 -0
- quantbt/core/certification.py +274 -0
- quantbt/core/constraints.py +155 -0
- quantbt/core/engine.py +1144 -0
- quantbt/core/event.py +869 -0
- quantbt/core/execution_contract.py +197 -0
- quantbt/core/execution_depth.py +613 -0
- quantbt/core/intrabar_kernel.py +1906 -0
- quantbt/core/intrabar_reference.py +907 -0
- quantbt/core/intrabar_session.py +156 -0
- quantbt/core/market_tape.py +480 -0
- quantbt/core/native_event_capabilities.py +121 -0
- quantbt/core/native_event_parity.py +336 -0
- quantbt/core/order_compiler.py +422 -0
- quantbt/core/orders.py +319 -0
- quantbt/core/portfolio.py +392 -0
- quantbt/core/preprocessor.py +256 -0
- quantbt/core/reactive.py +173 -0
- quantbt/core/results.py +341 -0
- quantbt/core/schema.py +211 -0
- quantbt/core/scopes.py +96 -0
- quantbt/core/structured_orders.py +378 -0
- quantbt/core/types.py +87 -0
- quantbt/core/vectorized.py +203 -0
- quantbt/endpoint.py +4465 -0
- quantbt/engines.py +1014 -0
- quantbt/metrics/__init__.py +45 -0
- quantbt/metrics/options_analytics.py +46 -0
- quantbt/metrics/performance.py +541 -0
- quantbt/optimization/__init__.py +94 -0
- quantbt/optimization/callbacks.py +116 -0
- quantbt/optimization/candidate_selection.py +397 -0
- quantbt/optimization/config.py +84 -0
- quantbt/optimization/constraints.py +22 -0
- quantbt/optimization/evaluator.py +21 -0
- quantbt/optimization/evaluators/__init__.py +32 -0
- quantbt/optimization/evaluators/arbitrage.py +22 -0
- quantbt/optimization/evaluators/generic.py +34 -0
- quantbt/optimization/evaluators/grid_dca.py +22 -0
- quantbt/optimization/evaluators/intrabar.py +54 -0
- quantbt/optimization/evaluators/native_event.py +51 -0
- quantbt/optimization/evaluators/options.py +22 -0
- quantbt/optimization/evaluators/portfolio.py +42 -0
- quantbt/optimization/evaluators/signal.py +43 -0
- quantbt/optimization/multiseed.py +176 -0
- quantbt/optimization/objectives.py +221 -0
- quantbt/optimization/optimizer.py +500 -0
- quantbt/optimization/result.py +80 -0
- quantbt/optimization/samplers.py +84 -0
- quantbt/optimization/space.py +223 -0
- quantbt/options/__init__.py +215 -0
- quantbt/options/cache.py +116 -0
- quantbt/options/conventions.py +158 -0
- quantbt/options/data.py +177 -0
- quantbt/options/execution.py +600 -0
- quantbt/options/fees.py +135 -0
- quantbt/options/greeks.py +173 -0
- quantbt/options/hedging.py +232 -0
- quantbt/options/iv.py +188 -0
- quantbt/options/ledger.py +263 -0
- quantbt/options/lifecycle.py +94 -0
- quantbt/options/margin.py +311 -0
- quantbt/options/packages.py +147 -0
- quantbt/options/pricing.py +191 -0
- quantbt/options/schema.py +227 -0
- quantbt/options/selectors.py +281 -0
- quantbt/options/strategy.py +249 -0
- quantbt/options/surface.py +142 -0
- quantbt/options/tape.py +226 -0
- quantbt/options/templates/__init__.py +33 -0
- quantbt/options/templates/packages.py +354 -0
- quantbt/portfolio.py +606 -0
- quantbt/py.typed +1 -0
- quantbt/reporting/__init__.py +38 -0
- quantbt/reporting/arbitrage_audit.py +211 -0
- quantbt/reporting/nautilus_bundle.py +758 -0
- quantbt/reporting/nautilus_certification.py +226 -0
- quantbt/reporting/nautilus_diagnostics.py +225 -0
- quantbt/reporting/parity.py +496 -0
- quantbt/reporting/portfolio_audit.py +206 -0
- quantbt/reporting/portfolio_nautilus.py +140 -0
- quantbt/sizing/__init__.py +3 -0
- quantbt/sizing/fast.py +67 -0
- quantbt/sizing/modes.py +162 -0
- quantbt/viz/__init__.py +4 -0
- quantbt/viz/plots.py +310 -0
- quantbt/viz/themes.py +102 -0
- quantbt/walkforward.py +3136 -0
- quantbt_engine-1.0.7.dist-info/METADATA +891 -0
- quantbt_engine-1.0.7.dist-info/RECORD +133 -0
- quantbt_engine-1.0.7.dist-info/WHEEL +5 -0
- quantbt_engine-1.0.7.dist-info/licenses/LICENSE +21 -0
- quantbt_engine-1.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantbt-engine
|
|
3
|
+
Version: 1.0.7
|
|
4
|
+
Summary: Transparent, high-performance quantitative backtesting engine
|
|
5
|
+
Author-email: Vu Cong Minh <vugioan11022002@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/BobbyAxerol/quantbt
|
|
8
|
+
Project-URL: Repository, https://github.com/BobbyAxerol/quantbt
|
|
9
|
+
Project-URL: Issues, https://github.com/BobbyAxerol/quantbt/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/BobbyAxerol/quantbt/tree/main/docs
|
|
11
|
+
Project-URL: Changelog, https://github.com/BobbyAxerol/quantbt/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: backtesting,quant,trading,numba,portfolio,event-driven
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: <3.14,>=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: numpy<2.3,>=2.2.6
|
|
27
|
+
Requires-Dist: pandas<2.4,>=2.3.3
|
|
28
|
+
Requires-Dist: numba<0.66,>=0.65.1
|
|
29
|
+
Provides-Extra: optimization
|
|
30
|
+
Requires-Dist: optuna<4.9,>=4.8.0; extra == "optimization"
|
|
31
|
+
Requires-Dist: arch<8.1,>=8.0.0; extra == "optimization"
|
|
32
|
+
Requires-Dist: scikit-learn<1.9,>=1.8.0; extra == "optimization"
|
|
33
|
+
Provides-Extra: reports
|
|
34
|
+
Requires-Dist: quantstats==0.0.81; extra == "reports"
|
|
35
|
+
Provides-Extra: viz
|
|
36
|
+
Requires-Dist: matplotlib<3.11,>=3.10.9; extra == "viz"
|
|
37
|
+
Requires-Dist: seaborn<0.14,>=0.13.2; extra == "viz"
|
|
38
|
+
Provides-Extra: validation
|
|
39
|
+
Requires-Dist: nautilus-trader<1.231,>=1.230.0; python_version >= "3.12" and extra == "validation"
|
|
40
|
+
Provides-Extra: native
|
|
41
|
+
Provides-Extra: all
|
|
42
|
+
Requires-Dist: optuna<4.9,>=4.8.0; extra == "all"
|
|
43
|
+
Requires-Dist: arch<8.1,>=8.0.0; extra == "all"
|
|
44
|
+
Requires-Dist: scikit-learn<1.9,>=1.8.0; extra == "all"
|
|
45
|
+
Requires-Dist: quantstats==0.0.81; extra == "all"
|
|
46
|
+
Requires-Dist: matplotlib<3.11,>=3.10.9; extra == "all"
|
|
47
|
+
Requires-Dist: seaborn<0.14,>=0.13.2; extra == "all"
|
|
48
|
+
Requires-Dist: nautilus-trader<1.231,>=1.230.0; python_version >= "3.12" and extra == "all"
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+
# QuantBT
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+

|
|
55
|
+

|
|
56
|
+

|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
QuantBT is a transparent, high-speed backtesting toolkit for crypto,
|
|
60
|
+
multi-symbol portfolios, systematic alphas, and execution validation.
|
|
61
|
+
|
|
62
|
+
It gives notebooks and services one stable endpoint over several simulation
|
|
63
|
+
layers: fast native vectorized research, native event-driven order/fill
|
|
64
|
+
simulation, legacy-compatible portfolio modes, walk-forward optimization, and
|
|
65
|
+
optional NautilusTrader validation for third-party execution/accounting checks.
|
|
66
|
+
|
|
67
|
+
The goal is simple: make alpha research fast enough for iteration, strict
|
|
68
|
+
enough for institutional-style review, and readable enough that stakeholders can
|
|
69
|
+
audit how a result was produced.
|
|
70
|
+
|
|
71
|
+
Current portfolio status: `native_portfolio` is the default multi-symbol
|
|
72
|
+
backend. It supports long/short, market-neutral, directional, equal-weight,
|
|
73
|
+
risk-parity, and beta-neutral portfolio modes, with live-equity sizing and
|
|
74
|
+
target exposure contracts. The legacy portfolio route remains available for
|
|
75
|
+
historical reproduction.
|
|
76
|
+
|
|
77
|
+
## Why QuantBT
|
|
78
|
+
|
|
79
|
+
- One public API for notebooks, services, portfolio research, and validation.
|
|
80
|
+
- Native vectorized engines for fast sweeps and large parameter grids.
|
|
81
|
+
- Native event-driven engines for market/limit orders, fills, baskets, and
|
|
82
|
+
arbitrage package execution.
|
|
83
|
+
- Prepared service contexts for repeated signal/portfolio replays on the same
|
|
84
|
+
market tape without re-normalizing pandas data each run.
|
|
85
|
+
- Native portfolio engine with target weights, target notionals, target units,
|
|
86
|
+
gross/net exposure, risk parity, beta neutrality, margin reports, and
|
|
87
|
+
per-symbol attribution.
|
|
88
|
+
- Optional NautilusTrader adapter for independent event-driven validation.
|
|
89
|
+
- Nautilus explicit order replay for single-symbol `OrderIntent` validation.
|
|
90
|
+
- Explicit margin, leverage, fees, slippage, funding, and liquidation handling.
|
|
91
|
+
- Stable audit artifacts: metrics, plots, raw reports, trade logs, config JSON,
|
|
92
|
+
run manifest, and optional QuantStats HTML.
|
|
93
|
+
- Nautilus certification bundles with parity CSVs, tolerance profiles, known
|
|
94
|
+
differences, and explicit skip/pass status for optional trustee workflows.
|
|
95
|
+
- Nautilus package-depth preflight with OHLCV volume caps and synthetic-book
|
|
96
|
+
stress tests for spread, queue, participation, partial-fill, and package
|
|
97
|
+
rejection assumptions.
|
|
98
|
+
- Walk-forward and train/test optimization designed to avoid leaking OOS data
|
|
99
|
+
into parameter selection, plus full-sample robust calibration for final
|
|
100
|
+
production parameter discovery.
|
|
101
|
+
- Domain-agnostic Optuna optimization adapters for prepared signal, intrabar,
|
|
102
|
+
portfolio, and generic endpoint workflows.
|
|
103
|
+
|
|
104
|
+
## Event-Driven Quick Start
|
|
105
|
+
|
|
106
|
+
New event-driven integrations should use the stable facade. Choose an input
|
|
107
|
+
mode, a retention profile, and a public backend; the facade keeps matching,
|
|
108
|
+
fills, fees, slippage, margin, funding, and PnL in the existing native-event
|
|
109
|
+
engine.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from quantbt import QuantBTEndpoint
|
|
113
|
+
|
|
114
|
+
bt = QuantBTEndpoint.event_driven(
|
|
115
|
+
input_mode="strategy", # strategy | orders
|
|
116
|
+
profile="research", # research | optimize | audit
|
|
117
|
+
backend="auto", # auto | python | rust
|
|
118
|
+
initial_capital=20_000,
|
|
119
|
+
leverage=5,
|
|
120
|
+
fee_rate=0.0005, # one-way fee per fill
|
|
121
|
+
slippage_bps=2.0,
|
|
122
|
+
use_funding=False,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
result = bt.simulate(data=df, strategy=strategy, symbols=["BTCUSDT"])
|
|
126
|
+
bt.show_metrics()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use `profile="research"` for compact notebook results, `"optimize"` for
|
|
130
|
+
scalar parameter-search results, and `"audit"` for replay-certified fills and
|
|
131
|
+
event artifacts. For an upstream order planner, switch to
|
|
132
|
+
`input_mode="orders"` and pass `order_commands=[...]`. The default `auto`
|
|
133
|
+
backend follows the release policy; `rust` is an explicit request for the
|
|
134
|
+
optional capability-gated native wheel.
|
|
135
|
+
|
|
136
|
+
The strategy owns signal generation and look-ahead control. QuantBT owns the
|
|
137
|
+
causal order lifecycle and accounting. Advanced users can still call
|
|
138
|
+
`native_event_strategy(...)` or `native_event_lifecycle(...)` directly when a
|
|
139
|
+
custom low-level execution/report combination is required.
|
|
140
|
+
|
|
141
|
+
See [`docs/endpoint.md`](docs/endpoint.md#stable-event-driven-facade) for the
|
|
142
|
+
full strategy protocol, profile matrix, explicit-order example, conflict
|
|
143
|
+
rules, and migration guidance. The broader endpoint map is in
|
|
144
|
+
[`docs/README.md`](docs/README.md).
|
|
145
|
+
|
|
146
|
+
## Performance Philosophy
|
|
147
|
+
|
|
148
|
+
QuantBT is built for research loops where speed matters as much as accounting
|
|
149
|
+
clarity. The public API stays Pythonic, but the heavy computation path is pushed
|
|
150
|
+
toward NumPy/Numba kernels so large signal matrices, parameter sweeps,
|
|
151
|
+
walk-forward runs, and multi-symbol portfolios can run close to native compiled
|
|
152
|
+
performance without forcing researchers into a C++ or C# codebase.
|
|
153
|
+
|
|
154
|
+
The intent is not to be a black-box replacement for mature execution engines.
|
|
155
|
+
LEAN/QuantConnect brings a large C# institutional platform, NautilusTrader
|
|
156
|
+
brings a Rust-backed event-driven trading stack, vectorbt is excellent for
|
|
157
|
+
vectorized research, and Backtrader remains a widely used Python event-driven
|
|
158
|
+
framework. QuantBT sits between those worlds: fast native-vectorized and
|
|
159
|
+
Numba-accelerated research paths for iteration, native event simulation for
|
|
160
|
+
transparent order accounting, and optional Nautilus validation when a run needs
|
|
161
|
+
third-party execution evidence.
|
|
162
|
+
|
|
163
|
+
Benchmarks are versioned under `benchmarks/` rather than hidden in marketing
|
|
164
|
+
claims. Phase 7 currently measures bars x symbols, order count, event count,
|
|
165
|
+
warmup/compile time, runtime, memory, throughput, and threshold pass/fail across
|
|
166
|
+
native vectorized, native event, portfolio, and optional Nautilus routes. The
|
|
167
|
+
rule is simple: keep hot loops near C/C++-style runtime with Numba first, profile
|
|
168
|
+
before optimizing, and only consider Cython/C++ when a proven hotspot cannot be
|
|
169
|
+
fixed safely in the Python/Numba stack.
|
|
170
|
+
|
|
171
|
+
Latest Phase 7 standard runtime benchmark after the Phase 11E native portfolio
|
|
172
|
+
default switch on this workspace:
|
|
173
|
+
|
|
174
|
+
| Route | Workload | Runtime | Throughput | Peak Memory | Threshold |
|
|
175
|
+
|---|---:|---:|---:|---:|---|
|
|
176
|
+
| `native_vectorized` | 25,000 bars x 20 symbols | 0.463s | 1,079,402 bar-symbols/s | 145.3 MB | pass |
|
|
177
|
+
| `native_event` | 25,000 explicit orders, cold preparation | 3.041s | 164,442 events/s | 99.4 MB | threshold miss; profiling target |
|
|
178
|
+
| `native_event_prepared` | 25,000 explicit orders, prepared replay | 1.621s | 308,545 events/s | 30.4 MB | threshold miss; faster reuse path |
|
|
179
|
+
| `portfolio_legacy` | 25,000 bars x 20 symbols | 1.669s | 299,575 bar-symbols/s | 236.3 MB | threshold miss |
|
|
180
|
+
| `native_portfolio` | 25,000 bars x 20 symbols | 1.750s | 285,724 bar-symbols/s | 236.1 MB | default; full audit/report route |
|
|
181
|
+
| `nautilus` | optional validation route | skipped | - | - | run with `--include-nautilus` |
|
|
182
|
+
|
|
183
|
+
The portfolio numbers measure the full facade with diagnostics, exposure
|
|
184
|
+
reports, per-symbol attribution, contract validation, and equity-aware sizing.
|
|
185
|
+
They are intentionally treated as correctness-first default routes; pure kernel
|
|
186
|
+
and reporting-layer profiling remains the next speed follow-up before any
|
|
187
|
+
Cython/C++ work. See `benchmarks/phase9_optimization_report.md`,
|
|
188
|
+
`benchmarks/phase7_profile_report.md`, and
|
|
189
|
+
`benchmarks/portfolio_real_parity_report.md` for parity and optimization
|
|
190
|
+
history.
|
|
191
|
+
|
|
192
|
+
Latest Phase 14C service-loop optimization benchmark:
|
|
193
|
+
|
|
194
|
+
| Workload | Cold / full route | Prepared / minimal route | Speedup | Parity |
|
|
195
|
+
|---|---:|---:|---:|---|
|
|
196
|
+
| Single-symbol WFO | 1.007s | 0.883s | 1.14x | pass |
|
|
197
|
+
| Portfolio WFO | 0.440s | 0.326s | 1.35x | pass |
|
|
198
|
+
| Native-event order replay | 0.0096s | 0.0046s | 2.10x | pass |
|
|
199
|
+
| Arbitrage package replay | 0.0746s | 0.0663s | 1.13x | pass |
|
|
200
|
+
| Native portfolio reports | 0.0410s full | 0.0233s minimal | 1.76x | pass |
|
|
201
|
+
|
|
202
|
+
Phase 14C added run-local prepared market-array reuse for WFO/service loops and
|
|
203
|
+
`report_level="full" | "standard" | "minimal"` for native portfolio reports.
|
|
204
|
+
The default remains `full`; lighter report levels are opt-in for optimizers and
|
|
205
|
+
services, and parity tests lock core accounting equality before any speed claim.
|
|
206
|
+
|
|
207
|
+
Latest Phase 16 service-context closure benchmark:
|
|
208
|
+
|
|
209
|
+
| Workload | Normal endpoint | Prepared context | Speedup | Parity |
|
|
210
|
+
|---|---:|---:|---:|---|
|
|
211
|
+
| Single-symbol signal_notional replays | 0.0711s | 0.0390s | 1.82x | pass |
|
|
212
|
+
| Native portfolio replays | 0.3115s | 0.0695s | 4.48x | pass |
|
|
213
|
+
| Native portfolio reports | 0.0755s full | 0.0394s minimal | 1.92x | pass |
|
|
214
|
+
|
|
215
|
+
Phase 16 adds `endpoint.prepare_service_context(...)`, an opt-in helper for
|
|
216
|
+
services that replay many signals or position matrices against one fixed market
|
|
217
|
+
tape. Normal `.backtest(...)` remains defensive and backward-compatible.
|
|
218
|
+
Cython/C++ remains deferred because the larger benchmark still points to
|
|
219
|
+
facade/report overhead rather than pure Numba kernels.
|
|
220
|
+
|
|
221
|
+
Latest Phase 31 intrabar execution benchmark:
|
|
222
|
+
|
|
223
|
+
| Route | Workload | Runtime | Throughput | Ratio | Parity |
|
|
224
|
+
|---|---:|---:|---:|---:|---|
|
|
225
|
+
| `close_target_v2_pure_kernel` | 25,000 bars | 0.0087s | 2,879,313 bars/s | baseline | baseline |
|
|
226
|
+
| `intrabar_bracket_v1_minimal` | 25,000 bars, 2,000 fills | 0.0118s | 2,115,865 bars/s | 1.36x close-target | oracle-checked |
|
|
227
|
+
| `intrabar_bracket_v1_audit` | 25,000 bars, fill ledger | 0.0527s | 474,427 bars/s | 4.46x minimal | pass |
|
|
228
|
+
| `intrabar_session_bracket_v1_minimal` | 25,000 bars, session state | 0.0117s | 2,145,495 bars/s | 0.99x minimal | reference-checked |
|
|
229
|
+
| `intrabar_session_bracket_v1_audit` | 25,000 bars, session ledger | 0.0499s | 501,156 bars/s | 4.22x minimal | pass |
|
|
230
|
+
| `intrabar_reference_python` | 25,000 bars | 0.2394s | 104,419 bars/s | 20.26x slower than minimal | truth model |
|
|
231
|
+
| `fill_replay_v1_kernel` | 25,000 bars, 2,000 fills | 0.0124s | 2,013,664 bars/s | 1.05x minimal | accounting |
|
|
232
|
+
| `native_event_explicit_orders_facade` | 25,000 bars, 2,000 market orders | 0.0761s | 328,618 bars/s | 6.44x minimal | speed reference |
|
|
233
|
+
|
|
234
|
+
Phase 31 adds execution-contract certification for close-target, fast intrabar
|
|
235
|
+
SL/TP/trailing, optional session-aware intraday execution state, and explicit
|
|
236
|
+
fill replay paths. The fast intrabar kernel is about 20.3x faster than the
|
|
237
|
+
readable Python oracle on the committed benchmark; the session kernel keeps the
|
|
238
|
+
non-session hot path separate while adding entry windows, EOD force-flat,
|
|
239
|
+
per-session quota, stale-signal cancellation, and re-entry suppression.
|
|
240
|
+
|
|
241
|
+
Latest Phase 32C optimization overhead benchmark:
|
|
242
|
+
|
|
243
|
+
| Measurement | Result |
|
|
244
|
+
|---|---:|
|
|
245
|
+
| Optimizer overhead | 0.0174s for 24 trials |
|
|
246
|
+
| Optimizer overhead / trial | 0.000723s |
|
|
247
|
+
| Prepared signal evaluator | 2.03x faster than normal endpoint replay |
|
|
248
|
+
| Intrabar first vs warm run | 3.70x first/warm ratio |
|
|
249
|
+
| Parity | pass, final equity diff 0.0 |
|
|
250
|
+
|
|
251
|
+
Phase 32C consolidates safe walk-forward optimization primitives with the new
|
|
252
|
+
domain-agnostic optimizer core while keeping WFO fold isolation and robust
|
|
253
|
+
selection semantics inside `walkforward.py`. Read
|
|
254
|
+
[`docs/optimization.md`](docs/optimization.md) and
|
|
255
|
+
`benchmarks/results/optimization_overhead.md` for signal, intrabar, portfolio,
|
|
256
|
+
arbitrage/grid/options fallback examples and benchmark details.
|
|
257
|
+
|
|
258
|
+
### Phase 46F package and dual-backend release evidence
|
|
259
|
+
|
|
260
|
+
The core distribution is packaged as `quantbt-engine` and imports as
|
|
261
|
+
`quantbt`. Its release gate is independent from the optional experimental Rust
|
|
262
|
+
wheel:
|
|
263
|
+
|
|
264
|
+
| Release artifact | Current status | Backend policy |
|
|
265
|
+
|---|---|---|
|
|
266
|
+
| `quantbt-engine==1.0.7` wheel/sdist | release-ready after local/TestPyPI approval | Python canonical; all existing endpoints remain available |
|
|
267
|
+
| `quantbt-native` PyO3 wheel | experimental, not published | explicit `native_backend="rust"` only |
|
|
268
|
+
| `quantbt-engine[native]` | intentionally empty | no dependency is advertised before native certification |
|
|
269
|
+
|
|
270
|
+
The committed Phase 46F rerun compares the same prepared static tape and keeps
|
|
271
|
+
Python/Rust accounting parity at 100%:
|
|
272
|
+
|
|
273
|
+
| Workload | Python median | Rust median | Python throughput | Rust throughput | Peak RSS | Parity |
|
|
274
|
+
|---|---:|---:|---:|---:|---:|---|
|
|
275
|
+
| Low churn, 2,000 bars | 20.33 ms | 0.109 ms | 98,385 bars/s | 18.30M bars/s | 181.97 MB | pass |
|
|
276
|
+
| High churn, 2,000 bars | 36.16 ms | 0.140 ms | 55,308 bars/s | 14.33M bars/s | 181.94 MB | pass |
|
|
277
|
+
| Prepared RSS reduction | - | - | - | - | -26.1% / -7.6%; absolute budget pass | gate fail |
|
|
278
|
+
|
|
279
|
+
These are score-kernel measurements, not claims about full facade/report
|
|
280
|
+
runtime. The table reports raw median time and bars/second from five warmed
|
|
281
|
+
repetitions so the result is readable without an internal speedup convention.
|
|
282
|
+
The earlier Phase 45F end-to-end reference is retained in the JSON evidence
|
|
283
|
+
for historical comparison. The evidence files are
|
|
284
|
+
[`phase46e_release_gate.json`](benchmarks/native_event/phase46e_release_gate.json),
|
|
285
|
+
[`phase46f_release_gate.json`](benchmarks/native_event/phase46f_release_gate.json),
|
|
286
|
+
[`phase46d1_score_rss.json`](benchmarks/native_event/phase46d1_score_rss.json),
|
|
287
|
+
and [`phase45f_release_gate.json`](benchmarks/native_event/phase45f_release_gate.json).
|
|
288
|
+
|
|
289
|
+
Phase 47C Grid integration evidence uses the external read-only Grid alpha on
|
|
290
|
+
the same deterministic 2,000-bar tape in both long-only and long-short modes:
|
|
291
|
+
|
|
292
|
+
| Mode | Python scalar median | Rust scalar median | Python peak RSS | Rust peak RSS | Fingerprint parity |
|
|
293
|
+
|---|---:|---:|---:|---:|---|
|
|
294
|
+
| Long-only | 1.138 s | 1.245 s | 265.6 MB | 273.2 MB | pass |
|
|
295
|
+
| Long-short | 1.846 s | 1.985 s | 291.1 MB | 293.4 MB | pass |
|
|
296
|
+
|
|
297
|
+
These are full reactive facade measurements, not pure Rust kernel claims. Rust
|
|
298
|
+
is currently slightly slower on this Grid integration but produces the same
|
|
299
|
+
command/fill/accounting fingerprint and is explicit fail-fast; `auto` remains
|
|
300
|
+
Python. The benchmark runner, five-run RSS slope gate, and scalar/audit
|
|
301
|
+
fingerprint contract are documented in
|
|
302
|
+
[`docs/grid_native_event_phase47c.md`](docs/grid_native_event_phase47c.md),
|
|
303
|
+
with raw JSON under `benchmarks/native_event/results/phase47c/`. The RSS
|
|
304
|
+
figures are the current Grid facade evidence; they are not compared directly
|
|
305
|
+
to the older ~180 MB core-process profile without a like-for-like baseline.
|
|
306
|
+
|
|
307
|
+
### Phase 47D Grid optimizer evidence
|
|
308
|
+
|
|
309
|
+
Phase 47D profiles the real prepared Grid optimizer path by separating alpha
|
|
310
|
+
preparation, strategy construction, engine score, and public report work. The
|
|
311
|
+
safe patch removes per-bar Grid diagnostics and diagnostic alias columns only
|
|
312
|
+
from scalar trials, while public/audit defaults remain unchanged. On the same
|
|
313
|
+
2,000-bar deterministic tape:
|
|
314
|
+
|
|
315
|
+
| Grid mode | Python scalar | Rust scalar | Python throughput | Rust throughput | Peak RSS Python/Rust | Parity |
|
|
316
|
+
|---|---:|---:|---:|---:|---:|---|
|
|
317
|
+
| Long-only | 0.850 s | 1.086 s | 2,354 bars/s | 1,842 bars/s | 265.4 / 271.2 MB | pass |
|
|
318
|
+
| Long-short | 1.412 s | 1.831 s | 1,416 bars/s | 1,092 bars/s | 291.0 / 293.6 MB | pass |
|
|
319
|
+
|
|
320
|
+
The apples-to-apples prepared scalar profile measured `0.813s` in the local
|
|
321
|
+
five-repeat profile. The timing breakdown shows the reactive engine callback
|
|
322
|
+
at about `97.9%` and alpha preparation at about `2.2%`, so an indicator cache
|
|
323
|
+
was deliberately not added. This evidence does not claim that Rust is faster
|
|
324
|
+
for the Python reactive Grid facade; Rust remains explicit experimental and
|
|
325
|
+
`auto` remains Python. See
|
|
326
|
+
[`docs/grid_native_event_phase47c.md`](docs/grid_native_event_phase47c.md)
|
|
327
|
+
for the scalar retention contract, RSS interpretation, and remaining debt.
|
|
328
|
+
Raw Phase 47D artifacts are kept under
|
|
329
|
+
`benchmarks/native_event/results/phase47d/`.
|
|
330
|
+
|
|
331
|
+
### Phase 48F final release handoff
|
|
332
|
+
|
|
333
|
+
The core `quantbt-engine` 1.0.7 artifact gate is now implemented locally and
|
|
334
|
+
in the release workflows: exact version/ref validation, wheel and sdist
|
|
335
|
+
`twine check`, archive allowlist and secret scan, clean import plus `pip check`,
|
|
336
|
+
and a SHA256 release manifest. The TestPyPI workflow is manual and OIDC
|
|
337
|
+
protected; it must be run with an unused matching RC version/tag and reviewed
|
|
338
|
+
before production PyPI publication. See
|
|
339
|
+
[`docs/testpypi_release_checklist.md`](docs/testpypi_release_checklist.md).
|
|
340
|
+
`quantbt-native` is intentionally excluded from this core upload, `auto`
|
|
341
|
+
remains Python, and explicit Rust remains capability-gated.
|
|
342
|
+
|
|
343
|
+
### Phase 48C stable event-driven facade evidence
|
|
344
|
+
|
|
345
|
+
The stable `QuantBTEndpoint.event_driven()` facade was benchmarked on the same
|
|
346
|
+
deterministic **2,000-bar** single-symbol baseline as the direct native-event
|
|
347
|
+
strategy constructor. Each route ran in a fresh process with five measured
|
|
348
|
+
repetitions. The Grid workload is reported separately because indicator
|
|
349
|
+
preparation and reactive state-machine work are part of its runtime.
|
|
350
|
+
|
|
351
|
+
| Common route | Median runtime | Throughput | Peak RSS | Fills | Final Equity | Parity |
|
|
352
|
+
|---|---:|---:|---:|---:|---:|---|
|
|
353
|
+
| `native_event_strategy` | 161.20 ms | 12,407 bars/s | 184.2 MB | 109 | 19,998.269072 | baseline |
|
|
354
|
+
| `event_driven(profile="research")` | 154.54 ms | 12,942 bars/s | 183.4 MB | 109 | 19,998.269072 | pass |
|
|
355
|
+
|
|
356
|
+
Separate reactive Grid benchmark on 2,000 bars:
|
|
357
|
+
|
|
358
|
+
| Grid route | Median runtime | Throughput | Peak RSS | Fills | Final Equity | Parity |
|
|
359
|
+
|---|---:|---:|---:|---:|---:|---|
|
|
360
|
+
| direct `native_event_strategy` | 1.4187 s | 1,410 bars/s | 274.5 MB | 839 | 28,972.788456 | baseline |
|
|
361
|
+
| `event_driven(profile="audit")` | 1.3986 s | 1,430 bars/s | 274.5 MB | 839 | 28,972.788456 | pass |
|
|
362
|
+
|
|
363
|
+
Both comparisons have identical accounting fingerprints, including equity,
|
|
364
|
+
positions, fees, funding, margin, lifecycle counters, fills, and liquidation
|
|
365
|
+
state. The facade adds no second execution loop; the small runtime difference
|
|
366
|
+
is measurement noise and configuration resolution. Reproduce with
|
|
367
|
+
`benchmarks/benchmark_phase48c_event_driven.py`; raw evidence is in
|
|
368
|
+
[`phase48c_event_driven_facade.md`](benchmarks/phase48c_event_driven_facade.md)
|
|
369
|
+
and [`phase48c_event_driven_facade.json`](benchmarks/phase48c_event_driven_facade.json).
|
|
370
|
+
|
|
371
|
+
The release workflow is documented in
|
|
372
|
+
[`docs/release_packaging.md`](docs/release_packaging.md): build and inspect
|
|
373
|
+
wheel/sdist, run clean-install and `pip check`, publish an RC to TestPyPI with
|
|
374
|
+
OIDC, then publish the final core package through the protected PyPI
|
|
375
|
+
environment. The exact handoff fields and artifact-hash procedure are in
|
|
376
|
+
[`docs/testpypi_release_checklist.md`](docs/testpypi_release_checklist.md).
|
|
377
|
+
No long-lived token is required. Native optimization remains an
|
|
378
|
+
open, domain-preserving roadmap for portfolio, arbitrage, options, vectorized,
|
|
379
|
+
intrabar, and Nautilus adapter workloads; each future route needs its own
|
|
380
|
+
parity and RSS certification.
|
|
381
|
+
|
|
382
|
+
### Pre-48E apples-to-apples native event evidence
|
|
383
|
+
|
|
384
|
+
The native-event headline below uses one deterministic **2,000-bar**,
|
|
385
|
+
single-symbol tape, a fresh process per route, the same compiled command tape,
|
|
386
|
+
separate score/audit runs, and seven measured warm repetitions. Runtime is in
|
|
387
|
+
seconds, throughput is bars per second, and RSS is peak resident memory. Every
|
|
388
|
+
Python/Rust score and audit fingerprint passed accounting and lifecycle parity
|
|
389
|
+
(equity, positions, fees, funding, margin, fills, and event counters).
|
|
390
|
+
|
|
391
|
+
| Workload | Route | Runtime s | Throughput | Peak RSS MB | Parity |
|
|
392
|
+
|---|---|---:|---:|---:|---|
|
|
393
|
+
| Common low churn | Python score | 0.087736 | 22,796 bars/s | 183.2 | pass |
|
|
394
|
+
| Common low churn | Rust score | 0.188448 | 10,613 bars/s | 185.7 | pass |
|
|
395
|
+
| Common low churn | Python audit | 0.087327 | 22,902 bars/s | 240.8 | pass |
|
|
396
|
+
| Common low churn | Rust audit | 0.176075 | 11,359 bars/s | 243.9 | pass |
|
|
397
|
+
| Common high churn | Python score | 0.086609 | 23,092 bars/s | 182.9 | pass |
|
|
398
|
+
| Common high churn | Rust score | 0.188299 | 10,621 bars/s | 186.1 | pass |
|
|
399
|
+
| Common high churn | Python audit | 0.119269 | 16,769 bars/s | 241.2 | pass |
|
|
400
|
+
| Common high churn | Rust audit | 0.198521 | 10,074 bars/s | 243.1 | pass |
|
|
401
|
+
|
|
402
|
+
The safe Python patch improved the common low-churn score from `0.148483s`
|
|
403
|
+
to `0.087736s` on the frozen pre-patch baseline, without skipping any domain
|
|
404
|
+
accounting or quantity preflight when constraints are enabled. Explicit order
|
|
405
|
+
and Rust full-tape results are also recorded, but they are kept as route-level
|
|
406
|
+
evidence rather than used to imply that every reactive strategy is faster in
|
|
407
|
+
Rust. Reactive Grid has a separate workload and remains outside this common
|
|
408
|
+
native-event headline.
|
|
409
|
+
|
|
410
|
+
Reproduce the gate with
|
|
411
|
+
[`benchmark_pre48e.py`](benchmarks/native_event/benchmark_pre48e.py). Read the
|
|
412
|
+
full before/after table and parity fingerprints in
|
|
413
|
+
[`pre48e/report.md`](benchmarks/native_event/results/pre48e/report.md); the
|
|
414
|
+
raw JSON artifacts are versioned beside it.
|
|
415
|
+
|
|
416
|
+
### Phase 48E native-event boundary evidence
|
|
417
|
+
|
|
418
|
+
The Phase 48E rerun keeps the same 2,000-bar tape, seven warm repetitions,
|
|
419
|
+
fresh-process routes, separate score/audit profiles, and `atol <= 1e-12`
|
|
420
|
+
accounting parity. The full raw result is in
|
|
421
|
+
[`phase48e/after.md`](benchmarks/native_event/results/phase48e/after.md).
|
|
422
|
+
The common rows are the comparable native-event/event-driven workload; the
|
|
423
|
+
explicit rows are a separate compiled-tape workload and must not be read as a
|
|
424
|
+
claim that Rust is faster for every Python callback strategy.
|
|
425
|
+
|
|
426
|
+
| Workload | Route | Runtime s | Throughput | Peak RSS MB | Parity |
|
|
427
|
+
|---|---|---:|---:|---:|---|
|
|
428
|
+
| Common low churn | Python score | 0.094448 | 21,176 bars/s | 182.0 | pass |
|
|
429
|
+
| Common low churn | Rust score | 0.179506 | 11,142 bars/s | 183.9 | pass |
|
|
430
|
+
| Common low churn | Python audit | 0.093893 | 21,301 bars/s | 239.0 | pass |
|
|
431
|
+
| Common low churn | Rust audit | 0.178550 | 11,201 bars/s | 242.6 | pass |
|
|
432
|
+
| Common high churn | Python score | 0.107369 | 18,627 bars/s | 183.5 | pass |
|
|
433
|
+
| Common high churn | Rust score | 0.188549 | 10,607 bars/s | 185.2 | pass |
|
|
434
|
+
| Common high churn | Python audit | 0.106375 | 18,801 bars/s | 241.1 | pass |
|
|
435
|
+
| Common high churn | Rust audit | 0.208654 | 9,585 bars/s | 241.3 | pass |
|
|
436
|
+
|
|
437
|
+
Phase 48E also reduced the static explicit Rust score to `0.000302s`
|
|
438
|
+
(`6,614,704 bars/s`) on the low-churn tape and `0.000392s`
|
|
439
|
+
(`5,103,342 bars/s`) on the high-churn tape. Those numbers benefit from the
|
|
440
|
+
scalar Rust output contract and prepared command-tape reuse, so they are
|
|
441
|
+
reported separately from callback execution. Rust and Python fingerprints,
|
|
442
|
+
fees, positions, fills, events, rejection counters, and final equity passed.
|
|
443
|
+
`backend="auto"` remains Python and `[native]` remains empty until the public
|
|
444
|
+
`quantbt-native` wheel matrix is clean-install certified.
|
|
445
|
+
|
|
446
|
+
### Phase 48E.1 native production-closure evidence
|
|
447
|
+
|
|
448
|
+
The Phase 48E.1 rerun uses the same isolated 2,000-bar tape, fresh subprocesses,
|
|
449
|
+
seven warm runs, separate score/audit routes, and exact Python/Rust fingerprints.
|
|
450
|
+
The complete report is [`phase48e1/after.md`](benchmarks/native_event/results/phase48e1/after.md).
|
|
451
|
+
This table separates the explicit prepared-tape path from the generic callback
|
|
452
|
+
facade; it is not a universal Rust speed claim.
|
|
453
|
+
|
|
454
|
+
| Workload | Route | Runtime s | Throughput | Peak RSS MB | Parity |
|
|
455
|
+
|---|---|---:|---:|---:|---|
|
|
456
|
+
| Common low churn | Python score | 0.085853 | 23,296 bars/s | 182.2 | pass |
|
|
457
|
+
| Common low churn | Rust score | 0.218293 | 9,162 bars/s | 185.7 | pass |
|
|
458
|
+
| Common low churn | Python audit | 0.095110 | 21,028 bars/s | 239.4 | pass |
|
|
459
|
+
| Common low churn | Rust audit | 0.230769 | 8,667 bars/s | 242.1 | pass |
|
|
460
|
+
| Common high churn | Python score | 0.091562 | 21,843 bars/s | 182.1 | pass |
|
|
461
|
+
| Common high churn | Rust score | 0.222166 | 9,002 bars/s | 185.1 | pass |
|
|
462
|
+
| Common high churn | Python audit | 0.104712 | 19,100 bars/s | 239.6 | pass |
|
|
463
|
+
| Common high churn | Rust audit | 0.237654 | 8,416 bars/s | 241.4 | pass |
|
|
464
|
+
| Explicit low churn | Python score | 0.023777 | 84,114 bars/s | 180.4 | pass |
|
|
465
|
+
| Explicit low churn | Rust score | 0.000289 | 6,921,851 bars/s | 181.6 | pass |
|
|
466
|
+
| Explicit low churn | Python audit | 0.007385 | 270,814 bars/s | 237.7 | pass |
|
|
467
|
+
| Explicit low churn | Rust audit | 0.004357 | 459,060 bars/s | 182.0 | pass |
|
|
468
|
+
| Explicit high churn | Python score | 0.021689 | 92,214 bars/s | 180.2 | pass |
|
|
469
|
+
| Explicit high churn | Rust score | 0.000366 | 5,461,021 bars/s | 181.9 | pass |
|
|
470
|
+
| Explicit high churn | Python audit | 0.013703 | 145,952 bars/s | 239.6 | pass |
|
|
471
|
+
| Explicit high churn | Rust audit | 0.006469 | 309,174 bars/s | 183.1 | pass |
|
|
472
|
+
|
|
473
|
+
Phase 48E.1 also locks typed API 0.4 step results, count-only score sinks,
|
|
474
|
+
reusable SoA audit buffers, separate command/lifecycle/fill reports, compact
|
|
475
|
+
validated Rust order state, and reset/compaction parity. `auto` remains Python;
|
|
476
|
+
the native extra remains empty until the CPython 3.11/3.12/3.13 manylinux
|
|
477
|
+
clean-install workflow passes.
|
|
478
|
+
|
|
479
|
+
Ecosystem positioning:
|
|
480
|
+
|
|
481
|
+
| Tool | Core strength | Runtime model | QuantBT role beside it |
|
|
482
|
+
|---|---|---|---|
|
|
483
|
+
| QuantBT | transparent research, WFO, portfolio, arbitrage, validation endpoints | Python API with NumPy/Numba hot paths | primary alpha research and auditable simulation layer |
|
|
484
|
+
| LEAN / QuantConnect | large institutional C# platform and live/research ecosystem | C# engine | external benchmark for platform breadth, but heavier adapter work for custom notebooks |
|
|
485
|
+
| NautilusTrader | high-fidelity event-driven execution and accounting | Rust-backed trading stack | optional third-party trustee for execution/account validation |
|
|
486
|
+
| vectorbt | very fast vectorized research | NumPy/Numba vectorization | closest research-speed peer; QuantBT adds domain-specific accounting and validation routes |
|
|
487
|
+
| Backtrader | classic Python event-driven strategy simulation | Python event loop | useful reference style; QuantBT focuses on faster vectorized/event hybrid workflows |
|
|
488
|
+
|
|
489
|
+
## Engine Stack
|
|
490
|
+
|
|
491
|
+
| Layer | Backend | Best use case |
|
|
492
|
+
|---|---|---|
|
|
493
|
+
| Fast research | `native_vectorized` | broad sweeps, signal research, WFO scoring |
|
|
494
|
+
| Order simulation | `native_event` | explicit orders, fills, baskets, pair trades |
|
|
495
|
+
| Native portfolio | `native_portfolio` | default multi-symbol portfolio matrix with risk/exposure reports |
|
|
496
|
+
| Legacy compatibility | `legacy`, `legacy_portfolio` | historical reproduction and single-symbol legacy routes |
|
|
497
|
+
| Third-party validation | `nautilus` | smaller high-fidelity event-driven checks |
|
|
498
|
+
|
|
499
|
+
Use the native engines for research velocity. Use Nautilus when the result needs
|
|
500
|
+
an external event-driven accounting layer with raw order, fill, position, and
|
|
501
|
+
account reports.
|
|
502
|
+
|
|
503
|
+
## Features
|
|
504
|
+
|
|
505
|
+
### Single-Symbol Backtests
|
|
506
|
+
|
|
507
|
+
- `signal_notional`: fixed units between signal changes.
|
|
508
|
+
- `%_equity`: legacy equity-fraction sizing.
|
|
509
|
+
- `notional`, `unit`, and target-unit style execution through V2 engines.
|
|
510
|
+
- Margin, leverage, fee, slippage, funding, liquidation, and contract size
|
|
511
|
+
controls.
|
|
512
|
+
- `use_pyramiding=False` snaps signals to `-1/0/1`; `True` preserves fractional
|
|
513
|
+
scales such as `1.4`.
|
|
514
|
+
- For crypto, `contract_size` is a notional/PnL multiplier. Exchange fractional
|
|
515
|
+
lots are governed by shared venue constraints:
|
|
516
|
+
`qty_step`/`lot_size`/`slot_size`/`min_qty`/`min_notional`, applied across
|
|
517
|
+
native legacy, native vectorized, native event/order, native portfolio, and
|
|
518
|
+
Nautilus validation routes.
|
|
519
|
+
|
|
520
|
+
### DCA And Grid
|
|
521
|
+
|
|
522
|
+
- Structural DCA ladder signals where `0` is flat, `1` is base order, `2+` are
|
|
523
|
+
safety-order levels.
|
|
524
|
+
- High/low intrabar limit-touch detection.
|
|
525
|
+
- Fill at trigger/grid price rather than free-market close.
|
|
526
|
+
- Designed for DCA ladder and grid strategies where position is a structural
|
|
527
|
+
level, not a continuously rebalanced weight.
|
|
528
|
+
|
|
529
|
+
### Portfolio And Basket
|
|
530
|
+
|
|
531
|
+
- Multi-symbol portfolio endpoint over position matrices. The default backend
|
|
532
|
+
is `native_portfolio`; use `backend="legacy_portfolio"` only for historical
|
|
533
|
+
reproduction.
|
|
534
|
+
- Portfolio modes: `longshort`, `market_neutral`, `directional`,
|
|
535
|
+
`equal_weight`, `risk_parity`, and `beta_neutral`.
|
|
536
|
+
- Portfolio sizing: `signal_notional`, `%_equity`, `target_weight`,
|
|
537
|
+
`target_notional`, `target_units`, `fixed_notional`, `gross_exposure`, and
|
|
538
|
+
`net_exposure`.
|
|
539
|
+
- Basket and pair-trading endpoint with frozen hedge-ratio units.
|
|
540
|
+
- Native event engine support for package-style component orders.
|
|
541
|
+
|
|
542
|
+
### Arbitrage
|
|
543
|
+
|
|
544
|
+
Supported executable specs:
|
|
545
|
+
|
|
546
|
+
- `BasisArbitrageSpec`
|
|
547
|
+
- `StatArbPairSpec`
|
|
548
|
+
- `CalendarSpreadSpec`
|
|
549
|
+
- `FundingArbitrageSpec`
|
|
550
|
+
- `SpotPerpCashCarrySpec`
|
|
551
|
+
- `IndexBasketArbSpec`
|
|
552
|
+
|
|
553
|
+
Schema-only specs, reserved for specialized engines:
|
|
554
|
+
|
|
555
|
+
- `CrossExchangeArbSpec`
|
|
556
|
+
- `TriangularArbSpec`
|
|
557
|
+
- `OptionsVolArbSpec`
|
|
558
|
+
|
|
559
|
+
Use `QuantBTEndpoint.arbitrage_support_matrix()` to check safe routes before a
|
|
560
|
+
service creates a run.
|
|
561
|
+
|
|
562
|
+
### Walk-Forward Optimization
|
|
563
|
+
|
|
564
|
+
- Walk-forward split/stitch engine with OOS-scoped metrics.
|
|
565
|
+
- Single train/test split endpoint using the same optimization framework.
|
|
566
|
+
- Optuna-supported modes:
|
|
567
|
+
- `mode_1_decay`
|
|
568
|
+
- `mode_2_sbb`
|
|
569
|
+
- `mode_3_flat_minima`
|
|
570
|
+
- `mode_4_is_only_robust`
|
|
571
|
+
- `mode_5_full_robust`
|
|
572
|
+
- Endpoint-backed scoring for supported single-symbol modes, so objective
|
|
573
|
+
metrics match the actual QuantBT backtest route.
|
|
574
|
+
- Train-only robust candidate selection such as `is_plateau_robust` and
|
|
575
|
+
`is_only_robust`.
|
|
576
|
+
- Full-sample robust calibration selectors: `full_robust`,
|
|
577
|
+
`full_plateau_robust`, `full_temporal_robust`, and `full_best`.
|
|
578
|
+
- Optional trade-count penalty to avoid overfit low-trade Sharpe traps.
|
|
579
|
+
- Shared domain-agnostic optimizer primitives for search-space parsing,
|
|
580
|
+
duplicate detection, early stopping, objective helpers, constraints, and
|
|
581
|
+
candidate selection.
|
|
582
|
+
|
|
583
|
+
### Nautilus Validation Reports
|
|
584
|
+
|
|
585
|
+
The Nautilus adapter can validate single-symbol signal strategies with:
|
|
586
|
+
|
|
587
|
+
- `signal_notional`
|
|
588
|
+
- `notional`
|
|
589
|
+
- `unit`
|
|
590
|
+
- `%_equity`
|
|
591
|
+
|
|
592
|
+
It can also replay explicit single-symbol `OrderIntent` orders through
|
|
593
|
+
`QuantBTEndpoint.orders(backend="nautilus", ...)` for market, limit,
|
|
594
|
+
stop-market, and stop-limit order factory routes where Nautilus supports the
|
|
595
|
+
instrument/order combination.
|
|
596
|
+
|
|
597
|
+
For validation work, `build_native_nautilus_parity_report(native, nautilus)`
|
|
598
|
+
creates an audit table comparing requested order quantities, fill prices, fees,
|
|
599
|
+
positions, equity, and diffs between native event replay and Nautilus replay.
|
|
600
|
+
Use `QuantBTEndpoint.nautilus_support_matrix()` to inspect which Nautilus routes
|
|
601
|
+
are supported, experimental, or planned before wiring a service.
|
|
602
|
+
Experimental Nautilus package validation is available for DCA/grid,
|
|
603
|
+
bracket/OCO, basket, and portfolio workflows by compiling strategy state into
|
|
604
|
+
explicit order packages.
|
|
605
|
+
|
|
606
|
+
Package-depth validation is opt-in. `depth_model="ohlcv_volume_cap"` is the
|
|
607
|
+
default Level-1 preflight, `depth_model="synthetic_book"` creates deterministic
|
|
608
|
+
Level-2 stress books from spread/depth assumptions, and future
|
|
609
|
+
`depth_model="l2_replay"` is intentionally gated until real venue snapshots,
|
|
610
|
+
incremental updates, and trade prints are provided.
|
|
611
|
+
|
|
612
|
+
Supported Binance perpetual validation instruments:
|
|
613
|
+
|
|
614
|
+
`BTCUSDT-PERP.BINANCE`, `ETHUSDT-PERP.BINANCE`, `BNBUSDT-PERP.BINANCE`,
|
|
615
|
+
`SOLUSDT-PERP.BINANCE`, `DOGEUSDT-PERP.BINANCE`, `ARBUSDT-PERP.BINANCE`,
|
|
616
|
+
`LINKUSDT-PERP.BINANCE`.
|
|
617
|
+
|
|
618
|
+
The report bundle exports:
|
|
619
|
+
|
|
620
|
+
- `account_report.csv`
|
|
621
|
+
- `orders_report.csv`
|
|
622
|
+
- `fills_report.csv`
|
|
623
|
+
- `positions_report.csv`
|
|
624
|
+
- `trade_log.csv`
|
|
625
|
+
- `fill_log.txt`
|
|
626
|
+
- `equity_curve.csv`
|
|
627
|
+
- `returns.csv`
|
|
628
|
+
- `metrics_summary.json`
|
|
629
|
+
- `run_manifest.json`
|
|
630
|
+
- `config.json`
|
|
631
|
+
- optional `quantstats_daily.html`
|
|
632
|
+
|
|
633
|
+
These artifacts make the run easier to present to stakeholders: the metrics are
|
|
634
|
+
not just a final equity number, but an auditable trail from config to orders,
|
|
635
|
+
fills, positions, account state, and performance report.
|
|
636
|
+
|
|
637
|
+
## Install
|
|
638
|
+
|
|
639
|
+
Install the released core package:
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
pip install quantbt-engine==1.0.7
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
Optional reports and third-party validation:
|
|
646
|
+
|
|
647
|
+
```bash
|
|
648
|
+
pip install "quantbt-engine[reports,validation]==1.0.7"
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Development from this repository:
|
|
652
|
+
|
|
653
|
+
```bash
|
|
654
|
+
uv sync --extra optimization --extra reports --extra viz --dev
|
|
655
|
+
.venv/bin/python -m pytest -q --ignore=tests/test_real.py --ignore=tests/test_real_endpoints.py --ignore=tests/native_event
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
For core-only package/build validation, use the smaller dependency boundary
|
|
659
|
+
used by the native gate:
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
uv sync --dev
|
|
663
|
+
uv build
|
|
664
|
+
uv run twine check dist/*
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Pool Alpha and notebooks can continue using an editable checkout while a
|
|
668
|
+
feature is under development:
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
pip install -e /root/bobby/pool_alpha/quantbt
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
After the release is approved, downstream services should use
|
|
675
|
+
`pip install quantbt-engine==1.0.7` and keep the unchanged import
|
|
676
|
+
`from quantbt import QuantBTEndpoint`.
|
|
677
|
+
|
|
678
|
+
## Quick Start
|
|
679
|
+
|
|
680
|
+
```python
|
|
681
|
+
from quantbt import QuantBTEndpoint
|
|
682
|
+
|
|
683
|
+
bt = QuantBTEndpoint.signal_notional(
|
|
684
|
+
backend="native_vectorized",
|
|
685
|
+
initial_capital=20_000,
|
|
686
|
+
leverage=5,
|
|
687
|
+
alloc_per_trade=10_000,
|
|
688
|
+
fee_rate=0.0002,
|
|
689
|
+
slippage_bps=1.0,
|
|
690
|
+
use_funding=False,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
result = bt.backtest(
|
|
694
|
+
data=df, # OHLCV DataFrame
|
|
695
|
+
signal_col="pos_weight", # 1.0 / -1.0 / 0.0 style signal
|
|
696
|
+
symbols=["ETHUSDT"],
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
result.show_metrics()
|
|
700
|
+
result.quick_plot()
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Public Endpoint
|
|
704
|
+
|
|
705
|
+
```python
|
|
706
|
+
QuantBTEndpoint.pct_equity(...) # legacy % equity sizing
|
|
707
|
+
QuantBTEndpoint.signal_notional(...) # fixed units between signal changes
|
|
708
|
+
QuantBTEndpoint.dca_ladder(...) # DCA/grid structural levels
|
|
709
|
+
QuantBTEndpoint.orders(...) # explicit OrderIntent simulation
|
|
710
|
+
QuantBTEndpoint.nautilus_dca_grid(...) # Nautilus DCA/grid package validation
|
|
711
|
+
QuantBTEndpoint.nautilus_bracket_orders(...) # Nautilus bracket/OCO validation
|
|
712
|
+
QuantBTEndpoint.basket(...) # pair/basket event simulation
|
|
713
|
+
QuantBTEndpoint.arbitrage(...) # arbitrage spec execution
|
|
714
|
+
QuantBTEndpoint.portfolio(...) # multi-symbol portfolio matrix
|
|
715
|
+
QuantBTEndpoint.walk_forward(...) # walk-forward OOS stitching
|
|
716
|
+
QuantBTEndpoint.train_test_split(...) # single holdout split
|
|
717
|
+
QuantBTEndpoint.nautilus_validation(...) # optional Nautilus validation
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
The native portfolio parity audit is stored at
|
|
721
|
+
`benchmarks/portfolio_real_parity_report.md`.
|
|
722
|
+
|
|
723
|
+
## Nautilus Example
|
|
724
|
+
|
|
725
|
+
```python
|
|
726
|
+
from quantbt import QuantBTEndpoint, export_nautilus_report_bundle
|
|
727
|
+
from quantbt.adapters.nautilus import NautilusBackendConfig
|
|
728
|
+
|
|
729
|
+
bt = QuantBTEndpoint.nautilus_validation(
|
|
730
|
+
initial_capital=20_000,
|
|
731
|
+
leverage=5,
|
|
732
|
+
alloc_per_trade=0.5,
|
|
733
|
+
hedge_type="%_equity",
|
|
734
|
+
fee_rate=0.0005,
|
|
735
|
+
slippage=0.0002,
|
|
736
|
+
use_funding=False,
|
|
737
|
+
use_pyramiding=False,
|
|
738
|
+
nautilus_config=NautilusBackendConfig(
|
|
739
|
+
timeframe="1h",
|
|
740
|
+
starting_balance=20_000,
|
|
741
|
+
trade_notional=0.5,
|
|
742
|
+
close_positions_on_stop=False,
|
|
743
|
+
),
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
result = bt.simulate(
|
|
747
|
+
data=df,
|
|
748
|
+
signal_col="pos_weight",
|
|
749
|
+
symbols=["ETHUSDT-PERP.BINANCE"],
|
|
750
|
+
show_order_logs=True,
|
|
751
|
+
order_log_mode="fills_only",
|
|
752
|
+
order_log_limit=200,
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
result.show_metrics()
|
|
756
|
+
|
|
757
|
+
diag = bt.nautilus_pct_equity_diagnostic(
|
|
758
|
+
data=df,
|
|
759
|
+
signal_col="pos_weight",
|
|
760
|
+
native_fee_round_trip=0.0005,
|
|
761
|
+
native_use_funding=False,
|
|
762
|
+
native_slippage=0.0002,
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
report_dir = export_nautilus_report_bundle(
|
|
766
|
+
result=result,
|
|
767
|
+
output_dir="reports",
|
|
768
|
+
strategy_id="eth_validation",
|
|
769
|
+
make_quantstats=True,
|
|
770
|
+
quantstats_periods_per_year=365,
|
|
771
|
+
)
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
## Walk-Forward Example
|
|
775
|
+
|
|
776
|
+
```python
|
|
777
|
+
from quantbt import QuantBTEndpoint
|
|
778
|
+
|
|
779
|
+
wf = QuantBTEndpoint.train_test_split(
|
|
780
|
+
strategy_class=strategy,
|
|
781
|
+
test_start="2025-01-01",
|
|
782
|
+
target_mode="pct_equity",
|
|
783
|
+
optimization_mode="mode_1_decay",
|
|
784
|
+
optimization_config={
|
|
785
|
+
"scoring_backend": "endpoint",
|
|
786
|
+
"candidate_selection_metric": "is_plateau_robust",
|
|
787
|
+
"top_is_fraction": 0.10,
|
|
788
|
+
"min_trades_per_year": 100,
|
|
789
|
+
"trade_penalty_factor": 0.5,
|
|
790
|
+
"use_numba": True,
|
|
791
|
+
},
|
|
792
|
+
optuna_trials=300,
|
|
793
|
+
random_seed=42,
|
|
794
|
+
initial_capital=20_000,
|
|
795
|
+
leverage=5,
|
|
796
|
+
alloc_per_trade=0.5,
|
|
797
|
+
fee=0.0005,
|
|
798
|
+
slippage=0.0002,
|
|
799
|
+
use_pyramiding=False,
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
result = wf.backtest(data=df, param_ranges=param_ranges)
|
|
803
|
+
wf.show_metrics(scope="auto")
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
`scope="auto"` reports only the tested/OOS segment for walk-forward and
|
|
807
|
+
train/test runs. Pass `scope="full"` when you need to audit the full stitched
|
|
808
|
+
timeline.
|
|
809
|
+
|
|
810
|
+
## Result Helpers
|
|
811
|
+
|
|
812
|
+
```python
|
|
813
|
+
result.show_metrics()
|
|
814
|
+
result.full_report()
|
|
815
|
+
result.quick_plot()
|
|
816
|
+
result.tearsheet()
|
|
817
|
+
|
|
818
|
+
bt.order_report
|
|
819
|
+
bt.fills_report
|
|
820
|
+
bt.export_orders("orders.csv")
|
|
821
|
+
bt.export_fills("fills.csv")
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
Example console output:
|
|
825
|
+
|
|
826
|
+
```text
|
|
827
|
+
Initial Capital $ 20,000
|
|
828
|
+
Final Equity $ 106,884.96
|
|
829
|
+
Total Return +434.42%
|
|
830
|
+
CAGR +33.54%
|
|
831
|
+
Sharpe Ratio 1.981
|
|
832
|
+
Sortino Ratio 0.875
|
|
833
|
+
Calmar Ratio 2.885
|
|
834
|
+
Omega Ratio 2.190
|
|
835
|
+
Max Drawdown 11.63%
|
|
836
|
+
Profit Factor 2.190
|
|
837
|
+
Number of Trades 228
|
|
838
|
+
Liquidated No
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
## Documentation
|
|
842
|
+
|
|
843
|
+
Start with the [documentation map](docs/README.md) if you are deciding which
|
|
844
|
+
backend, endpoint, or strategy route to use.
|
|
845
|
+
|
|
846
|
+
| Need | Read |
|
|
847
|
+
|---|---|
|
|
848
|
+
| Public API contract for notebooks/services | [Endpoint contract](docs/endpoint.md) |
|
|
849
|
+
| Backend choice by strategy type | [Backend selection](docs/backend_selection.md) |
|
|
850
|
+
| Speed vs execution-fidelity tradeoff | [Vectorized vs event-driven](docs/vectorized_vs_event_driven.md) |
|
|
851
|
+
| Leverage, buying power, margin, liquidation | [Margin and leverage](docs/margin_leverage.md) |
|
|
852
|
+
| Market/limit/stop fill behavior | [Order fill policies](docs/order_fill_policies.md) |
|
|
853
|
+
| Nautilus validation and report bundles | [Nautilus backend](docs/nautilus_backend.md) |
|
|
854
|
+
| Pair, basket, hedge-ratio package behavior | [Pair and basket guide](docs/pair_basket_guide.md) |
|
|
855
|
+
| Walk-forward methodology and anti-leakage scoring | [Walk-forward methodology](docs/walkforward_methodology_vi.md) |
|
|
856
|
+
| Runnable smoke templates | [Examples index](examples/README.md) |
|
|
857
|
+
|
|
858
|
+
Key examples:
|
|
859
|
+
|
|
860
|
+
- [DCA/grid ladder](examples/dca_grid_ladder.py)
|
|
861
|
+
- [Multi-symbol portfolio](examples/multi_symbol_portfolio.py)
|
|
862
|
+
- [Pair/basket event package](examples/pair_basket_event.py)
|
|
863
|
+
- [Basis arbitrage](examples/arbitrage_basis.py)
|
|
864
|
+
- [Walk-forward train/test split](examples/walk_forward_train_test.py)
|
|
865
|
+
- [Nautilus validation](examples/nautilus_validation.py)
|
|
866
|
+
- [Nautilus explicit orders](examples/nautilus_explicit_orders.py)
|
|
867
|
+
|
|
868
|
+
## Development
|
|
869
|
+
|
|
870
|
+
```bash
|
|
871
|
+
uv sync --extra optimization --extra reports --extra viz --dev
|
|
872
|
+
.venv/bin/python -m pytest -q --ignore=tests/test_real.py --ignore=tests/test_real_endpoints.py --ignore=tests/native_event
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
Contribution workflow:
|
|
876
|
+
|
|
877
|
+
- work from `dev` or feature branches;
|
|
878
|
+
- keep endpoint contracts stable for notebooks and services;
|
|
879
|
+
- add focused tests for every engine or accounting change;
|
|
880
|
+
- prefer Numba/vectorized paths for hot loops;
|
|
881
|
+
- use Nautilus validation where execution/accounting evidence matters.
|
|
882
|
+
|
|
883
|
+
## Design Principle
|
|
884
|
+
|
|
885
|
+
QuantBT is not trying to hide the backtest engine behind a black box. It is
|
|
886
|
+
designed so a researcher can move from a signal series to a reproducible audit
|
|
887
|
+
trail: parameters, market data alignment, target sizing, order generation,
|
|
888
|
+
fills, account reports, equity curve, metrics, and optional third-party
|
|
889
|
+
Nautilus validation.
|
|
890
|
+
|
|
891
|
+
That transparency is the product.
|