onesecondtrader 0.50.0__py3-none-any.whl → 0.51.0__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.
@@ -0,0 +1,11 @@
1
+ """
2
+ Provides interfaces for order execution via a simulated broker and adapters to real venues.
3
+ """
4
+
5
+ from onesecondtrader.brokers.base import BrokerBase
6
+ from onesecondtrader.brokers.simulated import SimulatedBroker
7
+
8
+ __all__ = [
9
+ "BrokerBase",
10
+ "SimulatedBroker",
11
+ ]
@@ -0,0 +1,110 @@
1
+ import abc
2
+
3
+ from onesecondtrader import events, messaging
4
+
5
+
6
+ class BrokerBase(messaging.Subscriber):
7
+ """
8
+ Abstract base class for broker components.
9
+
10
+ A broker component receives order-related request events from the event bus and translates them into actions against an external execution venue or simulated environment.
11
+ Responses to these requests are published back onto the event bus.
12
+
13
+ This class defines the event-handling interface and subscription logic common to all broker implementations.
14
+ """
15
+
16
+ def __init__(self, event_bus: messaging.EventBus) -> None:
17
+ """
18
+ Initialize the broker and subscribe to order request events.
19
+
20
+ Parameters:
21
+ event_bus:
22
+ Event bus used for receiving order requests and publishing response events.
23
+ """
24
+ super().__init__(event_bus)
25
+
26
+ self._subscribe(
27
+ events.requests.OrderSubmissionRequest,
28
+ events.requests.OrderCancellationRequest,
29
+ events.requests.OrderModificationRequest,
30
+ )
31
+
32
+ @abc.abstractmethod
33
+ def connect(self) -> None:
34
+ """
35
+ Establish a connection to the external broker API.
36
+ For simulated broker, this method is a no-op.
37
+
38
+ Implementations are responsible for initializing any external resources required to submit, modify, or cancel orders.
39
+ """
40
+ pass
41
+
42
+ def disconnect(self) -> None:
43
+ """
44
+ Disconnect the external broker API and stop event processing.
45
+ For simulated broker, this method is a no-op.
46
+
47
+ This method shuts down the subscriber and releases associated resources.
48
+ """
49
+ self.shutdown()
50
+
51
+ def _on_event(self, event: events.EventBase) -> None:
52
+ """
53
+ Dispatch incoming order-related events to the appropriate handler.
54
+
55
+ Parameters:
56
+ event:
57
+ Incoming event received from the event bus.
58
+ """
59
+ match event:
60
+ case events.requests.OrderSubmissionRequest() as submit_order:
61
+ self._on_submit_order(submit_order)
62
+ case events.requests.OrderCancellationRequest() as cancel_order:
63
+ self._on_cancel_order(cancel_order)
64
+ case events.requests.OrderModificationRequest() as modify_order:
65
+ self._on_modify_order(modify_order)
66
+ case _:
67
+ return
68
+
69
+ @abc.abstractmethod
70
+ def _on_submit_order(self, event: events.requests.OrderSubmissionRequest) -> None:
71
+ """
72
+ Handle an order submission request.
73
+
74
+ Parameters:
75
+ event:
76
+ Order submission request event.
77
+ """
78
+ pass
79
+
80
+ @abc.abstractmethod
81
+ def _on_cancel_order(self, event: events.requests.OrderCancellationRequest) -> None:
82
+ """
83
+ Handle an order cancellation request.
84
+
85
+ Parameters:
86
+ event:
87
+ Order cancellation request event.
88
+ """
89
+ pass
90
+
91
+ @abc.abstractmethod
92
+ def _on_modify_order(self, event: events.requests.OrderModificationRequest) -> None:
93
+ """
94
+ Handle an order modification request.
95
+
96
+ Parameters:
97
+ event:
98
+ Order modification request event.
99
+ """
100
+ pass
101
+
102
+ def _respond(self, response_event: events.responses.ResponseBase) -> None:
103
+ """
104
+ Publish a response event to the event bus.
105
+
106
+ Parameters:
107
+ response_event:
108
+ Response event generated by the broker.
109
+ """
110
+ self._publish(response_event)
@@ -0,0 +1,494 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import uuid
5
+
6
+ from onesecondtrader import events, messaging, models
7
+ from onesecondtrader.brokers.base import BrokerBase
8
+
9
+
10
+ @dataclasses.dataclass
11
+ class _PendingOrder:
12
+ """
13
+ Internal order state tracked by the simulated broker.
14
+
15
+ This structure represents broker-side pending order state and is distinct from order request events.
16
+ It is used to evaluate trigger conditions against incoming market bars and to generate fills when conditions are met.
17
+ """
18
+
19
+ order_id: uuid.UUID
20
+ symbol: str
21
+ order_type: models.OrderType
22
+ side: models.TradeSide
23
+ quantity: float
24
+ limit_price: float | None = None
25
+ stop_price: float | None = None
26
+
27
+
28
+ class SimulatedBroker(BrokerBase):
29
+ """
30
+ Event-driven simulated broker for backtesting.
31
+
32
+ The broker subscribes to order request events and market bar events.
33
+ Order requests are validated and accepted or rejected immediately.
34
+ Accepted orders are stored as pending broker-side state and evaluated against each incoming
35
+ bar.
36
+ When an order triggers, a fill event is published with a deterministic fill price model based on the bar's OHLC values.
37
+
38
+ The broker publishes response events using the event timestamp to preserve simulated time consistency.
39
+ """
40
+
41
+ commission_per_unit: float = 0.0
42
+ minimum_commission_per_order: float = 0.0
43
+
44
+ def __init__(self, event_bus: messaging.EventBus) -> None:
45
+ """
46
+ parameters:
47
+ event_bus:
48
+ Event bus used to receive order requests and market bars, and to publish broker responses and fills.
49
+ """
50
+ self._pending_market_orders: dict[uuid.UUID, _PendingOrder] = {}
51
+ self._pending_limit_orders: dict[uuid.UUID, _PendingOrder] = {}
52
+ self._pending_stop_orders: dict[uuid.UUID, _PendingOrder] = {}
53
+ self._pending_stop_limit_orders: dict[uuid.UUID, _PendingOrder] = {}
54
+
55
+ super().__init__(event_bus)
56
+ self._subscribe(events.market.BarReceived)
57
+
58
+ def connect(self) -> None:
59
+ """
60
+ Establish broker readiness.
61
+
62
+ The simulated broker has no external connectivity requirements.
63
+ This method is a no-op and exists to satisfy the broker interface.
64
+ """
65
+ pass
66
+
67
+ def _on_event(self, event: events.EventBase) -> None:
68
+ """
69
+ Dispatch incoming events.
70
+
71
+ Market bar events are routed to bar processing.
72
+ All other events are delegated to the broker base class for order request handling.
73
+
74
+ parameters:
75
+ event:
76
+ Incoming event received from the event bus.
77
+ """
78
+ match event:
79
+ case events.market.BarReceived() as bar:
80
+ self._on_bar(bar)
81
+ case _:
82
+ super()._on_event(event)
83
+
84
+ def _on_bar(self, event: events.market.BarReceived) -> None:
85
+ """
86
+ Process an incoming market bar.
87
+
88
+ Pending orders are evaluated against the bar in a fixed sequence to provide deterministic behavior.
89
+ Crucially, limit orders are processed after stop limit orders to ensure that limit orders created by stop limit orders are evaluated against the same bar.
90
+
91
+ parameters:
92
+ event:
93
+ Market bar used to trigger and price simulated fills.
94
+ """
95
+ self._process_market_orders(event)
96
+ self._process_stop_orders(event)
97
+ self._process_stop_limit_orders(event)
98
+ self._process_limit_orders(event)
99
+
100
+ def _process_market_orders(self, event: events.market.BarReceived) -> None:
101
+ """
102
+ Fill pending market orders for the bar symbol.
103
+
104
+ Market orders are filled at the bar open price on the next received bar for the matching symbol.
105
+
106
+ parameters:
107
+ event:
108
+ Market bar providing the simulated fill price and timestamps.
109
+ """
110
+ for order_id, order in list(self._pending_market_orders.items()):
111
+ if order.symbol != event.symbol:
112
+ continue
113
+
114
+ self._publish(
115
+ events.orders.FillEvent(
116
+ ts_event_ns=event.ts_event_ns,
117
+ ts_broker_ns=event.ts_event_ns,
118
+ associated_order_id=order.order_id,
119
+ symbol=order.symbol,
120
+ side=order.side,
121
+ quantity_filled=order.quantity,
122
+ fill_price=event.open,
123
+ commission=max(
124
+ order.quantity * self.commission_per_unit,
125
+ self.minimum_commission_per_order,
126
+ ),
127
+ )
128
+ )
129
+ del self._pending_market_orders[order_id]
130
+
131
+ def _process_stop_orders(self, event: events.market.BarReceived) -> None:
132
+ """
133
+ Evaluate and fill pending stop orders for the bar symbol.
134
+
135
+ Stop orders trigger when the bar crosses the stop level.
136
+ The fill price is modeled as the worse of the stop price and the bar open in the direction of the trade.
137
+
138
+ parameters:
139
+ event:
140
+ Market bar used to evaluate triggers and determine fill prices.
141
+ """
142
+ for order_id, order in list(self._pending_stop_orders.items()):
143
+ if order.symbol != event.symbol:
144
+ continue
145
+
146
+ # This is for mypy, it has already been validated on submission
147
+ assert order.stop_price is not None
148
+
149
+ triggered = False
150
+ match order.side:
151
+ case models.TradeSide.BUY:
152
+ triggered = event.high >= order.stop_price
153
+ case models.TradeSide.SELL:
154
+ triggered = event.low <= order.stop_price
155
+
156
+ if not triggered:
157
+ continue
158
+
159
+ fill_price = 0.0
160
+ match order.side:
161
+ case models.TradeSide.BUY:
162
+ fill_price = max(order.stop_price, event.open)
163
+ case models.TradeSide.SELL:
164
+ fill_price = min(order.stop_price, event.open)
165
+
166
+ self._publish(
167
+ events.orders.FillEvent(
168
+ ts_event_ns=event.ts_event_ns,
169
+ ts_broker_ns=event.ts_event_ns,
170
+ associated_order_id=order.order_id,
171
+ symbol=order.symbol,
172
+ side=order.side,
173
+ quantity_filled=order.quantity,
174
+ fill_price=fill_price,
175
+ commission=max(
176
+ order.quantity * self.commission_per_unit,
177
+ self.minimum_commission_per_order,
178
+ ),
179
+ )
180
+ )
181
+ del self._pending_stop_orders[order_id]
182
+
183
+ def _process_stop_limit_orders(self, event: events.market.BarReceived) -> None:
184
+ """
185
+ Evaluate pending stop-limit orders for the bar symbol.
186
+
187
+ Stop-limit orders trigger on stop conditions.
188
+ When triggered, they are converted into pending limit orders at the same identifier.
189
+
190
+ parameters:
191
+ event:
192
+ Market bar used to evaluate stop triggers.
193
+ """
194
+ for order_id, order in list(self._pending_stop_limit_orders.items()):
195
+ if order.symbol != event.symbol:
196
+ continue
197
+
198
+ assert order.stop_price is not None
199
+
200
+ triggered = False
201
+ match order.side:
202
+ case models.TradeSide.BUY:
203
+ triggered = event.high >= order.stop_price
204
+ case models.TradeSide.SELL:
205
+ triggered = event.low <= order.stop_price
206
+
207
+ if not triggered:
208
+ continue
209
+
210
+ limit_order = dataclasses.replace(order, order_type=models.OrderType.LIMIT)
211
+ self._pending_limit_orders[order_id] = limit_order
212
+ del self._pending_stop_limit_orders[order_id]
213
+
214
+ def _process_limit_orders(self, event: events.market.BarReceived) -> None:
215
+ """
216
+ Evaluate and fill pending limit orders for the bar symbol.
217
+
218
+ Limit orders trigger when the bar crosses the limit level.
219
+ The fill price is modeled as the better of the limit price and the bar open in the direction of the trade.
220
+
221
+ parameters:
222
+ event:
223
+ Market bar used to evaluate triggers and determine fill prices.
224
+ """
225
+ for order_id, order in list(self._pending_limit_orders.items()):
226
+ if order.symbol != event.symbol:
227
+ continue
228
+
229
+ assert order.limit_price is not None
230
+
231
+ triggered = False
232
+ match order.side:
233
+ case models.TradeSide.BUY:
234
+ triggered = event.low <= order.limit_price
235
+ case models.TradeSide.SELL:
236
+ triggered = event.high >= order.limit_price
237
+
238
+ if not triggered:
239
+ continue
240
+
241
+ fill_price = 0.0
242
+ match order.side:
243
+ case models.TradeSide.BUY:
244
+ fill_price = min(order.limit_price, event.open)
245
+ case models.TradeSide.SELL:
246
+ fill_price = max(order.limit_price, event.open)
247
+
248
+ self._publish(
249
+ events.orders.FillEvent(
250
+ ts_event_ns=event.ts_event_ns,
251
+ ts_broker_ns=event.ts_event_ns,
252
+ associated_order_id=order.order_id,
253
+ symbol=order.symbol,
254
+ side=order.side,
255
+ quantity_filled=order.quantity,
256
+ fill_price=fill_price,
257
+ commission=max(
258
+ order.quantity * self.commission_per_unit,
259
+ self.minimum_commission_per_order,
260
+ ),
261
+ )
262
+ )
263
+ del self._pending_limit_orders[order_id]
264
+
265
+ def _reject_if_invalid_submission(
266
+ self, event: events.requests.OrderSubmissionRequest
267
+ ) -> bool:
268
+ """
269
+ Validate an order submission request.
270
+
271
+ Invalid submissions are rejected immediately by publishing an `OrderRejected` response event.
272
+
273
+ parameters:
274
+ event:
275
+ Order submission request event to validate.
276
+
277
+ returns:
278
+ True if the submission is invalid and was rejected, otherwise False.
279
+ """
280
+ is_invalid = event.quantity <= 0
281
+
282
+ match event.order_type:
283
+ case models.OrderType.LIMIT:
284
+ is_invalid = (
285
+ is_invalid or event.limit_price is None or event.limit_price <= 0
286
+ )
287
+ case models.OrderType.STOP:
288
+ is_invalid = (
289
+ is_invalid or event.stop_price is None or event.stop_price <= 0
290
+ )
291
+ case models.OrderType.STOP_LIMIT:
292
+ is_invalid = is_invalid or (
293
+ event.limit_price is None
294
+ or event.limit_price <= 0
295
+ or event.stop_price is None
296
+ or event.stop_price <= 0
297
+ )
298
+
299
+ if is_invalid:
300
+ self._publish(
301
+ events.responses.OrderRejected(
302
+ ts_event_ns=event.ts_event_ns,
303
+ ts_broker_ns=event.ts_event_ns,
304
+ associated_order_id=event.system_order_id,
305
+ rejection_reason=models.OrderRejectionReason.UNKNOWN,
306
+ rejection_message="Unknown",
307
+ )
308
+ )
309
+
310
+ return is_invalid
311
+
312
+ def _on_submit_order(self, event: events.requests.OrderSubmissionRequest) -> None:
313
+ """
314
+ Handle an order submission request.
315
+
316
+ Valid orders are stored as pending broker-side state and acknowledged via an `OrderAccepted` response event.
317
+
318
+ parameters:
319
+ event:
320
+ Order submission request event.
321
+ """
322
+ if self._reject_if_invalid_submission(event):
323
+ return
324
+
325
+ order = _PendingOrder(
326
+ order_id=event.system_order_id,
327
+ symbol=event.symbol,
328
+ order_type=event.order_type,
329
+ side=event.side,
330
+ quantity=event.quantity,
331
+ limit_price=event.limit_price,
332
+ stop_price=event.stop_price,
333
+ )
334
+
335
+ match order.order_type:
336
+ case models.OrderType.MARKET:
337
+ self._pending_market_orders[order.order_id] = order
338
+ case models.OrderType.LIMIT:
339
+ self._pending_limit_orders[order.order_id] = order
340
+ case models.OrderType.STOP:
341
+ self._pending_stop_orders[order.order_id] = order
342
+ case models.OrderType.STOP_LIMIT:
343
+ self._pending_stop_limit_orders[order.order_id] = order
344
+
345
+ self._publish(
346
+ events.responses.OrderAccepted(
347
+ ts_event_ns=event.ts_event_ns,
348
+ ts_broker_ns=event.ts_event_ns,
349
+ associated_order_id=order.order_id,
350
+ )
351
+ )
352
+
353
+ def _on_cancel_order(self, event: events.requests.OrderCancellationRequest) -> None:
354
+ """
355
+ Handle an order cancellation request.
356
+
357
+ If the referenced order is pending, it is removed and acknowledged via `CancellationAccepted`.
358
+ Otherwise, `CancellationRejected` is published.
359
+
360
+ parameters:
361
+ event:
362
+ Order cancellation request event.
363
+ """
364
+ order_id = event.system_order_id
365
+
366
+ removed = False
367
+ for pending_orders in (
368
+ self._pending_market_orders,
369
+ self._pending_limit_orders,
370
+ self._pending_stop_orders,
371
+ self._pending_stop_limit_orders,
372
+ ):
373
+ if order_id in pending_orders:
374
+ del pending_orders[order_id]
375
+ removed = True
376
+ break
377
+
378
+ if removed:
379
+ self._publish(
380
+ events.responses.CancellationAccepted(
381
+ ts_event_ns=event.ts_event_ns,
382
+ ts_broker_ns=event.ts_event_ns,
383
+ associated_order_id=order_id,
384
+ )
385
+ )
386
+ else:
387
+ self._publish(
388
+ events.responses.CancellationRejected(
389
+ ts_event_ns=event.ts_event_ns,
390
+ ts_broker_ns=event.ts_event_ns,
391
+ associated_order_id=order_id,
392
+ rejection_reason=models.CancellationRejectionReason.UNKNOWN,
393
+ rejection_message="Unknown",
394
+ )
395
+ )
396
+
397
+ def _reject_if_invalid_modification(
398
+ self, event: events.requests.OrderModificationRequest
399
+ ) -> bool:
400
+ """
401
+ Validate an order modification request.
402
+
403
+ Invalid modifications are rejected immediately by publishing a `ModificationRejected` response event.
404
+
405
+ parameters:
406
+ event:
407
+ Order modification request event to validate.
408
+
409
+ returns:
410
+ True if the modification is invalid and was rejected, otherwise False.
411
+ """
412
+ is_invalid = (
413
+ (event.quantity is not None and event.quantity <= 0)
414
+ or (event.limit_price is not None and event.limit_price <= 0)
415
+ or (event.stop_price is not None and event.stop_price <= 0)
416
+ )
417
+
418
+ if is_invalid:
419
+ self._publish(
420
+ events.responses.ModificationRejected(
421
+ ts_event_ns=event.ts_event_ns,
422
+ ts_broker_ns=event.ts_event_ns,
423
+ associated_order_id=event.system_order_id,
424
+ rejection_reason=models.ModificationRejectionReason.UNKNOWN,
425
+ rejection_message="Unknown",
426
+ )
427
+ )
428
+
429
+ return is_invalid
430
+
431
+ def _on_modify_order(self, event: events.requests.OrderModificationRequest) -> None:
432
+ """
433
+ Handle an order modification request.
434
+
435
+ If the referenced order is pending, its fields are updated and acknowledged via `ModificationAccepted`.
436
+ Otherwise, `ModificationRejected` is published.
437
+
438
+ parameters:
439
+ event:
440
+ Order modification request event.
441
+ """
442
+ if self._reject_if_invalid_modification(event):
443
+ return
444
+
445
+ order_id = event.system_order_id
446
+
447
+ for pending_orders in (
448
+ self._pending_market_orders,
449
+ self._pending_limit_orders,
450
+ self._pending_stop_orders,
451
+ self._pending_stop_limit_orders,
452
+ ):
453
+ if order_id in pending_orders:
454
+ order = pending_orders[order_id]
455
+
456
+ new_quantity = (
457
+ event.quantity if event.quantity is not None else order.quantity
458
+ )
459
+ new_limit_price = (
460
+ event.limit_price
461
+ if event.limit_price is not None
462
+ else order.limit_price
463
+ )
464
+ new_stop_price = (
465
+ event.stop_price
466
+ if event.stop_price is not None
467
+ else order.stop_price
468
+ )
469
+
470
+ pending_orders[order_id] = dataclasses.replace(
471
+ order,
472
+ quantity=new_quantity,
473
+ limit_price=new_limit_price,
474
+ stop_price=new_stop_price,
475
+ )
476
+
477
+ self._publish(
478
+ events.responses.ModificationAccepted(
479
+ ts_event_ns=event.ts_event_ns,
480
+ ts_broker_ns=event.ts_event_ns,
481
+ associated_order_id=order_id,
482
+ )
483
+ )
484
+ return
485
+
486
+ self._publish(
487
+ events.responses.ModificationRejected(
488
+ ts_event_ns=event.ts_event_ns,
489
+ ts_broker_ns=event.ts_event_ns,
490
+ associated_order_id=order_id,
491
+ rejection_reason=models.ModificationRejectionReason.UNKNOWN,
492
+ rejection_message="Unknown",
493
+ )
494
+ )
@@ -9,17 +9,17 @@ from onesecondtrader import events
9
9
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
10
10
  class OrderBase(events.EventBase):
