onesecondtrader 0.5.0__py3-none-any.whl → 0.5.2__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/__init__.py +2 -2
- onesecondtrader/domain.py +695 -0
- {onesecondtrader-0.5.0.dist-info → onesecondtrader-0.5.2.dist-info}/METADATA +1 -1
- onesecondtrader-0.5.2.dist-info/RECORD +7 -0
- onesecondtrader/domain_models.py +0 -216
- onesecondtrader-0.5.0.dist-info/RECORD +0 -7
- /onesecondtrader/{log_config.py → monitoring.py} +0 -0
- {onesecondtrader-0.5.0.dist-info → onesecondtrader-0.5.2.dist-info}/LICENSE +0 -0
- {onesecondtrader-0.5.0.dist-info → onesecondtrader-0.5.2.dist-info}/WHEEL +0 -0
onesecondtrader/__init__.py
CHANGED
|
@@ -5,10 +5,10 @@ Research, simulate, and deploy algorithmic trading strategies — all in one pla
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
# Core infrastructure
|
|
8
|
-
from .
|
|
8
|
+
from .monitoring import logger
|
|
9
9
|
|
|
10
10
|
# Domain models
|
|
11
|
-
from .
|
|
11
|
+
from .domain import MarketData, PositionManagement, SystemManagement
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
14
|
# Core infrastructure
|
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Domain ontology for trading infrastructure.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
|
|
6
|
+
- the **non-component-specific domain models** used across the
|
|
7
|
+
trading infrastructure and
|
|
8
|
+
- the **event messages** used for decoupled communication
|
|
9
|
+
between the trading infrastructure's components.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import collections
|
|
13
|
+
import dataclasses
|
|
14
|
+
import enum
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import uuid
|
|
17
|
+
|
|
18
|
+
from .monitoring import logger
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
########################################################################################
|
|
22
|
+
# DOMAIN MODELS
|
|
23
|
+
########################################################################################
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DomainModel:
|
|
27
|
+
"""
|
|
28
|
+
Container class for non-component-specific domain models.
|
|
29
|
+
Domain models are organised into namespaces to provide clear semantic groupings
|
|
30
|
+
(e.g.: `PositionManagement.OrderType.MARKET`).
|
|
31
|
+
Note that the namespace prefix `DomainModel` can be omitted when accessing
|
|
32
|
+
the domain models, i.e.: `DomainModel.MarketData.OHLCV` can be aliased as
|
|
33
|
+
`MarketData.OHLCV`.
|
|
34
|
+
|
|
35
|
+
???+ note "Domain Model Hierarchy"
|
|
36
|
+
|
|
37
|
+
```mermaid
|
|
38
|
+
---
|
|
39
|
+
config:
|
|
40
|
+
themeVariables:
|
|
41
|
+
fontSize: "11px"
|
|
42
|
+
---
|
|
43
|
+
graph LR
|
|
44
|
+
|
|
45
|
+
A0[DomainModels]
|
|
46
|
+
|
|
47
|
+
A01[MarketData]
|
|
48
|
+
A02[PositionManagement]
|
|
49
|
+
A03[SystemManagement]
|
|
50
|
+
|
|
51
|
+
A0 --> A01
|
|
52
|
+
A0 --> A02
|
|
53
|
+
A0 --> A03
|
|
54
|
+
|
|
55
|
+
A1["**MarketData.OHLCV**"]
|
|
56
|
+
A2["**MarketData.RecordType**"]
|
|
57
|
+
A01 --> A1
|
|
58
|
+
A01 --> A2
|
|
59
|
+
|
|
60
|
+
B1["**PositionManagement.OrderType**"]
|
|
61
|
+
B2["**PositionManagement.OrderState**"]
|
|
62
|
+
B3["**PositionManagement.Side**"]
|
|
63
|
+
B4["**PositionManagement.TimeInForce**"]
|
|
64
|
+
B5["**PositionManagement.CancelReason**"]
|
|
65
|
+
A02 --> B1
|
|
66
|
+
A02 --> B2
|
|
67
|
+
A02 --> B3
|
|
68
|
+
A02 --> B4
|
|
69
|
+
A02 --> B5
|
|
70
|
+
|
|
71
|
+
C1["**SystemManagement.StopReason**"]
|
|
72
|
+
A03 --> C1
|
|
73
|
+
|
|
74
|
+
subgraph MarketData ["Market Data Domain Models"]
|
|
75
|
+
A1
|
|
76
|
+
A2
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
subgraph PositionManagement ["Position Management Domain Models"]
|
|
80
|
+
B1
|
|
81
|
+
B2
|
|
82
|
+
B3
|
|
83
|
+
B4
|
|
84
|
+
B5
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
subgraph SystemManagement ["System Management Domain Models"]
|
|
88
|
+
C1
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
subgraph DomainModelNamespaces ["Domain Model Namespaces"]
|
|
92
|
+
A0
|
|
93
|
+
A01
|
|
94
|
+
A02
|
|
95
|
+
A03
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
# ----------------------------------------------------------------------------------
|
|
101
|
+
# SYSTEM MANAGEMENT DOMAIN MODEL NAMESPACE
|
|
102
|
+
# ----------------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
class SystemManagement:
|
|
105
|
+
"""
|
|
106
|
+
Domain model namespace for system management related concepts.
|
|
107
|
+
|
|
108
|
+
???+ note "Domain Model Hierarchy"
|
|
109
|
+
|
|
110
|
+
```mermaid
|
|
111
|
+
---
|
|
112
|
+
config:
|
|
113
|
+
themeVariables:
|
|
114
|
+
fontSize: "11px"
|
|
115
|
+
---
|
|
116
|
+
graph LR
|
|
117
|
+
|
|
118
|
+
A0[DomainModels]
|
|
119
|
+
|
|
120
|
+
A01[MarketData]
|
|
121
|
+
A02[PositionManagement]
|
|
122
|
+
A03[SystemManagement]
|
|
123
|
+
|
|
124
|
+
A0 --> A01
|
|
125
|
+
A0 --> A02
|
|
126
|
+
A0 --> A03
|
|
127
|
+
|
|
128
|
+
A1["**MarketData.OHLCV**"]
|
|
129
|
+
A2["**MarketData.RecordType**"]
|
|
130
|
+
A01 --> A1
|
|
131
|
+
A01 --> A2
|
|
132
|
+
|
|
133
|
+
B1["**PositionManagement.OrderType**"]
|
|
134
|
+
B2["**PositionManagement.OrderState**"]
|
|
135
|
+
B3["**PositionManagement.Side**"]
|
|
136
|
+
B4["**PositionManagement.TimeInForce**"]
|
|
137
|
+
B5["**PositionManagement.CancelReason**"]
|
|
138
|
+
A02 --> B1
|
|
139
|
+
A02 --> B2
|
|
140
|
+
A02 --> B3
|
|
141
|
+
A02 --> B4
|
|
142
|
+
A02 --> B5
|
|
143
|
+
|
|
144
|
+
C1["**SystemManagement.StopReason**"]
|
|
145
|
+
A03 --> C1
|
|
146
|
+
|
|
147
|
+
subgraph MarketData ["Market Data Domain Models"]
|
|
148
|
+
A1
|
|
149
|
+
A2
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
subgraph PositionManagement ["Position Management Domain Models"]
|
|
153
|
+
B1
|
|
154
|
+
B2
|
|
155
|
+
B3
|
|
156
|
+
B4
|
|
157
|
+
B5
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
subgraph SystemManagement ["System Management Domain Models"]
|
|
161
|
+
C1
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
subgraph DomainModelNamespaces ["Domain Model Namespaces"]
|
|
165
|
+
A0
|
|
166
|
+
A01
|
|
167
|
+
A02
|
|
168
|
+
A03
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
style SystemManagement fill:#6F42C1,fill-opacity:0.3
|
|
172
|
+
```
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
class StopReason(enum.Enum):
|
|
176
|
+
"""
|
|
177
|
+
Reasons for system or component shutdown.
|
|
178
|
+
|
|
179
|
+
**Attributes:**
|
|
180
|
+
|
|
181
|
+
| Enum | Value | Description |
|
|
182
|
+
|------|-------|-------------|
|
|
183
|
+
| `SYSTEM_SHUTDOWN` | `enum.auto()` | Coordinated shutdown of entire system |
|
|
184
|
+
| `COMPONENT_DISCONNECT` | `enum.auto()` | Single component disconnect |
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
SYSTEM_SHUTDOWN = enum.auto()
|
|
188
|
+
COMPONENT_DISCONNECT = enum.auto()
|
|
189
|
+
|
|
190
|
+
# ----------------------------------------------------------------------------------
|
|
191
|
+
# POSITION MANAGEMENT DOMAIN MODEL NAMESPACE
|
|
192
|
+
# ----------------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
class PositionManagement:
|
|
195
|
+
"""
|
|
196
|
+
???+ note "Domain Model Hierarchy"
|
|
197
|
+
|
|
198
|
+
```mermaid
|
|
199
|
+
---
|
|
200
|
+
config:
|
|
201
|
+
themeVariables:
|
|
202
|
+
fontSize: "11px"
|
|
203
|
+
---
|
|
204
|
+
graph LR
|
|
205
|
+
|
|
206
|
+
A0[DomainModels]
|
|
207
|
+
|
|
208
|
+
A01[MarketData]
|
|
209
|
+
A02[PositionManagement]
|
|
210
|
+
A03[SystemManagement]
|
|
211
|
+
|
|
212
|
+
A0 --> A01
|
|
213
|
+
A0 --> A02
|
|
214
|
+
A0 --> A03
|
|
215
|
+
|
|
216
|
+
A1["**MarketData.OHLCV**"]
|
|
217
|
+
A2["**MarketData.RecordType**"]
|
|
218
|
+
A01 --> A1
|
|
219
|
+
A01 --> A2
|
|
220
|
+
|
|
221
|
+
B1["**PositionManagement.OrderType**"]
|
|
222
|
+
B2["**PositionManagement.OrderState**"]
|
|
223
|
+
B3["**PositionManagement.Side**"]
|
|
224
|
+
B4["**PositionManagement.TimeInForce**"]
|
|
225
|
+
B5["**PositionManagement.CancelReason**"]
|
|
226
|
+
A02 --> B1
|
|
227
|
+
A02 --> B2
|
|
228
|
+
A02 --> B3
|
|
229
|
+
A02 --> B4
|
|
230
|
+
A02 --> B5
|
|
231
|
+
|
|
232
|
+
C1["**SystemManagement.StopReason**"]
|
|
233
|
+
A03 --> C1
|
|
234
|
+
|
|
235
|
+
subgraph MarketData ["Market Data Domain Models"]
|
|
236
|
+
A1
|
|
237
|
+
A2
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
subgraph PositionManagement ["Position Management Domain Models"]
|
|
241
|
+
B1
|
|
242
|
+
B2
|
|
243
|
+
B3
|
|
244
|
+
B4
|
|
245
|
+
B5
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
subgraph SystemManagement ["System Management Domain Models"]
|
|
249
|
+
C1
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
subgraph DomainModelNamespaces ["Domain Model Namespaces"]
|
|
253
|
+
A0
|
|
254
|
+
A01
|
|
255
|
+
A02
|
|
256
|
+
A03
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
style PositionManagement fill:#6F42C1,fill-opacity:0.3
|
|
260
|
+
```
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
# ------------------------------------------------------------------------------
|
|
264
|
+
# ORDER TYPE
|
|
265
|
+
|
|
266
|
+
class OrderType(enum.Enum):
|
|
267
|
+
"""
|
|
268
|
+
Order execution types.
|
|
269
|
+
|
|
270
|
+
**Attributes:**
|
|
271
|
+
|
|
272
|
+
| Enum | Value | Description |
|
|
273
|
+
|------|-------|-------------|
|
|
274
|
+
| `MARKET` | `enum.auto()` | Execute immediately at best available price |
|
|
275
|
+
| `LIMIT` | `enum.auto()` | Execute only at specified price or better |
|
|
276
|
+
| `STOP` | `enum.auto()` | Becomes market order when trigger price is reached |
|
|
277
|
+
| `STOP_LIMIT` | `enum.auto()` | Becomes limit order when trigger price is reached |
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
MARKET = enum.auto()
|
|
281
|
+
LIMIT = enum.auto()
|
|
282
|
+
STOP = enum.auto()
|
|
283
|
+
STOP_LIMIT = enum.auto()
|
|
284
|
+
|
|
285
|
+
# ------------------------------------------------------------------------------
|
|
286
|
+
# ORDER STATE
|
|
287
|
+
|
|
288
|
+
class OrderState(enum.Enum):
|
|
289
|
+
"""
|
|
290
|
+
Order lifecycle states.
|
|
291
|
+
|
|
292
|
+
**Attributes:**
|
|
293
|
+
|
|
294
|
+
| Enum | Value | Description |
|
|
295
|
+
|------|-------|-------------|
|
|
296
|
+
| `NEW` | `enum.auto()` | Created but not submitted |
|
|
297
|
+
| `SUBMITTED` | `enum.auto()` | Sent to broker/exchange |
|
|
298
|
+
| `ACTIVE` | `enum.auto()` | Live in market |
|
|
299
|
+
| `PARTIALLY_FILLED` | `enum.auto()` | Partially executed |
|
|
300
|
+
| `FILLED` | `enum.auto()` | Completely executed |
|
|
301
|
+
| `CANCELLED` | `enum.auto()` | Cancelled before first fill |
|
|
302
|
+
| `CANCELLED_AT_PARTIAL_FILL` | `enum.auto()` | Cancelled after partial fill |
|
|
303
|
+
| `REJECTED` | `enum.auto()` | Rejected by broker/exchange |
|
|
304
|
+
| `EXPIRED` | `enum.auto()` | Expired due to time-in-force constraints |
|
|
305
|
+
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
NEW = enum.auto()
|
|
309
|
+
SUBMITTED = enum.auto()
|
|
310
|
+
ACTIVE = enum.auto()
|
|
311
|
+
PARTIALLY_FILLED = enum.auto()
|
|
312
|
+
FILLED = enum.auto()
|
|
313
|
+
CANCELLED = enum.auto()
|
|
314
|
+
CANCELLED_AT_PARTIAL_FILL = enum.auto()
|
|
315
|
+
REJECTED = enum.auto()
|
|
316
|
+
EXPIRED = enum.auto()
|
|
317
|
+
|
|
318
|
+
# ------------------------------------------------------------------------------
|
|
319
|
+
# SIDE
|
|
320
|
+
class Side(enum.Enum):
|
|
321
|
+
"""
|
|
322
|
+
Order direction - buy or sell.
|
|
323
|
+
|
|
324
|
+
**Attributes:**
|
|
325
|
+
|
|
326
|
+
| Enum | Value | Description |
|
|
327
|
+
|------|-------|-------------|
|
|
328
|
+
| `BUY` | `enum.auto()` | Buy the financial instrument |
|
|
329
|
+
| `SELL` | `enum.auto()` | Sell the financial instrument |
|
|
330
|
+
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
BUY = enum.auto()
|
|
334
|
+
SELL = enum.auto()
|
|
335
|
+
|
|
336
|
+
# ------------------------------------------------------------------------------
|
|
337
|
+
# TIME IN FORCE
|
|
338
|
+
class TimeInForce(enum.Enum):
|
|
339
|
+
"""
|
|
340
|
+
Order time-in-force specifications.
|
|
341
|
+
|
|
342
|
+
**Attributes:**
|
|
343
|
+
|
|
344
|
+
| Enum | Value | Description |
|
|
345
|
+
|------|-------|-------------|
|
|
346
|
+
| `DAY` | `enum.auto()` | Valid until end of trading day |
|
|
347
|
+
| `FOK` | `enum.auto()` | Fill entire order immediately or cancel (Fill-or-Kill) |
|
|
348
|
+
| `GTC` | `enum.auto()` | Active until explicitly cancelled (Good-Till-Cancelled) |
|
|
349
|
+
| `GTD` | `enum.auto()` | Active until specified date (Good-Till-Date) |
|
|
350
|
+
| `IOC` | `enum.auto()` | Execute available quantity immediately, cancel rest (Immediate-or-Cancel) |
|
|
351
|
+
"""
|
|
352
|
+
|
|
353
|
+
DAY = enum.auto()
|
|
354
|
+
FOK = enum.auto()
|
|
355
|
+
GTC = enum.auto()
|
|
356
|
+
GTD = enum.auto()
|
|
357
|
+
IOC = enum.auto()
|
|
358
|
+
|
|
359
|
+
# ------------------------------------------------------------------------------
|
|
360
|
+
# CANCEL REASON
|
|
361
|
+
class CancelReason(enum.Enum):
|
|
362
|
+
"""
|
|
363
|
+
Reasons for order cancellation.
|
|
364
|
+
|
|
365
|
+
**Attributes:**
|
|
366
|
+
|
|
367
|
+
| Enum | Value | Description |
|
|
368
|
+
|------|-------|-------------|
|
|
369
|
+
| `CLIENT_REQUEST` | `enum.auto()` | Order cancelled by client/trader request |
|
|
370
|
+
| `EXPIRED_TIME_IN_FORCE` | `enum.auto()` | Order expired due to time-in-force constraints |
|
|
371
|
+
| `BROKER_REJECTED_AT_SUBMISSION` | `enum.auto()` | Broker rejected order during submission |
|
|
372
|
+
| `BROKER_FORCED_CANCEL` | `enum.auto()` | Broker cancelled order due to risk or other constraints |
|
|
373
|
+
| `UNKNOWN` | `enum.auto()` | Cancellation reason not specified or unknown |
|
|
374
|
+
"""
|
|
375
|
+
|
|
376
|
+
CLIENT_REQUEST = enum.auto()
|
|
377
|
+
EXPIRED_TIME_IN_FORCE = enum.auto()
|
|
378
|
+
BROKER_REJECTED_AT_SUBMISSION = enum.auto()
|
|
379
|
+
BROKER_FORCED_CANCEL = enum.auto()
|
|
380
|
+
UNKNOWN = enum.auto()
|
|
381
|
+
|
|
382
|
+
# ----------------------------------------------------------------------------------
|
|
383
|
+
# MARKET DATA DOMAIN MODEL NAMESPACE
|
|
384
|
+
# ----------------------------------------------------------------------------------
|
|
385
|
+
|
|
386
|
+
class MarketData:
|
|
387
|
+
"""
|
|
388
|
+
Domain model namespace for market data related concepts.
|
|
389
|
+
(Can be aliased as `MarketData` for convenience.)
|
|
390
|
+
|
|
391
|
+
???+ note "Domain Model Hierarchy"
|
|
392
|
+
|
|
393
|
+
```mermaid
|
|
394
|
+
---
|
|
395
|
+
config:
|
|
396
|
+
themeVariables:
|
|
397
|
+
fontSize: "11px"
|
|
398
|
+
---
|
|
399
|
+
graph LR
|
|
400
|
+
|
|
401
|
+
A0[DomainModels]
|
|
402
|
+
|
|
403
|
+
A01[MarketData]
|
|
404
|
+
A02[PositionManagement]
|
|
405
|
+
A03[SystemManagement]
|
|
406
|
+
|
|
407
|
+
A0 --> A01
|
|
408
|
+
A0 --> A02
|
|
409
|
+
A0 --> A03
|
|
410
|
+
|
|
411
|
+
A1["**MarketData.OHLCV**"]
|
|
412
|
+
A2["**MarketData.RecordType**"]
|
|
413
|
+
A01 --> A1
|
|
414
|
+
A01 --> A2
|
|
415
|
+
|
|
416
|
+
B1["**PositionManagement.OrderType**"]
|
|
417
|
+
B2["**PositionManagement.OrderState**"]
|
|
418
|
+
B3["**PositionManagement.Side**"]
|
|
419
|
+
B4["**PositionManagement.TimeInForce**"]
|
|
420
|
+
B5["**PositionManagement.CancelReason**"]
|
|
421
|
+
A02 --> B1
|
|
422
|
+
A02 --> B2
|
|
423
|
+
A02 --> B3
|
|
424
|
+
A02 --> B4
|
|
425
|
+
A02 --> B5
|
|
426
|
+
|
|
427
|
+
C1["**SystemManagement.StopReason**"]
|
|
428
|
+
A03 --> C1
|
|
429
|
+
|
|
430
|
+
subgraph MarketData ["Market Data Domain Models"]
|
|
431
|
+
A1
|
|
432
|
+
A2
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
subgraph PositionManagement ["Position Management Domain Models"]
|
|
436
|
+
B1
|
|
437
|
+
B2
|
|
438
|
+
B3
|
|
439
|
+
B4
|
|
440
|
+
B5
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
subgraph SystemManagement ["System Management Domain Models"]
|
|
444
|
+
C1
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
subgraph DomainModelNamespaces ["Domain Model Namespaces"]
|
|
448
|
+
A0
|
|
449
|
+
A01
|
|
450
|
+
A02
|
|
451
|
+
A03
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
style MarketData fill:#6F42C1,fill-opacity:0.3
|
|
455
|
+
```
|
|
456
|
+
"""
|
|
457
|
+
|
|
458
|
+
# ------------------------------------------------------------------------------
|
|
459
|
+
# OHLCV
|
|
460
|
+
|
|
461
|
+
OHLCV = collections.namedtuple(
|
|
462
|
+
"OHLCV", ["open", "high", "low", "close", "volume"]
|
|
463
|
+
)
|
|
464
|
+
"""
|
|
465
|
+
Simple data class for Open-High-Low-Close-Volume (OHLCV) bar data.
|
|
466
|
+
|
|
467
|
+
Attributes:
|
|
468
|
+
open (float): Open price
|
|
469
|
+
high (float): High price
|
|
470
|
+
low (float): Low price
|
|
471
|
+
close (float): Close price
|
|
472
|
+
volume (int | float): Volume
|
|
473
|
+
|
|
474
|
+
Examples:
|
|
475
|
+
>>> from onesecondtrader.domain import MarketData
|
|
476
|
+
>>> bar = MarketData.OHLCV(12.34, 13.74, 11.26, 12.32, 56789)
|
|
477
|
+
>>> bar.open
|
|
478
|
+
12.34
|
|
479
|
+
>>> bar.high
|
|
480
|
+
13.74
|
|
481
|
+
"""
|
|
482
|
+
|
|
483
|
+
# ------------------------------------------------------------------------------
|
|
484
|
+
# RECORD TYPE
|
|
485
|
+
|
|
486
|
+
class RecordType(enum.Enum):
|
|
487
|
+
"""
|
|
488
|
+
Market data record type identifiers that preserve compatibility with
|
|
489
|
+
Databento's rtype integer identifiers.
|
|
490
|
+
|
|
491
|
+
**Attributes:**
|
|
492
|
+
|
|
493
|
+
| Enum | Value | Description |
|
|
494
|
+
|------|-------|-------------|
|
|
495
|
+
| `OHLCV_1S` | `32` | 1-second bars |
|
|
496
|
+
| `OHLCV_1M` | `33` | 1-minute bars |
|
|
497
|
+
| `OHLCV_1H` | `34` | 1-hour bars |
|
|
498
|
+
| `OHLCV_1D` | `35` | Daily bars |
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
Examples:
|
|
502
|
+
>>> from onesecondtrader.domain import MarketData
|
|
503
|
+
>>> MarketData.RecordType.OHLCV_1S
|
|
504
|
+
<MarketData.RecordType.OHLCV_1S: 32>
|
|
505
|
+
>>> MarketData.RecordType.OHLCV_1S.value
|
|
506
|
+
32
|
|
507
|
+
>>> MarketData.RecordType.to_string(32)
|
|
508
|
+
'1-second bars'
|
|
509
|
+
>>> MarketData.RecordType.to_string(99)
|
|
510
|
+
'unknown (99)'
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
OHLCV_1S = 32
|
|
514
|
+
OHLCV_1M = 33
|
|
515
|
+
OHLCV_1H = 34
|
|
516
|
+
OHLCV_1D = 35
|
|
517
|
+
|
|
518
|
+
@classmethod
|
|
519
|
+
def to_string(cls, record_type: int) -> str:
|
|
520
|
+
match record_type:
|
|
521
|
+
case cls.OHLCV_1S.value:
|
|
522
|
+
return "1-second bars"
|
|
523
|
+
case cls.OHLCV_1M.value:
|
|
524
|
+
return "1-minute bars"
|
|
525
|
+
case cls.OHLCV_1H.value:
|
|
526
|
+
return "1-hour bars"
|
|
527
|
+
case cls.OHLCV_1D.value:
|
|
528
|
+
return "daily bars"
|
|
529
|
+
case _:
|
|
530
|
+
return f"unknown ({record_type})"
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
# --------------------------------------------------------------------------------------
|
|
534
|
+
# ALIASES
|
|
535
|
+
# --------------------------------------------------------------------------------------
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
MarketData = DomainModel.MarketData
|
|
539
|
+
PositionManagement = DomainModel.PositionManagement
|
|
540
|
+
SystemManagement = DomainModel.SystemManagement
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
########################################################################################
|
|
544
|
+
# DOMAIN EVENTS
|
|
545
|
+
########################################################################################
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
class Event:
|
|
549
|
+
@dataclasses.dataclass(kw_only=True, frozen=True)
|
|
550
|
+
class _AbstractEvent:
|
|
551
|
+
"""
|
|
552
|
+
Root base class for all event types in the trading system.
|
|
553
|
+
|
|
554
|
+
This class prevents instantiation of any class that has subclasses.
|
|
555
|
+
Only leaf classes (classes with no children) can be instantiated. This ensures
|
|
556
|
+
that only concrete event classes can be created, while base/parent classes
|
|
557
|
+
remain non-instantiable.
|
|
558
|
+
|
|
559
|
+
The check is performed dynamically at instantiation time, so it automatically
|
|
560
|
+
adapts to changes in the inheritance hierarchy without manual maintenance.
|
|
561
|
+
|
|
562
|
+
Attributes:
|
|
563
|
+
ts_event (pd.Timestamp): Timestamp of the event. Must be timezone-aware.
|
|
564
|
+
event_id (uuid.UUID): Unique identifier for the event. Automatically
|
|
565
|
+
generated.
|
|
566
|
+
sequence_number (int | None): Global sequence number assigned by the event
|
|
567
|
+
bus at publish time.
|
|
568
|
+
"""
|
|
569
|
+
|
|
570
|
+
ts_event: pd.Timestamp
|
|
571
|
+
event_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
|
|
572
|
+
sequence_number: int | None = dataclasses.field(default=None, init=False)
|
|
573
|
+
|
|
574
|
+
@staticmethod
|
|
575
|
+
def _has_subclasses(cls):
|
|
576
|
+
"""
|
|
577
|
+
Check if a class has any subclasses (direct or indirect).
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
cls: The class to check for subclasses.
|
|
581
|
+
|
|
582
|
+
Returns:
|
|
583
|
+
bool: True if the class has any subclasses, False otherwise.
|
|
584
|
+
"""
|
|
585
|
+
|
|
586
|
+
def get_all_subclasses(cls):
|
|
587
|
+
all_subclasses = set()
|
|
588
|
+
for subclass in cls.__subclasses__():
|
|
589
|
+
all_subclasses.add(subclass)
|
|
590
|
+
all_subclasses.update(get_all_subclasses(subclass))
|
|
591
|
+
return all_subclasses
|
|
592
|
+
|
|
593
|
+
return len(get_all_subclasses(cls)) > 0
|
|
594
|
+
|
|
595
|
+
def __new__(cls, *args, **kwargs):
|
|
596
|
+
"""
|
|
597
|
+
Prevent instantiation of classes that have subclasses.
|
|
598
|
+
|
|
599
|
+
Only allows instantiation of leaf classes (classes with no children).
|
|
600
|
+
|
|
601
|
+
Raises:
|
|
602
|
+
TypeError: If attempting to instantiate a class that has subclasses.
|
|
603
|
+
"""
|
|
604
|
+
if Event._AbstractEvent._has_subclasses(cls):
|
|
605
|
+
subclass_names = [subcls.__name__ for subcls in cls.__subclasses__()]
|
|
606
|
+
logger.error(
|
|
607
|
+
f"Cannot instantiate class '{cls.__name__}' because it has subclasses: "
|
|
608
|
+
f"{subclass_names}. Only leaf classes (classes with no children) can be instantiated."
|
|
609
|
+
)
|
|
610
|
+
return super().__new__(cls)
|
|
611
|
+
|
|
612
|
+
def __post_init__(self) -> None:
|
|
613
|
+
if self.ts_event.tz is None:
|
|
614
|
+
raise ValueError(
|
|
615
|
+
f"Event timestamp should be timezone-aware, "
|
|
616
|
+
f"got timezone-naive: {self.ts_event}"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
@dataclasses.dataclass(kw_only=True, frozen=True)
|
|
620
|
+
class _AbstractMarketUpdateEvent(_AbstractEvent):
|
|
621
|
+
"""
|
|
622
|
+
Base class for events related to market data updates.
|
|
623
|
+
|
|
624
|
+
Attributes:
|
|
625
|
+
ts_event (pd.Timestamp): Timestamp of the event. Must be timezone-aware.
|
|
626
|
+
event_id (uuid.UUID): Unique identifier for the event. (auto-generated)
|
|
627
|
+
sequence_number (int | None): Global sequence number assigned by the event
|
|
628
|
+
bus at publish time.
|
|
629
|
+
symbol (str): The financial instrument symbol.
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
symbol: str
|
|
633
|
+
|
|
634
|
+
@dataclasses.dataclass(kw_only=True, frozen=True)
|
|
635
|
+
class _AbstractBrokerRequest(_AbstractEvent):
|
|
636
|
+
"""
|
|
637
|
+
Base class for broker request events.
|
|
638
|
+
|
|
639
|
+
Attributes:
|
|
640
|
+
ts_event (pd.Timestamp): Timestamp of the request. (defaults to current
|
|
641
|
+
UTC time)
|
|
642
|
+
event_id (uuid.UUID): Unique identifier for the event. (auto-generated)
|
|
643
|
+
sequence_number (int | None): Global sequence number assigned by the event
|
|
644
|
+
bus at publish time.
|
|
645
|
+
symbol (str): The financial instrument symbol.
|
|
646
|
+
"""
|
|
647
|
+
|
|
648
|
+
symbol: str
|
|
649
|
+
ts_event: pd.Timestamp = dataclasses.field(
|
|
650
|
+
default_factory=lambda: pd.Timestamp.now(tz="UTC")
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
@dataclasses.dataclass(kw_only=True, frozen=True)
|
|
654
|
+
class _AbstractBrokerResponse(_AbstractEvent):
|
|
655
|
+
"""
|
|
656
|
+
Base class for broker response events.
|
|
657
|
+
|
|
658
|
+
Attributes:
|
|
659
|
+
ts_event (pd.Timestamp): Timestamp of the event. Must be timezone-aware.
|
|
660
|
+
event_id (uuid.UUID): Unique identifier for the event. (auto-generated)
|
|
661
|
+
sequence_number (int | None): Global sequence number assigned by the event
|
|
662
|
+
bus at publish time.
|
|
663
|
+
symbol (str): The financial instrument symbol.
|
|
664
|
+
"""
|
|
665
|
+
|
|
666
|
+
symbol: str
|
|
667
|
+
|
|
668
|
+
@dataclasses.dataclass(kw_only=True, frozen=True)
|
|
669
|
+
class _AbstractSystemEvent(_AbstractEvent):
|
|
670
|
+
"""
|
|
671
|
+
Base class for system-level events.
|
|
672
|
+
|
|
673
|
+
Attributes:
|
|
674
|
+
ts_event (pd.Timestamp): Timestamp of the event. Must be timezone-aware.
|
|
675
|
+
event_id (uuid.UUID): Unique identifier for the event. (auto-generated)
|
|
676
|
+
sequence_number (int | None): Global sequence number assigned by the event
|
|
677
|
+
bus at publish time.
|
|
678
|
+
"""
|
|
679
|
+
|
|
680
|
+
pass
|
|
681
|
+
|
|
682
|
+
class MarketUpdate:
|
|
683
|
+
pass
|
|
684
|
+
|
|
685
|
+
class BrokerRequest:
|
|
686
|
+
pass
|
|
687
|
+
|
|
688
|
+
class Order:
|
|
689
|
+
pass
|
|
690
|
+
|
|
691
|
+
class BrokerResponse:
|
|
692
|
+
pass
|
|
693
|
+
|
|
694
|
+
class System:
|
|
695
|
+
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: onesecondtrader
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: The Trading Infrastructure Toolkit for Python. Research, simulate, and deploy algorithmic trading strategies — all in one place.
|
|
5
5
|
Author: Nils P. Kujath
|
|
6
6
|
Author-email: 63961429+NilsKujath@users.noreply.github.com
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
onesecondtrader/__init__.py,sha256=ctOAouU6EXxBlhflJwv39stoQcgYIb_Uk1U7RRQZPRk,423
|
|
2
|
+
onesecondtrader/domain.py,sha256=DeB6JbhCa0jlvwiBxRmCP1utJW41t2-9I-MnxBVvhX8,22523
|
|
3
|
+
onesecondtrader/monitoring.py,sha256=TGm53SD_TA4Xa57Go91BGHV3SEIA6MflwSO63r9Jt6g,366
|
|
4
|
+
onesecondtrader-0.5.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
5
|
+
onesecondtrader-0.5.2.dist-info/METADATA,sha256=xiTxHDSZ_Y6WekmKXPKuf8g7DlCJLbd_CtRlqDHo9CU,1220
|
|
6
|
+
onesecondtrader-0.5.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
7
|
+
onesecondtrader-0.5.2.dist-info/RECORD,,
|
onesecondtrader/domain_models.py
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module defines the core domain models for trading infrastructure.
|
|
3
|
-
These models are organised into namespaces to provide clear semantic groupings
|
|
4
|
-
(e.g.: `PositionManagement.OrderType.MARKET`).
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import collections
|
|
8
|
-
import enum
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class MarketData:
|
|
12
|
-
"""
|
|
13
|
-
Domain model namespace for market data related concepts.
|
|
14
|
-
|
|
15
|
-
???+ note "Market Data Record Types"
|
|
16
|
-
|
|
17
|
-
```mermaid
|
|
18
|
-
---
|
|
19
|
-
config:
|
|
20
|
-
themeVariables:
|
|
21
|
-
fontSize: "11px"
|
|
22
|
-
---
|
|
23
|
-
graph LR
|
|
24
|
-
A0[MarketData]
|
|
25
|
-
A[MarketData.OHLCV]
|
|
26
|
-
B[MarketData.RecordType]
|
|
27
|
-
A0 --> A
|
|
28
|
-
A0 --> B
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
OHLCV = collections.namedtuple("OHLCV", ["open", "high", "low", "close", "volume"])
|
|
34
|
-
|
|
35
|
-
class RecordType(enum.Enum):
|
|
36
|
-
"""
|
|
37
|
-
Market data record type identifiers that preserve compatibility with Databento's
|
|
38
|
-
rtype integers identifiers:
|
|
39
|
-
|
|
40
|
-
quote:
|
|
41
|
-
- `OHLCV_1S` (`32`): 1-second bars
|
|
42
|
-
- `OHLCV_1M` (`33`): 1-minute bars
|
|
43
|
-
- `OHLCV_1H` (`34`): 1-hour bars
|
|
44
|
-
- `OHLCV_1D` (`35`): Daily bars
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
OHLCV_1S = 32
|
|
48
|
-
OHLCV_1M = 33
|
|
49
|
-
OHLCV_1H = 34
|
|
50
|
-
OHLCV_1D = 35
|
|
51
|
-
|
|
52
|
-
@classmethod
|
|
53
|
-
def to_string(cls, record_type: int) -> str:
|
|
54
|
-
"""Convert record type integer to human-readable description."""
|
|
55
|
-
match record_type:
|
|
56
|
-
case cls.OHLCV_1S.value:
|
|
57
|
-
return "1-second bars"
|
|
58
|
-
case cls.OHLCV_1M.value:
|
|
59
|
-
return "1-minute bars"
|
|
60
|
-
case cls.OHLCV_1H.value:
|
|
61
|
-
return "1-hour bars"
|
|
62
|
-
case cls.OHLCV_1D.value:
|
|
63
|
-
return "daily bars"
|
|
64
|
-
case _:
|
|
65
|
-
return f"unknown ({record_type})"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class PositionManagement:
|
|
69
|
-
"""
|
|
70
|
-
Domain model namespace for position management related concepts.
|
|
71
|
-
|
|
72
|
-
???+ note "Position Management Concepts"
|
|
73
|
-
|
|
74
|
-
```mermaid
|
|
75
|
-
---
|
|
76
|
-
config:
|
|
77
|
-
themeVariables:
|
|
78
|
-
fontSize: "11px"
|
|
79
|
-
---
|
|
80
|
-
graph LR
|
|
81
|
-
A0[PositionManagement]
|
|
82
|
-
A[PositionManagement.OrderType]
|
|
83
|
-
B[PositionManagement.OrderState]
|
|
84
|
-
C[PositionManagement.Side]
|
|
85
|
-
D[PositionManagement.TimeInForce]
|
|
86
|
-
E[PositionManagement.CancelReason]
|
|
87
|
-
A0 --> A
|
|
88
|
-
A0 --> B
|
|
89
|
-
A0 --> C
|
|
90
|
-
A0 --> D
|
|
91
|
-
A0 --> E
|
|
92
|
-
```
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
class OrderType(enum.Enum):
|
|
96
|
-
"""
|
|
97
|
-
Order execution types.
|
|
98
|
-
|
|
99
|
-
quote:
|
|
100
|
-
- `MARKET`: Execute immediately at best available price
|
|
101
|
-
- `LIMIT`: Execute only at specified price or better
|
|
102
|
-
- `STOP`: Becomes market order when trigger price is reached
|
|
103
|
-
- `STOP_LIMIT`: Becomes limit order when trigger price is reached
|
|
104
|
-
"""
|
|
105
|
-
|
|
106
|
-
MARKET = enum.auto()
|
|
107
|
-
LIMIT = enum.auto()
|
|
108
|
-
STOP = enum.auto()
|
|
109
|
-
STOP_LIMIT = enum.auto()
|
|
110
|
-
|
|
111
|
-
class OrderState(enum.Enum):
|
|
112
|
-
"""
|
|
113
|
-
Order lifecycle states.
|
|
114
|
-
|
|
115
|
-
quote:
|
|
116
|
-
- `NEW`: Created but not submitted
|
|
117
|
-
- `SUBMITTED`: Sent to broker/exchange
|
|
118
|
-
- `ACTIVE`: Live in market
|
|
119
|
-
- `PARTIALLY_FILLED`: Partially executed
|
|
120
|
-
- `FILLED`: Completely executed
|
|
121
|
-
- `CANCELLED`: Cancelled before first fill
|
|
122
|
-
- `CANCELLED_AT_PARTIAL_FILL`: Cancelled after partial fill
|
|
123
|
-
- `REJECTED`: Rejected by broker/exchange
|
|
124
|
-
- `EXPIRED`: Expired due to time-in-force constraints
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
NEW = enum.auto()
|
|
128
|
-
SUBMITTED = enum.auto()
|
|
129
|
-
ACTIVE = enum.auto()
|
|
130
|
-
PARTIALLY_FILLED = enum.auto()
|
|
131
|
-
FILLED = enum.auto()
|
|
132
|
-
CANCELLED = enum.auto()
|
|
133
|
-
CANCELLED_AT_PARTIAL_FILL = enum.auto()
|
|
134
|
-
REJECTED = enum.auto()
|
|
135
|
-
EXPIRED = enum.auto()
|
|
136
|
-
|
|
137
|
-
class Side(enum.Enum):
|
|
138
|
-
"""
|
|
139
|
-
Order direction - buy or sell.
|
|
140
|
-
|
|
141
|
-
quote:
|
|
142
|
-
- `BUY`: Buy the financial instrument
|
|
143
|
-
- `SELL`: Sell the financial instrument
|
|
144
|
-
"""
|
|
145
|
-
|
|
146
|
-
BUY = enum.auto()
|
|
147
|
-
SELL = enum.auto()
|
|
148
|
-
|
|
149
|
-
class TimeInForce(enum.Enum):
|
|
150
|
-
"""
|
|
151
|
-
Order time-in-force specifications.
|
|
152
|
-
|
|
153
|
-
quote:
|
|
154
|
-
- `DAY`: Valid until end of trading day
|
|
155
|
-
- `FOK`: Fill entire order immediately or cancel (Fill-or-Kill)
|
|
156
|
-
- `GTC`: Active until explicitly cancelled (Good-Till-Cancelled)
|
|
157
|
-
- `GTD`: Active until specified date (Good-Till-Date)
|
|
158
|
-
- `IOC`: Execute available quantity immediately, cancel rest
|
|
159
|
-
(Immediate-or-Cancel)
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
DAY = enum.auto()
|
|
163
|
-
FOK = enum.auto()
|
|
164
|
-
GTC = enum.auto()
|
|
165
|
-
GTD = enum.auto()
|
|
166
|
-
IOC = enum.auto()
|
|
167
|
-
|
|
168
|
-
class CancelReason(enum.Enum):
|
|
169
|
-
"""
|
|
170
|
-
Reasons for order cancellation.
|
|
171
|
-
|
|
172
|
-
quote:
|
|
173
|
-
- `CLIENT_REQUEST`: Order cancelled by client/trader request
|
|
174
|
-
- `EXPIRED_TIME_IN_FORCE`: Order expired due to time-in-force constraints
|
|
175
|
-
- `BROKER_REJECTED_AT_SUBMISSION`: Broker rejected order during submission
|
|
176
|
-
- `BROKER_FORCED_CANCEL`: Broker cancelled order due to risk or other constraints
|
|
177
|
-
- `UNKNOWN`: Cancellation reason not specified or unknown
|
|
178
|
-
"""
|
|
179
|
-
|
|
180
|
-
CLIENT_REQUEST = enum.auto()
|
|
181
|
-
EXPIRED_TIME_IN_FORCE = enum.auto()
|
|
182
|
-
BROKER_REJECTED_AT_SUBMISSION = enum.auto()
|
|
183
|
-
BROKER_FORCED_CANCEL = enum.auto()
|
|
184
|
-
UNKNOWN = enum.auto()
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class SystemManagement:
|
|
188
|
-
"""
|
|
189
|
-
Domain model namespace for system management related concepts.
|
|
190
|
-
|
|
191
|
-
???+ note "System Management Concepts"
|
|
192
|
-
|
|
193
|
-
```mermaid
|
|
194
|
-
---
|
|
195
|
-
config:
|
|
196
|
-
themeVariables:
|
|
197
|
-
fontSize: "11px"
|
|
198
|
-
---
|
|
199
|
-
graph LR
|
|
200
|
-
A0[SystemManagement]
|
|
201
|
-
A[SystemManagement.StopReason]
|
|
202
|
-
A0 --> A
|
|
203
|
-
```
|
|
204
|
-
"""
|
|
205
|
-
|
|
206
|
-
class StopReason(enum.Enum):
|
|
207
|
-
"""
|
|
208
|
-
Reasons for system or component shutdown.
|
|
209
|
-
|
|
210
|
-
quote:
|
|
211
|
-
- `SYSTEM_SHUTDOWN`: Coordinated shutdown of entire system
|
|
212
|
-
- `COMPONENT_DISCONNECT`: Single component disconnect
|
|
213
|
-
"""
|
|
214
|
-
|
|
215
|
-
SYSTEM_SHUTDOWN = enum.auto()
|
|
216
|
-
COMPONENT_DISCONNECT = enum.auto()
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
onesecondtrader/__init__.py,sha256=xB2oXIiffWA19eaQbdEn3GZyymhyLHaxPLO-DhP4CLc,430
|
|
2
|
-
onesecondtrader/domain_models.py,sha256=3UigXXEBlZehQupwM_LnLh4rlsOil9e_iPMosjWKFcw,6229
|
|
3
|
-
onesecondtrader/log_config.py,sha256=TGm53SD_TA4Xa57Go91BGHV3SEIA6MflwSO63r9Jt6g,366
|
|
4
|
-
onesecondtrader-0.5.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
5
|
-
onesecondtrader-0.5.0.dist-info/METADATA,sha256=FRJXLOfZEs1Pr3PFNqfV41cBDNk1oT49RiO9cFYDwRw,1220
|
|
6
|
-
onesecondtrader-0.5.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
7
|
-
onesecondtrader-0.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|