onesecondtrader 0.55.0__py3-none-any.whl → 0.56.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.
- onesecondtrader/orchestrator/__init__.py +8 -0
- onesecondtrader/orchestrator/orchestrator.py +161 -0
- onesecondtrader/orchestrator/run_recorder.py +759 -0
- onesecondtrader/orchestrator/runs_schema.sql +500 -0
- {onesecondtrader-0.55.0.dist-info → onesecondtrader-0.56.0.dist-info}/METADATA +1 -1
- {onesecondtrader-0.55.0.dist-info → onesecondtrader-0.56.0.dist-info}/RECORD +8 -4
- {onesecondtrader-0.55.0.dist-info → onesecondtrader-0.56.0.dist-info}/WHEEL +0 -0
- {onesecondtrader-0.55.0.dist-info → onesecondtrader-0.56.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import pathlib
|
|
5
|
+
import sqlite3
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
from onesecondtrader import events, messaging
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
BATCH_SIZE = 1000
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RunRecorder(messaging.Subscriber):
|
|
15
|
+
"""
|
|
16
|
+
Subscriber that records all trading system events to a SQLite runs database.
|
|
17
|
+
|
|
18
|
+
The recorder subscribes to market data events, order requests, broker responses,
|
|
19
|
+
fills, and expirations, inserting them into the appropriate tables as defined
|
|
20
|
+
in the runs schema.
|
|
21
|
+
|
|
22
|
+
Events are buffered and inserted in batches for performance. The buffer is
|
|
23
|
+
flushed on shutdown via `_cleanup`.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
event_bus: messaging.EventBus,
|
|
29
|
+
db_path: pathlib.Path,
|
|
30
|
+
run_id: str,
|
|
31
|
+
name: str,
|
|
32
|
+
config: dict | None = None,
|
|
33
|
+
metadata: dict | None = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Initialize the recorder and register a new run in the database.
|
|
37
|
+
|
|
38
|
+
The database is created if it does not exist. A new row is inserted into the `runs`
|
|
39
|
+
table with status `running` and the current timestamp as `ts_start`.
|
|
40
|
+
|
|
41
|
+
Parameters:
|
|
42
|
+
event_bus:
|
|
43
|
+
Event bus used for subscribing to system events.
|
|
44
|
+
db_path:
|
|
45
|
+
Filesystem path to the SQLite runs database.
|
|
46
|
+
run_id:
|
|
47
|
+
Unique identifier for this run.
|
|
48
|
+
name:
|
|
49
|
+
Human-readable name for this run.
|
|
50
|
+
config:
|
|
51
|
+
Optional configuration dictionary to store as JSON.
|
|
52
|
+
metadata:
|
|
53
|
+
Optional metadata dictionary to store as JSON.
|
|
54
|
+
"""
|
|
55
|
+
self._db_path = db_path
|
|
56
|
+
self._run_id = run_id
|
|
57
|
+
self._name = name
|
|
58
|
+
self._config = config
|
|
59
|
+
self._metadata = metadata
|
|
60
|
+
|
|
61
|
+
self._conn = self._init_db()
|
|
62
|
+
self._buffers: dict[str, list[tuple]] = {
|
|
63
|
+
"bars": [],
|
|
64
|
+
"bars_processed": [],
|
|
65
|
+
"order_submissions": [],
|
|
66
|
+
"order_cancellations": [],
|
|
67
|
+
"order_modifications": [],
|
|
68
|
+
"orders_accepted": [],
|
|
69
|
+
"orders_rejected": [],
|
|
70
|
+
"cancellations_accepted": [],
|
|
71
|
+
"cancellations_rejected": [],
|
|
72
|
+
"modifications_accepted": [],
|
|
73
|
+
"modifications_rejected": [],
|
|
74
|
+
"fills": [],
|
|
75
|
+
"expirations": [],
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
self._register_run()
|
|
79
|
+
|
|
80
|
+
super().__init__(event_bus)
|
|
81
|
+
self._subscribe(
|
|
82
|
+
events.market.BarReceived,
|
|
83
|
+
events.market.BarProcessed,
|
|
84
|
+
events.requests.OrderSubmissionRequest,
|
|
85
|
+
events.requests.OrderCancellationRequest,
|
|
86
|
+
events.requests.OrderModificationRequest,
|
|
87
|
+
events.responses.OrderAccepted,
|
|
88
|
+
events.responses.OrderRejected,
|
|
89
|
+
events.responses.CancellationAccepted,
|
|
90
|
+
events.responses.CancellationRejected,
|
|
91
|
+
events.responses.ModificationAccepted,
|
|
92
|
+
events.responses.ModificationRejected,
|
|
93
|
+
events.orders.FillEvent,
|
|
94
|
+
events.orders.OrderExpired,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def _init_db(self) -> sqlite3.Connection:
|
|
98
|
+
"""
|
|
99
|
+
Initialize the SQLite database connection.
|
|
100
|
+
|
|
101
|
+
Creates the database file and parent directories if they do not exist.
|
|
102
|
+
Applies the runs schema if the database is newly created.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Open database connection configured with WAL journal mode.
|
|
106
|
+
"""
|
|
107
|
+
schema_path = pathlib.Path(__file__).parent / "runs_schema.sql"
|
|
108
|
+
db_exists = self._db_path.is_file()
|
|
109
|
+
|
|
110
|
+
self._db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
111
|
+
conn = sqlite3.connect(str(self._db_path), check_same_thread=False)
|
|
112
|
+
conn.execute("PRAGMA foreign_keys = ON")
|
|
113
|
+
conn.execute("PRAGMA journal_mode = WAL")
|
|
114
|
+
conn.execute("PRAGMA synchronous = NORMAL")
|
|
115
|
+
|
|
116
|
+
if not db_exists:
|
|
117
|
+
conn.executescript(schema_path.read_text())
|
|
118
|
+
|
|
119
|
+
return conn
|
|
120
|
+
|
|
121
|
+
def _register_run(self) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Insert a new run record into the database.
|
|
124
|
+
|
|
125
|
+
The run is created with status 'running' and the current timestamp as start time.
|
|
126
|
+
"""
|
|
127
|
+
self._conn.execute(
|
|
128
|
+
"""
|
|
129
|
+
INSERT INTO runs (run_id, name, ts_start, status, config, metadata)
|
|
130
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
131
|
+
""",
|
|
132
|
+
(
|
|
133
|
+
self._run_id,
|
|
134
|
+
self._name,
|
|
135
|
+
time.time_ns(),
|
|
136
|
+
"running",
|
|
137
|
+
json.dumps(self._config) if self._config else None,
|
|
138
|
+
json.dumps(self._metadata) if self._metadata else None,
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
self._conn.commit()
|
|
142
|
+
|
|
143
|
+
def update_run_status(
|
|
144
|
+
self,
|
|
145
|
+
status: str,
|
|
146
|
+
ts_end: int | None = None,
|
|
147
|
+
) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Update the status and end timestamp of the current run.
|
|
150
|
+
|
|
151
|
+
Parameters:
|
|
152
|
+
status:
|
|
153
|
+
New status value (e.g., 'completed', 'failed', 'cancelled').
|
|
154
|
+
ts_end:
|
|
155
|
+
End timestamp in nanoseconds since Unix epoch. Defaults to current time.
|
|
156
|
+
"""
|
|
157
|
+
if ts_end is None:
|
|
158
|
+
ts_end = time.time_ns()
|
|
159
|
+
self._conn.execute(
|
|
160
|
+
"UPDATE runs SET status = ?, ts_end = ? WHERE run_id = ?",
|
|
161
|
+
(status, ts_end, self._run_id),
|
|
162
|
+
)
|
|
163
|
+
self._conn.commit()
|
|
164
|
+
|
|
165
|
+
def _on_event(self, event: events.EventBase) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Dispatch an incoming event to the appropriate buffer method.
|
|
168
|
+
|
|
169
|
+
Parameters:
|
|
170
|
+
event:
|
|
171
|
+
Event received from the event bus.
|
|
172
|
+
"""
|
|
173
|
+
match event:
|
|
174
|
+
case events.market.BarProcessed() as matched:
|
|
175
|
+
self._buffer_bar_processed(matched)
|
|
176
|
+
case events.market.BarReceived() as matched:
|
|
177
|
+
self._buffer_bar_received(matched)
|
|
178
|
+
case events.requests.OrderSubmissionRequest() as matched:
|
|
179
|
+
self._buffer_order_submission(matched)
|
|
180
|
+
case events.requests.OrderCancellationRequest() as matched:
|
|
181
|
+
self._buffer_order_cancellation(matched)
|
|
182
|
+
case events.requests.OrderModificationRequest() as matched:
|
|
183
|
+
self._buffer_order_modification(matched)
|
|
184
|
+
case events.responses.OrderAccepted() as matched:
|
|
185
|
+
self._buffer_order_accepted(matched)
|
|
186
|
+
case events.responses.OrderRejected() as matched:
|
|
187
|
+
self._buffer_order_rejected(matched)
|
|
188
|
+
case events.responses.CancellationAccepted() as matched:
|
|
189
|
+
self._buffer_cancellation_accepted(matched)
|
|
190
|
+
case events.responses.CancellationRejected() as matched:
|
|
191
|
+
self._buffer_cancellation_rejected(matched)
|
|
192
|
+
case events.responses.ModificationAccepted() as matched:
|
|
193
|
+
self._buffer_modification_accepted(matched)
|
|
194
|
+
case events.responses.ModificationRejected() as matched:
|
|
195
|
+
self._buffer_modification_rejected(matched)
|
|
196
|
+
case events.orders.FillEvent() as matched:
|
|
197
|
+
self._buffer_fill(matched)
|
|
198
|
+
case events.orders.OrderExpired() as matched:
|
|
199
|
+
self._buffer_expiration(matched)
|
|
200
|
+
|
|
201
|
+
def _on_exception(self, exc: Exception) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Handle an exception raised during event processing.
|
|
204
|
+
|
|
205
|
+
Parameters:
|
|
206
|
+
exc:
|
|
207
|
+
Exception that was raised.
|
|
208
|
+
"""
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
def _cleanup(self) -> None:
|
|
212
|
+
"""
|
|
213
|
+
Flush all buffered records and close the database connection.
|
|
214
|
+
|
|
215
|
+
Called automatically during subscriber shutdown.
|
|
216
|
+
"""
|
|
217
|
+
self._flush_all()
|
|
218
|
+
self._conn.close()
|
|
219
|
+
|
|
220
|
+
def _flush_all(self) -> None:
|
|
221
|
+
"""
|
|
222
|
+
Flush all event buffers to the database.
|
|
223
|
+
"""
|
|
224
|
+
self._flush_bars()
|
|
225
|
+
self._flush_bars_processed()
|
|
226
|
+
self._flush_order_submissions()
|
|
227
|
+
self._flush_order_cancellations()
|
|
228
|
+
self._flush_order_modifications()
|
|
229
|
+
self._flush_orders_accepted()
|
|
230
|
+
self._flush_orders_rejected()
|
|
231
|
+
self._flush_cancellations_accepted()
|
|
232
|
+
self._flush_cancellations_rejected()
|
|
233
|
+
self._flush_modifications_accepted()
|
|
234
|
+
self._flush_modifications_rejected()
|
|
235
|
+
self._flush_fills()
|
|
236
|
+
self._flush_expirations()
|
|
237
|
+
|
|
238
|
+
def _buffer_bar_received(self, event: events.market.BarReceived) -> None:
|
|
239
|
+
"""
|
|
240
|
+
Buffer a bar received event for batch insertion.
|
|
241
|
+
|
|
242
|
+
Parameters:
|
|
243
|
+
event:
|
|
244
|
+
Bar received event to buffer.
|
|
245
|
+
"""
|
|
246
|
+
self._buffers["bars"].append(
|
|
247
|
+
(
|
|
248
|
+
self._run_id,
|
|
249
|
+
event.ts_event_ns,
|
|
250
|
+
event.ts_created_ns,
|
|
251
|
+
event.symbol,
|
|
252
|
+
event.bar_period.name,
|
|
253
|
+
event.open,
|
|
254
|
+
event.high,
|
|
255
|
+
event.low,
|
|
256
|
+
event.close,
|
|
257
|
+
event.volume,
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
if len(self._buffers["bars"]) >= BATCH_SIZE:
|
|
261
|
+
self._flush_bars()
|
|
262
|
+
|
|
263
|
+
def _buffer_bar_processed(self, event: events.market.BarProcessed) -> None:
|
|
264
|
+
"""
|
|
265
|
+
Buffer a bar processed event for batch insertion.
|
|
266
|
+
|
|
267
|
+
Parameters:
|
|
268
|
+
event:
|
|
269
|
+
Bar processed event to buffer.
|
|
270
|
+
"""
|
|
271
|
+
self._buffers["bars_processed"].append(
|
|
272
|
+
(
|
|
273
|
+
self._run_id,
|
|
274
|
+
event.ts_event_ns,
|
|
275
|
+
event.ts_created_ns,
|
|
276
|
+
event.symbol,
|
|
277
|
+
event.bar_period.name,
|
|
278
|
+
event.open,
|
|
279
|
+
event.high,
|
|
280
|
+
event.low,
|
|
281
|
+
event.close,
|
|
282
|
+
event.volume,
|
|
283
|
+
json.dumps(event.indicators),
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
if len(self._buffers["bars_processed"]) >= BATCH_SIZE:
|
|
287
|
+
self._flush_bars_processed()
|
|
288
|
+
|
|
289
|
+
def _buffer_order_submission(
|
|
290
|
+
self, event: events.requests.OrderSubmissionRequest
|
|
291
|
+
) -> None:
|
|
292
|
+
"""
|
|
293
|
+
Buffer an order submission request for batch insertion.
|
|
294
|
+
|
|
295
|
+
Parameters:
|
|
296
|
+
event:
|
|
297
|
+
Order submission request to buffer.
|
|
298
|
+
"""
|
|
299
|
+
self._buffers["order_submissions"].append(
|
|
300
|
+
(
|
|
301
|
+
self._run_id,
|
|
302
|
+
event.ts_event_ns,
|
|
303
|
+
event.ts_created_ns,
|
|
304
|
+
str(event.system_order_id),
|
|
305
|
+
event.symbol,
|
|
306
|
+
event.order_type.name,
|
|
307
|
+
event.side.name,
|
|
308
|
+
event.quantity,
|
|
309
|
+
event.limit_price,
|
|
310
|
+
event.stop_price,
|
|
311
|
+
event.action.name if event.action else None,
|
|
312
|
+
event.signal,
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
if len(self._buffers["order_submissions"]) >= BATCH_SIZE:
|
|
316
|
+
self._flush_order_submissions()
|
|
317
|
+
|
|
318
|
+
def _buffer_order_cancellation(
|
|
319
|
+
self, event: events.requests.OrderCancellationRequest
|
|
320
|
+
) -> None:
|
|
321
|
+
"""
|
|
322
|
+
Buffer an order cancellation request for batch insertion.
|
|
323
|
+
|
|
324
|
+
Parameters:
|
|
325
|
+
event:
|
|
326
|
+
Order cancellation request to buffer.
|
|
327
|
+
"""
|
|
328
|
+
self._buffers["order_cancellations"].append(
|
|
329
|
+
(
|
|
330
|
+
self._run_id,
|
|
331
|
+
event.ts_event_ns,
|
|
332
|
+
event.ts_created_ns,
|
|
333
|
+
str(event.system_order_id),
|
|
334
|
+
event.symbol,
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
if len(self._buffers["order_cancellations"]) >= BATCH_SIZE:
|
|
338
|
+
self._flush_order_cancellations()
|
|
339
|
+
|
|
340
|
+
def _buffer_order_modification(
|
|
341
|
+
self, event: events.requests.OrderModificationRequest
|
|
342
|
+
) -> None:
|
|
343
|
+
"""
|
|
344
|
+
Buffer an order modification request for batch insertion.
|
|
345
|
+
|
|
346
|
+
Parameters:
|
|
347
|
+
event:
|
|
348
|
+
Order modification request to buffer.
|
|
349
|
+
"""
|
|
350
|
+
self._buffers["order_modifications"].append(
|
|
351
|
+
(
|
|
352
|
+
self._run_id,
|
|
353
|
+
event.ts_event_ns,
|
|
354
|
+
event.ts_created_ns,
|
|
355
|
+
str(event.system_order_id),
|
|
356
|
+
event.symbol,
|
|
357
|
+
event.quantity,
|
|
358
|
+
event.limit_price,
|
|
359
|
+
event.stop_price,
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
if len(self._buffers["order_modifications"]) >= BATCH_SIZE:
|
|
363
|
+
self._flush_order_modifications()
|
|
364
|
+
|
|
365
|
+
def _buffer_order_accepted(self, event: events.responses.OrderAccepted) -> None:
|
|
366
|
+
"""
|
|
367
|
+
Buffer an order accepted response for batch insertion.
|
|
368
|
+
|
|
369
|
+
Parameters:
|
|
370
|
+
event:
|
|
371
|
+
Order accepted response to buffer.
|
|
372
|
+
"""
|
|
373
|
+
self._buffers["orders_accepted"].append(
|
|
374
|
+
(
|
|
375
|
+
self._run_id,
|
|
376
|
+
event.ts_event_ns,
|
|
377
|
+
event.ts_created_ns,
|
|
378
|
+
event.ts_broker_ns,
|
|
379
|
+
str(event.associated_order_id),
|
|
380
|
+
event.broker_order_id,
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
if len(self._buffers["orders_accepted"]) >= BATCH_SIZE:
|
|
384
|
+
self._flush_orders_accepted()
|
|
385
|
+
|
|
386
|
+
def _buffer_order_rejected(self, event: events.responses.OrderRejected) -> None:
|
|
387
|
+
"""
|
|
388
|
+
Buffer an order rejected response for batch insertion.
|
|
389
|
+
|
|
390
|
+
Parameters:
|
|
391
|
+
event:
|
|
392
|
+
Order rejected response to buffer.
|
|
393
|
+
"""
|
|
394
|
+
self._buffers["orders_rejected"].append(
|
|
395
|
+
(
|
|
396
|
+
self._run_id,
|
|
397
|
+
event.ts_event_ns,
|
|
398
|
+
event.ts_created_ns,
|
|
399
|
+
event.ts_broker_ns,
|
|
400
|
+
str(event.associated_order_id),
|
|
401
|
+
event.rejection_reason.name,
|
|
402
|
+
event.rejection_message,
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
if len(self._buffers["orders_rejected"]) >= BATCH_SIZE:
|
|
406
|
+
self._flush_orders_rejected()
|
|
407
|
+
|
|
408
|
+
def _buffer_cancellation_accepted(
|
|
409
|
+
self, event: events.responses.CancellationAccepted
|
|
410
|
+
) -> None:
|
|
411
|
+
"""
|
|
412
|
+
Buffer a cancellation accepted response for batch insertion.
|
|
413
|
+
|
|
414
|
+
Parameters:
|
|
415
|
+
event:
|
|
416
|
+
Cancellation accepted response to buffer.
|
|
417
|
+
"""
|
|
418
|
+
self._buffers["cancellations_accepted"].append(
|
|
419
|
+
(
|
|
420
|
+
self._run_id,
|
|
421
|
+
event.ts_event_ns,
|
|
422
|
+
event.ts_created_ns,
|
|
423
|
+
event.ts_broker_ns,
|
|
424
|
+
str(event.associated_order_id),
|
|
425
|
+
event.broker_order_id,
|
|
426
|
+
)
|
|
427
|
+
)
|
|
428
|
+
if len(self._buffers["cancellations_accepted"]) >= BATCH_SIZE:
|
|
429
|
+
self._flush_cancellations_accepted()
|
|
430
|
+
|
|
431
|
+
def _buffer_cancellation_rejected(
|
|
432
|
+
self, event: events.responses.CancellationRejected
|
|
433
|
+
) -> None:
|
|
434
|
+
"""
|
|
435
|
+
Buffer a cancellation rejected response for batch insertion.
|
|
436
|
+
|
|
437
|
+
Parameters:
|
|
438
|
+
event:
|
|
439
|
+
Cancellation rejected response to buffer.
|
|
440
|
+
"""
|
|
441
|
+
self._buffers["cancellations_rejected"].append(
|
|
442
|
+
(
|
|
443
|
+
self._run_id,
|
|
444
|
+
event.ts_event_ns,
|
|
445
|
+
event.ts_created_ns,
|
|
446
|
+
event.ts_broker_ns,
|
|
447
|
+
str(event.associated_order_id),
|
|
448
|
+
event.rejection_reason.name,
|
|
449
|
+
event.rejection_message,
|
|
450
|
+
)
|
|
451
|
+
)
|
|
452
|
+
if len(self._buffers["cancellations_rejected"]) >= BATCH_SIZE:
|
|
453
|
+
self._flush_cancellations_rejected()
|
|
454
|
+
|
|
455
|
+
def _buffer_modification_accepted(
|
|
456
|
+
self, event: events.responses.ModificationAccepted
|
|
457
|
+
) -> None:
|
|
458
|
+
"""
|
|
459
|
+
Buffer a modification accepted response for batch insertion.
|
|
460
|
+
|
|
461
|
+
Parameters:
|
|
462
|
+
event:
|
|
463
|
+
Modification accepted response to buffer.
|
|
464
|
+
"""
|
|
465
|
+
self._buffers["modifications_accepted"].append(
|
|
466
|
+
(
|
|
467
|
+
self._run_id,
|
|
468
|
+
event.ts_event_ns,
|
|
469
|
+
event.ts_created_ns,
|
|
470
|
+
event.ts_broker_ns,
|
|
471
|
+
str(event.associated_order_id),
|
|
472
|
+
event.broker_order_id,
|
|
473
|
+
)
|
|
474
|
+
)
|
|
475
|
+
if len(self._buffers["modifications_accepted"]) >= BATCH_SIZE:
|
|
476
|
+
self._flush_modifications_accepted()
|
|
477
|
+
|
|
478
|
+
def _buffer_modification_rejected(
|
|
479
|
+
self, event: events.responses.ModificationRejected
|
|
480
|
+
) -> None:
|
|
481
|
+
"""
|
|
482
|
+
Buffer a modification rejected response for batch insertion.
|
|
483
|
+
|
|
484
|
+
Parameters:
|
|
485
|
+
event:
|
|
486
|
+
Modification rejected response to buffer.
|
|
487
|
+
"""
|
|
488
|
+
self._buffers["modifications_rejected"].append(
|
|
489
|
+
(
|
|
490
|
+
self._run_id,
|
|
491
|
+
event.ts_event_ns,
|
|
492
|
+
event.ts_created_ns,
|
|
493
|
+
event.ts_broker_ns,
|
|
494
|
+
str(event.associated_order_id),
|
|
495
|
+
event.rejection_reason.name,
|
|
496
|
+
event.rejection_message,
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
if len(self._buffers["modifications_rejected"]) >= BATCH_SIZE:
|
|
500
|
+
self._flush_modifications_rejected()
|
|
501
|
+
|
|
502
|
+
def _buffer_fill(self, event: events.orders.FillEvent) -> None:
|
|
503
|
+
"""
|
|
504
|
+
Buffer a fill event for batch insertion.
|
|
505
|
+
|
|
506
|
+
Parameters:
|
|
507
|
+
event:
|
|
508
|
+
Fill event to buffer.
|
|
509
|
+
"""
|
|
510
|
+
self._buffers["fills"].append(
|
|
511
|
+
(
|
|
512
|
+
self._run_id,
|
|
513
|
+
event.ts_event_ns,
|
|
514
|
+
event.ts_created_ns,
|
|
515
|
+
event.ts_broker_ns,
|
|
516
|
+
str(event.associated_order_id),
|
|
517
|
+
event.broker_order_id,
|
|
518
|
+
event.symbol,
|
|
519
|
+
str(event.fill_id),
|
|
520
|
+
event.broker_fill_id,
|
|
521
|
+
event.side.name,
|
|
522
|
+
event.quantity_filled,
|
|
523
|
+
event.fill_price,
|
|
524
|
+
event.commission,
|
|
525
|
+
event.exchange,
|
|
526
|
+
)
|
|
527
|
+
)
|
|
528
|
+
if len(self._buffers["fills"]) >= BATCH_SIZE:
|
|
529
|
+
self._flush_fills()
|
|
530
|
+
|
|
531
|
+
def _buffer_expiration(self, event: events.orders.OrderExpired) -> None:
|
|
532
|
+
"""
|
|
533
|
+
Buffer an order expiration event for batch insertion.
|
|
534
|
+
|
|
535
|
+
Parameters:
|
|
536
|
+
event:
|
|
537
|
+
Order expiration event to buffer.
|
|
538
|
+
"""
|
|
539
|
+
self._buffers["expirations"].append(
|
|
540
|
+
(
|
|
541
|
+
self._run_id,
|
|
542
|
+
event.ts_event_ns,
|
|
543
|
+
event.ts_created_ns,
|
|
544
|
+
event.ts_broker_ns,
|
|
545
|
+
str(event.associated_order_id),
|
|
546
|
+
event.broker_order_id,
|
|
547
|
+
event.symbol,
|
|
548
|
+
)
|
|
549
|
+
)
|
|
550
|
+
if len(self._buffers["expirations"]) >= BATCH_SIZE:
|
|
551
|
+
self._flush_expirations()
|
|
552
|
+
|
|
553
|
+
def _flush_bars(self) -> None:
|
|
554
|
+
"""
|
|
555
|
+
Insert buffered bar received records into the database.
|
|
556
|
+
"""
|
|
557
|
+
if not self._buffers["bars"]:
|
|
558
|
+
return
|
|
559
|
+
self._conn.executemany(
|
|
560
|
+
"""
|
|
561
|
+
INSERT INTO bars (run_id, ts_event_ns, ts_created_ns, symbol, bar_period, open, high, low, close, volume)
|
|
562
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
563
|
+
""",
|
|
564
|
+
self._buffers["bars"],
|
|
565
|
+
)
|
|
566
|
+
self._conn.commit()
|
|
567
|
+
self._buffers["bars"].clear()
|
|
568
|
+
|
|
569
|
+
def _flush_bars_processed(self) -> None:
|
|
570
|
+
"""
|
|
571
|
+
Insert buffered bar processed records into the database.
|
|
572
|
+
"""
|
|
573
|
+
if not self._buffers["bars_processed"]:
|
|
574
|
+
return
|
|
575
|
+
self._conn.executemany(
|
|
576
|
+
"""
|
|
577
|
+
INSERT INTO bars_processed (run_id, ts_event_ns, ts_created_ns, symbol, bar_period, open, high, low, close, volume, indicators)
|
|
578
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
579
|
+
""",
|
|
580
|
+
self._buffers["bars_processed"],
|
|
581
|
+
)
|
|
582
|
+
self._conn.commit()
|
|
583
|
+
self._buffers["bars_processed"].clear()
|
|
584
|
+
|
|
585
|
+
def _flush_order_submissions(self) -> None:
|
|
586
|
+
"""
|
|
587
|
+
Insert buffered order submission records into the database.
|
|
588
|
+
"""
|
|
589
|
+
if not self._buffers["order_submissions"]:
|
|
590
|
+
return
|
|
591
|
+
self._conn.executemany(
|
|
592
|
+
"""
|
|
593
|
+
INSERT INTO order_submissions (run_id, ts_event_ns, ts_created_ns, system_order_id, symbol, order_type, side, quantity, limit_price, stop_price, action, signal)
|
|
594
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
595
|
+
""",
|
|
596
|
+
self._buffers["order_submissions"],
|
|
597
|
+
)
|
|
598
|
+
self._conn.commit()
|
|
599
|
+
self._buffers["order_submissions"].clear()
|
|
600
|
+
|
|
601
|
+
def _flush_order_cancellations(self) -> None:
|
|
602
|
+
"""
|
|
603
|
+
Insert buffered order cancellation records into the database.
|
|
604
|
+
"""
|
|
605
|
+
if not self._buffers["order_cancellations"]:
|
|
606
|
+
return
|
|
607
|
+
self._conn.executemany(
|
|
608
|
+
"""
|
|
609
|
+
INSERT INTO order_cancellations (run_id, ts_event_ns, ts_created_ns, system_order_id, symbol)
|
|
610
|
+
VALUES (?, ?, ?, ?, ?)
|
|
611
|
+
""",
|
|
612
|
+
self._buffers["order_cancellations"],
|
|
613
|
+
)
|
|
614
|
+
self._conn.commit()
|
|
615
|
+
self._buffers["order_cancellations"].clear()
|
|
616
|
+
|
|
617
|
+
def _flush_order_modifications(self) -> None:
|
|
618
|
+
"""
|
|
619
|
+
Insert buffered order modification records into the database.
|
|
620
|
+
"""
|
|
621
|
+
if not self._buffers["order_modifications"]:
|
|
622
|
+
return
|
|
623
|
+
self._conn.executemany(
|
|
624
|
+
"""
|
|
625
|
+
INSERT INTO order_modifications (run_id, ts_event_ns, ts_created_ns, system_order_id, symbol, quantity, limit_price, stop_price)
|
|
626
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
627
|
+
""",
|
|
628
|
+
self._buffers["order_modifications"],
|
|
629
|
+
)
|
|
630
|
+
self._conn.commit()
|
|
631
|
+
self._buffers["order_modifications"].clear()
|
|
632
|
+
|
|
633
|
+
def _flush_orders_accepted(self) -> None:
|
|
634
|
+
"""
|
|
635
|
+
Insert buffered order accepted records into the database.
|
|
636
|
+
"""
|
|
637
|
+
if not self._buffers["orders_accepted"]:
|
|
638
|
+
return
|
|
639
|
+
self._conn.executemany(
|
|
640
|
+
"""
|
|
641
|
+
INSERT INTO orders_accepted (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, broker_order_id)
|
|
642
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
643
|
+
""",
|
|
644
|
+
self._buffers["orders_accepted"],
|
|
645
|
+
)
|
|
646
|
+
self._conn.commit()
|
|
647
|
+
self._buffers["orders_accepted"].clear()
|
|
648
|
+
|
|
649
|
+
def _flush_orders_rejected(self) -> None:
|
|
650
|
+
"""
|
|
651
|
+
Insert buffered order rejected records into the database.
|
|
652
|
+
"""
|
|
653
|
+
if not self._buffers["orders_rejected"]:
|
|
654
|
+
return
|
|
655
|
+
self._conn.executemany(
|
|
656
|
+
"""
|
|
657
|
+
INSERT INTO orders_rejected (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, rejection_reason, rejection_message)
|
|
658
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
659
|
+
""",
|
|
660
|
+
self._buffers["orders_rejected"],
|
|
661
|
+
)
|
|
662
|
+
self._conn.commit()
|
|
663
|
+
self._buffers["orders_rejected"].clear()
|
|
664
|
+
|
|
665
|
+
def _flush_cancellations_accepted(self) -> None:
|
|
666
|
+
"""
|
|
667
|
+
Insert buffered cancellation accepted records into the database.
|
|
668
|
+
"""
|
|
669
|
+
if not self._buffers["cancellations_accepted"]:
|
|
670
|
+
return
|
|
671
|
+
self._conn.executemany(
|
|
672
|
+
"""
|
|
673
|
+
INSERT INTO cancellations_accepted (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, broker_order_id)
|
|
674
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
675
|
+
""",
|
|
676
|
+
self._buffers["cancellations_accepted"],
|
|
677
|
+
)
|
|
678
|
+
self._conn.commit()
|
|
679
|
+
self._buffers["cancellations_accepted"].clear()
|
|
680
|
+
|
|
681
|
+
def _flush_cancellations_rejected(self) -> None:
|
|
682
|
+
"""
|
|
683
|
+
Insert buffered cancellation rejected records into the database.
|
|
684
|
+
"""
|
|
685
|
+
if not self._buffers["cancellations_rejected"]:
|
|
686
|
+
return
|
|
687
|
+
self._conn.executemany(
|
|
688
|
+
"""
|
|
689
|
+
INSERT INTO cancellations_rejected (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, rejection_reason, rejection_message)
|
|
690
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
691
|
+
""",
|
|
692
|
+
self._buffers["cancellations_rejected"],
|
|
693
|
+
)
|
|
694
|
+
self._conn.commit()
|
|
695
|
+
self._buffers["cancellations_rejected"].clear()
|
|
696
|
+
|
|
697
|
+
def _flush_modifications_accepted(self) -> None:
|
|
698
|
+
"""
|
|
699
|
+
Insert buffered modification accepted records into the database.
|
|
700
|
+
"""
|
|
701
|
+
if not self._buffers["modifications_accepted"]:
|
|
702
|
+
return
|
|
703
|
+
self._conn.executemany(
|
|
704
|
+
"""
|
|
705
|
+
INSERT INTO modifications_accepted (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, broker_order_id)
|
|
706
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
707
|
+
""",
|
|
708
|
+
self._buffers["modifications_accepted"],
|
|
709
|
+
)
|
|
710
|
+
self._conn.commit()
|
|
711
|
+
self._buffers["modifications_accepted"].clear()
|
|
712
|
+
|
|
713
|
+
def _flush_modifications_rejected(self) -> None:
|
|
714
|
+
"""
|
|
715
|
+
Insert buffered modification rejected records into the database.
|
|
716
|
+
"""
|
|
717
|
+
if not self._buffers["modifications_rejected"]:
|
|
718
|
+
return
|
|
719
|
+
self._conn.executemany(
|
|
720
|
+
"""
|
|
721
|
+
INSERT INTO modifications_rejected (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, rejection_reason, rejection_message)
|
|
722
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
723
|
+
""",
|
|
724
|
+
self._buffers["modifications_rejected"],
|
|
725
|
+
)
|
|
726
|
+
self._conn.commit()
|
|
727
|
+
self._buffers["modifications_rejected"].clear()
|
|
728
|
+
|
|
729
|
+
def _flush_fills(self) -> None:
|
|
730
|
+
"""
|
|
731
|
+
Insert buffered fill records into the database.
|
|
732
|
+
"""
|
|
733
|
+
if not self._buffers["fills"]:
|
|
734
|
+
return
|
|
735
|
+
self._conn.executemany(
|
|
736
|
+
"""
|
|
737
|
+
INSERT INTO fills (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, broker_order_id, symbol, fill_id, broker_fill_id, side, quantity_filled, fill_price, commission, exchange)
|
|
738
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
739
|
+
""",
|
|
740
|
+
self._buffers["fills"],
|
|
741
|
+
)
|
|
742
|
+
self._conn.commit()
|
|
743
|
+
self._buffers["fills"].clear()
|
|
744
|
+
|
|
745
|
+
def _flush_expirations(self) -> None:
|
|
746
|
+
"""
|
|
747
|
+
Insert buffered expiration records into the database.
|
|
748
|
+
"""
|
|
749
|
+
if not self._buffers["expirations"]:
|
|
750
|
+
return
|
|
751
|
+
self._conn.executemany(
|
|
752
|
+
"""
|
|
753
|
+
INSERT INTO expirations (run_id, ts_event_ns, ts_created_ns, ts_broker_ns, associated_order_id, broker_order_id, symbol)
|
|
754
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
755
|
+
""",
|
|
756
|
+
self._buffers["expirations"],
|
|
757
|
+
)
|
|
758
|
+
self._conn.commit()
|
|
759
|
+
self._buffers["expirations"].clear()
|