11
11
  """
12
- Base class for broker-originated order events.
12
+ Base class for brokers-originated order events.
13
13
 
14
- Order events are broker-originated facts about the state or execution of an order.
14
+ Order events are brokers-originated facts about the state or execution of an order.
15
15
  Each order event is correlated to a system order identifier via `associated_order_id`.
16
16
 
17
17
  | Field | Type | Semantics |
18
18
  |-----------------------|-----------------|---------------------------------------------------------------------------------------|
19
19
  | `ts_event_ns` | `int` | Time at which the response event was observed by the system, as UTC epoch nanoseconds.|
20
20
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
21
- | `ts_broker_ns` | `int` | Time reported by the broker for the response, as UTC epoch nanoseconds. |
22
- | `associated_order_id` | `uuid.UUID`. | Identifier of the order associated with the broker response. |
21
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the response, as UTC epoch nanoseconds. |
22
+ | `associated_order_id` | `uuid.UUID`. | Identifier of the order associated with the brokers response. |
23
23
  | `broker_order_id` | `str` or `None` | Broker-assigned identifier of the order, if reported. |
24
24
  | `symbol` | `str` | Identifier of the traded instrument. |
25
25
  """
@@ -8,13 +8,13 @@ from onesecondtrader.events.orders.base import OrderBase
8
8
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
9
9
  class OrderExpired(OrderBase):
