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.
Files changed (133) hide show
  1. quantbt/__init__.py +862 -0
  2. quantbt/adapters/__init__.py +5 -0
  3. quantbt/adapters/nautilus/__init__.py +42 -0
  4. quantbt/adapters/nautilus/_dependency.py +53 -0
  5. quantbt/adapters/nautilus/backend.py +639 -0
  6. quantbt/adapters/nautilus/instruments.py +275 -0
  7. quantbt/adapters/nautilus/options.py +465 -0
  8. quantbt/adapters/nautilus/reports.py +231 -0
  9. quantbt/backends/__init__.py +29 -0
  10. quantbt/backends/_native_event_rust.py +2168 -0
  11. quantbt/backends/native_event.py +5048 -0
  12. quantbt/backends/native_option.py +796 -0
  13. quantbt/backends/native_portfolio.py +928 -0
  14. quantbt/backends/native_vectorized.py +1299 -0
  15. quantbt/backtester.py +485 -0
  16. quantbt/benchmarks/__init__.py +1 -0
  17. quantbt/benchmarks/compare_phase9_parity.py +302 -0
  18. quantbt/benchmarks/gamma_scalping_backtestsample.py +802 -0
  19. quantbt/benchmarks/profile_phase7.py +415 -0
  20. quantbt/benchmarks/run_arbitrage_phase_e.py +160 -0
  21. quantbt/benchmarks/run_optimization_overhead.py +195 -0
  22. quantbt/benchmarks/run_options_engine.py +272 -0
  23. quantbt/benchmarks/run_pct_equity_nautilus_smoke.py +227 -0
  24. quantbt/benchmarks/run_phase12_arbitrage_cert.py +402 -0
  25. quantbt/benchmarks/run_phase12_benchmark_nautilus_cert.py +430 -0
  26. quantbt/benchmarks/run_phase13_portfolio_report.py +97 -0
  27. quantbt/benchmarks/run_phase13_wfo_cache.py +162 -0
  28. quantbt/benchmarks/run_phase14_service_loop.py +694 -0
  29. quantbt/benchmarks/run_phase15a_nautilus_certification.py +514 -0
  30. quantbt/benchmarks/run_phase15b_synthetic_depth.py +175 -0
  31. quantbt/benchmarks/run_phase16_performance_debt.py +389 -0
  32. quantbt/benchmarks/run_phase30e_reactive_runner.py +161 -0
  33. quantbt/benchmarks/run_phase31_intrabar.py +441 -0
  34. quantbt/benchmarks/run_phase34a_native_event_memory.py +179 -0
  35. quantbt/benchmarks/run_phase34b_native_event_prepared_score.py +173 -0
  36. quantbt/benchmarks/run_phase34c_native_event_single_pass.py +176 -0
  37. quantbt/benchmarks/run_phase7.py +654 -0
  38. quantbt/benchmarks/run_portfolio_real_parity.py +537 -0
  39. quantbt/core/__init__.py +323 -0
  40. quantbt/core/arbitrage.py +767 -0
  41. quantbt/core/basket.py +234 -0
  42. quantbt/core/certification.py +274 -0
  43. quantbt/core/constraints.py +155 -0
  44. quantbt/core/engine.py +1144 -0
  45. quantbt/core/event.py +869 -0
  46. quantbt/core/execution_contract.py +197 -0
  47. quantbt/core/execution_depth.py +613 -0
  48. quantbt/core/intrabar_kernel.py +1906 -0
  49. quantbt/core/intrabar_reference.py +907 -0
  50. quantbt/core/intrabar_session.py +156 -0
  51. quantbt/core/market_tape.py +480 -0
  52. quantbt/core/native_event_capabilities.py +121 -0
  53. quantbt/core/native_event_parity.py +336 -0
  54. quantbt/core/order_compiler.py +422 -0
  55. quantbt/core/orders.py +319 -0
  56. quantbt/core/portfolio.py +392 -0
  57. quantbt/core/preprocessor.py +256 -0
  58. quantbt/core/reactive.py +173 -0
  59. quantbt/core/results.py +341 -0
  60. quantbt/core/schema.py +211 -0
  61. quantbt/core/scopes.py +96 -0
  62. quantbt/core/structured_orders.py +378 -0
  63. quantbt/core/types.py +87 -0
  64. quantbt/core/vectorized.py +203 -0
  65. quantbt/endpoint.py +4465 -0
  66. quantbt/engines.py +1014 -0
  67. quantbt/metrics/__init__.py +45 -0
  68. quantbt/metrics/options_analytics.py +46 -0
  69. quantbt/metrics/performance.py +541 -0
  70. quantbt/optimization/__init__.py +94 -0
  71. quantbt/optimization/callbacks.py +116 -0
  72. quantbt/optimization/candidate_selection.py +397 -0
  73. quantbt/optimization/config.py +84 -0
  74. quantbt/optimization/constraints.py +22 -0
  75. quantbt/optimization/evaluator.py +21 -0
  76. quantbt/optimization/evaluators/__init__.py +32 -0
  77. quantbt/optimization/evaluators/arbitrage.py +22 -0
  78. quantbt/optimization/evaluators/generic.py +34 -0
  79. quantbt/optimization/evaluators/grid_dca.py +22 -0
  80. quantbt/optimization/evaluators/intrabar.py +54 -0
  81. quantbt/optimization/evaluators/native_event.py +51 -0
  82. quantbt/optimization/evaluators/options.py +22 -0
  83. quantbt/optimization/evaluators/portfolio.py +42 -0
  84. quantbt/optimization/evaluators/signal.py +43 -0
  85. quantbt/optimization/multiseed.py +176 -0
  86. quantbt/optimization/objectives.py +221 -0
  87. quantbt/optimization/optimizer.py +500 -0
  88. quantbt/optimization/result.py +80 -0
  89. quantbt/optimization/samplers.py +84 -0
  90. quantbt/optimization/space.py +223 -0
  91. quantbt/options/__init__.py +215 -0
  92. quantbt/options/cache.py +116 -0
  93. quantbt/options/conventions.py +158 -0
  94. quantbt/options/data.py +177 -0
  95. quantbt/options/execution.py +600 -0
  96. quantbt/options/fees.py +135 -0
  97. quantbt/options/greeks.py +173 -0
  98. quantbt/options/hedging.py +232 -0
  99. quantbt/options/iv.py +188 -0
  100. quantbt/options/ledger.py +263 -0
  101. quantbt/options/lifecycle.py +94 -0
  102. quantbt/options/margin.py +311 -0
  103. quantbt/options/packages.py +147 -0
  104. quantbt/options/pricing.py +191 -0
  105. quantbt/options/schema.py +227 -0
  106. quantbt/options/selectors.py +281 -0
  107. quantbt/options/strategy.py +249 -0
  108. quantbt/options/surface.py +142 -0
  109. quantbt/options/tape.py +226 -0
  110. quantbt/options/templates/__init__.py +33 -0
  111. quantbt/options/templates/packages.py +354 -0
  112. quantbt/portfolio.py +606 -0
  113. quantbt/py.typed +1 -0
  114. quantbt/reporting/__init__.py +38 -0
  115. quantbt/reporting/arbitrage_audit.py +211 -0
  116. quantbt/reporting/nautilus_bundle.py +758 -0
  117. quantbt/reporting/nautilus_certification.py +226 -0
  118. quantbt/reporting/nautilus_diagnostics.py +225 -0
  119. quantbt/reporting/parity.py +496 -0
  120. quantbt/reporting/portfolio_audit.py +206 -0
  121. quantbt/reporting/portfolio_nautilus.py +140 -0
  122. quantbt/sizing/__init__.py +3 -0
  123. quantbt/sizing/fast.py +67 -0
  124. quantbt/sizing/modes.py +162 -0
  125. quantbt/viz/__init__.py +4 -0
  126. quantbt/viz/plots.py +310 -0
  127. quantbt/viz/themes.py +102 -0
  128. quantbt/walkforward.py +3136 -0
  129. quantbt_engine-1.0.7.dist-info/METADATA +891 -0
  130. quantbt_engine-1.0.7.dist-info/RECORD +133 -0
  131. quantbt_engine-1.0.7.dist-info/WHEEL +5 -0
  132. quantbt_engine-1.0.7.dist-info/licenses/LICENSE +21 -0
  133. 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
+ )