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/orders.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quantbt.core.orders
|
|
3
|
+
-------------------
|
|
4
|
+
Order, fill, and trade records used by event-driven backends and result V2.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Dict, Optional, Sequence, Tuple
|
|
12
|
+
|
|
13
|
+
from .schema import LiquiditySide, OrderSide, OrderType, TimeInForce
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OrderAction(str, Enum):
|
|
17
|
+
"""Lifecycle command consumed by the native-event v2 compiler."""
|
|
18
|
+
|
|
19
|
+
PLACE = "place"
|
|
20
|
+
CANCEL = "cancel"
|
|
21
|
+
REPLACE = "replace"
|
|
22
|
+
AMEND = "amend"
|
|
23
|
+
CANCEL_ALL = "cancel_all"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OrderActivationPolicy(str, Enum):
|
|
27
|
+
"""When a placed child order becomes eligible for matching."""
|
|
28
|
+
|
|
29
|
+
IMMEDIATE = "immediate"
|
|
30
|
+
ON_PARENT_FIRST_FILL = "on_parent_first_fill"
|
|
31
|
+
ON_PARENT_FULL_FILL = "on_parent_full_fill"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class OrderIntent:
|
|
36
|
+
timestamp: object
|
|
37
|
+
symbol: str
|
|
38
|
+
side: OrderSide
|
|
39
|
+
order_type: OrderType
|
|
40
|
+
qty: float
|
|
41
|
+
price: Optional[float] = None
|
|
42
|
+
trigger_price: Optional[float] = None
|
|
43
|
+
tif: TimeInForce = TimeInForce.GTC
|
|
44
|
+
reduce_only: bool = False
|
|
45
|
+
order_id: Optional[str] = None
|
|
46
|
+
tag: Optional[str] = None
|
|
47
|
+
metadata: Dict = field(default_factory=dict)
|
|
48
|
+
|
|
49
|
+
def __post_init__(self) -> None:
|
|
50
|
+
if not self.symbol:
|
|
51
|
+
raise ValueError("symbol is required")
|
|
52
|
+
if self.qty <= 0.0:
|
|
53
|
+
raise ValueError("qty must be > 0")
|
|
54
|
+
if self.order_type in (OrderType.LIMIT, OrderType.STOP_LIMIT):
|
|
55
|
+
if self.price is None or self.price <= 0.0:
|
|
56
|
+
raise ValueError("limit orders require price > 0")
|
|
57
|
+
if self.order_type in (OrderType.STOP_MARKET, OrderType.STOP_LIMIT):
|
|
58
|
+
if self.trigger_price is None or self.trigger_price <= 0.0:
|
|
59
|
+
raise ValueError("stop orders require trigger_price > 0")
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def signed_qty(self) -> float:
|
|
63
|
+
return self.qty * self.side.sign
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class OrderCommand:
|
|
68
|
+
"""
|
|
69
|
+
Canonical order-lifecycle command for native-event v2 and adapters.
|
|
70
|
+
|
|
71
|
+
`OrderIntent` remains the backwards-compatible shorthand for an immediate
|
|
72
|
+
PLACE command. Phase 30A only defines and compiles this contract; lifecycle
|
|
73
|
+
matching is wired into a dedicated v2 engine phase.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
timestamp: object
|
|
77
|
+
action: OrderAction = OrderAction.PLACE
|
|
78
|
+
symbol: Optional[str] = None
|
|
79
|
+
side: Optional[OrderSide] = None
|
|
80
|
+
order_type: Optional[OrderType] = None
|
|
81
|
+
qty: Optional[float] = None
|
|
82
|
+
price: Optional[float] = None
|
|
83
|
+
trigger_price: Optional[float] = None
|
|
84
|
+
tif: TimeInForce = TimeInForce.GTC
|
|
85
|
+
reduce_only: bool = False
|
|
86
|
+
order_id: Optional[str] = None
|
|
87
|
+
target_order_id: Optional[str] = None
|
|
88
|
+
parent_order_id: Optional[str] = None
|
|
89
|
+
group_id: Optional[str] = None
|
|
90
|
+
oco_group_id: Optional[str] = None
|
|
91
|
+
activation_policy: OrderActivationPolicy = OrderActivationPolicy.IMMEDIATE
|
|
92
|
+
expires_at: Optional[object] = None
|
|
93
|
+
tag: Optional[str] = None
|
|
94
|
+
metadata: Dict = field(default_factory=dict)
|
|
95
|
+
tag_prefix: Optional[str] = None
|
|
96
|
+
|
|
97
|
+
def __post_init__(self) -> None:
|
|
98
|
+
action = _normalize_order_action(self.action)
|
|
99
|
+
object.__setattr__(self, "action", action)
|
|
100
|
+
|
|
101
|
+
activation = _normalize_activation_policy(self.activation_policy)
|
|
102
|
+
object.__setattr__(self, "activation_policy", activation)
|
|
103
|
+
|
|
104
|
+
if action in (OrderAction.PLACE, OrderAction.REPLACE):
|
|
105
|
+
if not self.symbol:
|
|
106
|
+
raise ValueError(f"{action.value} command requires symbol")
|
|
107
|
+
if self.side is None:
|
|
108
|
+
raise ValueError(f"{action.value} command requires side")
|
|
109
|
+
if self.order_type is None:
|
|
110
|
+
raise ValueError(f"{action.value} command requires order_type")
|
|
111
|
+
if self.qty is None or self.qty <= 0.0:
|
|
112
|
+
raise ValueError(f"{action.value} command requires qty > 0")
|
|
113
|
+
if self.order_type in (OrderType.LIMIT, OrderType.STOP_LIMIT):
|
|
114
|
+
if self.price is None or self.price <= 0.0:
|
|
115
|
+
raise ValueError("limit commands require price > 0")
|
|
116
|
+
if self.order_type in (OrderType.STOP_MARKET, OrderType.STOP_LIMIT):
|
|
117
|
+
if self.trigger_price is None or self.trigger_price <= 0.0:
|
|
118
|
+
raise ValueError("stop commands require trigger_price > 0")
|
|
119
|
+
if action is OrderAction.REPLACE and not self.target_order_id:
|
|
120
|
+
raise ValueError("replace command requires target_order_id")
|
|
121
|
+
elif action in (OrderAction.CANCEL, OrderAction.AMEND):
|
|
122
|
+
if not self.target_order_id:
|
|
123
|
+
raise ValueError(f"{action.value} command requires target_order_id")
|
|
124
|
+
if action is OrderAction.AMEND:
|
|
125
|
+
if self.qty is not None and self.qty <= 0.0:
|
|
126
|
+
raise ValueError("amend qty must be > 0")
|
|
127
|
+
if self.price is not None and self.price <= 0.0:
|
|
128
|
+
raise ValueError("amend price must be > 0")
|
|
129
|
+
if self.trigger_price is not None and self.trigger_price <= 0.0:
|
|
130
|
+
raise ValueError("amend trigger_price must be > 0")
|
|
131
|
+
elif action is OrderAction.CANCEL_ALL:
|
|
132
|
+
pass
|
|
133
|
+
else:
|
|
134
|
+
raise NotImplementedError(f"unsupported order action={action!r}")
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def from_intent(cls, intent: OrderIntent) -> "OrderCommand":
|
|
138
|
+
return cls(
|
|
139
|
+
timestamp=intent.timestamp,
|
|
140
|
+
action=OrderAction.PLACE,
|
|
141
|
+
symbol=intent.symbol,
|
|
142
|
+
side=intent.side,
|
|
143
|
+
order_type=intent.order_type,
|
|
144
|
+
qty=float(intent.qty),
|
|
145
|
+
price=intent.price,
|
|
146
|
+
trigger_price=intent.trigger_price,
|
|
147
|
+
tif=intent.tif,
|
|
148
|
+
reduce_only=intent.reduce_only,
|
|
149
|
+
order_id=intent.order_id,
|
|
150
|
+
tag=intent.tag,
|
|
151
|
+
metadata=dict(intent.metadata),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def to_intent(self) -> OrderIntent:
|
|
155
|
+
if self.action is not OrderAction.PLACE:
|
|
156
|
+
raise ValueError("only place commands can be converted to OrderIntent")
|
|
157
|
+
if self.symbol is None or self.side is None or self.order_type is None or self.qty is None:
|
|
158
|
+
raise ValueError("place command is incomplete")
|
|
159
|
+
return OrderIntent(
|
|
160
|
+
timestamp=self.timestamp,
|
|
161
|
+
symbol=self.symbol,
|
|
162
|
+
side=self.side,
|
|
163
|
+
order_type=self.order_type,
|
|
164
|
+
qty=float(self.qty),
|
|
165
|
+
price=self.price,
|
|
166
|
+
trigger_price=self.trigger_price,
|
|
167
|
+
tif=self.tif,
|
|
168
|
+
reduce_only=self.reduce_only,
|
|
169
|
+
order_id=self.order_id,
|
|
170
|
+
tag=self.tag,
|
|
171
|
+
metadata=dict(self.metadata),
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def signed_qty(self) -> float:
|
|
176
|
+
if self.side is None or self.qty is None:
|
|
177
|
+
return 0.0
|
|
178
|
+
return float(self.qty) * self.side.sign
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass(frozen=True)
|
|
182
|
+
class BasketIntent:
|
|
183
|
+
timestamp: object
|
|
184
|
+
basket_id: str
|
|
185
|
+
signal: float
|
|
186
|
+
gross_notional: Optional[float] = None
|
|
187
|
+
tag: Optional[str] = None
|
|
188
|
+
metadata: Dict = field(default_factory=dict)
|
|
189
|
+
|
|
190
|
+
def __post_init__(self) -> None:
|
|
191
|
+
if not self.basket_id:
|
|
192
|
+
raise ValueError("basket_id is required")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass(frozen=True)
|
|
196
|
+
class Fill:
|
|
197
|
+
timestamp: object
|
|
198
|
+
symbol: str
|
|
199
|
+
side: OrderSide
|
|
200
|
+
qty: float
|
|
201
|
+
price: float
|
|
202
|
+
fee: float = 0.0
|
|
203
|
+
liquidity: LiquiditySide = LiquiditySide.TAKER
|
|
204
|
+
order_id: Optional[str] = None
|
|
205
|
+
trade_id: Optional[str] = None
|
|
206
|
+
metadata: Dict = field(default_factory=dict)
|
|
207
|
+
|
|
208
|
+
def __post_init__(self) -> None:
|
|
209
|
+
if not self.symbol:
|
|
210
|
+
raise ValueError("symbol is required")
|
|
211
|
+
if self.qty <= 0.0:
|
|
212
|
+
raise ValueError("qty must be > 0")
|
|
213
|
+
if self.price <= 0.0:
|
|
214
|
+
raise ValueError("price must be > 0")
|
|
215
|
+
if self.fee < 0.0:
|
|
216
|
+
raise ValueError("fee must be >= 0")
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def signed_qty(self) -> float:
|
|
220
|
+
return self.qty * self.side.sign
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def notional(self) -> float:
|
|
224
|
+
return self.qty * self.price
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@dataclass(frozen=True)
|
|
228
|
+
class Trade:
|
|
229
|
+
symbol: str
|
|
230
|
+
qty: float
|
|
231
|
+
side: OrderSide
|
|
232
|
+
opened_at: object
|
|
233
|
+
closed_at: object
|
|
234
|
+
avg_entry: float
|
|
235
|
+
avg_exit: float
|
|
236
|
+
realized_pnl: float
|
|
237
|
+
fees: float = 0.0
|
|
238
|
+
trade_id: Optional[str] = None
|
|
239
|
+
metadata: Dict = field(default_factory=dict)
|
|
240
|
+
|
|
241
|
+
def __post_init__(self) -> None:
|
|
242
|
+
if not self.symbol:
|
|
243
|
+
raise ValueError("symbol is required")
|
|
244
|
+
if self.qty <= 0.0:
|
|
245
|
+
raise ValueError("qty must be > 0")
|
|
246
|
+
if self.avg_entry <= 0.0 or self.avg_exit <= 0.0:
|
|
247
|
+
raise ValueError("avg_entry and avg_exit must be > 0")
|
|
248
|
+
if self.fees < 0.0:
|
|
249
|
+
raise ValueError("fees must be >= 0")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _normalize_order_action(action: OrderAction | str) -> OrderAction:
|
|
253
|
+
if isinstance(action, OrderAction):
|
|
254
|
+
return action
|
|
255
|
+
return OrderAction(str(action))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _normalize_activation_policy(policy: OrderActivationPolicy | str) -> OrderActivationPolicy:
|
|
259
|
+
if isinstance(policy, OrderActivationPolicy):
|
|
260
|
+
return policy
|
|
261
|
+
return OrderActivationPolicy(str(policy))
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def order_intents_to_lifecycle_commands(
|
|
265
|
+
orders: Sequence[OrderIntent],
|
|
266
|
+
*,
|
|
267
|
+
linked_metadata: bool = True,
|
|
268
|
+
) -> Tuple[OrderCommand, ...]:
|
|
269
|
+
"""
|
|
270
|
+
Convert `OrderIntent` records into lifecycle-v2 `OrderCommand` records.
|
|
271
|
+
|
|
272
|
+
Structured package builders already carry parent/OCO information in
|
|
273
|
+
metadata. This helper lifts those fields into the explicit command contract
|
|
274
|
+
while preserving all old order intent fields for compatibility.
|
|
275
|
+
"""
|
|
276
|
+
commands = []
|
|
277
|
+
tag_to_id = {}
|
|
278
|
+
for idx, order in enumerate(orders):
|
|
279
|
+
order_id = order.order_id or order.tag or f"order-{idx}"
|
|
280
|
+
tag_to_id[order.tag] = order_id
|
|
281
|
+
|
|
282
|
+
for idx, order in enumerate(orders):
|
|
283
|
+
metadata = dict(order.metadata)
|
|
284
|
+
order_id = order.order_id or order.tag or f"order-{idx}"
|
|
285
|
+
parent_id = None
|
|
286
|
+
oco_group_id = None
|
|
287
|
+
activation = OrderActivationPolicy.IMMEDIATE
|
|
288
|
+
group_id = None
|
|
289
|
+
if linked_metadata:
|
|
290
|
+
group_id = metadata.get("group_id") or metadata.get("package_id") or metadata.get("arb_id")
|
|
291
|
+
leg_role = str(metadata.get("leg_role", "")).lower().strip()
|
|
292
|
+
if order.reduce_only or leg_role in {"take_profit", "stop_loss", "exit"}:
|
|
293
|
+
oco_group_id = metadata.get("oco_group_id")
|
|
294
|
+
parent_ref = metadata.get("parent_order_id") or metadata.get("parent_tag")
|
|
295
|
+
if parent_ref is not None:
|
|
296
|
+
parent_id = tag_to_id.get(parent_ref, str(parent_ref))
|
|
297
|
+
activation = OrderActivationPolicy.ON_PARENT_FIRST_FILL
|
|
298
|
+
commands.append(
|
|
299
|
+
OrderCommand(
|
|
300
|
+
timestamp=order.timestamp,
|
|
301
|
+
action=OrderAction.PLACE,
|
|
302
|
+
symbol=order.symbol,
|
|
303
|
+
side=order.side,
|
|
304
|
+
order_type=order.order_type,
|
|
305
|
+
qty=float(order.qty),
|
|
306
|
+
price=order.price,
|
|
307
|
+
trigger_price=order.trigger_price,
|
|
308
|
+
tif=order.tif,
|
|
309
|
+
reduce_only=order.reduce_only,
|
|
310
|
+
order_id=order_id,
|
|
311
|
+
parent_order_id=parent_id,
|
|
312
|
+
group_id=None if group_id is None else str(group_id),
|
|
313
|
+
oco_group_id=None if oco_group_id is None else str(oco_group_id),
|
|
314
|
+
activation_policy=activation,
|
|
315
|
+
tag=order.tag,
|
|
316
|
+
metadata=metadata,
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
return tuple(commands)
|