10
10
  """
11
- Event indicating that the order is no longer active at the venue due to expiration according to broker- or venue-specific rules (e.g. time-in-force constraints).
11
+ Event indicating that the order is no longer active at the venue due to expiration according to brokers- or venue-specific rules (e.g. time-in-force constraints).
12
12
 
13
13
  | Field | Type | Semantics |
14
14
  |-----------------------|-----------------|------------------------------------------------------------------------------------|
15
15
  | `ts_event_ns` | `int` | Time at which the expiration was observed by the system, as UTC epoch nanoseconds. |
16
16
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
17
- | `ts_broker_ns` | `int` | Time reported by the broker for the expiration, as UTC epoch nanoseconds. |
17
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the expiration, as UTC epoch nanoseconds. |
18
18
  | `associated_order_id` | `uuid.UUID` | Identifier of the expired order. |
19
19
  | `broker_order_id` | `str` or `None` | Broker-assigned identifier of the expired order, if reported. |
20
20
  | `symbol` | `str` | Identifier of the traded instrument. |
@@ -19,7 +19,7 @@ class FillEvent(OrderBase):
19
19
  |-----------------------|---------------------|---------------------------------------------------------------------------------|
20
20
  | `ts_event_ns` | `int` | Time at which the fill was observed by the system, as UTC epoch nanoseconds. |
21
21
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
22
- | `ts_broker_ns` | `int` | Time reported by the broker for the fill, as UTC epoch nanoseconds. |
22
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the fill, as UTC epoch nanoseconds. |
23
23
  | `associated_order_id` | `uuid.UUID` | Identifier of the order associated with the fill. |
24
24
  | `broker_order_id` | `str` or `None` | Broker-assigned identifier of the order associated with the fill, if available. |
25
25
  | `symbol` | `str` | Identifier of the traded instrument. |
@@ -11,7 +11,7 @@ class RequestBase(events.EventBase):
11
11
  """
