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
quantbt/core/types.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quantbt.core.types
|
|
3
|
+
------------------
|
|
4
|
+
Shared dataclasses. BacktestResult is the single output contract used by
|
|
5
|
+
metrics, viz, and optimizer modules — nothing downstream imports BacktestEngine.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas as pd
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class BacktestResult:
|
|
19
|
+
"""
|
|
20
|
+
Immutable output of a single backtest run.
|
|
21
|
+
|
|
22
|
+
Attributes
|
|
23
|
+
----------
|
|
24
|
+
equity Equity curve indexed by DatetimeIndex (UTC).
|
|
25
|
+
returns Bar-frequency net return series.
|
|
26
|
+
positions DataFrame, one column per symbol, target units per bar.
|
|
27
|
+
closes DataFrame, one column per symbol, close prices.
|
|
28
|
+
symbols Ordered list of symbol names.
|
|
29
|
+
initial_capital
|
|
30
|
+
leverage
|
|
31
|
+
liquidated True if the account was margin-called.
|
|
32
|
+
liquidation_bar Integer bar index of liquidation, -1 if none.
|
|
33
|
+
metadata Arbitrary dict for storing run parameters.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
equity: pd.Series
|
|
37
|
+
returns: pd.Series
|
|
38
|
+
positions: pd.DataFrame
|
|
39
|
+
closes: pd.DataFrame
|
|
40
|
+
symbols: List[str]
|
|
41
|
+
initial_capital: float
|
|
42
|
+
leverage: float
|
|
43
|
+
liquidated: bool = False
|
|
44
|
+
liquidation_bar: int = -1
|
|
45
|
+
metadata: Dict = field(default_factory=dict)
|
|
46
|
+
|
|
47
|
+
# ── computed on first access ──────────────────────────────────────────
|
|
48
|
+
@property
|
|
49
|
+
def drawdown(self) -> pd.Series:
|
|
50
|
+
"""Drawdown series as a positive fraction (0 = at peak, 1 = 100% loss)."""
|
|
51
|
+
peak = self.equity.cummax()
|
|
52
|
+
return (peak - self.equity) / peak.replace(0, np.nan)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def daily_equity(self) -> pd.Series:
|
|
56
|
+
return self.equity.resample("1D").last().ffill().dropna()
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def daily_returns(self) -> pd.Series:
|
|
60
|
+
return self.daily_equity.pct_change().dropna()
|
|
61
|
+
|
|
62
|
+
def full_report(self, trading_days: int = 365, scope: str = "auto") -> Dict:
|
|
63
|
+
"""Return the standard QuantBT metrics dictionary for this result."""
|
|
64
|
+
from .scopes import scoped_result
|
|
65
|
+
from ..metrics.performance import full_report
|
|
66
|
+
|
|
67
|
+
return full_report(scoped_result(self, scope=scope), trading_days=trading_days)
|
|
68
|
+
|
|
69
|
+
def show_metrics(self, trading_days: int = 365, scope: str = "auto") -> Dict:
|
|
70
|
+
"""Print a legacy-style metrics report and return the metrics dict."""
|
|
71
|
+
from ..endpoint import format_metrics_report
|
|
72
|
+
|
|
73
|
+
report = self.full_report(trading_days=trading_days, scope=scope)
|
|
74
|
+
print(format_metrics_report(report))
|
|
75
|
+
return report
|
|
76
|
+
|
|
77
|
+
def quick_plot(self, theme: str = "dark", figsize: tuple = (14, 6), scope: str = "auto"):
|
|
78
|
+
"""Plot cumulative return and drawdown for this result."""
|
|
79
|
+
from ..viz import quick_plot
|
|
80
|
+
|
|
81
|
+
return quick_plot(self, theme=theme, figsize=figsize, scope=scope)
|
|
82
|
+
|
|
83
|
+
def tearsheet(self, theme: str = "dark", benchmark=None, scope: str = "auto"):
|
|
84
|
+
"""Render the QuantBT tearsheet for this result."""
|
|
85
|
+
from ..viz import tearsheet
|
|
86
|
+
|
|
87
|
+
return tearsheet(self, theme=theme, benchmark=benchmark, scope=scope)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quantbt.core.vectorized
|
|
3
|
+
-----------------------
|
|
4
|
+
Numba kernels for the V2 native vectorized backend.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from numba import njit
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
REJECT_NONE = 0
|
|
14
|
+
REJECT_INSUFFICIENT_MARGIN = 1
|
|
15
|
+
|
|
16
|
+
LIQ_NONE = 0
|
|
17
|
+
LIQ_INTRABAR = 1
|
|
18
|
+
LIQ_AFTER_FUNDING = 2
|
|
19
|
+
LIQ_AFTER_REBALANCE = 3
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@njit(cache=True)
|
|
23
|
+
def _engine_units_v2(
|
|
24
|
+
n_bars: int,
|
|
25
|
+
n_syms: int,
|
|
26
|
+
highs: np.ndarray,
|
|
27
|
+
lows: np.ndarray,
|
|
28
|
+
closes: np.ndarray,
|
|
29
|
+
target_units: np.ndarray,
|
|
30
|
+
funding_rates: np.ndarray,
|
|
31
|
+
is_funding_bar: np.ndarray,
|
|
32
|
+
init_capital: float,
|
|
33
|
+
leverages: np.ndarray,
|
|
34
|
+
maint_ratio: float,
|
|
35
|
+
fee_rates: np.ndarray,
|
|
36
|
+
contract_sizes: np.ndarray,
|
|
37
|
+
slippage: float,
|
|
38
|
+
use_funding: bool,
|
|
39
|
+
):
|
|
40
|
+
equity_curve = np.zeros(n_bars, dtype=np.float64)
|
|
41
|
+
pos_out = np.zeros((n_bars, n_syms), dtype=np.float64)
|
|
42
|
+
fee_arr = np.zeros(n_bars, dtype=np.float64)
|
|
43
|
+
turnover_arr = np.zeros(n_bars, dtype=np.float64)
|
|
44
|
+
funding_arr = np.zeros(n_bars, dtype=np.float64)
|
|
45
|
+
init_margin = np.zeros(n_bars, dtype=np.float64)
|
|
46
|
+
maint_margin = np.zeros(n_bars, dtype=np.float64)
|
|
47
|
+
rejected = np.zeros(n_bars, dtype=np.int64)
|
|
48
|
+
reject_code = np.zeros(n_bars, dtype=np.int64)
|
|
49
|
+
|
|
50
|
+
current_pos = np.zeros(n_syms, dtype=np.float64)
|
|
51
|
+
equity = init_capital
|
|
52
|
+
liq_flag = False
|
|
53
|
+
liq_idx = -1
|
|
54
|
+
liq_reason = LIQ_NONE
|
|
55
|
+
|
|
56
|
+
equity_curve[0] = equity
|
|
57
|
+
|
|
58
|
+
for i in range(1, n_bars):
|
|
59
|
+
if liq_flag:
|
|
60
|
+
equity_curve[i] = 0.0
|
|
61
|
+
for s in range(n_syms):
|
|
62
|
+
pos_out[i, s] = 0.0
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# Mark carried positions close-to-close.
|
|
66
|
+
for s in range(n_syms):
|
|
67
|
+
p = current_pos[s]
|
|
68
|
+
if p != 0.0:
|
|
69
|
+
equity += p * (closes[i, s] - closes[i - 1, s]) * contract_sizes[s]
|
|
70
|
+
|
|
71
|
+
# Intrabar liquidation before funding and new orders.
|
|
72
|
+
worst_equity = equity
|
|
73
|
+
worst_mm = 0.0
|
|
74
|
+
for s in range(n_syms):
|
|
75
|
+
p = current_pos[s]
|
|
76
|
+
if p == 0.0:
|
|
77
|
+
continue
|
|
78
|
+
worst_p = lows[i, s] if p > 0.0 else highs[i, s]
|
|
79
|
+
worst_equity += p * (worst_p - closes[i, s]) * contract_sizes[s]
|
|
80
|
+
worst_mm += abs(p) * worst_p * contract_sizes[s] * maint_ratio
|
|
81
|
+
|
|
82
|
+
if worst_mm > 0.0 and worst_equity <= worst_mm:
|
|
83
|
+
liq_flag = True
|
|
84
|
+
liq_idx = i
|
|
85
|
+
liq_reason = LIQ_INTRABAR
|
|
86
|
+
equity = 0.0
|
|
87
|
+
for s in range(n_syms):
|
|
88
|
+
current_pos[s] = 0.0
|
|
89
|
+
pos_out[i, s] = 0.0
|
|
90
|
+
equity_curve[i] = 0.0
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
# Funding on carried positions. Positive value is a cost paid.
|
|
94
|
+
if is_funding_bar[i] and use_funding:
|
|
95
|
+
for s in range(n_syms):
|
|
96
|
+
p = current_pos[s]
|
|
97
|
+
if p != 0.0:
|
|
98
|
+
cost = p * closes[i, s] * contract_sizes[s] * funding_rates[i, s]
|
|
99
|
+
equity -= cost
|
|
100
|
+
funding_arr[i] += cost
|
|
101
|
+
|
|
102
|
+
close_mm = 0.0
|
|
103
|
+
for s in range(n_syms):
|
|
104
|
+
p = current_pos[s]
|
|
105
|
+
if p != 0.0:
|
|
106
|
+
close_mm += abs(p) * closes[i, s] * contract_sizes[s] * maint_ratio
|
|
107
|
+
|
|
108
|
+
if close_mm > 0.0 and equity <= close_mm:
|
|
109
|
+
liq_flag = True
|
|
110
|
+
liq_idx = i
|
|
111
|
+
liq_reason = LIQ_AFTER_FUNDING
|
|
112
|
+
equity = 0.0
|
|
113
|
+
for s in range(n_syms):
|
|
114
|
+
current_pos[s] = 0.0
|
|
115
|
+
pos_out[i, s] = 0.0
|
|
116
|
+
equity_curve[i] = 0.0
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
cur_im = 0.0
|
|
120
|
+
for s in range(n_syms):
|
|
121
|
+
cur_im += abs(current_pos[s]) * closes[i, s] * contract_sizes[s] / leverages[s]
|
|
122
|
+
|
|
123
|
+
avail = equity - cur_im
|
|
124
|
+
if avail < 0.0:
|
|
125
|
+
avail = 0.0
|
|
126
|
+
|
|
127
|
+
# Execute target-unit changes at close with optional slippage.
|
|
128
|
+
for s in range(n_syms):
|
|
129
|
+
target = target_units[i, s]
|
|
130
|
+
delta = target - current_pos[s]
|
|
131
|
+
if abs(delta) < 1e-12:
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
c = closes[i, s]
|
|
135
|
+
cs = contract_sizes[s]
|
|
136
|
+
exec_p = c * (1.0 + slippage if delta > 0.0 else 1.0 - slippage)
|
|
137
|
+
trade_notional = abs(delta) * exec_p * cs
|
|
138
|
+
fee_cost = trade_notional * fee_rates[s]
|
|
139
|
+
slip_cost = abs(delta) * abs(exec_p - c) * cs
|
|
140
|
+
|
|
141
|
+
old_im = abs(current_pos[s]) * c * cs / leverages[s]
|
|
142
|
+
new_im = abs(target) * exec_p * cs / leverages[s]
|
|
143
|
+
margin_delta = new_im - old_im
|
|
144
|
+
required = fee_cost + slip_cost
|
|
145
|
+
if margin_delta > 0.0:
|
|
146
|
+
required += margin_delta
|
|
147
|
+
|
|
148
|
+
if required > avail:
|
|
149
|
+
rejected[i] += 1
|
|
150
|
+
reject_code[i] = REJECT_INSUFFICIENT_MARGIN
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
equity -= fee_cost + slip_cost
|
|
154
|
+
current_pos[s] = target
|
|
155
|
+
fee_arr[i] += fee_cost
|
|
156
|
+
turnover_arr[i] += trade_notional
|
|
157
|
+
avail -= fee_cost + slip_cost + margin_delta
|
|
158
|
+
if avail < 0.0:
|
|
159
|
+
avail = 0.0
|
|
160
|
+
|
|
161
|
+
close_im = 0.0
|
|
162
|
+
close_mm = 0.0
|
|
163
|
+
for s in range(n_syms):
|
|
164
|
+
p = current_pos[s]
|
|
165
|
+
if p != 0.0:
|
|
166
|
+
notional = abs(p) * closes[i, s] * contract_sizes[s]
|
|
167
|
+
close_im += notional / leverages[s]
|
|
168
|
+
close_mm += notional * maint_ratio
|
|
169
|
+
|
|
170
|
+
if close_mm > 0.0 and equity <= close_mm:
|
|
171
|
+
liq_flag = True
|
|
172
|
+
liq_idx = i
|
|
173
|
+
liq_reason = LIQ_AFTER_REBALANCE
|
|
174
|
+
equity = 0.0
|
|
175
|
+
for s in range(n_syms):
|
|
176
|
+
current_pos[s] = 0.0
|
|
177
|
+
pos_out[i, s] = 0.0
|
|
178
|
+
equity_curve[i] = 0.0
|
|
179
|
+
init_margin[i] = 0.0
|
|
180
|
+
maint_margin[i] = 0.0
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
for s in range(n_syms):
|
|
184
|
+
pos_out[i, s] = current_pos[s]
|
|
185
|
+
|
|
186
|
+
init_margin[i] = close_im
|
|
187
|
+
maint_margin[i] = close_mm
|
|
188
|
+
equity_curve[i] = equity
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
equity_curve,
|
|
192
|
+
pos_out,
|
|
193
|
+
fee_arr,
|
|
194
|
+
turnover_arr,
|
|
195
|
+
funding_arr,
|
|
196
|
+
init_margin,
|
|
197
|
+
maint_margin,
|
|
198
|
+
rejected,
|
|
199
|
+
reject_code,
|
|
200
|
+
liq_flag,
|
|
201
|
+
liq_idx,
|
|
202
|
+
liq_reason,
|
|
203
|
+
)
|