itchfeed 1.0.4__tar.gz → 1.0.6__tar.gz
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.
- {itchfeed-1.0.4/itchfeed.egg-info → itchfeed-1.0.6}/PKG-INFO +28 -50
- {itchfeed-1.0.4 → itchfeed-1.0.6}/README.md +22 -30
- itchfeed-1.0.6/itch/__init__.py +17 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/itch/messages.py +51 -34
- itchfeed-1.0.6/itch/parser.py +228 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6/itchfeed.egg-info}/PKG-INFO +28 -50
- {itchfeed-1.0.4 → itchfeed-1.0.6}/itchfeed.egg-info/SOURCES.txt +3 -1
- itchfeed-1.0.6/pyproject.toml +53 -0
- itchfeed-1.0.6/setup.py +5 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/tests/test_create_message.py +2 -2
- {itchfeed-1.0.4 → itchfeed-1.0.6}/tests/test_messages.py +5 -5
- itchfeed-1.0.6/tests/test_parser.py +110 -0
- itchfeed-1.0.4/itch/__init__.py +0 -11
- itchfeed-1.0.4/itch/parser.py +0 -173
- itchfeed-1.0.4/setup.py +0 -68
- {itchfeed-1.0.4 → itchfeed-1.0.6}/LICENSE +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/MANIFEST.in +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/itch/indicators.py +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/itchfeed.egg-info/dependency_links.txt +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/itchfeed.egg-info/requires.txt +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/itchfeed.egg-info/top_level.txt +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/requirements.txt +0 -0
- {itchfeed-1.0.4 → itchfeed-1.0.6}/setup.cfg +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: itchfeed
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.6
|
|
4
4
|
Summary: Simple parser for ITCH messages
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
License: The MIT License (MIT)
|
|
5
|
+
Author-email: Bertin Balouki SIMYELI <bertin@bbs-trading.com>
|
|
6
|
+
Maintainer-email: Bertin Balouki SIMYELI <bertin@bbs-trading.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/bbalouki/itch
|
|
9
|
+
Project-URL: Download, https://pypi.org/project/itchfeed/
|
|
11
10
|
Project-URL: Source Code, https://github.com/bbalouki/itch
|
|
12
11
|
Keywords: Finance,Financial,Quantitative,Equities,Totalview-ITCH,Totalview,Nasdaq-ITCH,Nasdaq,ITCH,Data,Feed,ETFs,Funds,Trading,Investing
|
|
13
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -19,20 +18,7 @@ Classifier: Operating System :: OS Independent
|
|
|
19
18
|
Description-Content-Type: text/markdown
|
|
20
19
|
License-File: LICENSE
|
|
21
20
|
Requires-Dist: pytest
|
|
22
|
-
Dynamic: author
|
|
23
|
-
Dynamic: author-email
|
|
24
|
-
Dynamic: classifier
|
|
25
|
-
Dynamic: description
|
|
26
|
-
Dynamic: description-content-type
|
|
27
|
-
Dynamic: download-url
|
|
28
|
-
Dynamic: home-page
|
|
29
|
-
Dynamic: keywords
|
|
30
|
-
Dynamic: license
|
|
31
21
|
Dynamic: license-file
|
|
32
|
-
Dynamic: maintainer
|
|
33
|
-
Dynamic: project-url
|
|
34
|
-
Dynamic: requires-dist
|
|
35
|
-
Dynamic: summary
|
|
36
22
|
|
|
37
23
|
# Nasdaq TotalView-ITCH 5.0 Parser
|
|
38
24
|
[](https://pypi.org/project/itchfeed/)
|
|
@@ -59,7 +45,7 @@ Dynamic: summary
|
|
|
59
45
|
* [Data Representation](#data-representation)
|
|
60
46
|
* [Common Attributes of `MarketMessage`](#common-attributes-of-marketmessage)
|
|
61
47
|
* [Common Methods of `MarketMessage`](#common-methods-of-marketmessage)
|
|
62
|
-
* [Serializing Messages with `
|
|
48
|
+
* [Serializing Messages with `to_bytes()`](#serializing-messages-with-to_bytes)
|
|
63
49
|
* [Data Types in Parsed Messages](#data-types-in-parsed-messages)
|
|
64
50
|
* [Error Handling](#error-handling)
|
|
65
51
|
* [Handling Strategies](#handling-strategies)
|
|
@@ -111,6 +97,8 @@ After installation (typically via pip), import the necessary modules directly in
|
|
|
111
97
|
|
|
112
98
|
## Usage
|
|
113
99
|
|
|
100
|
+
Download some sample data [here](https://emi.nasdaq.com/ITCH/Nasdaq%20ITCH/)
|
|
101
|
+
|
|
114
102
|
### Parsing from a Binary File
|
|
115
103
|
|
|
116
104
|
This is useful for processing historical ITCH data stored in files. The `MessageParser` handles buffering efficiently.
|
|
@@ -131,9 +119,8 @@ parser = MessageParser() # Parses all messages by default
|
|
|
131
119
|
|
|
132
120
|
# Path to your ITCH 5.0 data file
|
|
133
121
|
itch_file_path = 'path/to/your/data'
|
|
134
|
-
# you can find sample data [here](https://emi.nasdaq.com/ITCH/Nasdaq%20ITCH/)
|
|
135
122
|
|
|
136
|
-
# The `
|
|
123
|
+
# The `parse_file()` method reads the ITCH data in chunks.
|
|
137
124
|
# - `cachesize` (optional, default: 65536 bytes): This parameter determines the size of data chunks
|
|
138
125
|
# read from the file at a time. Adjusting this might impact performance for very large files
|
|
139
126
|
# or memory usage, but the default is generally suitable.
|
|
@@ -144,13 +131,9 @@ itch_file_path = 'path/to/your/data'
|
|
|
144
131
|
|
|
145
132
|
try:
|
|
146
133
|
with open(itch_file_path, 'rb') as itch_file:
|
|
147
|
-
#
|
|
148
|
-
parsed_messages = parser.read_message_from_file(itch_file) # You can also pass cachesize here, e.g., parser.read_message_from_file(itch_file, cachesize=131072)
|
|
149
|
-
|
|
150
|
-
print(f"Parsed {len(parsed_messages)} messages.")
|
|
151
|
-
|
|
134
|
+
# parse_file returns an Iterator of parsed message objects
|
|
152
135
|
# Process the messages
|
|
153
|
-
for message in
|
|
136
|
+
for message in parser.parse_file(itch_file):
|
|
154
137
|
# Access attributes directly
|
|
155
138
|
print(f"Type: {message.message_type.decode()}, Timestamp: {message.timestamp}")
|
|
156
139
|
|
|
@@ -170,8 +153,8 @@ try:
|
|
|
170
153
|
|
|
171
154
|
except FileNotFoundError:
|
|
172
155
|
print(f"Error: File not found at {itch_file_path}")
|
|
173
|
-
except
|
|
174
|
-
print(f"An error occurred: {e}")
|
|
156
|
+
except ValueError as e:
|
|
157
|
+
print(f"An error occurred during parsing: {e}")
|
|
175
158
|
|
|
176
159
|
```
|
|
177
160
|
|
|
@@ -192,14 +175,8 @@ parser = MessageParser()
|
|
|
192
175
|
# Example: \x00\x0bS...\x00\x25R...\x00\x27F...
|
|
193
176
|
raw_binary_data: bytes = b"..." # Your raw ITCH 5.0 data chunk
|
|
194
177
|
|
|
195
|
-
#
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
print(f"Parsed {message_queue.qsize()} messages from the byte chunk.")
|
|
199
|
-
|
|
200
|
-
# Process messages from the queue
|
|
201
|
-
while not message_queue.empty():
|
|
202
|
-
message = message_queue.get()
|
|
178
|
+
# parse_stream returns an Iterator of parsed message objects
|
|
179
|
+
for message in parser.parse_stream(raw_binary_data)
|
|
203
180
|
|
|
204
181
|
print(f"Type: {message.message_type.decode()}, Timestamp: {message.timestamp}")
|
|
205
182
|
|
|
@@ -276,10 +253,10 @@ for message_type, sample_data in TEST_DATA.items():
|
|
|
276
253
|
print(f"Creating message of type {message_type}")
|
|
277
254
|
message = create_message(message_type, **sample_data)
|
|
278
255
|
print(f"Created message: {message}")
|
|
279
|
-
print(f"Packed message: {message.
|
|
256
|
+
print(f"Packed message: {message.to_bytes()}")
|
|
280
257
|
print(f"Message size: {message.message_size}")
|
|
281
258
|
print(f"Message Attributes: {message.get_attributes()}")
|
|
282
|
-
assert len(message.
|
|
259
|
+
assert len(message.to_bytes()) == message.message_size
|
|
283
260
|
print()
|
|
284
261
|
```
|
|
285
262
|
|
|
@@ -388,14 +365,14 @@ The `MarketMessage` base class, and therefore all specific message classes, prov
|
|
|
388
365
|
* Returns a dictionary of all attributes (fields) of the message instance, along with their current values.
|
|
389
366
|
* This can be useful for generic inspection or logging of message contents without needing to know the specific type of the message beforehand.
|
|
390
367
|
|
|
391
|
-
### Serializing Messages with `
|
|
368
|
+
### Serializing Messages with `to_bytes()`
|
|
392
369
|
|
|
393
|
-
Each specific message class (e.g., `SystemEventMessage`, `AddOrderNoMPIAttributionMessage`) also provides a `
|
|
370
|
+
Each specific message class (e.g., `SystemEventMessage`, `AddOrderNoMPIAttributionMessage`) also provides a `to_bytes()` method. This method is the inverse of the parsing process.
|
|
394
371
|
|
|
395
372
|
* **Purpose:** It serializes the message object, with its current attribute values, back into its raw ITCH 5.0 binary format. The output is a `bytes` object representing the exact byte sequence that would appear in an ITCH data feed for that message.
|
|
396
373
|
* **Usefulness:**
|
|
397
374
|
* **Generating Test Data:** Create custom ITCH messages for testing your own ITCH processing applications.
|
|
398
|
-
* **Modifying Messages:** Parse an existing message, modify some of its attributes, and then `
|
|
375
|
+
* **Modifying Messages:** Parse an existing message, modify some of its attributes, and then `to_bytes()` it back into binary form.
|
|
399
376
|
* **Creating Custom ITCH Feeds:** While more involved, you could use this to construct sequences of ITCH messages for specialized scenarios.
|
|
400
377
|
|
|
401
378
|
**Example:**
|
|
@@ -415,21 +392,21 @@ import time
|
|
|
415
392
|
# for packing requires setting attributes manually if not using raw bytes for construction)
|
|
416
393
|
|
|
417
394
|
event_msg = SystemEventMessage.__new__(SystemEventMessage) # Create instance without calling __init__
|
|
418
|
-
event_msg.message_type = b'S' # Must be set for
|
|
395
|
+
event_msg.message_type = b'S' # Must be set for to_bytes() to know its type
|
|
419
396
|
event_msg.stock_locate = 0 # Placeholder or actual value
|
|
420
397
|
event_msg.tracking_number = 0 # Placeholder or actual value
|
|
421
398
|
event_msg.event_code = b'O' # Example: Start of Messages
|
|
422
399
|
|
|
423
400
|
# 2. Set the timestamp.
|
|
424
401
|
# The `timestamp` attribute (nanoseconds since midnight) must be set.
|
|
425
|
-
# The `
|
|
402
|
+
# The `to_bytes()` method will internally use `split_timestamp()` to get the parts.
|
|
426
403
|
current_nanoseconds = int(time.time() * 1e9) % (24 * 60 * 60 * int(1e9))
|
|
427
404
|
event_msg.timestamp = current_nanoseconds # Directly set the nanosecond timestamp
|
|
428
405
|
|
|
429
406
|
# 3. Pack the message into binary format.
|
|
430
|
-
# The
|
|
407
|
+
# The to_bytes() method prepends the message type and then packs stock_locate,
|
|
431
408
|
# tracking_number, the split timestamp, and then the message-specific fields.
|
|
432
|
-
packed_bytes = event_msg.
|
|
409
|
+
packed_bytes = event_msg.to_bytes()
|
|
433
410
|
|
|
434
411
|
# 4. The result is a bytes object
|
|
435
412
|
print(f"Packed {len(packed_bytes)} bytes: {packed_bytes.hex().upper()}")
|
|
@@ -462,11 +439,12 @@ Common scenarios that can lead to a `ValueError` include:
|
|
|
462
439
|
|
|
463
440
|
It's crucial to anticipate these errors in your application:
|
|
464
441
|
|
|
465
|
-
* **Use `try-except` Blocks:** Wrap your parsing calls (especially `
|
|
442
|
+
* **Use `try-except` Blocks:** Wrap your parsing calls (especially `parse_file` or `parse_stream`) in `try-except ValueError as e:` blocks.
|
|
466
443
|
```python
|
|
467
444
|
try:
|
|
468
445
|
# ... parsing operations ...
|
|
469
|
-
|
|
446
|
+
for message in parser.parse_file(itch_file):
|
|
447
|
+
...
|
|
470
448
|
except ValueError as e:
|
|
471
449
|
print(f"An error occurred during parsing: {e}")
|
|
472
450
|
# Log the error, problematic data chunk, or take other actions
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* [Data Representation](#data-representation)
|
|
24
24
|
* [Common Attributes of `MarketMessage`](#common-attributes-of-marketmessage)
|
|
25
25
|
* [Common Methods of `MarketMessage`](#common-methods-of-marketmessage)
|
|
26
|
-
* [Serializing Messages with `
|
|
26
|
+
* [Serializing Messages with `to_bytes()`](#serializing-messages-with-to_bytes)
|
|
27
27
|
* [Data Types in Parsed Messages](#data-types-in-parsed-messages)
|
|
28
28
|
* [Error Handling](#error-handling)
|
|
29
29
|
* [Handling Strategies](#handling-strategies)
|
|
@@ -75,6 +75,8 @@ After installation (typically via pip), import the necessary modules directly in
|
|
|
75
75
|
|
|
76
76
|
## Usage
|
|
77
77
|
|
|
78
|
+
Download some sample data [here](https://emi.nasdaq.com/ITCH/Nasdaq%20ITCH/)
|
|
79
|
+
|
|
78
80
|
### Parsing from a Binary File
|
|
79
81
|
|
|
80
82
|
This is useful for processing historical ITCH data stored in files. The `MessageParser` handles buffering efficiently.
|
|
@@ -95,9 +97,8 @@ parser = MessageParser() # Parses all messages by default
|
|
|
95
97
|
|
|
96
98
|
# Path to your ITCH 5.0 data file
|
|
97
99
|
itch_file_path = 'path/to/your/data'
|
|
98
|
-
# you can find sample data [here](https://emi.nasdaq.com/ITCH/Nasdaq%20ITCH/)
|
|
99
100
|
|
|
100
|
-
# The `
|
|
101
|
+
# The `parse_file()` method reads the ITCH data in chunks.
|
|
101
102
|
# - `cachesize` (optional, default: 65536 bytes): This parameter determines the size of data chunks
|
|
102
103
|
# read from the file at a time. Adjusting this might impact performance for very large files
|
|
103
104
|
# or memory usage, but the default is generally suitable.
|
|
@@ -108,13 +109,9 @@ itch_file_path = 'path/to/your/data'
|
|
|
108
109
|
|
|
109
110
|
try:
|
|
110
111
|
with open(itch_file_path, 'rb') as itch_file:
|
|
111
|
-
#
|
|
112
|
-
parsed_messages = parser.read_message_from_file(itch_file) # You can also pass cachesize here, e.g., parser.read_message_from_file(itch_file, cachesize=131072)
|
|
113
|
-
|
|
114
|
-
print(f"Parsed {len(parsed_messages)} messages.")
|
|
115
|
-
|
|
112
|
+
# parse_file returns an Iterator of parsed message objects
|
|
116
113
|
# Process the messages
|
|
117
|
-
for message in
|
|
114
|
+
for message in parser.parse_file(itch_file):
|
|
118
115
|
# Access attributes directly
|
|
119
116
|
print(f"Type: {message.message_type.decode()}, Timestamp: {message.timestamp}")
|
|
120
117
|
|
|
@@ -134,8 +131,8 @@ try:
|
|
|
134
131
|
|
|
135
132
|
except FileNotFoundError:
|
|
136
133
|
print(f"Error: File not found at {itch_file_path}")
|
|
137
|
-
except
|
|
138
|
-
print(f"An error occurred: {e}")
|
|
134
|
+
except ValueError as e:
|
|
135
|
+
print(f"An error occurred during parsing: {e}")
|
|
139
136
|
|
|
140
137
|
```
|
|
141
138
|
|
|
@@ -156,14 +153,8 @@ parser = MessageParser()
|
|
|
156
153
|
# Example: \x00\x0bS...\x00\x25R...\x00\x27F...
|
|
157
154
|
raw_binary_data: bytes = b"..." # Your raw ITCH 5.0 data chunk
|
|
158
155
|
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
print(f"Parsed {message_queue.qsize()} messages from the byte chunk.")
|
|
163
|
-
|
|
164
|
-
# Process messages from the queue
|
|
165
|
-
while not message_queue.empty():
|
|
166
|
-
message = message_queue.get()
|
|
156
|
+
# parse_stream returns an Iterator of parsed message objects
|
|
157
|
+
for message in parser.parse_stream(raw_binary_data)
|
|
167
158
|
|
|
168
159
|
print(f"Type: {message.message_type.decode()}, Timestamp: {message.timestamp}")
|
|
169
160
|
|
|
@@ -240,10 +231,10 @@ for message_type, sample_data in TEST_DATA.items():
|
|
|
240
231
|
print(f"Creating message of type {message_type}")
|
|
241
232
|
message = create_message(message_type, **sample_data)
|
|
242
233
|
print(f"Created message: {message}")
|
|
243
|
-
print(f"Packed message: {message.
|
|
234
|
+
print(f"Packed message: {message.to_bytes()}")
|
|
244
235
|
print(f"Message size: {message.message_size}")
|
|
245
236
|
print(f"Message Attributes: {message.get_attributes()}")
|
|
246
|
-
assert len(message.
|
|
237
|
+
assert len(message.to_bytes()) == message.message_size
|
|
247
238
|
print()
|
|
248
239
|
```
|
|
249
240
|
|
|
@@ -352,14 +343,14 @@ The `MarketMessage` base class, and therefore all specific message classes, prov
|
|
|
352
343
|
* Returns a dictionary of all attributes (fields) of the message instance, along with their current values.
|
|
353
344
|
* This can be useful for generic inspection or logging of message contents without needing to know the specific type of the message beforehand.
|
|
354
345
|
|
|
355
|
-
### Serializing Messages with `
|
|
346
|
+
### Serializing Messages with `to_bytes()`
|
|
356
347
|
|
|
357
|
-
Each specific message class (e.g., `SystemEventMessage`, `AddOrderNoMPIAttributionMessage`) also provides a `
|
|
348
|
+
Each specific message class (e.g., `SystemEventMessage`, `AddOrderNoMPIAttributionMessage`) also provides a `to_bytes()` method. This method is the inverse of the parsing process.
|
|
358
349
|
|
|
359
350
|
* **Purpose:** It serializes the message object, with its current attribute values, back into its raw ITCH 5.0 binary format. The output is a `bytes` object representing the exact byte sequence that would appear in an ITCH data feed for that message.
|
|
360
351
|
* **Usefulness:**
|
|
361
352
|
* **Generating Test Data:** Create custom ITCH messages for testing your own ITCH processing applications.
|
|
362
|
-
* **Modifying Messages:** Parse an existing message, modify some of its attributes, and then `
|
|
353
|
+
* **Modifying Messages:** Parse an existing message, modify some of its attributes, and then `to_bytes()` it back into binary form.
|
|
363
354
|
* **Creating Custom ITCH Feeds:** While more involved, you could use this to construct sequences of ITCH messages for specialized scenarios.
|
|
364
355
|
|
|
365
356
|
**Example:**
|
|
@@ -379,21 +370,21 @@ import time
|
|
|
379
370
|
# for packing requires setting attributes manually if not using raw bytes for construction)
|
|
380
371
|
|
|
381
372
|
event_msg = SystemEventMessage.__new__(SystemEventMessage) # Create instance without calling __init__
|
|
382
|
-
event_msg.message_type = b'S' # Must be set for
|
|
373
|
+
event_msg.message_type = b'S' # Must be set for to_bytes() to know its type
|
|
383
374
|
event_msg.stock_locate = 0 # Placeholder or actual value
|
|
384
375
|
event_msg.tracking_number = 0 # Placeholder or actual value
|
|
385
376
|
event_msg.event_code = b'O' # Example: Start of Messages
|
|
386
377
|
|
|
387
378
|
# 2. Set the timestamp.
|
|
388
379
|
# The `timestamp` attribute (nanoseconds since midnight) must be set.
|
|
389
|
-
# The `
|
|
380
|
+
# The `to_bytes()` method will internally use `split_timestamp()` to get the parts.
|
|
390
381
|
current_nanoseconds = int(time.time() * 1e9) % (24 * 60 * 60 * int(1e9))
|
|
391
382
|
event_msg.timestamp = current_nanoseconds # Directly set the nanosecond timestamp
|
|
392
383
|
|
|
393
384
|
# 3. Pack the message into binary format.
|
|
394
|
-
# The
|
|
385
|
+
# The to_bytes() method prepends the message type and then packs stock_locate,
|
|
395
386
|
# tracking_number, the split timestamp, and then the message-specific fields.
|
|
396
|
-
packed_bytes = event_msg.
|
|
387
|
+
packed_bytes = event_msg.to_bytes()
|
|
397
388
|
|
|
398
389
|
# 4. The result is a bytes object
|
|
399
390
|
print(f"Packed {len(packed_bytes)} bytes: {packed_bytes.hex().upper()}")
|
|
@@ -426,11 +417,12 @@ Common scenarios that can lead to a `ValueError` include:
|
|
|
426
417
|
|
|
427
418
|
It's crucial to anticipate these errors in your application:
|
|
428
419
|
|
|
429
|
-
* **Use `try-except` Blocks:** Wrap your parsing calls (especially `
|
|
420
|
+
* **Use `try-except` Blocks:** Wrap your parsing calls (especially `parse_file` or `parse_stream`) in `try-except ValueError as e:` blocks.
|
|
430
421
|
```python
|
|
431
422
|
try:
|
|
432
423
|
# ... parsing operations ...
|
|
433
|
-
|
|
424
|
+
for message in parser.parse_file(itch_file):
|
|
425
|
+
...
|
|
434
426
|
except ValueError as e:
|
|
435
427
|
print(f"An error occurred during parsing: {e}")
|
|
436
428
|
# Log the error, problematic data chunk, or take other actions
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Nasdaq TotalView-ITCH 5.0 Parser
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__author__ = "Bertin Balouki SIMYELI"
|
|
6
|
+
__copyright__ = "2025 Bertin Balouki SIMYELI"
|
|
7
|
+
__email__ = "bertin@bbs-trading.com"
|
|
8
|
+
__license__ = "MIT"
|
|
9
|
+
|
|
10
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
__version__ = version("itchfeed")
|
|
14
|
+
except PackageNotFoundError:
|
|
15
|
+
__version__ = "unknown"
|
|
16
|
+
|
|
17
|
+
|
|
@@ -13,13 +13,21 @@ class MarketMessage(object):
|
|
|
13
13
|
|
|
14
14
|
All Message have the following attributes:
|
|
15
15
|
- message_type: A single letter that identify the message
|
|
16
|
+
- timestamp: Time at which the message was generated (Nanoseconds past midnight)
|
|
17
|
+
- stock_locate: Locate code identifying the security
|
|
18
|
+
- tracking_number: Nasdaq internal tracking number
|
|
19
|
+
|
|
20
|
+
The following attributes are not part of the message, but are used to describe the message:
|
|
16
21
|
- description: Describe the message
|
|
17
22
|
- message_format: string format using to unpack the message
|
|
18
23
|
- message_pack_format: string format using to pack the message
|
|
19
24
|
- message_size: The size in bytes of the message
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
|
|
26
|
+
# NOTE:
|
|
27
|
+
Prices are integers fields supplied with an associated precision. When converted to a decimal format, prices are in
|
|
28
|
+
fixed point format, where the precision defines the number of decimal places. For example, a field flagged as Price
|
|
29
|
+
(4) has an implied 4 decimal places. The maximum value of price (4) in TotalView ITCH is 200,000.0000 (decimal,
|
|
30
|
+
77359400 hex). ``price_precision`` is 4 for all messages except MWCBDeclineLeveMessage where ``price_precision`` is 8.
|
|
23
31
|
"""
|
|
24
32
|
|
|
25
33
|
message_type: bytes
|
|
@@ -32,13 +40,24 @@ class MarketMessage(object):
|
|
|
32
40
|
tracking_number: int
|
|
33
41
|
price_precision: int = 4
|
|
34
42
|
|
|
35
|
-
def __repr__(self):
|
|
43
|
+
def __repr__(self) -> str:
|
|
36
44
|
return repr(self.decode())
|
|
37
45
|
|
|
38
|
-
def
|
|
46
|
+
def __bytes__(self) -> bytes:
|
|
47
|
+
return self.to_bytes()
|
|
48
|
+
|
|
49
|
+
def to_bytes(self) -> bytes: # type: ignore
|
|
39
50
|
"""
|
|
40
51
|
Packs the message into bytes using the defined message_pack_format.
|
|
41
52
|
This method should be overridden by subclasses to include specific fields.
|
|
53
|
+
|
|
54
|
+
Note:
|
|
55
|
+
All packed messages do not include
|
|
56
|
+
- ``description``,
|
|
57
|
+
- ``message_format``,
|
|
58
|
+
- ``message_pack_format``,
|
|
59
|
+
- ``message_size``
|
|
60
|
+
- ``price_precision``
|
|
42
61
|
"""
|
|
43
62
|
pass
|
|
44
63
|
|
|
@@ -90,9 +109,7 @@ class MarketMessage(object):
|
|
|
90
109
|
ts1 = self.timestamp >> 32
|
|
91
110
|
ts2 = self.timestamp - (ts1 << 32)
|
|
92
111
|
return (ts1, ts2)
|
|
93
|
-
|
|
94
|
-
ts2 = self.timestamp - (ts1 << 32)
|
|
95
|
-
return (ts1, ts2)
|
|
112
|
+
|
|
96
113
|
|
|
97
114
|
def decode_price(self, price_attr: str) -> float:
|
|
98
115
|
precision = getattr(self, "price_precision")
|
|
@@ -166,7 +183,7 @@ class SystemEventMessage(MarketMessage):
|
|
|
166
183
|
) = struct.unpack(self.message_format, message[1:])
|
|
167
184
|
self.set_timestamp(timestamp1, timestamp2)
|
|
168
185
|
|
|
169
|
-
def
|
|
186
|
+
def to_bytes(self):
|
|
170
187
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
171
188
|
message = struct.pack(
|
|
172
189
|
self.message_pack_format,
|
|
@@ -288,7 +305,7 @@ class StockDirectoryMessage(MarketMessage):
|
|
|
288
305
|
) = struct.unpack(self.message_format, message[1:])
|
|
289
306
|
self.set_timestamp(timestamp1, timestamp2)
|
|
290
307
|
|
|
291
|
-
def
|
|
308
|
+
def to_bytes(self):
|
|
292
309
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
293
310
|
message = struct.pack(
|
|
294
311
|
self.message_pack_format,
|
|
@@ -368,7 +385,7 @@ class StockTradingActionMessage(MarketMessage):
|
|
|
368
385
|
) = struct.unpack(self.message_format, message[1:])
|
|
369
386
|
self.set_timestamp(timestamp1, timestamp2)
|
|
370
387
|
|
|
371
|
-
def
|
|
388
|
+
def to_bytes(self):
|
|
372
389
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
373
390
|
message = struct.pack(
|
|
374
391
|
self.message_pack_format,
|
|
@@ -429,7 +446,7 @@ class RegSHOMessage(MarketMessage):
|
|
|
429
446
|
) = struct.unpack(self.message_format, message[1:])
|
|
430
447
|
self.set_timestamp(timestamp1, timestamp2)
|
|
431
448
|
|
|
432
|
-
def
|
|
449
|
+
def to_bytes(self):
|
|
433
450
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
434
451
|
message = struct.pack(
|
|
435
452
|
self.message_pack_format,
|
|
@@ -491,7 +508,7 @@ class MarketParticipantPositionMessage(MarketMessage):
|
|
|
491
508
|
) = struct.unpack(self.message_format, message[1:])
|
|
492
509
|
self.set_timestamp(timestamp1, timestamp2)
|
|
493
510
|
|
|
494
|
-
def
|
|
511
|
+
def to_bytes(self):
|
|
495
512
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
496
513
|
message = struct.pack(
|
|
497
514
|
self.message_pack_format,
|
|
@@ -542,7 +559,7 @@ class MWCBDeclineLeveMessage(MarketMessage):
|
|
|
542
559
|
) = struct.unpack(self.message_format, message[1:])
|
|
543
560
|
self.set_timestamp(timestamp1, timestamp2)
|
|
544
561
|
|
|
545
|
-
def
|
|
562
|
+
def to_bytes(self):
|
|
546
563
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
547
564
|
message = struct.pack(
|
|
548
565
|
self.message_pack_format,
|
|
@@ -587,7 +604,7 @@ class MWCBStatusMessage(MarketMessage):
|
|
|
587
604
|
) = struct.unpack(self.message_format, message[1:])
|
|
588
605
|
self.set_timestamp(timestamp1, timestamp2)
|
|
589
606
|
|
|
590
|
-
def
|
|
607
|
+
def to_bytes(self):
|
|
591
608
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
592
609
|
message = struct.pack(
|
|
593
610
|
self.message_pack_format,
|
|
@@ -648,7 +665,7 @@ class IPOQuotingPeriodUpdateMessage(MarketMessage):
|
|
|
648
665
|
) = struct.unpack(self.message_format, message[1:])
|
|
649
666
|
self.set_timestamp(timestamp1, timestamp2)
|
|
650
667
|
|
|
651
|
-
def
|
|
668
|
+
def to_bytes(self):
|
|
652
669
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
653
670
|
message = struct.pack(
|
|
654
671
|
self.message_pack_format,
|
|
@@ -704,7 +721,7 @@ class LULDAuctionCollarMessage(MarketMessage):
|
|
|
704
721
|
) = struct.unpack(self.message_format, message[1:])
|
|
705
722
|
self.set_timestamp(timestamp1, timestamp2)
|
|
706
723
|
|
|
707
|
-
def
|
|
724
|
+
def to_bytes(self):
|
|
708
725
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
709
726
|
message = struct.pack(
|
|
710
727
|
self.message_pack_format,
|
|
@@ -769,7 +786,7 @@ class OperationalHaltMessage(MarketMessage):
|
|
|
769
786
|
) = struct.unpack(self.message_format, message[1:])
|
|
770
787
|
self.set_timestamp(timestamp1, timestamp2)
|
|
771
788
|
|
|
772
|
-
def
|
|
789
|
+
def to_bytes(self):
|
|
773
790
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
774
791
|
message = struct.pack(
|
|
775
792
|
self.message_pack_format,
|
|
@@ -832,7 +849,7 @@ class AddOrderNoMPIAttributionMessage(AddOrderMessage):
|
|
|
832
849
|
) = struct.unpack(self.message_format, message[1:])
|
|
833
850
|
self.set_timestamp(timestamp1, timestamp2)
|
|
834
851
|
|
|
835
|
-
def
|
|
852
|
+
def to_bytes(self):
|
|
836
853
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
837
854
|
message = struct.pack(
|
|
838
855
|
self.message_pack_format,
|
|
@@ -886,7 +903,7 @@ class AddOrderMPIDAttribution(AddOrderMessage):
|
|
|
886
903
|
) = struct.unpack(self.message_format, message[1:])
|
|
887
904
|
self.set_timestamp(timestamp1, timestamp2)
|
|
888
905
|
|
|
889
|
-
def
|
|
906
|
+
def to_bytes(self):
|
|
890
907
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
891
908
|
message = struct.pack(
|
|
892
909
|
self.message_pack_format,
|
|
@@ -957,7 +974,7 @@ class OrderExecutedMessage(ModifyOrderMessage):
|
|
|
957
974
|
) = struct.unpack(self.message_format, message[1:])
|
|
958
975
|
self.set_timestamp(timestamp1, timestamp2)
|
|
959
976
|
|
|
960
|
-
def
|
|
977
|
+
def to_bytes(self):
|
|
961
978
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
962
979
|
message = struct.pack(
|
|
963
980
|
self.message_pack_format,
|
|
@@ -1025,7 +1042,7 @@ class OrderExecutedWithPriceMessage(ModifyOrderMessage):
|
|
|
1025
1042
|
) = struct.unpack(self.message_format, message[1:])
|
|
1026
1043
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1027
1044
|
|
|
1028
|
-
def
|
|
1045
|
+
def to_bytes(self):
|
|
1029
1046
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1030
1047
|
message = struct.pack(
|
|
1031
1048
|
self.message_pack_format,
|
|
@@ -1071,7 +1088,7 @@ class OrderCancelMessage(ModifyOrderMessage):
|
|
|
1071
1088
|
) = struct.unpack(self.message_format, message[1:])
|
|
1072
1089
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1073
1090
|
|
|
1074
|
-
def
|
|
1091
|
+
def to_bytes(self):
|
|
1075
1092
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1076
1093
|
message = struct.pack(
|
|
1077
1094
|
self.message_pack_format,
|
|
@@ -1111,7 +1128,7 @@ class OrderDeleteMessage(ModifyOrderMessage):
|
|
|
1111
1128
|
) = struct.unpack(self.message_format, message[1:])
|
|
1112
1129
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1113
1130
|
|
|
1114
|
-
def
|
|
1131
|
+
def to_bytes(self):
|
|
1115
1132
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1116
1133
|
message = struct.pack(
|
|
1117
1134
|
self.message_pack_format,
|
|
@@ -1164,7 +1181,7 @@ class OrderReplaceMessage(ModifyOrderMessage):
|
|
|
1164
1181
|
) = struct.unpack(self.message_format, message[1:])
|
|
1165
1182
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1166
1183
|
|
|
1167
|
-
def
|
|
1184
|
+
def to_bytes(self):
|
|
1168
1185
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1169
1186
|
message = struct.pack(
|
|
1170
1187
|
self.message_pack_format,
|
|
@@ -1238,7 +1255,7 @@ class NonCrossTradeMessage(TradeMessage):
|
|
|
1238
1255
|
) = struct.unpack(self.message_format, message[1:])
|
|
1239
1256
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1240
1257
|
|
|
1241
|
-
def
|
|
1258
|
+
def to_bytes(self):
|
|
1242
1259
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1243
1260
|
message = struct.pack(
|
|
1244
1261
|
self.message_pack_format,
|
|
@@ -1309,7 +1326,7 @@ class CrossTradeMessage(TradeMessage):
|
|
|
1309
1326
|
) = struct.unpack(self.message_format, message[1:])
|
|
1310
1327
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1311
1328
|
|
|
1312
|
-
def
|
|
1329
|
+
def to_bytes(self):
|
|
1313
1330
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1314
1331
|
message = struct.pack(
|
|
1315
1332
|
self.message_pack_format,
|
|
@@ -1359,7 +1376,7 @@ class BrokenTradeMessage(TradeMessage):
|
|
|
1359
1376
|
) = struct.unpack(self.message_format, message[1:])
|
|
1360
1377
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1361
1378
|
|
|
1362
|
-
def
|
|
1379
|
+
def to_bytes(self):
|
|
1363
1380
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1364
1381
|
message = struct.pack(
|
|
1365
1382
|
self.message_pack_format,
|
|
@@ -1441,7 +1458,7 @@ class NOIIMessage(MarketMessage):
|
|
|
1441
1458
|
) = struct.unpack(self.message_format, message[1:])
|
|
1442
1459
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1443
1460
|
|
|
1444
|
-
def
|
|
1461
|
+
def to_bytes(self):
|
|
1445
1462
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1446
1463
|
message = struct.pack(
|
|
1447
1464
|
self.message_pack_format,
|
|
@@ -1496,7 +1513,7 @@ class RetailPriceImprovementIndicator(MarketMessage):
|
|
|
1496
1513
|
) = struct.unpack(self.message_format, message[1:])
|
|
1497
1514
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1498
1515
|
|
|
1499
|
-
def
|
|
1516
|
+
def to_bytes(self):
|
|
1500
1517
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1501
1518
|
message = struct.pack(
|
|
1502
1519
|
self.message_pack_format,
|
|
@@ -1559,7 +1576,7 @@ class DLCRMessage(MarketMessage):
|
|
|
1559
1576
|
) = struct.unpack(self.message_format, message[1:])
|
|
1560
1577
|
self.set_timestamp(timestamp1, timestamp2)
|
|
1561
1578
|
|
|
1562
|
-
def
|
|
1579
|
+
def to_bytes(self):
|
|
1563
1580
|
(timestamp1, timestamp2) = self.split_timestamp()
|
|
1564
1581
|
message = struct.pack(
|
|
1565
1582
|
self.message_pack_format,
|
|
@@ -1579,8 +1596,8 @@ class DLCRMessage(MarketMessage):
|
|
|
1579
1596
|
)
|
|
1580
1597
|
return message
|
|
1581
1598
|
|
|
1582
|
-
|
|
1583
|
-
messages
|
|
1599
|
+
messages: Dict[bytes, Type[MarketMessage]]
|
|
1600
|
+
messages = {
|
|
1584
1601
|
b"S": SystemEventMessage,
|
|
1585
1602
|
b"R": StockDirectoryMessage,
|
|
1586
1603
|
b"H": StockTradingActionMessage,
|
|
@@ -1607,7 +1624,7 @@ messages: Dict[bytes, Type[MarketMessage]] = {
|
|
|
1607
1624
|
}
|
|
1608
1625
|
|
|
1609
1626
|
|
|
1610
|
-
def create_message(message_type: bytes, **kwargs) ->
|
|
1627
|
+
def create_message(message_type: bytes, **kwargs) -> MarketMessage:
|
|
1611
1628
|
"""
|
|
1612
1629
|
Creates a new message of a given type with specified attributes.
|
|
1613
1630
|
|