12
12
  Base class for request events.
13
13
 
14
- This class defines attributes common to all requests issued to a broker.
14
+ This class defines attributes common to all requests issued to a brokers.
15
15
 
16
16
  | Field | Type | Semantics |
17
17
  |-------------------|-------------|----------------------------------------------------------------------------|
@@ -10,7 +10,7 @@ from onesecondtrader.events.requests.base import RequestBase
10
10
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
11
11
  class OrderSubmissionRequest(RequestBase):
12
12
  """
13
- Event representing a request to submit a new order to a broker.
13
+ Event representing a request to submit a new order to a brokers.
14
14
 
15
15
  The `system_order_id` is a unique identifier assigned by the system to the order submission request by default at object creation.
16
16
 
@@ -10,16 +10,16 @@ from onesecondtrader import events
10
10
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
11
11
  class ResponseBase(events.EventBase):
12
12
  """
13
- Base class for broker response events.
13
+ Base class for brokers response events.
14
14
 
15
- This class defines attributes common to all responses received from a broker in reaction to previously issued requests.
15
+ This class defines attributes common to all responses received from a brokers in reaction to previously issued requests.
16
16
 
17
17
  | Field | Type | Semantics |
18
18
  |-----------------------|-------------|---------------------------------------------------------------------------------------|
