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,354 @@
1
+ """
2
+ V1 option package builders.
3
+
4
+ Builders intentionally emit `OptionPackageIntent` only. They do not calculate
5
+ payoff, PnL, Greeks, margin, or account state.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Optional, Sequence, Tuple
11
+
12
+ from ...core.schema import OrderSide, OrderType, TimeInForce
13
+ from ..packages import OptionPackageExecutionPolicy, OptionPackageIntent, OptionPackageLeg
14
+
15
+
16
+ def long_call(timestamp_ns: int, call_id: str, *, quantity: float = 1.0, package_id: Optional[str] = None, **kwargs) -> OptionPackageIntent:
17
+ """Buy one call package."""
18
+ return _single(timestamp_ns, call_id, OrderSide.BUY, "long_call", quantity=quantity, package_id=package_id, **kwargs)
19
+
20
+
21
+ def short_call(timestamp_ns: int, call_id: str, *, quantity: float = 1.0, package_id: Optional[str] = None, **kwargs) -> OptionPackageIntent:
22
+ """Sell one call package."""
23
+ return _single(timestamp_ns, call_id, OrderSide.SELL, "short_call", quantity=quantity, package_id=package_id, **kwargs)
24
+
25
+
26
+ def long_put(timestamp_ns: int, put_id: str, *, quantity: float = 1.0, package_id: Optional[str] = None, **kwargs) -> OptionPackageIntent:
27
+ """Buy one put package."""
28
+ return _single(timestamp_ns, put_id, OrderSide.BUY, "long_put", quantity=quantity, package_id=package_id, **kwargs)
29
+
30
+
31
+ def short_put(timestamp_ns: int, put_id: str, *, quantity: float = 1.0, package_id: Optional[str] = None, **kwargs) -> OptionPackageIntent:
32
+ """Sell one put package."""
33
+ return _single(timestamp_ns, put_id, OrderSide.SELL, "short_put", quantity=quantity, package_id=package_id, **kwargs)
34
+
35
+
36
+ def straddle(
37
+ timestamp_ns: int,
38
+ call_id: str,
39
+ put_id: str,
40
+ *,
41
+ side: str = "long",
42
+ quantity: float = 1.0,
43
+ package_id: Optional[str] = None,
44
+ **kwargs,
45
+ ) -> OptionPackageIntent:
46
+ """Create a long or short straddle."""
47
+ order_side = _side_from_direction(side, long_side=OrderSide.BUY)
48
+ return _package(
49
+ timestamp_ns,
50
+ package_id or f"{side}_straddle:{call_id}:{put_id}",
51
+ (
52
+ _leg(call_id, order_side, 1.0, role="call", **kwargs),
53
+ _leg(put_id, order_side, 1.0, role="put", **kwargs),
54
+ ),
55
+ quantity=quantity,
56
+ strategy="straddle",
57
+ **_package_kwargs(kwargs),
58
+ )
59
+
60
+
61
+ def strangle(
62
+ timestamp_ns: int,
63
+ call_id: str,
64
+ put_id: str,
65
+ *,
66
+ side: str = "long",
67
+ quantity: float = 1.0,
68
+ package_id: Optional[str] = None,
69
+ **kwargs,
70
+ ) -> OptionPackageIntent:
71
+ """Create a long or short strangle."""
72
+ order_side = _side_from_direction(side, long_side=OrderSide.BUY)
73
+ return _package(
74
+ timestamp_ns,
75
+ package_id or f"{side}_strangle:{call_id}:{put_id}",
76
+ (
77
+ _leg(call_id, order_side, 1.0, role="call", **kwargs),
78
+ _leg(put_id, order_side, 1.0, role="put", **kwargs),
79
+ ),
80
+ quantity=quantity,
81
+ strategy="strangle",
82
+ **_package_kwargs(kwargs),
83
+ )
84
+
85
+
86
+ def vertical(
87
+ timestamp_ns: int,
88
+ long_option_id: str,
89
+ short_option_id: str,
90
+ *,
91
+ quantity: float = 1.0,
92
+ package_id: Optional[str] = None,
93
+ **kwargs,
94
+ ) -> OptionPackageIntent:
95
+ """Create a debit vertical: buy one option and sell another same-type option."""
96
+ return _package(
97
+ timestamp_ns,
98
+ package_id or f"vertical:{long_option_id}:{short_option_id}",
99
+ (
100
+ _leg(long_option_id, OrderSide.BUY, 1.0, role="long_strike", **kwargs),
101
+ _leg(short_option_id, OrderSide.SELL, 1.0, role="short_strike", **kwargs),
102
+ ),
103
+ quantity=quantity,
104
+ strategy="vertical",
105
+ **_package_kwargs(kwargs),
106
+ )
107
+
108
+
109
+ def butterfly(
110
+ timestamp_ns: int,
111
+ lower_id: str,
112
+ middle_id: str,
113
+ upper_id: str,
114
+ *,
115
+ quantity: float = 1.0,
116
+ package_id: Optional[str] = None,
117
+ **kwargs,
118
+ ) -> OptionPackageIntent:
119
+ """Create a 1:-2:1 long butterfly."""
120
+ return _package(
121
+ timestamp_ns,
122
+ package_id or f"butterfly:{lower_id}:{middle_id}:{upper_id}",
123
+ (
124
+ _leg(lower_id, OrderSide.BUY, 1.0, role="lower_wing", **kwargs),
125
+ _leg(middle_id, OrderSide.SELL, 2.0, role="body", **kwargs),
126
+ _leg(upper_id, OrderSide.BUY, 1.0, role="upper_wing", **kwargs),
127
+ ),
128
+ quantity=quantity,
129
+ strategy="butterfly",
130
+ **_package_kwargs(kwargs),
131
+ )
132
+
133
+
134
+ def condor(
135
+ timestamp_ns: int,
136
+ lower_long_id: str,
137
+ lower_short_id: str,
138
+ upper_short_id: str,
139
+ upper_long_id: str,
140
+ *,
141
+ quantity: float = 1.0,
142
+ package_id: Optional[str] = None,
143
+ **kwargs,
144
+ ) -> OptionPackageIntent:
145
+ """Create a 1:-1:-1:1 long condor."""
146
+ return _package(
147
+ timestamp_ns,
148
+ package_id or f"condor:{lower_long_id}:{lower_short_id}:{upper_short_id}:{upper_long_id}",
149
+ (
150
+ _leg(lower_long_id, OrderSide.BUY, 1.0, role="lower_wing", **kwargs),
151
+ _leg(lower_short_id, OrderSide.SELL, 1.0, role="lower_body", **kwargs),
152
+ _leg(upper_short_id, OrderSide.SELL, 1.0, role="upper_body", **kwargs),
153
+ _leg(upper_long_id, OrderSide.BUY, 1.0, role="upper_wing", **kwargs),
154
+ ),
155
+ quantity=quantity,
156
+ strategy="condor",
157
+ **_package_kwargs(kwargs),
158
+ )
159
+
160
+
161
+ def calendar(
162
+ timestamp_ns: int,
163
+ near_id: str,
164
+ far_id: str,
165
+ *,
166
+ side: str = "long",
167
+ quantity: float = 1.0,
168
+ package_id: Optional[str] = None,
169
+ **kwargs,
170
+ ) -> OptionPackageIntent:
171
+ """Create a calendar spread. Long calendar sells near expiry and buys far expiry."""
172
+ near_side = OrderSide.SELL if str(side).lower() == "long" else OrderSide.BUY
173
+ far_side = OrderSide.BUY if str(side).lower() == "long" else OrderSide.SELL
174
+ return _package(
175
+ timestamp_ns,
176
+ package_id or f"{side}_calendar:{near_id}:{far_id}",
177
+ (
178
+ _leg(near_id, near_side, 1.0, role="near_expiry", **kwargs),
179
+ _leg(far_id, far_side, 1.0, role="far_expiry", **kwargs),
180
+ ),
181
+ quantity=quantity,
182
+ strategy="calendar",
183
+ **_package_kwargs(kwargs),
184
+ )
185
+
186
+
187
+ def covered_call(
188
+ timestamp_ns: int,
189
+ underlying_id: str,
190
+ call_id: str,
191
+ *,
192
+ quantity: float = 1.0,
193
+ underlying_ratio: float = 1.0,
194
+ package_id: Optional[str] = None,
195
+ **kwargs,
196
+ ) -> OptionPackageIntent:
197
+ """Create a covered call package: long underlying, short call."""
198
+ return _package(
199
+ timestamp_ns,
200
+ package_id or f"covered_call:{underlying_id}:{call_id}",
201
+ (
202
+ _leg(underlying_id, OrderSide.BUY, underlying_ratio, role="underlying", **_with_leg_metadata(kwargs, {"asset_role": "underlying"})),
203
+ _leg(call_id, OrderSide.SELL, 1.0, role="short_call", **kwargs),
204
+ ),
205
+ quantity=quantity,
206
+ strategy="covered_call",
207
+ **_package_kwargs(kwargs),
208
+ )
209
+
210
+
211
+ def collar(
212
+ timestamp_ns: int,
213
+ underlying_id: str,
214
+ put_id: str,
215
+ call_id: str,
216
+ *,
217
+ quantity: float = 1.0,
218
+ underlying_ratio: float = 1.0,
219
+ package_id: Optional[str] = None,
220
+ **kwargs,
221
+ ) -> OptionPackageIntent:
222
+ """Create a collar package: long underlying, long put, short call."""
223
+ return _package(
224
+ timestamp_ns,
225
+ package_id or f"collar:{underlying_id}:{put_id}:{call_id}",
226
+ (
227
+ _leg(underlying_id, OrderSide.BUY, underlying_ratio, role="underlying", **_with_leg_metadata(kwargs, {"asset_role": "underlying"})),
228
+ _leg(put_id, OrderSide.BUY, 1.0, role="protective_put", **kwargs),
229
+ _leg(call_id, OrderSide.SELL, 1.0, role="covered_call", **kwargs),
230
+ ),
231
+ quantity=quantity,
232
+ strategy="collar",
233
+ **_package_kwargs(kwargs),
234
+ )
235
+
236
+
237
+ def risk_reversal(
238
+ timestamp_ns: int,
239
+ put_id: str,
240
+ call_id: str,
241
+ *,
242
+ direction: str = "bullish",
243
+ quantity: float = 1.0,
244
+ package_id: Optional[str] = None,
245
+ **kwargs,
246
+ ) -> OptionPackageIntent:
247
+ """Create a bullish or bearish risk reversal."""
248
+ bullish = str(direction).lower() == "bullish"
249
+ return _package(
250
+ timestamp_ns,
251
+ package_id or f"{direction}_risk_reversal:{put_id}:{call_id}",
252
+ (
253
+ _leg(put_id, OrderSide.SELL if bullish else OrderSide.BUY, 1.0, role="put", **kwargs),
254
+ _leg(call_id, OrderSide.BUY if bullish else OrderSide.SELL, 1.0, role="call", **kwargs),
255
+ ),
256
+ quantity=quantity,
257
+ strategy="risk_reversal",
258
+ **_package_kwargs(kwargs),
259
+ )
260
+
261
+
262
+ def _single(
263
+ timestamp_ns: int,
264
+ instrument_id: str,
265
+ side: OrderSide,
266
+ strategy: str,
267
+ *,
268
+ quantity: float,
269
+ package_id: Optional[str],
270
+ **kwargs,
271
+ ) -> OptionPackageIntent:
272
+ return _package(
273
+ timestamp_ns,
274
+ package_id or f"{strategy}:{instrument_id}",
275
+ (_leg(instrument_id, side, 1.0, role=strategy, **kwargs),),
276
+ quantity=quantity,
277
+ strategy=strategy,
278
+ **_package_kwargs(kwargs),
279
+ )
280
+
281
+
282
+ def _package(
283
+ timestamp_ns: int,
284
+ package_id: str,
285
+ legs: Sequence[OptionPackageLeg],
286
+ *,
287
+ quantity: float,
288
+ strategy: str,
289
+ execution_policy: OptionPackageExecutionPolicy = OptionPackageExecutionPolicy.ATOMIC_ALL_OR_NONE,
290
+ max_debit: Optional[float] = None,
291
+ min_credit: Optional[float] = None,
292
+ tag: Optional[str] = None,
293
+ metadata: Optional[dict] = None,
294
+ ) -> OptionPackageIntent:
295
+ return OptionPackageIntent(
296
+ timestamp_ns=timestamp_ns,
297
+ package_id=package_id,
298
+ legs=tuple(legs),
299
+ quantity=quantity,
300
+ execution_policy=execution_policy,
301
+ max_debit=max_debit,
302
+ min_credit=min_credit,
303
+ tag=tag,
304
+ metadata={"template": strategy, **(metadata or {})},
305
+ )
306
+
307
+
308
+ def _leg(
309
+ instrument_id: str,
310
+ side: OrderSide,
311
+ ratio: float,
312
+ *,
313
+ role: str,
314
+ order_type: OrderType = OrderType.MARKET,
315
+ limit_price: Optional[float] = None,
316
+ tif: TimeInForce = TimeInForce.FOK,
317
+ tag: Optional[str] = None,
318
+ metadata: Optional[dict] = None,
319
+ **_,
320
+ ) -> OptionPackageLeg:
321
+ return OptionPackageLeg(
322
+ instrument_id=instrument_id,
323
+ side=side,
324
+ ratio=ratio,
325
+ order_type=order_type,
326
+ limit_price=limit_price,
327
+ tif=tif,
328
+ role=role,
329
+ tag=tag,
330
+ metadata=dict(metadata or {}),
331
+ )
332
+
333
+
334
+ def _package_kwargs(kwargs: dict) -> dict:
335
+ return {
336
+ key: kwargs[key]
337
+ for key in ("execution_policy", "max_debit", "min_credit", "tag", "metadata")
338
+ if key in kwargs
339
+ }
340
+
341
+
342
+ def _with_leg_metadata(kwargs: dict, extra: dict) -> dict:
343
+ out = dict(kwargs)
344
+ out["metadata"] = {**dict(kwargs.get("metadata") or {}), **extra}
345
+ return out
346
+
347
+
348
+ def _side_from_direction(direction: str, *, long_side: OrderSide) -> OrderSide:
349
+ value = str(direction).lower().strip()
350
+ if value == "long":
351
+ return long_side
352
+ if value == "short":
353
+ return OrderSide.SELL if long_side is OrderSide.BUY else OrderSide.BUY
354
+ raise ValueError("direction must be long or short")