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
@@ -0,0 +1,173 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import resource
6
+ import time
7
+ from pathlib import Path
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+
12
+ from quantbt import QuantBTEndpoint
13
+ from quantbt.core.orders import OrderCommand
14
+ from quantbt.core.schema import OrderSide, OrderType, TimeInForce
15
+
16
+
17
+ def _rss_mb() -> float:
18
+ return float(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) / 1024.0
19
+
20
+
21
+ def _bars(rows: int) -> pd.DataFrame:
22
+ idx = pd.date_range("2020-01-01", periods=rows, freq="1h", tz="UTC")
23
+ x = np.arange(rows, dtype=np.float64)
24
+ close = pd.Series(100.0 + np.sin(x / 11.0) * 2.0 + x * 0.001, index=idx)
25
+ return pd.DataFrame(
26
+ {
27
+ "open": close,
28
+ "high": close + 2.0,
29
+ "low": close - 2.0,
30
+ "close": close,
31
+ "volume": 1_000.0,
32
+ },
33
+ index=idx,
34
+ )
35
+
36
+
37
+ class TimedStrategy:
38
+ def __init__(self, entry_mod: int, hold: int, qty: float):
39
+ self.entry_mod = int(entry_mod)
40
+ self.hold = int(hold)
41
+ self.qty = float(qty)
42
+ self.open_bar = -1
43
+
44
+ def on_bar_close(self, context):
45
+ symbol = context.symbols[0]
46
+ if context.positions[symbol] == 0.0 and context.bar_index % self.entry_mod == 0:
47
+ self.open_bar = int(context.bar_index)
48
+ return [
49
+ OrderCommand(
50
+ timestamp=context.timestamp,
51
+ symbol=symbol,
52
+ side=OrderSide.BUY,
53
+ order_type=OrderType.MARKET,
54
+ qty=self.qty,
55
+ tif=TimeInForce.IOC,
56
+ order_id=f"entry-{context.bar_index}",
57
+ )
58
+ ]
59
+ if context.positions[symbol] > 0.0 and self.open_bar >= 0 and context.bar_index - self.open_bar >= self.hold:
60
+ self.open_bar = -1
61
+ return [
62
+ OrderCommand(
63
+ timestamp=context.timestamp,
64
+ symbol=symbol,
65
+ side=OrderSide.SELL,
66
+ order_type=OrderType.MARKET,
67
+ qty=abs(context.positions[symbol]),
68
+ tif=TimeInForce.IOC,
69
+ reduce_only=True,
70
+ order_id=f"exit-{context.bar_index}",
71
+ )
72
+ ]
73
+ return []
74
+
75
+
76
+ def _params(trials: int):
77
+ return [
78
+ {
79
+ "entry_mod": 5 + (i % 7),
80
+ "hold": 2 + (i % 5),
81
+ "qty": 0.1 + (i % 4) * 0.05,
82
+ }
83
+ for i in range(trials)
84
+ ]
85
+
86
+
87
+ def _metrics_subset(report: dict) -> dict:
88
+ return {
89
+ "sharpe": report["sharpe"],
90
+ "max_drawdown_pct": report["max_drawdown_pct"],
91
+ "profit_factor": report["profit_factor"],
92
+ "num_trades": report["num_trades"],
93
+ "final_equity": report["final_equity"],
94
+ "liquidated": report["liquidated"],
95
+ }
96
+
97
+
98
+ def run(rows: int, trials: int) -> dict:
99
+ df = _bars(rows)
100
+ params = _params(trials)
101
+ public_endpoint = QuantBTEndpoint.native_event_strategy(
102
+ initial_capital=50_000,
103
+ leverage=10,
104
+ use_funding=False,
105
+ fee_rate=0.0002,
106
+ report_level="audit",
107
+ )
108
+ start = time.perf_counter()
109
+ public_reports = []
110
+ for param in params:
111
+ result = public_endpoint.simulate(data=df, strategy=TimedStrategy(**param), symbols=["BTC"])
112
+ public_reports.append(_metrics_subset(result.full_report(scope="full")))
113
+ public_seconds = time.perf_counter() - start
114
+
115
+ prepared_endpoint = QuantBTEndpoint.native_event_strategy(
116
+ initial_capital=50_000,
117
+ leverage=10,
118
+ use_funding=False,
119
+ fee_rate=0.0002,
120
+ report_level="audit",
121
+ )
122
+ prepared = prepared_endpoint.prepare_native_event_strategy(data=df, symbols=["BTC"])
123
+ start = time.perf_counter()
124
+ score_reports = []
125
+ for param in params:
126
+ score = prepared.score(TimedStrategy(**param))
127
+ score_reports.append(_metrics_subset(score.metrics))
128
+ prepared_seconds = time.perf_counter() - start
129
+
130
+ parity = public_reports == score_reports
131
+ return {
132
+ "rows": int(rows),
133
+ "trials": int(trials),
134
+ "public_audit_seconds": float(public_seconds),
135
+ "prepared_score_seconds": float(prepared_seconds),
136
+ "speedup": float(public_seconds / prepared_seconds) if prepared_seconds > 0.0 else np.inf,
137
+ "peak_rss_mb": float(_rss_mb()),
138
+ "metric_parity": bool(parity),
139
+ "prepared_scores": int(prepared.metadata["scores"]),
140
+ "public_last_report_level": public_endpoint.result.metadata["report_level"],
141
+ "prepared_endpoint_result_retained": prepared_endpoint.result is not None,
142
+ }
143
+
144
+
145
+ def main() -> None:
146
+ parser = argparse.ArgumentParser()
147
+ parser.add_argument("--rows", type=int, default=1_000)
148
+ parser.add_argument("--trials", type=int, default=20)
149
+ parser.add_argument("--json-out", default="benchmarks/phase34b_native_event_prepared_score.json")
150
+ parser.add_argument("--md-out", default="benchmarks/phase34b_native_event_prepared_score.md")
151
+ args = parser.parse_args()
152
+ payload = run(rows=args.rows, trials=args.trials)
153
+ Path(args.json_out).write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
154
+ lines = [
155
+ "# Phase 34B Native Event Prepared Score Benchmark",
156
+ "",
157
+ f"- Rows: `{payload['rows']}`",
158
+ f"- Trials: `{payload['trials']}`",
159
+ f"- Public audit seconds: `{payload['public_audit_seconds']:.6f}`",
160
+ f"- Prepared score seconds: `{payload['prepared_score_seconds']:.6f}`",
161
+ f"- Speedup: `{payload['speedup']:.3f}x`",
162
+ f"- Peak RSS MB: `{payload['peak_rss_mb']:.3f}`",
163
+ f"- Metric parity: `{payload['metric_parity']}`",
164
+ f"- Prepared endpoint result retained: `{payload['prepared_endpoint_result_retained']}`",
165
+ "",
166
+ "Prepared score reuses market arrays and returns `NativeEventScoreResult` rather than storing full public artifacts on the endpoint.",
167
+ ]
168
+ Path(args.md_out).write_text("\n".join(lines) + "\n")
169
+ print(json.dumps(payload, indent=2, sort_keys=True))
170
+
171
+
172
+ if __name__ == "__main__":
173
+ main()
@@ -0,0 +1,176 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import resource
6
+ import time
7
+ from pathlib import Path
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+
12
+ from quantbt import OrderCommand, QuantBTEndpoint
13
+ from quantbt.core.schema import OrderSide, OrderType, TimeInForce
14
+
15
+
16
+ def _rss_mb() -> float:
17
+ return float(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) / 1024.0
18
+
19
+
20
+ def _bars(rows: int) -> pd.DataFrame:
21
+ idx = pd.date_range("2020-01-01", periods=rows, freq="1h", tz="UTC")
22
+ x = np.arange(rows, dtype=np.float64)
23
+ close = pd.Series(100.0 + np.sin(x / 9.0) * 3.0 + np.cos(x / 23.0) * 1.5, index=idx)
24
+ return pd.DataFrame(
25
+ {
26
+ "open": close.shift(1).fillna(close.iloc[0]),
27
+ "high": close + 2.5,
28
+ "low": close - 2.5,
29
+ "close": close,
30
+ "volume": 1_000.0 + (x % 50.0),
31
+ },
32
+ index=idx,
33
+ )
34
+
35
+
36
+ class CyclicStrategy:
37
+ def __init__(self, entry_mod: int, hold: int, qty: float):
38
+ self.entry_mod = int(entry_mod)
39
+ self.hold = int(hold)
40
+ self.qty = float(qty)
41
+ self.open_bar = -1
42
+
43
+ def on_bar_close(self, context):
44
+ symbol = context.symbols[0]
45
+ if context.positions[symbol] == 0.0 and context.bar_index % self.entry_mod == 0:
46
+ self.open_bar = int(context.bar_index)
47
+ return [
48
+ OrderCommand(
49
+ timestamp=context.timestamp,
50
+ symbol=symbol,
51
+ side=OrderSide.BUY,
52
+ order_type=OrderType.MARKET,
53
+ qty=self.qty,
54
+ tif=TimeInForce.IOC,
55
+ order_id=f"entry-{context.bar_index}",
56
+ )
57
+ ]
58
+ if context.positions[symbol] > 0.0 and self.open_bar >= 0 and context.bar_index - self.open_bar >= self.hold:
59
+ self.open_bar = -1
60
+ return [
61
+ OrderCommand(
62
+ timestamp=context.timestamp,
63
+ symbol=symbol,
64
+ side=OrderSide.SELL,
65
+ order_type=OrderType.MARKET,
66
+ qty=abs(context.positions[symbol]),
67
+ tif=TimeInForce.IOC,
68
+ reduce_only=True,
69
+ order_id=f"exit-{context.bar_index}",
70
+ )
71
+ ]
72
+ return []
73
+
74
+
75
+ def _params(trials: int):
76
+ return [
77
+ {
78
+ "entry_mod": 4 + (i % 9),
79
+ "hold": 2 + (i % 6),
80
+ "qty": 0.1 + (i % 5) * 0.025,
81
+ }
82
+ for i in range(trials)
83
+ ]
84
+
85
+
86
+ def _accounting_tuple(result) -> tuple:
87
+ return (
88
+ tuple(np.round(result.equity.to_numpy(dtype=np.float64), 12)),
89
+ tuple(np.round(result.returns.to_numpy(dtype=np.float64), 12)),
90
+ tuple(np.round(result.positions.to_numpy(dtype=np.float64).ravel(), 12)),
91
+ tuple(np.round(result.fees.to_numpy(dtype=np.float64), 12)),
92
+ tuple(np.round(result.funding.to_numpy(dtype=np.float64), 12)),
93
+ tuple(np.round(result.margin.to_numpy(dtype=np.float64).ravel(), 12)),
94
+ bool(result.liquidated),
95
+ int(result.liquidation_bar),
96
+ )
97
+
98
+
99
+ def run(rows: int, trials: int) -> dict:
100
+ df = _bars(rows)
101
+ params = _params(trials)
102
+ kwargs = dict(
103
+ initial_capital=50_000,
104
+ leverage=10,
105
+ use_funding=False,
106
+ fee_rate=0.0002,
107
+ report_level="minimal",
108
+ )
109
+
110
+ replay_endpoint = QuantBTEndpoint.native_event_strategy(**kwargs, reactive_kernel_mode="replay_certified")
111
+ start = time.perf_counter()
112
+ replay_fingerprints = []
113
+ replay_static_replays = 0
114
+ for param in params:
115
+ result = replay_endpoint.simulate(data=df, strategy=CyclicStrategy(**param), symbols=["BTC"])
116
+ replay_fingerprints.append(_accounting_tuple(result))
117
+ replay_static_replays += int(result.metadata.get("reactive_static_replay_count", 0))
118
+ replay_seconds = time.perf_counter() - start
119
+
120
+ single_endpoint = QuantBTEndpoint.native_event_strategy(**kwargs, reactive_kernel_mode="single_pass")
121
+ start = time.perf_counter()
122
+ single_fingerprints = []
123
+ single_static_replays = 0
124
+ for param in params:
125
+ result = single_endpoint.simulate(data=df, strategy=CyclicStrategy(**param), symbols=["BTC"])
126
+ single_fingerprints.append(_accounting_tuple(result))
127
+ single_static_replays += int(result.metadata.get("reactive_static_replay_count", 0))
128
+ single_seconds = time.perf_counter() - start
129
+
130
+ return {
131
+ "rows": int(rows),
132
+ "trials": int(trials),
133
+ "replay_certified_seconds": float(replay_seconds),
134
+ "single_pass_seconds": float(single_seconds),
135
+ "speedup": float(replay_seconds / single_seconds) if single_seconds > 0.0 else np.inf,
136
+ "replay_certified_static_replays": int(replay_static_replays),
137
+ "single_pass_static_replays": int(single_static_replays),
138
+ "accounting_parity": bool(replay_fingerprints == single_fingerprints),
139
+ "peak_rss_mb": float(_rss_mb()),
140
+ }
141
+
142
+
143
+ def main() -> None:
144
+ parser = argparse.ArgumentParser()
145
+ parser.add_argument("--rows", type=int, default=1_000)
146
+ parser.add_argument("--trials", type=int, default=20)
147
+ parser.add_argument("--json-out", default="benchmarks/phase34c_native_event_single_pass.json")
148
+ parser.add_argument("--md-out", default="benchmarks/phase34c_native_event_single_pass.md")
149
+ args = parser.parse_args()
150
+ payload = run(rows=args.rows, trials=args.trials)
151
+ json_path = Path(args.json_out)
152
+ md_path = Path(args.md_out)
153
+ json_path.parent.mkdir(parents=True, exist_ok=True)
154
+ md_path.parent.mkdir(parents=True, exist_ok=True)
155
+ json_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
156
+ lines = [
157
+ "# Phase 34C Native Event Single-Pass Benchmark",
158
+ "",
159
+ f"- Rows: `{payload['rows']}`",
160
+ f"- Trials: `{payload['trials']}`",
161
+ f"- Replay-certified seconds: `{payload['replay_certified_seconds']:.6f}`",
162
+ f"- Single-pass seconds: `{payload['single_pass_seconds']:.6f}`",
163
+ f"- Speedup: `{payload['speedup']:.3f}x`",
164
+ f"- Replay-certified static replays: `{payload['replay_certified_static_replays']}`",
165
+ f"- Single-pass static replays: `{payload['single_pass_static_replays']}`",
166
+ f"- Accounting parity: `{payload['accounting_parity']}`",
167
+ f"- Peak RSS MB: `{payload['peak_rss_mb']:.3f}`",
168
+ "",
169
+ "This benchmark isolates the Phase 34C mode switch: `single_pass` materializes accounting from the reactive session for minimal/score runs and skips the final static replay.",
170
+ ]
171
+ md_path.write_text("\n".join(lines) + "\n")
172
+ print(json.dumps(payload, indent=2, sort_keys=True))
173
+
174
+
175
+ if __name__ == "__main__":
176
+ main()