19
19
  | `ts_event_ns` | `int` | Time at which the response event was observed by the system, as UTC epoch nanoseconds.|
20
20
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
21
- | `ts_broker_ns` | `int` | Time reported by the broker for the response, as UTC epoch nanoseconds. |
22
- | `associated_order_id` | `uuid.UUID` | Identifier of the order associated with the broker response. |
21
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the response, as UTC epoch nanoseconds. |
22
+ | `associated_order_id` | `uuid.UUID` | Identifier of the order associated with the brokers response. |
23
23
  """
24
24
 
25
25
  ts_broker_ns: int
@@ -9,13 +9,13 @@ from onesecondtrader.events.responses.base import ResponseBase
9
9
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
10
10
  class CancellationAccepted(ResponseBase):
11
11
  """
12
- Event indicating that the order cancellation has been acknowledged by the broker and the order is no longer active at the execution venue.
12
+ Event indicating that the order cancellation has been acknowledged by the brokers and the order is no longer active at the execution venue.
13
13
 
14
14
  | Field | Type | Semantics |
15
15
  |-----------------------|-----------------|----------------------------------------------------------------------------------------|
16
16
  | `ts_event_ns` | `int` | Time at which the cancellation was observed by the system, as UTC epoch nanoseconds. |
17
17
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
18
- | `ts_broker_ns` | `int` | Time reported by the broker for the cancellation, as UTC epoch nanoseconds. |
18
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the cancellation, as UTC epoch nanoseconds. |
19
19
  | `associated_order_id` | `uuid.UUID` | Identifier of the cancelled order. |
