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,422 @@
1
+ """
2
+ OrderIntent compiler for native event kernels.
3
+
4
+ The compiler is an internal performance helper: it converts immutable order
5
+ intent objects into contiguous ndarray inputs while preserving the old event
6
+ kernel semantics.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, replace
12
+ import hashlib
13
+ from typing import Dict, Sequence, Tuple
14
+
15
+ import numpy as np
16
+ import pandas as pd
17
+
18
+ from .event import (
19
+ ORDER_TYPE_LIMIT,
20
+ ORDER_TYPE_MARKET,
21
+ ORDER_TYPE_STOP_LIMIT,
22
+ ORDER_TYPE_STOP_MARKET,
23
+ TIF_FOK,
24
+ TIF_GTC,
25
+ TIF_GTD,
26
+ TIF_IOC,
27
+ )
28
+ from .orders import OrderAction, OrderActivationPolicy, OrderCommand, OrderIntent
29
+ from .preprocessor import MarketDataSignature, market_data_signature
30
+ from .schema import OrderSide, OrderType, TimeInForce
31
+
32
+
33
+ COMMAND_ACTION_PLACE = 0
34
+ COMMAND_ACTION_CANCEL = 1
35
+ COMMAND_ACTION_REPLACE = 2
36
+ COMMAND_ACTION_AMEND = 3
37
+ COMMAND_ACTION_CANCEL_ALL = 4
38
+
39
+ ACTIVATION_IMMEDIATE = 0
40
+ ACTIVATION_ON_PARENT_FIRST_FILL = 1
41
+ ACTIVATION_ON_PARENT_FULL_FILL = 2
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class CompiledOrderArrays:
46
+ index_signature: MarketDataSignature
47
+ symbols: Tuple[str, ...]
48
+ sorted_orders: Tuple[Tuple[int, OrderIntent], ...]
49
+ order_ptr: np.ndarray
50
+ order_symbol: np.ndarray
51
+ order_side: np.ndarray
52
+ order_type: np.ndarray
53
+ order_qty: np.ndarray
54
+ order_price: np.ndarray
55
+ order_tif: np.ndarray
56
+ original_index: np.ndarray
57
+
58
+ @property
59
+ def n_orders(self) -> int:
60
+ return int(len(self.original_index))
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class CompiledOrderCommandArrays:
65
+ """
66
+ Array contract for native-event lifecycle commands.
67
+
68
+ This v2 compiler is intentionally separate from `CompiledOrderArrays` so
69
+ the legacy v1 kernel remains byte-for-byte compatible with old endpoints.
70
+ """
71
+
72
+ index_signature: MarketDataSignature
73
+ symbols: Tuple[str, ...]
74
+ sorted_commands: Tuple[Tuple[int, OrderCommand], ...]
75
+ command_ptr: np.ndarray
76
+ command_bar: np.ndarray
77
+ command_action: np.ndarray
78
+ command_symbol: np.ndarray
79
+ command_side: np.ndarray
80
+ command_type: np.ndarray
81
+ command_qty: np.ndarray
82
+ command_price: np.ndarray
83
+ command_trigger_price: np.ndarray
84
+ command_tif: np.ndarray
85
+ command_reduce_only: np.ndarray
86
+ command_order_id: np.ndarray
87
+ command_target_order_id: np.ndarray
88
+ command_parent_order_id: np.ndarray
89
+ command_group_id: np.ndarray
90
+ command_oco_group_id: np.ndarray
91
+ command_activation: np.ndarray
92
+ command_expires_bar: np.ndarray
93
+ original_index: np.ndarray
94
+ id_values: Tuple[str, ...]
95
+ tape_fingerprint: str = ""
96
+
97
+ @property
98
+ def n_commands(self) -> int:
99
+ return int(len(self.original_index))
100
+
101
+
102
+ def command_tape_fingerprint(compiled: CompiledOrderCommandArrays) -> str:
103
+ """Return a complete identity for the immutable primitive command tape.
104
+
105
+ The digest includes fields used by Rust validation as well as execution.
106
+ It is computed when the compiler creates a tape so repeated score calls do
107
+ not hash every array on the measured execution path.
108
+ """
109
+
110
+ digest = hashlib.blake2b(digest_size=16)
111
+ digest.update(repr(compiled.index_signature).encode("utf-8"))
112
+ digest.update(repr(tuple(compiled.symbols)).encode("utf-8"))
113
+ digest.update(repr(tuple(compiled.id_values)).encode("utf-8"))
114
+ for name in (
115
+ "command_ptr",
116
+ "command_bar",
117
+ "command_action",
118
+ "command_symbol",
119
+ "command_side",
120
+ "command_type",
121
+ "command_qty",
122
+ "command_price",
123
+ "command_trigger_price",
124
+ "command_tif",
125
+ "command_reduce_only",
126
+ "command_order_id",
127
+ "command_target_order_id",
128
+ "command_parent_order_id",
129
+ "command_group_id",
130
+ "command_oco_group_id",
131
+ "command_activation",
132
+ "command_expires_bar",
133
+ "original_index",
134
+ ):
135
+ array = np.ascontiguousarray(getattr(compiled, name))
136
+ digest.update(name.encode("ascii"))
137
+ digest.update(str(array.dtype).encode("ascii"))
138
+ digest.update(str(array.shape).encode("ascii"))
139
+ digest.update(array.tobytes())
140
+ return digest.hexdigest()
141
+
142
+
143
+ def compile_order_intents(
144
+ idx: pd.DatetimeIndex,
145
+ orders: Sequence[OrderIntent],
146
+ symbol_to_col: Dict[str, int],
147
+ ) -> CompiledOrderArrays:
148
+ """
149
+ Compile order intents into the exact array contract expected by event v1.
150
+
151
+ The sort is stable by effective bar, matching Python's previous
152
+ `sorted(enumerate(orders), key=bar_index)` behavior.
153
+ """
154
+ n_orders = len(orders)
155
+ order_bar_unsorted = np.zeros(n_orders, dtype=np.int64)
156
+ symbol_unsorted = np.zeros(n_orders, dtype=np.int64)
157
+ side_unsorted = np.zeros(n_orders, dtype=np.int64)
158
+ type_unsorted = np.zeros(n_orders, dtype=np.int64)
159
+ qty_unsorted = np.zeros(n_orders, dtype=np.float64)
160
+ price_unsorted = np.zeros(n_orders, dtype=np.float64)
161
+ tif_unsorted = np.zeros(n_orders, dtype=np.int64)
162
+ original_unsorted = np.arange(n_orders, dtype=np.int64)
163
+
164
+ idx_ns = idx.view("int64")
165
+ ts_ns = np.zeros(n_orders, dtype=np.int64)
166
+ for k, order in enumerate(orders):
167
+ if order.symbol not in symbol_to_col:
168
+ raise ValueError(f"order symbol {order.symbol!r} is not in symbols")
169
+ ts = pd.Timestamp(order.timestamp)
170
+ if ts.tz is None:
171
+ ts = ts.tz_localize("UTC")
172
+ else:
173
+ ts = ts.tz_convert("UTC")
174
+ ts_ns[k] = ts.value
175
+ symbol_unsorted[k] = symbol_to_col[order.symbol]
176
+ side_unsorted[k] = _side_code(order.side)
177
+ type_unsorted[k] = _order_type_code(order.order_type)
178
+ qty_unsorted[k] = float(order.qty)
179
+ price_unsorted[k] = 0.0 if order.price is None else float(order.price)
180
+ tif_unsorted[k] = _tif_code(order.tif)
181
+
182
+ order_bar_unsorted = np.searchsorted(idx_ns, ts_ns, side="left").astype(np.int64)
183
+ if n_orders > 0 and int(order_bar_unsorted.max()) >= len(idx):
184
+ raise ValueError("order timestamp is after the available data")
185
+ order_sort = np.argsort(order_bar_unsorted, kind="stable")
186
+
187
+ order_bar = np.ascontiguousarray(order_bar_unsorted[order_sort], dtype=np.int64)
188
+ order_symbol = np.ascontiguousarray(symbol_unsorted[order_sort], dtype=np.int64)
189
+ order_side = np.ascontiguousarray(side_unsorted[order_sort], dtype=np.int64)
190
+ order_type = np.ascontiguousarray(type_unsorted[order_sort], dtype=np.int64)
191
+ order_qty = np.ascontiguousarray(qty_unsorted[order_sort], dtype=np.float64)
192
+ order_price = np.ascontiguousarray(price_unsorted[order_sort], dtype=np.float64)
193
+ order_tif = np.ascontiguousarray(tif_unsorted[order_sort], dtype=np.int64)
194
+ original_index = np.ascontiguousarray(original_unsorted[order_sort], dtype=np.int64)
195
+
196
+ order_ptr = np.zeros(len(idx) + 1, dtype=np.int64)
197
+ if n_orders > 0:
198
+ counts = np.bincount(order_bar + 1, minlength=len(idx) + 1)
199
+ order_ptr[:] = np.cumsum(counts, dtype=np.int64)
200
+
201
+ sorted_orders = tuple((int(orig_idx), orders[int(orig_idx)]) for orig_idx in original_index)
202
+ return CompiledOrderArrays(
203
+ index_signature=market_data_signature(idx, list(symbol_to_col.keys())),
204
+ symbols=tuple(symbol_to_col.keys()),
205
+ sorted_orders=sorted_orders,
206
+ order_ptr=order_ptr,
207
+ order_symbol=order_symbol,
208
+ order_side=order_side,
209
+ order_type=order_type,
210
+ order_qty=order_qty,
211
+ order_price=order_price,
212
+ order_tif=order_tif,
213
+ original_index=original_index,
214
+ )
215
+
216
+
217
+ def compile_order_commands(
218
+ idx: pd.DatetimeIndex,
219
+ commands: Sequence[OrderCommand],
220
+ symbol_to_col: Dict[str, int],
221
+ ) -> CompiledOrderCommandArrays:
222
+ """
223
+ Compile lifecycle commands into contiguous arrays for native-event v2.
224
+
225
+ The compiler validates timestamps/symbols, keeps a stable command order
226
+ within each bar, and maps sparse string IDs to dense integer codes. No fill
227
+ or accounting logic is performed here; this is only the deterministic input
228
+ contract for a lifecycle kernel or adapter.
229
+ """
230
+ n_commands = len(commands)
231
+ command_bar_unsorted = np.zeros(n_commands, dtype=np.int64)
232
+ action_unsorted = np.zeros(n_commands, dtype=np.int64)
233
+ symbol_unsorted = np.full(n_commands, -1, dtype=np.int64)
234
+ side_unsorted = np.zeros(n_commands, dtype=np.int64)
235
+ type_unsorted = np.full(n_commands, -1, dtype=np.int64)
236
+ qty_unsorted = np.zeros(n_commands, dtype=np.float64)
237
+ price_unsorted = np.zeros(n_commands, dtype=np.float64)
238
+ trigger_unsorted = np.zeros(n_commands, dtype=np.float64)
239
+ tif_unsorted = np.full(n_commands, TIF_GTC, dtype=np.int64)
240
+ reduce_only_unsorted = np.zeros(n_commands, dtype=np.int64)
241
+ order_id_unsorted = np.full(n_commands, -1, dtype=np.int64)
242
+ target_id_unsorted = np.full(n_commands, -1, dtype=np.int64)
243
+ parent_id_unsorted = np.full(n_commands, -1, dtype=np.int64)
244
+ group_id_unsorted = np.full(n_commands, -1, dtype=np.int64)
245
+ oco_id_unsorted = np.full(n_commands, -1, dtype=np.int64)
246
+ activation_unsorted = np.zeros(n_commands, dtype=np.int64)
247
+ expires_bar_unsorted = np.full(n_commands, -1, dtype=np.int64)
248
+ original_unsorted = np.arange(n_commands, dtype=np.int64)
249
+
250
+ id_map: Dict[str, int] = {}
251
+ idx_ns = idx.view("int64")
252
+ ts_ns = np.zeros(n_commands, dtype=np.int64)
253
+ for k, command in enumerate(commands):
254
+ ts_ns[k] = _timestamp_ns(command.timestamp)
255
+ action_unsorted[k] = _action_code(command.action)
256
+ if command.symbol is not None:
257
+ if command.symbol not in symbol_to_col:
258
+ raise ValueError(f"command symbol {command.symbol!r} is not in symbols")
259
+ symbol_unsorted[k] = symbol_to_col[command.symbol]
260
+ if command.side is not None:
261
+ side_unsorted[k] = _side_code(command.side)
262
+ if command.order_type is not None:
263
+ type_unsorted[k] = _command_order_type_code(command.order_type)
264
+ if command.qty is not None:
265
+ qty_unsorted[k] = float(command.qty)
266
+ price_unsorted[k] = 0.0 if command.price is None else float(command.price)
267
+ trigger_unsorted[k] = 0.0 if command.trigger_price is None else float(command.trigger_price)
268
+ tif_unsorted[k] = _tif_code(command.tif)
269
+ reduce_only_unsorted[k] = 1 if command.reduce_only else 0
270
+ order_id_unsorted[k] = _id_code(command.order_id, id_map)
271
+ target_id_unsorted[k] = _id_code(command.target_order_id, id_map)
272
+ parent_id_unsorted[k] = _id_code(command.parent_order_id, id_map)
273
+ group_id_unsorted[k] = _id_code(command.group_id, id_map)
274
+ oco_id_unsorted[k] = _id_code(command.oco_group_id, id_map)
275
+ activation_unsorted[k] = _activation_code(command.activation_policy)
276
+ if command.expires_at is not None:
277
+ expires_bar_unsorted[k] = int(np.searchsorted(idx_ns, _timestamp_ns(command.expires_at), side="left"))
278
+
279
+ command_bar_unsorted = np.searchsorted(idx_ns, ts_ns, side="left").astype(np.int64)
280
+ if n_commands > 0 and int(command_bar_unsorted.max()) >= len(idx):
281
+ raise ValueError("command timestamp is after the available data")
282
+ order_sort = np.argsort(command_bar_unsorted, kind="stable")
283
+
284
+ command_bar = np.ascontiguousarray(command_bar_unsorted[order_sort], dtype=np.int64)
285
+ command_ptr = np.zeros(len(idx) + 1, dtype=np.int64)
286
+ if n_commands > 0:
287
+ counts = np.bincount(command_bar + 1, minlength=len(idx) + 1)
288
+ command_ptr[:] = np.cumsum(counts, dtype=np.int64)
289
+
290
+ original_index = np.ascontiguousarray(original_unsorted[order_sort], dtype=np.int64)
291
+ sorted_commands = tuple((int(orig_idx), commands[int(orig_idx)]) for orig_idx in original_index)
292
+ id_values = tuple(sorted(id_map, key=id_map.get))
293
+ compiled = CompiledOrderCommandArrays(
294
+ index_signature=market_data_signature(idx, list(symbol_to_col.keys())),
295
+ symbols=tuple(symbol_to_col.keys()),
296
+ sorted_commands=sorted_commands,
297
+ command_ptr=command_ptr,
298
+ command_bar=np.ascontiguousarray(command_bar, dtype=np.int64),
299
+ command_action=np.ascontiguousarray(action_unsorted[order_sort], dtype=np.int64),
300
+ command_symbol=np.ascontiguousarray(symbol_unsorted[order_sort], dtype=np.int64),
301
+ command_side=np.ascontiguousarray(side_unsorted[order_sort], dtype=np.int64),
302
+ command_type=np.ascontiguousarray(type_unsorted[order_sort], dtype=np.int64),
303
+ command_qty=np.ascontiguousarray(qty_unsorted[order_sort], dtype=np.float64),
304
+ command_price=np.ascontiguousarray(price_unsorted[order_sort], dtype=np.float64),
305
+ command_trigger_price=np.ascontiguousarray(trigger_unsorted[order_sort], dtype=np.float64),
306
+ command_tif=np.ascontiguousarray(tif_unsorted[order_sort], dtype=np.int64),
307
+ command_reduce_only=np.ascontiguousarray(reduce_only_unsorted[order_sort], dtype=np.int64),
308
+ command_order_id=np.ascontiguousarray(order_id_unsorted[order_sort], dtype=np.int64),
309
+ command_target_order_id=np.ascontiguousarray(target_id_unsorted[order_sort], dtype=np.int64),
310
+ command_parent_order_id=np.ascontiguousarray(parent_id_unsorted[order_sort], dtype=np.int64),
311
+ command_group_id=np.ascontiguousarray(group_id_unsorted[order_sort], dtype=np.int64),
312
+ command_oco_group_id=np.ascontiguousarray(oco_id_unsorted[order_sort], dtype=np.int64),
313
+ command_activation=np.ascontiguousarray(activation_unsorted[order_sort], dtype=np.int64),
314
+ command_expires_bar=np.ascontiguousarray(expires_bar_unsorted[order_sort], dtype=np.int64),
315
+ original_index=original_index,
316
+ id_values=id_values,
317
+ )
318
+ for name in (
319
+ "command_ptr",
320
+ "command_bar",
321
+ "command_action",
322
+ "command_symbol",
323
+ "command_side",
324
+ "command_type",
325
+ "command_qty",
326
+ "command_price",
327
+ "command_trigger_price",
328
+ "command_tif",
329
+ "command_reduce_only",
330
+ "command_order_id",
331
+ "command_target_order_id",
332
+ "command_parent_order_id",
333
+ "command_group_id",
334
+ "command_oco_group_id",
335
+ "command_activation",
336
+ "command_expires_bar",
337
+ "original_index",
338
+ ):
339
+ getattr(compiled, name).flags.writeable = False
340
+ return replace(compiled, tape_fingerprint=command_tape_fingerprint(compiled))
341
+
342
+
343
+ def order_intents_to_commands(orders: Sequence[OrderIntent]) -> Tuple[OrderCommand, ...]:
344
+ """Convert legacy intents to immediate PLACE lifecycle commands."""
345
+ return tuple(OrderCommand.from_intent(order) for order in orders)
346
+
347
+
348
+ def _side_code(side: OrderSide) -> int:
349
+ return 1 if side is OrderSide.BUY else -1
350
+
351
+
352
+ def _order_type_code(order_type: OrderType) -> int:
353
+ if order_type is OrderType.MARKET:
354
+ return ORDER_TYPE_MARKET
355
+ if order_type is OrderType.LIMIT:
356
+ return ORDER_TYPE_LIMIT
357
+ raise NotImplementedError(f"unsupported order_type={order_type!r}")
358
+
359
+
360
+ def _command_order_type_code(order_type: OrderType) -> int:
361
+ if order_type is OrderType.MARKET:
362
+ return ORDER_TYPE_MARKET
363
+ if order_type is OrderType.LIMIT:
364
+ return ORDER_TYPE_LIMIT
365
+ if order_type is OrderType.STOP_MARKET:
366
+ return ORDER_TYPE_STOP_MARKET
367
+ if order_type is OrderType.STOP_LIMIT:
368
+ return ORDER_TYPE_STOP_LIMIT
369
+ raise NotImplementedError(f"unsupported order_type={order_type!r}")
370
+
371
+
372
+ def _tif_code(tif: TimeInForce) -> int:
373
+ if tif is TimeInForce.GTC:
374
+ return TIF_GTC
375
+ if tif is TimeInForce.IOC:
376
+ return TIF_IOC
377
+ if tif is TimeInForce.FOK:
378
+ return TIF_FOK
379
+ if tif is TimeInForce.GTD:
380
+ return TIF_GTD
381
+ raise NotImplementedError(f"unsupported tif={tif!r}")
382
+
383
+
384
+ def _action_code(action: OrderAction) -> int:
385
+ if action is OrderAction.PLACE:
386
+ return COMMAND_ACTION_PLACE
387
+ if action is OrderAction.CANCEL:
388
+ return COMMAND_ACTION_CANCEL
389
+ if action is OrderAction.REPLACE:
390
+ return COMMAND_ACTION_REPLACE
391
+ if action is OrderAction.AMEND:
392
+ return COMMAND_ACTION_AMEND
393
+ if action is OrderAction.CANCEL_ALL:
394
+ return COMMAND_ACTION_CANCEL_ALL
395
+ raise NotImplementedError(f"unsupported action={action!r}")
396
+
397
+
398
+ def _activation_code(policy: OrderActivationPolicy) -> int:
399
+ if policy is OrderActivationPolicy.IMMEDIATE:
400
+ return ACTIVATION_IMMEDIATE
401
+ if policy is OrderActivationPolicy.ON_PARENT_FIRST_FILL:
402
+ return ACTIVATION_ON_PARENT_FIRST_FILL
403
+ if policy is OrderActivationPolicy.ON_PARENT_FULL_FILL:
404
+ return ACTIVATION_ON_PARENT_FULL_FILL
405
+ raise NotImplementedError(f"unsupported activation_policy={policy!r}")
406
+
407
+
408
+ def _id_code(value: str | None, id_map: Dict[str, int]) -> int:
409
+ if value is None or value == "":
410
+ return -1
411
+ if value not in id_map:
412
+ id_map[value] = len(id_map)
413
+ return id_map[value]
414
+
415
+
416
+ def _timestamp_ns(value: object) -> int:
417
+ ts = pd.Timestamp(value)
418
+ if ts.tz is None:
419
+ ts = ts.tz_localize("UTC")
420
+ else:
421
+ ts = ts.tz_convert("UTC")
422
+ return int(ts.value)