20
20
  | `broker_order_id` | `str` or `None` | Broker-assigned identifier of the cancelled order, if reported. |
21
21
  """
@@ -26,16 +26,16 @@ class CancellationAccepted(ResponseBase):
26
26
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
27
27
  class CancellationRejected(ResponseBase):
28
28
  """
29
- Event indicating that the order cancellation has been rejected by the broker.
29
+ Event indicating that the order cancellation has been rejected by the brokers.
30
30
 
31
31
  | Field | Type | Semantics |
32
32
  |-----------------------|--------------------------------------|------------------------------------------------------------------------------------|
33
33
  | `ts_event_ns` | `int` | Time at which the rejection was observed by the system, as UTC epoch nanoseconds. |
34
34
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
35
- | `ts_broker_ns` | `int` | Time reported by the broker for the rejection, as UTC epoch nanoseconds. |
35
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the rejection, as UTC epoch nanoseconds. |
36
36
  | `associated_order_id` | `uuid.UUID` | Identifier of the order associated with the rejected cancellation. |
37
37
  | `rejection_reason` | `models.CancellationRejectionReason` | Canonical classification of the cancellation rejection cause. |
38
- | `rejection_message` | `str` | Human-readable explanation provided by the broker. |
38
+ | `rejection_message` | `str` | Human-readable explanation provided by the brokers. |
39
39
  """
40
40
 
41
41
  rejection_reason: models.CancellationRejectionReason
@@ -10,13 +10,13 @@ from onesecondtrader.events.responses.base import ResponseBase
10
10
  class ModificationAccepted(ResponseBase):
11
11
  """
12
12
  Event indicating that the requested modification has been acknowledged by
13
- the broker and that the updated order parameters are active at the execution venue.
13
+ the brokers and that the updated order parameters are active at the execution venue.
14
14
 
15
15
  | Field | Type | Semantics |
16
16
  |-----------------------|-----------------|----------------------------------------------------------------------------------------|
17
17
  | `ts_event_ns` | `int` | Time at which the acceptance was observed by the system, as UTC epoch nanoseconds. |
18
18
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
19
- | `ts_broker_ns` | `int` | Time reported by the broker for the modification acceptance, as UTC epoch nanoseconds. |
19
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the modification acceptance, as UTC epoch nanoseconds. |
20
20
  | `associated_order_id` | `uuid.UUID` | Identifier of the modified order. |
21
21
  | `broker_order_id` | `str` or `None` | Broker-assigned identifier of the order after modification, if reported. |
22
22
  """
@@ -27,16 +27,16 @@ class ModificationAccepted(ResponseBase):
27
27
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
28
28
  class ModificationRejected(ResponseBase):
29
29
  """
30
- Event indicating that the requested modification has been rejected by the broker.
30
+ Event indicating that the requested modification has been rejected by the brokers.
31
31
 
32
32
  | Field | Type | Semantics |
33
33
  |-----------------------|--------------------------------------|------------------------------------------------------------------------------------|
34
34
  | `ts_event_ns` | `int` | Time at which the rejection was observed by the system, as UTC epoch nanoseconds. |
35
35
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
36
- | `ts_broker_ns` | `int` | Time reported by the broker for the rejection, as UTC epoch nanoseconds. |
36
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the rejection, as UTC epoch nanoseconds. |
37
37
  | `associated_order_id` | `uuid.UUID` | Identifier of the order associated with the rejected modification. |
38
38
  | `rejection_reason` | `models.ModificationRejectionReason` | Canonical classification of the modification rejection cause. |
39
- | `rejection_message` | `str` | Human-readable explanation provided by the broker. |
39
+ | `rejection_message` | `str` | Human-readable explanation provided by the brokers. |
40
40
  """
41
41
 
42
42
  rejection_reason: models.ModificationRejectionReason
@@ -9,13 +9,13 @@ from onesecondtrader.events.responses.base import ResponseBase
9
9
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
10
10
  class OrderAccepted(ResponseBase):
11
11
  """
12
- Event indicating that the order has been accepted by the broker and is active at the execution venue.
12
+ Event indicating that the order has been accepted by the brokers and is active at the execution venue.
13
13
 
14
14
  | Field | Type | Semantics |
15
15
  |-----------------------|-----------------|------------------------------------------------------------------------------------|
16
16
  | `ts_event_ns` | `int` | Time at which the acceptance was observed by the system, as UTC epoch nanoseconds. |
17
17
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
18
- | `ts_broker_ns` | `int` | Time reported by the broker for the acceptance, as UTC epoch nanoseconds. |
18
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the acceptance, as UTC epoch nanoseconds. |
19
19
  | `associated_order_id` | `uuid.UUID` | Identifier of the accepted order. |
20
20
  | `broker_order_id` | `str` or `None` | Broker-assigned identifier of the accepted order. |
21
21
  """
@@ -26,16 +26,16 @@ class OrderAccepted(ResponseBase):
26
26
  @dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
27
27
  class OrderRejected(ResponseBase):
28
28
  """
29
- Event indicating that the order has been rejected by the broker.
29
+ Event indicating that the order has been rejected by the brokers.
30
30
 
31
31
  | Field | Type | Semantics |
32
32
  |-----------------------|-------------------------------|------------------------------------------------------------------------------------|
33
33
  | `ts_event_ns` | `int` | Time at which the rejection was observed by the system, as UTC epoch nanoseconds. |
34
34
  | `ts_created_ns` | `int` | Time at which the event object was created, as UTC epoch nanoseconds. |
35
- | `ts_broker_ns` | `int` | Time reported by the broker for the rejection, as UTC epoch nanoseconds. |
35
+ | `ts_broker_ns` | `int` | Time reported by the brokers for the rejection, as UTC epoch nanoseconds. |
36
36
  | `associated_order_id` | `uuid.UUID` | Identifier of the rejected order. |
37
37
  | `rejection_reason` | `models.OrderRejectionReason` | Canonical classification of the rejection cause. |
38
- | `rejection_message` | `str` | Human-readable explanation provided by the broker. |
38
+ | `rejection_message` | `str` | Human-readable explanation provided by the brokers. |
39
39
  """
40
40
 
41
41
  rejection_reason: models.OrderRejectionReason
@@ -8,7 +8,7 @@ class OrderRejectionReason(enum.Enum):
8
8
  Enumeration of canonical order rejection reasons.
9
9
 
10
10
  This enumeration defines the system-level classification of order rejection causes.
11
- It provides a stable, broker-agnostic taxonomy for programmatic handling of rejected orders.
11
+ It provides a stable, brokers-agnostic taxonomy for programmatic handling of rejected orders.
12
12
 
13
13
  | Value | Semantics |
14
14
  |-----------|---------------------------------------------------------------------------|
@@ -22,8 +22,8 @@ class ModificationRejectionReason(enum.Enum):
22
22
  """
23
23
  Enumeration of canonical order modification rejection reasons.
24
24
 
25
- This enumeration defines the system-level classification of reasons for which an order modification request may be rejected by a broker.
26
- It provides a stable, broker-agnostic taxonomy intended for programmatic handling and observability of modification rejections.
25
+ This enumeration defines the system-level classification of reasons for which an order modification request may be rejected by a brokers.
26
+ It provides a stable, brokers-agnostic taxonomy intended for programmatic handling and observability of modification rejections.
27
27
 
28
28
  | Value | Semantics |
29
29
  |-----------|----------------------------------------------------------------------------------|
@@ -37,8 +37,8 @@ class CancellationRejectionReason(enum.Enum):
37
37
  """
38
38
  Enumeration of canonical order cancellation rejection reasons.
39
39
 
40
- This enumeration defines the system-level classification of reasons for which an order cancellation request may be rejected by a broker.
41
- It provides a stable, broker-agnostic taxonomy intended for programmatic handling and observability of cancellation rejections.
40
+ This enumeration defines the system-level classification of reasons for which an order cancellation request may be rejected by a brokers.
41
+ It provides a stable, brokers-agnostic taxonomy intended for programmatic handling and observability of cancellation rejections.
42
42
 
43
43
  | Value | Semantics |
44
44
  |-----------|----------------------------------------------------------------------------------|
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onesecondtrader
3
- Version: 0.50.0
3
+ Version: 0.51.0
4
4
  Summary: The Trading Infrastructure Toolkit for Python. Research, simulate, and deploy algorithmic trading strategies — all in one place.
5
5
  License-File: LICENSE
6
6
  Author: Nils P. Kujath
@@ -1,23 +1,26 @@
1
1
  onesecondtrader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ onesecondtrader/brokers/__init__.py,sha256=CmOhwKOayuYCeg5KRiTp4fc8nSDnsLzIBkUNWhUevlo,271
3
+ onesecondtrader/brokers/base.py,sha256=I4tQFr7P1DF5QAWb3I9tHz5D_zTleH8vEXS2WsH55DE,3531
4
+ onesecondtrader/brokers/simulated.py,sha256=ZY39a84J2BmC2ADMkrSRzNBumPXudVBz2eUSnnHb0LM,17930
2
5
  onesecondtrader/events/__init__.py,sha256=1T7hJA6afxClEXvvnbXtHu9iMyhduRdJZWlg4ObWaKE,222
3
6
  onesecondtrader/events/base.py,sha256=WpLo1bSKJe7Poh2IuDDCiBYZo9vE8mkq3cQlUpyTXsY,850
4
7
  onesecondtrader/events/market/__init__.py,sha256=49z6maexBIDkAjIfkLbYzSZWEbyTpQ_HEEgT0eacrDo,132
5
8
  onesecondtrader/events/market/bar_processed.py,sha256=NsCDGC21ykmioJoITspoAuz5mesKP0_hkM4UumWk1eU,1993
6
9
  onesecondtrader/events/market/bar_received.py,sha256=_LJEZ7P-AfYQEIkXYNt-toO9_WG_XTS2Y86tb-qdOVU,1961
7
10
  onesecondtrader/events/orders/__init__.py,sha256=IgVmiVUGMwDlFnL1ItAgvPwmkqEMJDLgQK8aJIKQ1Ic,164
8
- onesecondtrader/events/orders/base.py,sha256=3zTY8JMkXiOV35k0tp6rZZ9X7pDN40Btm0SFT5Mq3nw,1650
9
- onesecondtrader/events/orders/expirations.py,sha256=nW_28REmNNyxLpKzkAhwnC_mMqw00SqRediiZW8Zyz8,1465
10
- onesecondtrader/events/orders/fills.py,sha256=IDSCR7__AAPWm69noV9TeE2C8LukZv-ju8yjDfgmA9w,2772
11
+ onesecondtrader/events/orders/base.py,sha256=t4sw98Xv5oeUlJM9zfyVjP8rlqwKyoAUOaiBNuN7iEU,1654
12
+ onesecondtrader/events/orders/expirations.py,sha256=cJiDpfPwIbrMm8FC344jfKQlWxwZZcZCNF2TYxXSVLk,1467
13
+ onesecondtrader/events/orders/fills.py,sha256=oi5tZVImGNw1FzXsbUcPjScFIY8seN_x2TTBZgc90-M,2773
11
14
  onesecondtrader/events/requests/__init__.py,sha256=PWVAjNdgHWZArWXz9HU7E-6ZgMdfCpn4wAJiM9a5q6E,325
12
- onesecondtrader/events/requests/base.py,sha256=XXdiOy946hW3LmF7lMShi10kPtGftl928aPMDwEdnao,1084
15
+ onesecondtrader/events/requests/base.py,sha256=xmG4rQ0rfWAmo_lMiLgtplHqUo_P6du7JykXXr0QtoM,1085
13
16
  onesecondtrader/events/requests/order_cancellation.py,sha256=X_ZMOx09gz0Hf8hvDE1OahqK1SoH4nw9IOMJIdbbTKs,1029
14
17
  onesecondtrader/events/requests/order_modification.py,sha256=Tks2mXn1bkuOVLsk98tTk3E2CTld6SF1IwmkCvGvsc8,1538
15
- onesecondtrader/events/requests/order_submission.py,sha256=ejLBEWJ7pzhXWcTncK1x_ZJfcaC21CnuEz00wvi1q-8,2155
18
+ onesecondtrader/events/requests/order_submission.py,sha256=nEqAYl7QcEeu6wE_HnKqjvG34JWhTtjTjkIrkDiQ0ME,2156
16
19
  onesecondtrader/events/responses/__init__.py,sha256=ihg3zxjygLi-sA6wIbsm163ic8346WiAVtztb6ONy_4,409
17
- onesecondtrader/events/responses/base.py,sha256=tXOP3lvd6EqpC7NbfIJinUCI-KMz6xsR1YSQw5qNddk,1241
18
- onesecondtrader/events/responses/cancellations.py,sha256=HWF15Flz2JNOaZvArAjl9jLAt1NrQxB3MASG-0A2Z2Y,2941
19
- onesecondtrader/events/responses/modifications.py,sha256=ZQyehdwgAIg6HMmpQ5T_bWZ7yc4nfSeVwAGQ3T2O5-A,2968
20
- onesecondtrader/events/responses/orders.py,sha256=enkIUYRw9G3bzAq-it20XvXBew2gLg1mAm44m-12bns,2786
20
+ onesecondtrader/events/responses/base.py,sha256=rq5-avQ-1JqkrahhA7xt892UzecYUHF-3Al9EFSQtAU,1245
21
+ onesecondtrader/events/responses/cancellations.py,sha256=zB8kLCNFRQVVg-IoiAZOxhjPpvZrV6cbIPBkwASQG4Y,2946
22
+ onesecondtrader/events/responses/modifications.py,sha256=sG_KQU-4PsiakZrNKLmE6TOB-lSzSNBoBxJjfSQ9sy4,2973
23
+ onesecondtrader/events/responses/orders.py,sha256=g0N6ak3rOCQ1Vb97gmHYPIMgPtSpavbfL99OMhvzCZg,2791
21
24
  onesecondtrader/indicators/__init__.py,sha256=i-syRqCdQdC2BLL-7bDBcdl2Mll1LgE7hi2GleCJ4HI,366
22
25
  onesecondtrader/indicators/base.py,sha256=iGfgOj1B_LRGBeh7VjM5OjxoYnSxV7JZUP1C3O_dfmE,4653
23
26
  onesecondtrader/indicators/market_fields.py,sha256=Znyii0egBkJT3CFlYhQ5ArRNidmBLF0TzgCr2AWu5CA,4306
@@ -29,9 +32,9 @@ onesecondtrader/models/__init__.py,sha256=XWL6aNLwAA2JQMoqK2PY-_CwigV0ighx4zwGQV
29
32
  onesecondtrader/models/bar_fields.py,sha256=GnLBL08ueUr35w2dAbKwOBWrdBS98OC9r0T2NifwTH8,646
30
33
  onesecondtrader/models/bar_period.py,sha256=J8ncVtcAxR52uD0nbC8Knds_GUP5wiuNj5rAKq4vv-4,475
31
34
  onesecondtrader/models/order_types.py,sha256=SiJamarLQ7zkHzHLLbd86I_TeZrQJ4QEIMqNHj4dxXU,737
32
- onesecondtrader/models/rejection_reasons.py,sha256=BToLmPholsP9GcLGZ874nQJpehZ1yB1SFyYqlf3AOwc,2127
35
+ onesecondtrader/models/rejection_reasons.py,sha256=Avp1JYf413_aUQQkEeswI-9EJBmQdd7B6bnQ1MslDNE,2132
33
36
  onesecondtrader/models/trade_sides.py,sha256=Pf9BpxoUxqgKC_EKAExfSqgfIIK9NW-RpJES0XHRF-8,583
34
- onesecondtrader-0.50.0.dist-info/METADATA,sha256=osrTVFj2ZqYZwPeBnaMBkRnfbfZaBIcl9YDpBhyrD-U,9951
35
- onesecondtrader-0.50.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
36
- onesecondtrader-0.50.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
37
- onesecondtrader-0.50.0.dist-info/RECORD,,
37
+ onesecondtrader-0.51.0.dist-info/METADATA,sha256=5qFkty3l7BfcTm5Ah9vlUWOEi7Ad7jtSYAj5KUNVGMo,9951
38
+ onesecondtrader-0.51.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
39
+ onesecondtrader-0.51.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
40
+ onesecondtrader-0.51.0.dist-info/RECORD,,