itchfeed 1.0.0__py3-none-any.whl → 1.0.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.
- itch/__init__.py +10 -14
- itch/indicators.py +206 -206
- itch/messages.py +1600 -1600
- itch/parser.py +173 -180
- {itchfeed-1.0.0.dist-info → itchfeed-1.0.2.dist-info}/METADATA +225 -217
- itchfeed-1.0.2.dist-info/RECORD +9 -0
- {itchfeed-1.0.0.dist-info → itchfeed-1.0.2.dist-info}/WHEEL +1 -1
- {itchfeed-1.0.0.dist-info → itchfeed-1.0.2.dist-info}/licenses/LICENSE +21 -21
- itchfeed-1.0.0.dist-info/RECORD +0 -9
- {itchfeed-1.0.0.dist-info → itchfeed-1.0.2.dist-info}/top_level.txt +0 -0
itch/messages.py
CHANGED
@@ -1,1600 +1,1600 @@
|
|
1
|
-
import struct
|
2
|
-
from dataclasses import make_dataclass
|
3
|
-
from typing import List, Tuple
|
4
|
-
|
5
|
-
MESSAGES = b"SAFECXDUBHRYPQINLVWKJhO"
|
6
|
-
|
7
|
-
|
8
|
-
class MarketMessage(object):
|
9
|
-
"""
|
10
|
-
The TotalView ITCH feed is composed of a series of messages that describe orders added to, removed from, and executed
|
11
|
-
on Nasdaq as well as disseminate Cross and Stock Directory information.
|
12
|
-
This is the base class for all message type.
|
13
|
-
|
14
|
-
All Message have the following attributes:
|
15
|
-
- message_type: A single letter that identify the message
|
16
|
-
- description: Describe the message
|
17
|
-
- message_format: string format using to unpack the message
|
18
|
-
- message_pack_format: string format using to pack the message
|
19
|
-
- message_size: The size in bytes of the message
|
20
|
-
- timestamp: Time at which the message was generated (Nanoseconds past midnight)
|
21
|
-
- stock_locate: Locate code identifying the security
|
22
|
-
- tracking_number: Nasdaq internal tracking number
|
23
|
-
"""
|
24
|
-
|
25
|
-
message_type: bytes
|
26
|
-
description: str
|
27
|
-
message_format: str
|
28
|
-
message_pack_format: str
|
29
|
-
message_size: int
|
30
|
-
timestamp: int
|
31
|
-
stock_locate: int
|
32
|
-
tracking_number: int
|
33
|
-
price_precision: int = 4
|
34
|
-
|
35
|
-
def __repr__(self):
|
36
|
-
return repr(self.decode())
|
37
|
-
|
38
|
-
def set_timestamp(self, ts1: int, ts2: int):
|
39
|
-
"""
|
40
|
-
Reconstructs a 6-byte timestamp (48 bits) from two 32-bit unsigned integers.
|
41
|
-
|
42
|
-
This method combines the high 32 bits (`ts1`) and low 32 bits (`ts2`) into a single
|
43
|
-
64-bit integer to reconstruct the complete timestamp. For more details on how the
|
44
|
-
timestamp is processed and split, refer to the `split_timestamp()` method.
|
45
|
-
|
46
|
-
Args:
|
47
|
-
ts1 (int): The high-order 32 bits (most significant 32 bits).
|
48
|
-
ts2 (int): The low-order 32 bits (least significant 32 bits).
|
49
|
-
"""
|
50
|
-
self.timestamp = ts2 | (ts1 << 32)
|
51
|
-
|
52
|
-
def split_timestamp(self) -> Tuple[int, int]:
|
53
|
-
"""
|
54
|
-
Splits a 6-byte timestamp (48 bits) into two 32-bit unsigned integers.
|
55
|
-
|
56
|
-
The ITCH protocol defines the timestamp as a **6-byte (48-bit) unsigned integer**.
|
57
|
-
Python's native `struct` module does not support 6-byte integers, so we manage
|
58
|
-
the timestamp as a 64-bit integer for simplicity. This method splits it into two
|
59
|
-
32-bit integers for easier handling, packing, and unpacking.
|
60
|
-
|
61
|
-
Process:
|
62
|
-
1. The timestamp is a 6-byte unsigned integer that we treat as a 64-bit integer
|
63
|
-
for ease of use (since Python handles 64-bit integers natively).
|
64
|
-
2. We extract the **high 32 bits** (most significant bits) and the **low 32 bits**
|
65
|
-
(least significant bits) using bitwise operations.
|
66
|
-
|
67
|
-
- The high 32 bits are extracted by shifting the 64-bit timestamp 32 bits to the right
|
68
|
-
(`self.timestamp >> 32`).
|
69
|
-
- The low 32 bits are isolated by subtracting the high 32 bits (shifted back to the left)
|
70
|
-
from the original 64-bit value (`self.timestamp - (ts1 << 32)`).
|
71
|
-
|
72
|
-
Returns:
|
73
|
-
Tuple[int, int]: A tuple containing two integers:
|
74
|
-
- `ts1`: The high 32 bits (most significant 32 bits)
|
75
|
-
- `ts2`: The low 32 bits (least significant 32 bits)
|
76
|
-
|
77
|
-
Example:
|
78
|
-
If `self.timestamp = 0x123456789ABC` (in hex, which is 6 bytes long),
|
79
|
-
then:
|
80
|
-
ts1 = 0x12345678 # high 32 bits
|
81
|
-
ts2 = 0x9ABCDEF0 # low 32 bits
|
82
|
-
"""
|
83
|
-
ts1 = self.timestamp >> 32
|
84
|
-
ts2 = self.timestamp - (ts1 << 32)
|
85
|
-
return (ts1, ts2)
|
86
|
-
ts1 = self.timestamp >> 32
|
87
|
-
ts2 = self.timestamp - (ts1 << 32)
|
88
|
-
return (ts1, ts2)
|
89
|
-
|
90
|
-
def decode_price(self, price_attr: str) -> float:
|
91
|
-
precision = getattr(self, "price_precision")
|
92
|
-
price = getattr(self, price_attr)
|
93
|
-
if callable(price):
|
94
|
-
raise ValueError(f"Please check the price attribute for {price_attr}")
|
95
|
-
return price / (10**precision)
|
96
|
-
|
97
|
-
def decode(self, prefix: str = ""):
|
98
|
-
"""
|
99
|
-
Converts the message into a human-readable dataclass with all built-in fields.
|
100
|
-
- All bytes fields are converted to ASCII strings.
|
101
|
-
- Trailing spaces are stripped from ASCII fields (left-justified padded).
|
102
|
-
|
103
|
-
Args:
|
104
|
-
prefix : The prefix to the dataclass create from the Original message
|
105
|
-
"""
|
106
|
-
builtin_attrs = {}
|
107
|
-
for attr in dir(self):
|
108
|
-
if attr.startswith("__"):
|
109
|
-
continue
|
110
|
-
value = getattr(self, attr)
|
111
|
-
if "price" in attr and attr != "price_precision" and attr != "decode_price":
|
112
|
-
value = self.decode_price(attr)
|
113
|
-
if callable(value):
|
114
|
-
continue
|
115
|
-
if isinstance(value, (int, float, str, bool)):
|
116
|
-
builtin_attrs[attr] = value
|
117
|
-
elif isinstance(value, bytes):
|
118
|
-
try:
|
119
|
-
builtin_attrs[attr] = value.decode(encoding="ascii").rstrip()
|
120
|
-
except UnicodeDecodeError:
|
121
|
-
builtin_attrs[attr] = value
|
122
|
-
fields = [(k, type(v)) for k, v in builtin_attrs.items()]
|
123
|
-
|
124
|
-
decoded_class_name = f"{prefix}{self.__class__.__name__}"
|
125
|
-
DecodedMessageClass = make_dataclass(decoded_class_name, fields)
|
126
|
-
return DecodedMessageClass(**builtin_attrs)
|
127
|
-
|
128
|
-
def get_attributes(self, call_able=False) -> List[str]:
|
129
|
-
attrs = [attr for attr in dir(self) if not attr.startswith("__")]
|
130
|
-
if call_able:
|
131
|
-
return [a for a in attrs if callable(getattr(self, a))]
|
132
|
-
else:
|
133
|
-
return [a for a in attrs if not callable(getattr(self, a))]
|
134
|
-
|
135
|
-
|
136
|
-
class SystemEventMessage(MarketMessage):
|
137
|
-
"""
|
138
|
-
The system event message type is used to signal a market or data feed handler event.
|
139
|
-
|
140
|
-
Attributes:
|
141
|
-
- event_code: see ``itch.indicators.SYSTEM_EVENT_CODES``
|
142
|
-
"""
|
143
|
-
|
144
|
-
message_type = b"S"
|
145
|
-
description = "System Event Message"
|
146
|
-
message_format = "!HHHIc"
|
147
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
148
|
-
message_size = struct.calcsize(message_format) + 1
|
149
|
-
|
150
|
-
event_code: bytes
|
151
|
-
|
152
|
-
def __init__(self, message: bytes):
|
153
|
-
(
|
154
|
-
self.stock_locate,
|
155
|
-
self.tracking_number,
|
156
|
-
timestamp1,
|
157
|
-
timestamp2,
|
158
|
-
self.event_code,
|
159
|
-
) = struct.unpack(self.message_format, message[1:])
|
160
|
-
self.set_timestamp(timestamp1, timestamp2)
|
161
|
-
|
162
|
-
def pack(self):
|
163
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
164
|
-
message = struct.pack(
|
165
|
-
self.message_pack_format,
|
166
|
-
self.message_type,
|
167
|
-
self.stock_locate,
|
168
|
-
self.tracking_number,
|
169
|
-
timestamp1,
|
170
|
-
timestamp2,
|
171
|
-
self.event_code,
|
172
|
-
)
|
173
|
-
return message
|
174
|
-
|
175
|
-
|
176
|
-
class StockDirectoryMessage(MarketMessage):
|
177
|
-
"""
|
178
|
-
At the start of each trading day, Nasdaq disseminates stock directory messages for all active symbols in the Nasdaq
|
179
|
-
execution system.
|
180
|
-
|
181
|
-
Market data redistributors should process this message to populate the Financial Status Indicator (required displayfield) and
|
182
|
-
the Market Category (recommended display field) for Nasdaq listed issues
|
183
|
-
|
184
|
-
Attributes:
|
185
|
-
- stock : Denotes the security symbol for the issue in the Nasdaq execution system.
|
186
|
-
|
187
|
-
- market_category : see ``itch.indicators.MARKET_CATEGORY``.
|
188
|
-
|
189
|
-
- financial_status_indicator : see ``itch.indicators.FINANCIAL_STATUS_INDICATOR``.
|
190
|
-
|
191
|
-
- round_lot_size : Denotes the number of shares that represent a round lot for the issue
|
192
|
-
|
193
|
-
- round_lots_only : Indicates if Nasdaq system limits order entry for issue
|
194
|
-
(``b"Y": "Only round lots", b"N": "Odd and Mixed lots"``)
|
195
|
-
|
196
|
-
- issue_classification : Identifies the security class for the issue as assigned by Nasdaq.
|
197
|
-
see ``itch.indicators.ISSUE_CLASSIFICATION_VALUES``.
|
198
|
-
|
199
|
-
- issue_sub_type : Identifies the security sub-•-type for the issue as assigned by Nasdaq.
|
200
|
-
See ``itch.indicators.ISSUE_SUB_TYPE_VALUES``
|
201
|
-
|
202
|
-
- authenticity : Denotes if an issue or quoting participant record is set-•-up in Nasdaq systems in a live/production, test, or demo state.
|
203
|
-
Please note that firms should only show live issues and quoting participants on public quotation displays.
|
204
|
-
(``b"P"=Live/Production, b"T"=Test``)
|
205
|
-
|
206
|
-
- short_sale_threshold_indicator : Indicates if a security is subject to mandatory close-•-out of short sales under SEC Rule 203(b)(3):
|
207
|
-
b"Y": "Issue is restricted under SEC Rule 203(b)(3)"
|
208
|
-
b"N": "Issue is not restricted"
|
209
|
-
b" ": "Threshold Indicator not available"
|
210
|
-
|
211
|
-
- ipo_flag : Indicates if the Nasdaq security is set up for IPO release. This field is intended to help Nasdaq market
|
212
|
-
participant firms comply with FINRA Rule 5131(b):
|
213
|
-
b"Y": "Nasdaq listed instrument is set up as a new IPO security"
|
214
|
-
b"N": "Nasdaq listed instrument is not set up as a new IPO security"
|
215
|
-
b" ": "Not available"
|
216
|
-
|
217
|
-
- luld_ref : Indicates which Limit Up / Limit Down price band calculationparameter is to be used for the instrument.
|
218
|
-
Refer to [LULD Rule ](https://www.nasdaqtrader.com/content/MarketRegulation/LULD_FAQ.pdf) for details:
|
219
|
-
b"1": "Tier 1 NMS Stocks and select ETPs"
|
220
|
-
b"2": "Tier 2 NMS Stocks"
|
221
|
-
b" ": "Not available"
|
222
|
-
|
223
|
-
- etp_flag : Indicates whether the security is an exchange traded product (ETP):
|
224
|
-
b"Y": "Tier 1 NMS Stocks and select ETPs"
|
225
|
-
b"N": "Instrument is not an ETP"
|
226
|
-
b" ": "Not available"
|
227
|
-
|
228
|
-
- etp_leverage_factor : Tracks the integral relationship of the ETP to the underlying index.
|
229
|
-
Example: If the underlying Index increases by a value of 1 and the ETP's Leverage factor is 3, indicates the ETF will increase/decrease (see Inverse) by 3.
|
230
|
-
Leverage Factor is rounded to the nearest integer below, e.g. leverage factor 1 would represent leverage factors of 1 to 1.99.
|
231
|
-
This field is used for LULD Tier I price band calculation purpose.
|
232
|
-
|
233
|
-
- inverse_indicator : Indicates the directional relationship between the ETP and Underlying index:
|
234
|
-
b"Y": "ETP is an Inverse ETP "
|
235
|
-
b"N": "ETP is not an Inverse ETP "
|
236
|
-
Example: An ETP Leverage Factor of 3 and an Inverse value of "Y" indicates the ETP will decrease by a value of 3.
|
237
|
-
|
238
|
-
"""
|
239
|
-
|
240
|
-
message_type = b"R"
|
241
|
-
description = "Stock Directory Message"
|
242
|
-
message_format = "!HHHI8sccIcc2scccccIc"
|
243
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
244
|
-
message_size = struct.calcsize(message_format) + 1
|
245
|
-
|
246
|
-
stock: bytes
|
247
|
-
market_category: bytes
|
248
|
-
financial_status_indicator: bytes
|
249
|
-
round_lot_size: int
|
250
|
-
round_lots_only: bytes
|
251
|
-
issue_classification: bytes
|
252
|
-
issue_sub_type: bytes
|
253
|
-
authenticity: bytes
|
254
|
-
short_sale_threshold_indicator: bytes
|
255
|
-
ipo_flag: bytes
|
256
|
-
luld_ref: bytes
|
257
|
-
etp_flag: bytes
|
258
|
-
etp_leverage_factor: int
|
259
|
-
inverse_indicator: bytes
|
260
|
-
|
261
|
-
def __init__(self, message: bytes):
|
262
|
-
(
|
263
|
-
self.stock_locate,
|
264
|
-
self.tracking_number,
|
265
|
-
timestamp1,
|
266
|
-
timestamp2,
|
267
|
-
self.stock,
|
268
|
-
self.market_category,
|
269
|
-
self.financial_status_indicator,
|
270
|
-
self.round_lot_size,
|
271
|
-
self.round_lots_only,
|
272
|
-
self.issue_classification,
|
273
|
-
self.issue_sub_type,
|
274
|
-
self.authenticity,
|
275
|
-
self.short_sale_threshold_indicator,
|
276
|
-
self.ipo_flag,
|
277
|
-
self.luld_ref,
|
278
|
-
self.etp_flag,
|
279
|
-
self.etp_leverage_factor,
|
280
|
-
self.inverse_indicator,
|
281
|
-
) = struct.unpack(self.message_format, message[1:])
|
282
|
-
self.set_timestamp(timestamp1, timestamp2)
|
283
|
-
|
284
|
-
def pack(self):
|
285
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
286
|
-
message = struct.pack(
|
287
|
-
self.message_pack_format,
|
288
|
-
self.message_type,
|
289
|
-
self.stock_locate,
|
290
|
-
self.tracking_number,
|
291
|
-
timestamp1,
|
292
|
-
timestamp2,
|
293
|
-
self.stock,
|
294
|
-
self.market_category,
|
295
|
-
self.financial_status_indicator,
|
296
|
-
self.round_lot_size,
|
297
|
-
self.round_lots_only,
|
298
|
-
self.issue_classification,
|
299
|
-
self.issue_sub_type,
|
300
|
-
self.authenticity,
|
301
|
-
self.short_sale_threshold_indicator,
|
302
|
-
self.ipo_flag,
|
303
|
-
self.luld_ref,
|
304
|
-
self.etp_flag,
|
305
|
-
self.etp_leverage_factor,
|
306
|
-
self.inverse_indicator,
|
307
|
-
)
|
308
|
-
return message
|
309
|
-
|
310
|
-
|
311
|
-
class StockTradingActionMessage(MarketMessage):
|
312
|
-
"""
|
313
|
-
Nasdaq uses this administrative message to indicate the current trading status of a security to the trading
|
314
|
-
community.
|
315
|
-
|
316
|
-
Prior to the start of system hours, Nasdaq will send out a Trading Action spin. In the spin, Nasdaq will send out a
|
317
|
-
Stock Trading Action message with the “T” (Trading Resumption) for all Nasdaq--- and other exchange-•-listed
|
318
|
-
securities that are eligible for trading at the start of the system hours. If a security is absent from the pre-•-
|
319
|
-
opening Trading Action spin, firms should assume that the security is being treated as halted in the Nasdaq
|
320
|
-
platform at the start of the system hours. Please note that securities may be halted in the Nasdaq system for
|
321
|
-
regulatory or operational reasons.
|
322
|
-
|
323
|
-
After the start of system hours, Nasdaq will use the Trading Action message to relay changes in trading status for an
|
324
|
-
individual security. Messages will be sent when a stock is:
|
325
|
-
- Halted
|
326
|
-
- Paused*
|
327
|
-
- Released for quotation
|
328
|
-
- Released for trading
|
329
|
-
|
330
|
-
The paused status will be disseminated for NASDAQ---listed securities only. Trading pauses on non---NASDAQ listed securities
|
331
|
-
will be treated simply as a halt.
|
332
|
-
|
333
|
-
Attributes:
|
334
|
-
- stock : Stock symbol, right padded with spaces
|
335
|
-
- trading_state : Indicates the current trading state for the stock, see `itch.indicators.TRADING_STATES`
|
336
|
-
- reserved : Reserved
|
337
|
-
- reason : Trading Action reason, see `itch.indicators.TRADING_ACTION_REASON_CODES`
|
338
|
-
"""
|
339
|
-
|
340
|
-
message_type = b"H"
|
341
|
-
description = "Stock Trading Action Message"
|
342
|
-
message_format = "!HHHI8scc4s"
|
343
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
344
|
-
message_size = struct.calcsize(message_format) + 1
|
345
|
-
|
346
|
-
stock: bytes
|
347
|
-
trading_state: bytes
|
348
|
-
reserved: bytes
|
349
|
-
reason: bytes
|
350
|
-
|
351
|
-
def __init__(self, message: bytes):
|
352
|
-
(
|
353
|
-
self.stock_locate,
|
354
|
-
self.tracking_number,
|
355
|
-
timestamp1,
|
356
|
-
timestamp2,
|
357
|
-
self.stock,
|
358
|
-
self.trading_state,
|
359
|
-
self.reserved,
|
360
|
-
self.reason,
|
361
|
-
) = struct.unpack(self.message_format, message[1:])
|
362
|
-
self.set_timestamp(timestamp1, timestamp2)
|
363
|
-
|
364
|
-
def pack(self):
|
365
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
366
|
-
message = struct.pack(
|
367
|
-
self.message_pack_format,
|
368
|
-
self.message_type,
|
369
|
-
self.stock_locate,
|
370
|
-
self.tracking_number,
|
371
|
-
timestamp1,
|
372
|
-
timestamp2,
|
373
|
-
self.stock,
|
374
|
-
self.trading_state,
|
375
|
-
self.reserved,
|
376
|
-
self.reason,
|
377
|
-
)
|
378
|
-
return message
|
379
|
-
|
380
|
-
|
381
|
-
class RegSHOMessage(MarketMessage):
|
382
|
-
"""
|
383
|
-
In February 2011, the Securities and Exchange Commission (SEC) implemented changes to Rule 201 of the
|
384
|
-
Regulation SHO (Reg SHO). For details, please refer to [SEC Release Number 34-61595](https://www.sec.gov/files/rules/final/2010/34-61595.pdf).
|
385
|
-
In association with the Reg SHO rule change, Nasdaq will introduce the following Reg SHO Short Sale Price Test Restricted
|
386
|
-
Indicator message format.
|
387
|
-
|
388
|
-
For Nasdaq-listed issues, Nasdaq supports a full pre-•-opening spin of Reg SHO Short Sale Price Test Restricted
|
389
|
-
Indicator messages indicating the Rule 201 status for all active issues. Nasdaq also sends the Reg SHO
|
390
|
-
Short Sale Price Test Restricted Indicator message in the event of an intraday status change.
|
391
|
-
|
392
|
-
For other exchange-listed issues, Nasdaq relays the Reg SHO Short Sale Price Test Restricted Indicator
|
393
|
-
message when it receives an update from the primary listing exchange.
|
394
|
-
|
395
|
-
Nasdaq processes orders based on the most Reg SHO Restriction status value.
|
396
|
-
|
397
|
-
Attributes:
|
398
|
-
- stock : Stock symbol, right padded with spaces
|
399
|
-
- reg_sho_action : Denotes the Reg SHO Short Sale Price Test Restriction status for the issue at the time of the message dissemination:
|
400
|
-
b"0": "No price test in place"
|
401
|
-
b"1": "Reg SHO Short Sale Price Test Restriction in effect due to an intra-day price drop in security"
|
402
|
-
b"2": " Reg SHO Short Sale Price Test Restriction remains in effect"
|
403
|
-
"""
|
404
|
-
|
405
|
-
message_type = b"Y"
|
406
|
-
description = "Reg SHO Short Sale Price Test Restricted Indicator"
|
407
|
-
message_format = "!HHHI8sc"
|
408
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
409
|
-
message_size = struct.calcsize(message_format) + 1
|
410
|
-
|
411
|
-
stock: bytes
|
412
|
-
reg_sho_action: bytes
|
413
|
-
|
414
|
-
def __init__(self, message: bytes):
|
415
|
-
(
|
416
|
-
self.stock_locate,
|
417
|
-
self.tracking_number,
|
418
|
-
timestamp1,
|
419
|
-
timestamp2,
|
420
|
-
self.stock,
|
421
|
-
self.reg_sho_action,
|
422
|
-
) = struct.unpack(self.message_format, message[1:])
|
423
|
-
self.set_timestamp(timestamp1, timestamp2)
|
424
|
-
|
425
|
-
def pack(self):
|
426
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
427
|
-
message = struct.pack(
|
428
|
-
self.message_pack_format,
|
429
|
-
self.message_type,
|
430
|
-
self.stock_locate,
|
431
|
-
self.tracking_number,
|
432
|
-
timestamp1,
|
433
|
-
timestamp2,
|
434
|
-
self.stock,
|
435
|
-
self.reg_sho_action,
|
436
|
-
)
|
437
|
-
return message
|
438
|
-
|
439
|
-
|
440
|
-
class MarketParticipantPositionMessage(MarketMessage):
|
441
|
-
"""
|
442
|
-
At the start of each trading day, Nasdaq disseminates a spin of market participant position messages. The
|
443
|
-
message provides the Primary Market Maker status, Market Maker mode and Market Participant state for
|
444
|
-
each Nasdaq market participant firm registered in an issue. Market participant firms may use these fields to
|
445
|
-
comply with certain marketplace rules.
|
446
|
-
|
447
|
-
Throughout the day, Nasdaq will send out this message only if Nasdaq Operations changes the status of a
|
448
|
-
market participant firm in an issue.
|
449
|
-
|
450
|
-
Attributes:
|
451
|
-
- mpid : Denotes the market participant identifier for which the position message is being generated
|
452
|
-
- stock : Stock symbol, right padded with spaces
|
453
|
-
- primary_market_maker : Indicates if the market participant firm qualifies as a Primary Market Maker in accordance with Nasdaq marketplace rules
|
454
|
-
see ``itch.indicators.PRIMARY_MARKET_MAKER``
|
455
|
-
- market_maker_mode : Indicates the quoting participant's registration status in relation to SEC Rules 101 and 104 of Regulation M
|
456
|
-
see ``itch.indicators.MARKET_MAKER_MODE``
|
457
|
-
- market_participant_state : Indicates the market participant's current registration status in the issue
|
458
|
-
see ``itch.indicators.MARKET_PARTICIPANT_STATE``
|
459
|
-
"""
|
460
|
-
|
461
|
-
message_type = b"L"
|
462
|
-
description = "Market Participant Position message"
|
463
|
-
message_format = "!HHHI4s8sccc"
|
464
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
465
|
-
message_size = struct.calcsize(message_format) + 1
|
466
|
-
|
467
|
-
mpid: bytes
|
468
|
-
stock: bytes
|
469
|
-
primary_market_maker: bytes
|
470
|
-
market_maker_mode: bytes
|
471
|
-
market_participant_state: bytes
|
472
|
-
|
473
|
-
def __init__(self, message: bytes):
|
474
|
-
(
|
475
|
-
self.stock_locate,
|
476
|
-
self.tracking_number,
|
477
|
-
timestamp1,
|
478
|
-
timestamp2,
|
479
|
-
self.mpid,
|
480
|
-
self.stock,
|
481
|
-
self.primary_market_maker,
|
482
|
-
self.market_maker_mode,
|
483
|
-
self.market_participant_state,
|
484
|
-
) = struct.unpack(self.message_format, message[1:])
|
485
|
-
self.set_timestamp(timestamp1, timestamp2)
|
486
|
-
|
487
|
-
def pack(self):
|
488
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
489
|
-
message = struct.pack(
|
490
|
-
self.message_pack_format,
|
491
|
-
self.message_type,
|
492
|
-
self.stock_locate,
|
493
|
-
self.tracking_number,
|
494
|
-
timestamp1,
|
495
|
-
timestamp2,
|
496
|
-
self.mpid,
|
497
|
-
self.stock,
|
498
|
-
self.primary_market_maker,
|
499
|
-
self.market_maker_mode,
|
500
|
-
self.market_participant_state,
|
501
|
-
)
|
502
|
-
return message
|
503
|
-
|
504
|
-
|
505
|
-
class MWCBDeclineLeveMessage(MarketMessage):
|
506
|
-
"""
|
507
|
-
Informs data recipients what the daily Market-Wide Circuit Breaker (MWCB)
|
508
|
-
breach points are set to for the current trading day.
|
509
|
-
|
510
|
-
Attributes:
|
511
|
-
- level1_price : Denotes the MWCB Level 1 Value.
|
512
|
-
- level2_price : Denotes the MWCB Level 2 Value.
|
513
|
-
- level3_price : Denotes the MWCB Level 3 Value.
|
514
|
-
"""
|
515
|
-
|
516
|
-
message_type = b"V"
|
517
|
-
description = "Market wide circuit breaker Decline Level Message"
|
518
|
-
message_format = "!HHHIQQQ"
|
519
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
520
|
-
message_size = struct.calcsize(message_format) + 1
|
521
|
-
price_precision = 8
|
522
|
-
level1_price: float
|
523
|
-
level2_price: float
|
524
|
-
level3_price: float
|
525
|
-
|
526
|
-
def __init__(self, message: bytes):
|
527
|
-
(
|
528
|
-
self.stock_locate,
|
529
|
-
self.tracking_number,
|
530
|
-
timestamp1,
|
531
|
-
timestamp2,
|
532
|
-
self.level1_price,
|
533
|
-
self.level2_price,
|
534
|
-
self.level3_price,
|
535
|
-
) = struct.unpack(self.message_format, message[1:])
|
536
|
-
self.set_timestamp(timestamp1, timestamp2)
|
537
|
-
|
538
|
-
def pack(self):
|
539
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
540
|
-
message = struct.pack(
|
541
|
-
self.message_pack_format,
|
542
|
-
self.message_type,
|
543
|
-
self.stock_locate,
|
544
|
-
self.tracking_number,
|
545
|
-
timestamp1,
|
546
|
-
timestamp2,
|
547
|
-
self.level1_price,
|
548
|
-
self.level2_price,
|
549
|
-
self.level3_price,
|
550
|
-
)
|
551
|
-
return message
|
552
|
-
|
553
|
-
|
554
|
-
class MWCBStatusMessage(MarketMessage):
|
555
|
-
"""
|
556
|
-
Informs data recipients when a MWCB has breached one of the established levels
|
557
|
-
|
558
|
-
Attributes:
|
559
|
-
- breached_level : Denotes the MWCB Level that was breached:
|
560
|
-
b"1" = Level 1
|
561
|
-
b"2" = Level 2
|
562
|
-
b"3" = Level 3
|
563
|
-
"""
|
564
|
-
|
565
|
-
message_type = b"W"
|
566
|
-
description = "Market-Wide Circuit Breaker Status message"
|
567
|
-
message_format = "!HHHIc"
|
568
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
569
|
-
message_size = struct.calcsize(message_format) + 1
|
570
|
-
|
571
|
-
breached_level: bytes
|
572
|
-
|
573
|
-
def __init__(self, message: bytes):
|
574
|
-
(
|
575
|
-
self.stock_locate,
|
576
|
-
self.tracking_number,
|
577
|
-
timestamp1,
|
578
|
-
timestamp2,
|
579
|
-
self.breached_level,
|
580
|
-
) = struct.unpack(self.message_format, message[1:])
|
581
|
-
self.set_timestamp(timestamp1, timestamp2)
|
582
|
-
|
583
|
-
def pack(self):
|
584
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
585
|
-
message = struct.pack(
|
586
|
-
self.message_pack_format,
|
587
|
-
self.message_type,
|
588
|
-
self.stock_locate,
|
589
|
-
self.tracking_number,
|
590
|
-
timestamp1,
|
591
|
-
timestamp2,
|
592
|
-
self.breached_level,
|
593
|
-
)
|
594
|
-
return message
|
595
|
-
|
596
|
-
|
597
|
-
class IPOQuotingPeriodUpdateMessage(MarketMessage):
|
598
|
-
"""
|
599
|
-
Indicates the anticipated IPO quotation release time of a security.
|
600
|
-
|
601
|
-
Attributes:
|
602
|
-
- stock : Stock symbol, right padded with spaces
|
603
|
-
|
604
|
-
- ipo_release_time : Denotes the IPO release time, in seconds since midnight, for quotation to the nearest second.
|
605
|
-
NOTE: If the quotation period is being canceled/postponed, we should state that:
|
606
|
-
1. IPO Quotation Time will be set to 0
|
607
|
-
2. 2. IPO Price will be set to 0
|
608
|
-
|
609
|
-
- ipo_release_qualifier :
|
610
|
-
b"A": "Anticipated Quotation Release Time"
|
611
|
-
b"C": " IPO Release Canceled/Postponed"
|
612
|
-
b"A" value would be used when Nasdaq Market Operations initially enters the IPO instrument for release
|
613
|
-
b"C" value would be sued when Nasdaq Market Operations cancels or postpones the release of the new IPO instrument
|
614
|
-
|
615
|
-
- ipo_price : Denotes the IPO Price to be used for intraday net change calculations Prices are given in decimal format with 6 whole number
|
616
|
-
places followed by 4 decimal digits. The whole number portion is padded on the left with spaces; the decimal portion is padded on the right with zeroes. The decimal point is
|
617
|
-
implied by position, it does not appear inside the price field
|
618
|
-
"""
|
619
|
-
|
620
|
-
message_type = b"K"
|
621
|
-
description = "IPO Quoting Period Update Message"
|
622
|
-
message_format = "!HHHI8sIcI"
|
623
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
624
|
-
message_size = struct.calcsize(message_format) + 1
|
625
|
-
|
626
|
-
stock: bytes
|
627
|
-
ipo_release_time: int
|
628
|
-
ipo_release_qualifier: bytes
|
629
|
-
ipo_price: float
|
630
|
-
|
631
|
-
def __init__(self, message: bytes):
|
632
|
-
(
|
633
|
-
self.stock_locate,
|
634
|
-
self.tracking_number,
|
635
|
-
timestamp1,
|
636
|
-
timestamp2,
|
637
|
-
self.stock,
|
638
|
-
self.ipo_release_time,
|
639
|
-
self.ipo_release_qualifier,
|
640
|
-
self.ipo_price,
|
641
|
-
) = struct.unpack(self.message_format, message[1:])
|
642
|
-
self.set_timestamp(timestamp1, timestamp2)
|
643
|
-
|
644
|
-
def pack(self):
|
645
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
646
|
-
message = struct.pack(
|
647
|
-
self.message_pack_format,
|
648
|
-
self.message_type,
|
649
|
-
self.stock_locate,
|
650
|
-
self.tracking_number,
|
651
|
-
timestamp1,
|
652
|
-
timestamp2,
|
653
|
-
self.stock,
|
654
|
-
self.ipo_release_time,
|
655
|
-
self.ipo_release_qualifier,
|
656
|
-
self.ipo_price,
|
657
|
-
)
|
658
|
-
return message
|
659
|
-
|
660
|
-
|
661
|
-
class LULDAuctionCollarMessage(MarketMessage):
|
662
|
-
"""
|
663
|
-
Indicates the auction collar thresholds within which a paused security can reopen following a LULD Trading Pause.
|
664
|
-
Stock 11 8 Alpha Stock symbol, right padded with spaces
|
665
|
-
|
666
|
-
Attributes:
|
667
|
-
- stock : Stock symbol, right padded with spaces
|
668
|
-
- auction_collar_reference_price : Reference price used to set the Auction Collars
|
669
|
-
- upper_auction_collar_price : Indicates the price of the Upper Auction Collar Threshold
|
670
|
-
- lower_auctiin_collar_price : Indicates the price of the Lower Auction Collar Threshold
|
671
|
-
- auction_collar_extention : Indicates the number of the extensions to the Reopening Auction
|
672
|
-
"""
|
673
|
-
|
674
|
-
message_type = b"J"
|
675
|
-
description = "LULD Auction Collar"
|
676
|
-
message_format = "!HHHI8sIIII"
|
677
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
678
|
-
message_size = struct.calcsize(message_format) + 1
|
679
|
-
|
680
|
-
stock: bytes
|
681
|
-
auction_collar_reference_price: float
|
682
|
-
upper_auction_collar_price: float
|
683
|
-
lower_auction_collar_price: float
|
684
|
-
auction_collar_extention: int
|
685
|
-
|
686
|
-
def __init__(self, message: bytes):
|
687
|
-
(
|
688
|
-
self.stock_locate,
|
689
|
-
self.tracking_number,
|
690
|
-
timestamp1,
|
691
|
-
timestamp2,
|
692
|
-
self.stock,
|
693
|
-
self.auction_collar_reference_price,
|
694
|
-
self.upper_auction_collar_price,
|
695
|
-
self.lower_auction_collar_price,
|
696
|
-
self.auction_collar_extention,
|
697
|
-
) = struct.unpack(self.message_format, message[1:])
|
698
|
-
self.set_timestamp(timestamp1, timestamp2)
|
699
|
-
|
700
|
-
def pack(self):
|
701
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
702
|
-
message = struct.pack(
|
703
|
-
self.message_pack_format,
|
704
|
-
self.message_type,
|
705
|
-
self.stock_locate,
|
706
|
-
self.tracking_number,
|
707
|
-
timestamp1,
|
708
|
-
timestamp2,
|
709
|
-
self.stock,
|
710
|
-
self.auction_collar_reference_price,
|
711
|
-
self.upper_auction_collar_price,
|
712
|
-
self.lower_auction_collar_price,
|
713
|
-
self.auction_collar_extention,
|
714
|
-
)
|
715
|
-
return message
|
716
|
-
|
717
|
-
|
718
|
-
class OperationalHaltMessage(MarketMessage):
|
719
|
-
"""
|
720
|
-
The Exchange uses this message to indicate the current Operational Status of a security to the trading
|
721
|
-
community. An Operational Halt means that there has been an interruption of service on the identified
|
722
|
-
security impacting only the designated Market Center. These Halts differ from the “Stock Trading
|
723
|
-
Action” message types since an Operational Halt is specific to the exchange for which it is declared, and
|
724
|
-
does not interrupt the ability of the trading community to trade the identified instrument on any other
|
725
|
-
marketplace.
|
726
|
-
|
727
|
-
Nasdaq uses this administrative message to indicate the current trading status of the three market centers
|
728
|
-
operated by Nasdaq.
|
729
|
-
|
730
|
-
Attributes:
|
731
|
-
- stock : Denotes the security symbol for the issue in Nasdaq execution system
|
732
|
-
- market_code :
|
733
|
-
b"Q": "Nasdaq"
|
734
|
-
b"B": "BX"
|
735
|
-
b"X": "PSX"
|
736
|
-
|
737
|
-
- operational_halt_action :
|
738
|
-
b"H": "Operationally Halted on the identified Market"
|
739
|
-
b"T": "Operational Halt has been lifted and Trading resumed "
|
740
|
-
|
741
|
-
"""
|
742
|
-
|
743
|
-
message_type = b"h"
|
744
|
-
description = "Operational Halt"
|
745
|
-
message_format = "!HHHI8scc"
|
746
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
747
|
-
message_size = struct.calcsize(message_format) + 1
|
748
|
-
|
749
|
-
stock: bytes
|
750
|
-
market_code: bytes
|
751
|
-
operational_halt_action: bytes
|
752
|
-
|
753
|
-
def __init__(self, message: bytes):
|
754
|
-
(
|
755
|
-
self.stock_locate,
|
756
|
-
self.tracking_number,
|
757
|
-
timestamp1,
|
758
|
-
timestamp2,
|
759
|
-
self.stock,
|
760
|
-
self.market_code,
|
761
|
-
self.operational_halt_action,
|
762
|
-
) = struct.unpack(self.message_format, message[1:])
|
763
|
-
self.set_timestamp(timestamp1, timestamp2)
|
764
|
-
|
765
|
-
def pack(self):
|
766
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
767
|
-
message = struct.pack(
|
768
|
-
self.message_pack_format,
|
769
|
-
self.message_type,
|
770
|
-
self.stock_locate,
|
771
|
-
self.tracking_number,
|
772
|
-
timestamp1,
|
773
|
-
timestamp2,
|
774
|
-
self.stock,
|
775
|
-
self.market_code,
|
776
|
-
self.operational_halt_action,
|
777
|
-
)
|
778
|
-
return message
|
779
|
-
|
780
|
-
|
781
|
-
class AddOrderMessage(MarketMessage):
|
782
|
-
"""
|
783
|
-
An Add Order Message indicates that a new order has been accepted by the Nasdaq system and was added to the
|
784
|
-
displayable book. The message includes a day-•-unique Order Reference Number used by Nasdaq to track the order. Nasdaq
|
785
|
-
will support two variations of the Add Order message format.
|
786
|
-
"""
|
787
|
-
|
788
|
-
order_reference_number: int
|
789
|
-
buy_sell_indicator: bytes
|
790
|
-
shares: int
|
791
|
-
stock: bytes
|
792
|
-
price: float
|
793
|
-
|
794
|
-
|
795
|
-
class AddOrderNoMPIAttributionMessage(AddOrderMessage):
|
796
|
-
"""
|
797
|
-
This message will be generated for unattributed orders accepted by the Nasdaq system. (Note: If a firm wants to
|
798
|
-
display a MPID for unattributed orders, Nasdaq recommends that it use the MPID of “NSDQ”.)
|
799
|
-
|
800
|
-
Attributes:
|
801
|
-
- order_reference_number : The unique reference number assigned to the new order at the time of receipt.
|
802
|
-
- buy_sell_indicator : The type of order being added. b"B" = Buy Order. b"S" = Sell Order.
|
803
|
-
- shares : The total number of shares associated with the order being added to the book.
|
804
|
-
- stock : Stock symbol, right padded with spaces
|
805
|
-
- price : The display price of the new order. Refer to Data Types for field processing notes.
|
806
|
-
"""
|
807
|
-
|
808
|
-
message_type = b"A"
|
809
|
-
description = "Add Order - No MPID Attribution Message"
|
810
|
-
message_format = "!HHHIQcI8sI"
|
811
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
812
|
-
message_size = struct.calcsize(message_format) + 1
|
813
|
-
|
814
|
-
def __init__(self, message: bytes):
|
815
|
-
(
|
816
|
-
self.stock_locate,
|
817
|
-
self.tracking_number,
|
818
|
-
timestamp1,
|
819
|
-
timestamp2,
|
820
|
-
self.order_reference_number,
|
821
|
-
self.buy_sell_indicator,
|
822
|
-
self.shares,
|
823
|
-
self.stock,
|
824
|
-
self.price,
|
825
|
-
) = struct.unpack(self.message_format, message[1:])
|
826
|
-
self.set_timestamp(timestamp1, timestamp2)
|
827
|
-
|
828
|
-
def pack(self):
|
829
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
830
|
-
message = struct.pack(
|
831
|
-
self.message_pack_format,
|
832
|
-
self.message_type,
|
833
|
-
self.stock_locate,
|
834
|
-
self.tracking_number,
|
835
|
-
timestamp1,
|
836
|
-
timestamp2,
|
837
|
-
self.order_reference_number,
|
838
|
-
self.buy_sell_indicator,
|
839
|
-
self.shares,
|
840
|
-
self.stock,
|
841
|
-
self.price,
|
842
|
-
)
|
843
|
-
return message
|
844
|
-
|
845
|
-
|
846
|
-
class AddOrderMPIDAttribution(AddOrderMessage):
|
847
|
-
"""
|
848
|
-
This message will be generated for attributed orders and quotations accepted by the Nasdaq system.
|
849
|
-
|
850
|
-
Attributes:
|
851
|
-
- order_reference_number : The unique reference number assigned to the new order at the time of receipt.
|
852
|
-
- buy_sell_indicator : The type of order being added. “B” = Buy Order. “S” = Sell Order.
|
853
|
-
- shares : The total number of shares associated with the order being added to the book.
|
854
|
-
- stock : Stock symbol, right padded with spaces
|
855
|
-
- price : The display price of the new order. Refer to Data Types for field processing notes.
|
856
|
-
- attribution : Nasdaq Market participant identifier associated with the entered order
|
857
|
-
"""
|
858
|
-
|
859
|
-
message_type = b"F"
|
860
|
-
description = "Add Order - MPID Attribution Message"
|
861
|
-
message_format = "!HHHIQcI8sI4s"
|
862
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
863
|
-
message_size = struct.calcsize(message_format) + 1
|
864
|
-
|
865
|
-
attribution: bytes
|
866
|
-
|
867
|
-
def __init__(self, message: bytes):
|
868
|
-
(
|
869
|
-
self.stock_locate,
|
870
|
-
self.tracking_number,
|
871
|
-
timestamp1,
|
872
|
-
timestamp2,
|
873
|
-
self.order_reference_number,
|
874
|
-
self.buy_sell_indicator,
|
875
|
-
self.shares,
|
876
|
-
self.stock,
|
877
|
-
self.price,
|
878
|
-
self.attribution,
|
879
|
-
) = struct.unpack(self.message_format, message[1:])
|
880
|
-
self.set_timestamp(timestamp1, timestamp2)
|
881
|
-
|
882
|
-
def pack(self):
|
883
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
884
|
-
message = struct.pack(
|
885
|
-
self.message_pack_format,
|
886
|
-
self.message_type,
|
887
|
-
self.stock_locate,
|
888
|
-
self.tracking_number,
|
889
|
-
timestamp1,
|
890
|
-
timestamp2,
|
891
|
-
self.order_reference_number,
|
892
|
-
self.buy_sell_indicator,
|
893
|
-
self.shares,
|
894
|
-
self.stock,
|
895
|
-
self.price,
|
896
|
-
self.attribution,
|
897
|
-
)
|
898
|
-
return message
|
899
|
-
|
900
|
-
|
901
|
-
class ModifyOrderMessage(MarketMessage):
|
902
|
-
"""
|
903
|
-
Modify Order messages always include the Order Reference Number of the Add Order to which the update
|
904
|
-
applies. To determine the current display shares for an order, ITCH subscribers must deduct the number of shares
|
905
|
-
stated in the Modify message from the original number of shares stated in the Add Order message with the same
|
906
|
-
reference number. Nasdaq may send multiple Modify Order messages for the same order reference number and
|
907
|
-
the effects are cumulative. When the number of display shares for an order reaches zero, the order is dead and
|
908
|
-
should be removed from the book.
|
909
|
-
"""
|
910
|
-
|
911
|
-
order_reference_number: int
|
912
|
-
|
913
|
-
|
914
|
-
class OrderExecutedMessage(ModifyOrderMessage):
|
915
|
-
"""
|
916
|
-
This message is sent whenever an order on the book is executed in whole or in part. It is possible to receive several
|
917
|
-
Order Executed Messages for the same order reference number if that order is executed in several parts. The
|
918
|
-
multiple Order Executed Messages on the same order are cumulative.
|
919
|
-
|
920
|
-
By combining the executions from both types of Order Executed Messages and the Trade Message, it is possible to
|
921
|
-
build a complete view of all non-•-cross executions that happen on Nasdaq. Cross execution information is available in
|
922
|
-
one bulk print per symbol via the Cross Trade Message.
|
923
|
-
|
924
|
-
Attributes:
|
925
|
-
- order_reference_number : The unique reference number assigned to the new order at the time of receipt
|
926
|
-
- executed_shares : The number of shares executed
|
927
|
-
- match_number : The Nasdaq generated day unique Match Number of this execution.
|
928
|
-
The Match Number is also referenced in the Trade Break Message
|
929
|
-
|
930
|
-
"""
|
931
|
-
|
932
|
-
message_type = b"E"
|
933
|
-
description = "Add Order - Order Executed Message"
|
934
|
-
message_format = "!HHHIQIQ"
|
935
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
936
|
-
message_size = struct.calcsize(message_format) + 1
|
937
|
-
|
938
|
-
executed_shares: int
|
939
|
-
match_number: int
|
940
|
-
|
941
|
-
def __init__(self, message: bytes):
|
942
|
-
(
|
943
|
-
self.stock_locate,
|
944
|
-
self.tracking_number,
|
945
|
-
timestamp1,
|
946
|
-
timestamp2,
|
947
|
-
self.order_reference_number,
|
948
|
-
self.executed_shares,
|
949
|
-
self.match_number,
|
950
|
-
) = struct.unpack(self.message_format, message[1:])
|
951
|
-
self.set_timestamp(timestamp1, timestamp2)
|
952
|
-
|
953
|
-
def pack(self):
|
954
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
955
|
-
message = struct.pack(
|
956
|
-
self.message_pack_format,
|
957
|
-
self.message_type,
|
958
|
-
self.stock_locate,
|
959
|
-
self.tracking_number,
|
960
|
-
timestamp1,
|
961
|
-
timestamp2,
|
962
|
-
self.order_reference_number,
|
963
|
-
self.executed_shares,
|
964
|
-
self.match_number,
|
965
|
-
)
|
966
|
-
return message
|
967
|
-
|
968
|
-
|
969
|
-
class OrderExecutedWithPriceMessage(ModifyOrderMessage):
|
970
|
-
"""
|
971
|
-
This message is sent whenever an order on the book is executed in whole or in part at a price different from the
|
972
|
-
initial display price. Since the execution price is different than the display price of the original Add Order, Nasdaq
|
973
|
-
includes a price field within this execution message.
|
974
|
-
|
975
|
-
It is possible to receive multiple Order Executed and Order Executed With Price messages for the same order if that
|
976
|
-
order is executed in several parts. The multiple Order Executed messages on the same order are cumulative.
|
977
|
-
|
978
|
-
These executions may be marked as non-•-printable.
|
979
|
-
If the execution is marked as non-•-printed, it means that the shares will be included into a later bulk print (e.g., in the case of cross executions).
|
980
|
-
If a firm is looking to use the data in time-•-and-•-sales displays or volume calculations,
|
981
|
-
Nasdaq recommends that firms ignore messages marked as non- -- printable to prevent double counting.
|
982
|
-
|
983
|
-
Attributes:
|
984
|
-
- order_reference_number : The unique reference number assigned to the new order at the time of receipt
|
985
|
-
- executed_shares : The number of shares executed
|
986
|
-
- match_number : The Nasdaq generated day unique Match Number of this execution.
|
987
|
-
The Match Number is also referenced in the Trade Break Message
|
988
|
-
|
989
|
-
- printable : Indicates if the execution should be reflected on time and sales displays and volume calculations
|
990
|
-
b"N" = Non-Printable
|
991
|
-
b"Y" = Printable
|
992
|
-
|
993
|
-
- execution_price : The Price at which the order execution occurred. Refer to Data Types for field processing notes
|
994
|
-
"""
|
995
|
-
|
996
|
-
message_type = b"C"
|
997
|
-
description = "Add Order - Order Executed with Price Message"
|
998
|
-
message_format = "!HHHIQIQcI"
|
999
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1000
|
-
message_size = struct.calcsize(message_format) + 1
|
1001
|
-
|
1002
|
-
executed_shares: int
|
1003
|
-
match_number: int
|
1004
|
-
printable: bytes
|
1005
|
-
execution_price: float
|
1006
|
-
|
1007
|
-
def __init__(self, message: bytes):
|
1008
|
-
(
|
1009
|
-
self.stock_locate,
|
1010
|
-
self.tracking_number,
|
1011
|
-
timestamp1,
|
1012
|
-
timestamp2,
|
1013
|
-
self.order_reference_number,
|
1014
|
-
self.executed_shares,
|
1015
|
-
self.match_number,
|
1016
|
-
self.printable,
|
1017
|
-
self.execution_price,
|
1018
|
-
) = struct.unpack(self.message_format, message[1:])
|
1019
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1020
|
-
|
1021
|
-
def pack(self):
|
1022
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1023
|
-
message = struct.pack(
|
1024
|
-
self.message_pack_format,
|
1025
|
-
self.message_type,
|
1026
|
-
self.stock_locate,
|
1027
|
-
self.tracking_number,
|
1028
|
-
timestamp1,
|
1029
|
-
timestamp2,
|
1030
|
-
self.order_reference_number,
|
1031
|
-
self.executed_shares,
|
1032
|
-
self.match_number,
|
1033
|
-
self.printable,
|
1034
|
-
self.execution_price,
|
1035
|
-
)
|
1036
|
-
return message
|
1037
|
-
|
1038
|
-
|
1039
|
-
class OrderCancelMessage(ModifyOrderMessage):
|
1040
|
-
"""
|
1041
|
-
This message is sent whenever an order on the book is modified as a result of a partial cancellation.
|
1042
|
-
|
1043
|
-
Attributes:
|
1044
|
-
- order_reference_number : The reference number of the order being canceled
|
1045
|
-
- cancelled_shares : The number of shares being removed from the display size of the order as a result of a cancellation
|
1046
|
-
"""
|
1047
|
-
|
1048
|
-
message_type = b"X"
|
1049
|
-
description = "Order Cancel Message"
|
1050
|
-
message_format = "!HHHIQI"
|
1051
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1052
|
-
message_size = struct.calcsize(message_format) + 1
|
1053
|
-
|
1054
|
-
cancelled_shares: int
|
1055
|
-
|
1056
|
-
def __init__(self, message: bytes):
|
1057
|
-
(
|
1058
|
-
self.stock_locate,
|
1059
|
-
self.tracking_number,
|
1060
|
-
timestamp1,
|
1061
|
-
timestamp2,
|
1062
|
-
self.order_reference_number,
|
1063
|
-
self.cancelled_shares,
|
1064
|
-
) = struct.unpack(self.message_format, message[1:])
|
1065
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1066
|
-
|
1067
|
-
def pack(self):
|
1068
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1069
|
-
message = struct.pack(
|
1070
|
-
self.message_pack_format,
|
1071
|
-
self.message_type,
|
1072
|
-
self.stock_locate,
|
1073
|
-
self.tracking_number,
|
1074
|
-
timestamp1,
|
1075
|
-
timestamp2,
|
1076
|
-
self.order_reference_number,
|
1077
|
-
self.cancelled_shares,
|
1078
|
-
)
|
1079
|
-
return message
|
1080
|
-
|
1081
|
-
|
1082
|
-
class OrderDeleteMessage(ModifyOrderMessage):
|
1083
|
-
"""
|
1084
|
-
This message is sent whenever an order on the book is being cancelled. All remaining shares are no longer
|
1085
|
-
accessible so the order must be removed from the book.
|
1086
|
-
|
1087
|
-
Attributes:
|
1088
|
-
- order_reference_number : The reference number of the order being canceled
|
1089
|
-
"""
|
1090
|
-
|
1091
|
-
message_type = b"D"
|
1092
|
-
description = "Order Delete Message"
|
1093
|
-
message_format = "!HHHIQ"
|
1094
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1095
|
-
message_size = struct.calcsize(message_format) + 1
|
1096
|
-
|
1097
|
-
def __init__(self, message: bytes):
|
1098
|
-
(
|
1099
|
-
self.stock_locate,
|
1100
|
-
self.tracking_number,
|
1101
|
-
timestamp1,
|
1102
|
-
timestamp2,
|
1103
|
-
self.order_reference_number,
|
1104
|
-
) = struct.unpack(self.message_format, message[1:])
|
1105
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1106
|
-
|
1107
|
-
def pack(self):
|
1108
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1109
|
-
message = struct.pack(
|
1110
|
-
self.message_pack_format,
|
1111
|
-
self.message_type,
|
1112
|
-
self.stock_locate,
|
1113
|
-
self.tracking_number,
|
1114
|
-
timestamp1,
|
1115
|
-
timestamp2,
|
1116
|
-
self.order_reference_number,
|
1117
|
-
)
|
1118
|
-
return message
|
1119
|
-
|
1120
|
-
|
1121
|
-
class OrderReplaceMessage(ModifyOrderMessage):
|
1122
|
-
"""
|
1123
|
-
This message is sent whenever an order on the book has been cancel-•-replaced. All remaining shares from the
|
1124
|
-
original order are no longer accessible, and must be removed. The new order details are provided for the
|
1125
|
-
replacement, along with a new order reference number which will be used henceforth. Since the side, stock
|
1126
|
-
symbol and attribution (if any) cannot be changed by an Order Replace event, these fields are not included in the
|
1127
|
-
message. Firms should retain the side, stock symbol and MPID from the original Add Order message.
|
1128
|
-
|
1129
|
-
Attributes:
|
1130
|
-
- order_reference_number : The original order reference number of the order being replaced
|
1131
|
-
- new_order_reference_number : The new reference number for this order at time of replacement
|
1132
|
-
Please note that the Nasdaq system will use this new order reference number for all subsequent updates
|
1133
|
-
- shares : The new total displayed quantity
|
1134
|
-
- price : The new display price for the order. Please refer to Data Types for field processing notes
|
1135
|
-
"""
|
1136
|
-
|
1137
|
-
message_type = b"U"
|
1138
|
-
description = "Order Replace Message"
|
1139
|
-
message_format = "!HHHIQQII"
|
1140
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1141
|
-
message_size = struct.calcsize(message_format) + 1
|
1142
|
-
|
1143
|
-
new_order_reference_number: int
|
1144
|
-
shares: int
|
1145
|
-
price: float
|
1146
|
-
|
1147
|
-
def __init__(self, message: bytes):
|
1148
|
-
(
|
1149
|
-
self.stock_locate,
|
1150
|
-
self.tracking_number,
|
1151
|
-
timestamp1,
|
1152
|
-
timestamp2,
|
1153
|
-
self.order_reference_number,
|
1154
|
-
self.new_order_reference_number,
|
1155
|
-
self.shares,
|
1156
|
-
self.price,
|
1157
|
-
) = struct.unpack(self.message_format, message[1:])
|
1158
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1159
|
-
|
1160
|
-
def pack(self):
|
1161
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1162
|
-
message = struct.pack(
|
1163
|
-
self.message_pack_format,
|
1164
|
-
self.message_type,
|
1165
|
-
self.stock_locate,
|
1166
|
-
self.tracking_number,
|
1167
|
-
timestamp1,
|
1168
|
-
timestamp2,
|
1169
|
-
self.order_reference_number,
|
1170
|
-
self.new_order_reference_number,
|
1171
|
-
self.shares,
|
1172
|
-
self.price,
|
1173
|
-
)
|
1174
|
-
return message
|
1175
|
-
|
1176
|
-
|
1177
|
-
class TradeMessage(MarketMessage):
|
1178
|
-
match_number: int
|
1179
|
-
|
1180
|
-
|
1181
|
-
class NonCrossTradeMessage(TradeMessage):
|
1182
|
-
"""
|
1183
|
-
The Trade Message is designed to provide execution details for normal match events involving non-•-displayable
|
1184
|
-
order types. (Note: There is a separate message for Nasdaq cross events.)
|
1185
|
-
Since no Add Order Message is generated when a non-•-displayed order is initially received, Nasdaq cannot use the
|
1186
|
-
Order Executed messages for all matches. Therefore this message indicates when a match occurs between non---
|
1187
|
-
displayable order types.
|
1188
|
-
|
1189
|
-
A Trade Message is transmitted each time a non-•-displayable order is executed in whole or in part.
|
1190
|
-
It is possible to receive multiple Trade Messages for the same order if that order is executed in several parts.
|
1191
|
-
Trade Messages for the same order are cumulative.
|
1192
|
-
|
1193
|
-
Trade Messages should be included in Nasdaq time-•-and-•-sales displays as well as volume and other market statistics.
|
1194
|
-
Since Trade Messages do not affect the book, however, they may be ignored by firms just looking to build
|
1195
|
-
and track the Nasdaq execution system display.
|
1196
|
-
|
1197
|
-
Attributes:
|
1198
|
-
- order_reference_number : The unique reference number assigned to the order on the book being executed.
|
1199
|
-
- buy_sell_indicator : The type of non-display order on the book being matched b"B" = Buy Order, b"S" = Sell Order
|
1200
|
-
- shares : The number of shares being matched in this execution
|
1201
|
-
- stock : Stock Symbol, right padded with spaces
|
1202
|
-
- price : The match price of the order
|
1203
|
-
- match_number : The Nasdaq generated session unique Match Number for this trade
|
1204
|
-
The Match Number is referenced in the Trade Break Message
|
1205
|
-
"""
|
1206
|
-
|
1207
|
-
message_type = b"P"
|
1208
|
-
description = "Trade Message"
|
1209
|
-
message_format = "!HHHIQcI8sIQ"
|
1210
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1211
|
-
message_size = struct.calcsize(message_format) + 1
|
1212
|
-
|
1213
|
-
order_reference_number: int
|
1214
|
-
buy_sell_indicator: bytes
|
1215
|
-
shares: int
|
1216
|
-
stock: bytes
|
1217
|
-
price: float
|
1218
|
-
|
1219
|
-
def __init__(self, message: bytes):
|
1220
|
-
(
|
1221
|
-
self.stock_locate,
|
1222
|
-
self.tracking_number,
|
1223
|
-
timestamp1,
|
1224
|
-
timestamp2,
|
1225
|
-
self.order_reference_number,
|
1226
|
-
self.buy_sell_indicator,
|
1227
|
-
self.shares,
|
1228
|
-
self.stock,
|
1229
|
-
self.price,
|
1230
|
-
self.match_number,
|
1231
|
-
) = struct.unpack(self.message_format, message[1:])
|
1232
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1233
|
-
|
1234
|
-
def pack(self):
|
1235
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1236
|
-
message = struct.pack(
|
1237
|
-
self.message_pack_format,
|
1238
|
-
self.message_type,
|
1239
|
-
self.stock_locate,
|
1240
|
-
self.tracking_number,
|
1241
|
-
timestamp1,
|
1242
|
-
timestamp2,
|
1243
|
-
self.order_reference_number,
|
1244
|
-
self.buy_sell_indicator,
|
1245
|
-
self.shares,
|
1246
|
-
self.stock,
|
1247
|
-
self.price,
|
1248
|
-
self.match_number,
|
1249
|
-
)
|
1250
|
-
return message
|
1251
|
-
|
1252
|
-
|
1253
|
-
class CrossTradeMessage(TradeMessage):
|
1254
|
-
"""
|
1255
|
-
Cross Trade message indicates that Nasdaq has completed its cross process for a specific security. Nasdaq sends out
|
1256
|
-
a Cross Trade message for all active issues in the system following the Opening, Closing and EMC cross events.
|
1257
|
-
Firms may use the Cross Trade message to determine when the cross for each security has been completed.
|
1258
|
-
(Note: For the halted / paused securities, firms should use the Trading Action message to determine when an issue has been
|
1259
|
-
released for trading.)
|
1260
|
-
|
1261
|
-
For most issues, the Cross Trade message will indicate the bulk volume associated with the cross event. If the order
|
1262
|
-
interest is insufficient to conduct a cross in a particular issue, however, the Cross Trade message may show the
|
1263
|
-
shares as zero.
|
1264
|
-
|
1265
|
-
To avoid double counting of cross volume, firms should not include transactions marked as non-•-printable in time---
|
1266
|
-
and-•-sales displays or market statistic calculations.
|
1267
|
-
|
1268
|
-
Attributes:
|
1269
|
-
- shares : The number of shares being matched in this execution
|
1270
|
-
- stock : Stock Symbol, right padded with spaces
|
1271
|
-
- cross_price : The match price of the order
|
1272
|
-
- match_number : The Nasdaq generated session unique Match Number for this trade
|
1273
|
-
The Match Number is referenced in the Trade Break Message
|
1274
|
-
- cross_type : The Nasdaq cross session for which the message is being generated:
|
1275
|
-
b"O": "Nasdaq Opening Cross"
|
1276
|
-
b"C": "Nasdaq Closing Cross"
|
1277
|
-
b"H": "Cross for IPO and halted / paused securities"
|
1278
|
-
"""
|
1279
|
-
|
1280
|
-
message_type = b"Q"
|
1281
|
-
description = "Cross Trade Message"
|
1282
|
-
message_format = "!HHHIQ8sIQc"
|
1283
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1284
|
-
message_size = struct.calcsize(message_format) + 1
|
1285
|
-
|
1286
|
-
shares: int
|
1287
|
-
stock: bytes
|
1288
|
-
cross_price: float
|
1289
|
-
cross_type: bytes
|
1290
|
-
|
1291
|
-
def __init__(self, message: bytes):
|
1292
|
-
(
|
1293
|
-
self.stock_locate,
|
1294
|
-
self.tracking_number,
|
1295
|
-
timestamp1,
|
1296
|
-
timestamp2,
|
1297
|
-
self.shares,
|
1298
|
-
self.stock,
|
1299
|
-
self.cross_price,
|
1300
|
-
self.match_number,
|
1301
|
-
self.cross_type,
|
1302
|
-
) = struct.unpack(self.message_format, message[1:])
|
1303
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1304
|
-
|
1305
|
-
def pack(self):
|
1306
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1307
|
-
message = struct.pack(
|
1308
|
-
self.message_pack_format,
|
1309
|
-
self.message_type,
|
1310
|
-
self.stock_locate,
|
1311
|
-
self.tracking_number,
|
1312
|
-
timestamp1,
|
1313
|
-
timestamp2,
|
1314
|
-
self.shares,
|
1315
|
-
self.stock,
|
1316
|
-
self.cross_price,
|
1317
|
-
self.match_number,
|
1318
|
-
self.cross_type,
|
1319
|
-
)
|
1320
|
-
return message
|
1321
|
-
|
1322
|
-
|
1323
|
-
class BrokenTradeMessage(TradeMessage):
|
1324
|
-
"""
|
1325
|
-
The Broken Trade Message is sent whenever an execution on Nasdaq is broken. An execution may be broken if it is
|
1326
|
-
found to be “clearly erroneous” pursuant to [Nasdaq's Clearly Erroneous Policy](https://www.nasdaqtrader.com/Trader.aspx?id=ClearlyErroneous#:~:text=The%20terms%20of%20a%20transaction,or%20identification%20of%20the%20security.).
|
1327
|
-
A trade break is final; once a trade is broken, it cannot be reinstated.
|
1328
|
-
|
1329
|
-
Firms that use the ITCH feed to create time---and---sales displays or calculate market statistics should be prepared
|
1330
|
-
to process the broken trade message. If a firm is only using the ITCH feed to build a book, however, it may ignore
|
1331
|
-
these messages as they have no impact on the current book.
|
1332
|
-
|
1333
|
-
Attributes:
|
1334
|
-
- match_number : The Nasdaq Match Number of the execution that was broken.
|
1335
|
-
This refers to a Match Number from a previously transmitted Order Executed Message,
|
1336
|
-
Order Executed With Price Message, or Trade Message.
|
1337
|
-
"""
|
1338
|
-
|
1339
|
-
message_type = b"B"
|
1340
|
-
description = "Broken Trade Message"
|
1341
|
-
message_format = "!HHHIQ"
|
1342
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1343
|
-
message_size = struct.calcsize(message_format) + 1
|
1344
|
-
|
1345
|
-
def __init__(self, message: bytes):
|
1346
|
-
(
|
1347
|
-
self.stock_locate,
|
1348
|
-
self.tracking_number,
|
1349
|
-
timestamp1,
|
1350
|
-
timestamp2,
|
1351
|
-
self.match_number,
|
1352
|
-
) = struct.unpack(self.message_format, message[1:])
|
1353
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1354
|
-
|
1355
|
-
def pack(self):
|
1356
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1357
|
-
message = struct.pack(
|
1358
|
-
self.message_pack_format,
|
1359
|
-
self.message_type,
|
1360
|
-
self.stock_locate,
|
1361
|
-
self.tracking_number,
|
1362
|
-
timestamp1,
|
1363
|
-
timestamp2,
|
1364
|
-
self.match_number,
|
1365
|
-
)
|
1366
|
-
return message
|
1367
|
-
|
1368
|
-
|
1369
|
-
class NOIIMessage(MarketMessage):
|
1370
|
-
"""
|
1371
|
-
- Nasdaq begins disseminating Net Order Imbalance Indicators (NOII) at 9:25 a.m. for the Opening Cross and 3:50 p.m. for the Closing Cross.
|
1372
|
-
- Between 9:25 and 9:28 a.m. and 3:50 and 3:55 p.m., Nasdaq disseminates the NOII information every 10 seconds.
|
1373
|
-
- Between 9:28 and 9:30 a.m. and 3:55 and 4:00 p.m., Nasdaq disseminates the NOII information every second.
|
1374
|
-
- For Nasdaq Halt, IPO and Pauses, NOII messages will be disseminated at 1 second intervals starting 1 second after quoting period starts/trading action is released.
|
1375
|
-
- For more information, please see the [FAQ on Opening and Closing Crosses](https://www.nasdaqtrader.com/content/productsservices/trading/crosses/openclose_faqs.pdf).
|
1376
|
-
- Nasdaq will also disseminate an Extended Trading Close (ETC) message from 4:00 p.m. to 4:05 p.m. at five second intervals.
|
1377
|
-
- For more information, please see the [FAQ on Extended Trading Close](https://www.nasdaqtrader.com/content/productsservices/trading/After-Hour-Cross-FAQ-Factsheet-NAM.pdf).
|
1378
|
-
|
1379
|
-
Attributes:
|
1380
|
-
- paired_shares : The total number of shares that are eligible to be matched at the Current Reference Price.
|
1381
|
-
- imbalance_shares : The number of shares not paired at the Current Reference Price.
|
1382
|
-
- imbalance_direction : The market side of the order imbalance:
|
1383
|
-
b"B": "buy imbalance"
|
1384
|
-
b"S": "sell imbalance"
|
1385
|
-
b"N": "no imbalance"
|
1386
|
-
b"O": "Insufficient orders to calculate"
|
1387
|
-
b"P": "Paused"
|
1388
|
-
|
1389
|
-
- stock : Stock symbol, right padded with spaces
|
1390
|
-
- far_price : A hypothetical auction---clearing price for cross orders only. Refer to Data Types for field processing notes.
|
1391
|
-
- near_price : A hypothetical auction-•-clearing price for cross orders as well as continuous orders. Refer to Data Types for field
|
1392
|
-
- current_reference_price : The price at which the NOII shares are being calculated. Refer to Data Types for field processing notes.
|
1393
|
-
- cross_type : The type of Nasdaq cross for which the NOII message is being generated:
|
1394
|
-
b"O": "Nasdaq Opening Cross"
|
1395
|
-
b"C": "Nasdaq Closing Cross"
|
1396
|
-
b"H": "Cross for IPO and halted / paused securities"
|
1397
|
-
b"A": "Extended Trading Close"
|
1398
|
-
|
1399
|
-
- variation_indicator : This field indicates the absolute value of the percentage of deviation of
|
1400
|
-
the Near Indicative Clearing Price to the nearest Current Reference Price, see ``itch.indicators.``
|
1401
|
-
"""
|
1402
|
-
|
1403
|
-
message_type = b"I"
|
1404
|
-
description = "NOII Message"
|
1405
|
-
message_format = "!HHHIQQc8sIIIcc"
|
1406
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1407
|
-
message_size = struct.calcsize(message_format) + 1
|
1408
|
-
|
1409
|
-
paired_shares: int
|
1410
|
-
imbalance_shares: bytes
|
1411
|
-
imbalance_direction: bytes
|
1412
|
-
stock: bytes
|
1413
|
-
far_price: float
|
1414
|
-
near_price: float
|
1415
|
-
current_reference_price: float
|
1416
|
-
cross_type: bytes
|
1417
|
-
variation_indicator: bytes
|
1418
|
-
|
1419
|
-
def __init__(self, message: bytes):
|
1420
|
-
(
|
1421
|
-
self.stock_locate,
|
1422
|
-
self.tracking_number,
|
1423
|
-
timestamp1,
|
1424
|
-
timestamp2,
|
1425
|
-
self.paired_shares,
|
1426
|
-
self.imbalance_shares,
|
1427
|
-
self.imbalance_direction,
|
1428
|
-
self.stock,
|
1429
|
-
self.far_price,
|
1430
|
-
self.near_price,
|
1431
|
-
self.current_reference_price,
|
1432
|
-
self.cross_type,
|
1433
|
-
self.variation_indicator,
|
1434
|
-
) = struct.unpack(self.message_format, message[1:])
|
1435
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1436
|
-
|
1437
|
-
def pack(self):
|
1438
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1439
|
-
message = struct.pack(
|
1440
|
-
self.message_pack_format,
|
1441
|
-
self.message_type,
|
1442
|
-
self.stock_locate,
|
1443
|
-
self.tracking_number,
|
1444
|
-
timestamp1,
|
1445
|
-
timestamp2,
|
1446
|
-
self.paired_shares,
|
1447
|
-
self.imbalance_shares,
|
1448
|
-
self.imbalance_direction,
|
1449
|
-
self.stock,
|
1450
|
-
self.far_price,
|
1451
|
-
self.near_price,
|
1452
|
-
self.current_reference_price,
|
1453
|
-
self.cross_type,
|
1454
|
-
self.variation_indicator,
|
1455
|
-
)
|
1456
|
-
return message
|
1457
|
-
|
1458
|
-
|
1459
|
-
class RetailPriceImprovementIndicator(MarketMessage):
|
1460
|
-
"""
|
1461
|
-
Identifies a retail interest indication of the Bid, Ask or both the Bid and Ask for Nasdaq-•-listed securities.
|
1462
|
-
|
1463
|
-
Attributes:
|
1464
|
-
- stock : Stock symbol, right padded with spaces
|
1465
|
-
- interest_flag :
|
1466
|
-
b"B": "RPI orders available on the buy side"
|
1467
|
-
b"S": "RPI orders available on the sell side"
|
1468
|
-
b"A": "RPI orders available on both sides (buy and sell)"
|
1469
|
-
b"N": "No RPI orders available "
|
1470
|
-
"""
|
1471
|
-
|
1472
|
-
message_type = b"N"
|
1473
|
-
description = "Retail Interest message"
|
1474
|
-
message_format = "!HHHI8sc"
|
1475
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1476
|
-
message_size = struct.calcsize(message_format) + 1
|
1477
|
-
|
1478
|
-
stock: bytes
|
1479
|
-
interest_flag: bytes
|
1480
|
-
|
1481
|
-
def __init__(self, message: bytes):
|
1482
|
-
(
|
1483
|
-
self.stock_locate,
|
1484
|
-
self.tracking_number,
|
1485
|
-
timestamp1,
|
1486
|
-
timestamp2,
|
1487
|
-
self.stock,
|
1488
|
-
self.interest_flag,
|
1489
|
-
) = struct.unpack(self.message_format, message[1:])
|
1490
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1491
|
-
|
1492
|
-
def pack(self):
|
1493
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1494
|
-
message = struct.pack(
|
1495
|
-
self.message_pack_format,
|
1496
|
-
self.message_type,
|
1497
|
-
self.stock_locate,
|
1498
|
-
self.tracking_number,
|
1499
|
-
timestamp1,
|
1500
|
-
timestamp2,
|
1501
|
-
self.stock,
|
1502
|
-
self.interest_flag,
|
1503
|
-
)
|
1504
|
-
return message
|
1505
|
-
|
1506
|
-
|
1507
|
-
class DLCRMessage(MarketMessage):
|
1508
|
-
"""
|
1509
|
-
The following message is disseminated only for Direct Listing with Capital Raise (DLCR) securities.
|
1510
|
-
Nasdaq begins disseminating messages once per second as soon as the DLCR volatility test has successfully passed.
|
1511
|
-
|
1512
|
-
Attributes:
|
1513
|
-
- stock : Stock symbol, right padded with spaces.
|
1514
|
-
- open_eligibility_status : Indicates if the security is eligible to be released for trading (b"N": Not Eligible, b"Y": Eligible)
|
1515
|
-
- minimum_allowable_price : 20% below Registration Statement Lower Price
|
1516
|
-
- maximum_allowable_price : 80% above Registration Statement Highest Price
|
1517
|
-
- near_execution_price : The current reference price when the DLCR volatility test has successfully passed
|
1518
|
-
- near_execution_time : The time at which the near execution price was set
|
1519
|
-
- lower_price_range_collar : Indicates the price of the Lower Auction Collar Threshold (10% below the Near Execution Price)
|
1520
|
-
- upper_price_range_collar : Indicates the price of the Upper Auction Collar Threshold (10% above the Near Execution Price)
|
1521
|
-
"""
|
1522
|
-
|
1523
|
-
message_type = b"O"
|
1524
|
-
description = "Direct Listing with Capital Raise Message"
|
1525
|
-
message_format = "!HHHI8scIIIQII"
|
1526
|
-
message_pack_format = "!" + "c" + message_format[1:]
|
1527
|
-
message_size = struct.calcsize(message_format) + 1
|
1528
|
-
|
1529
|
-
stock: bytes
|
1530
|
-
open_eligibility_status: bytes
|
1531
|
-
minimum_allowable_price: float
|
1532
|
-
maximum_allowable_price: float
|
1533
|
-
near_execution_price: float
|
1534
|
-
near_execution_time: int
|
1535
|
-
lower_price_range_collar: float
|
1536
|
-
upper_price_range_collar: float
|
1537
|
-
|
1538
|
-
def __init__(self, message: bytes):
|
1539
|
-
(
|
1540
|
-
self.stock_locate,
|
1541
|
-
self.tracking_number,
|
1542
|
-
timestamp1,
|
1543
|
-
timestamp2,
|
1544
|
-
self.stock,
|
1545
|
-
self.open_eligibility_status,
|
1546
|
-
self.minimum_allowable_price,
|
1547
|
-
self.maximum_allowable_price,
|
1548
|
-
self.near_execution_price,
|
1549
|
-
self.near_execution_time,
|
1550
|
-
self.lower_price_range_collar,
|
1551
|
-
self.upper_price_range_collar,
|
1552
|
-
) = struct.unpack(self.message_format, message[1:])
|
1553
|
-
self.set_timestamp(timestamp1, timestamp2)
|
1554
|
-
|
1555
|
-
def pack(self):
|
1556
|
-
(timestamp1, timestamp2) = self.split_timestamp()
|
1557
|
-
message = struct.pack(
|
1558
|
-
self.message_pack_format,
|
1559
|
-
self.message_type,
|
1560
|
-
self.stock_locate,
|
1561
|
-
self.tracking_number,
|
1562
|
-
timestamp1,
|
1563
|
-
timestamp2,
|
1564
|
-
self.stock,
|
1565
|
-
self.open_eligibility_status,
|
1566
|
-
self.minimum_allowable_price,
|
1567
|
-
self.maximum_allowable_price,
|
1568
|
-
self.near_execution_price,
|
1569
|
-
self.near_execution_time,
|
1570
|
-
self.lower_price_range_collar,
|
1571
|
-
self.upper_price_range_collar,
|
1572
|
-
)
|
1573
|
-
return message
|
1574
|
-
|
1575
|
-
|
1576
|
-
messages = {
|
1577
|
-
b"S": SystemEventMessage,
|
1578
|
-
b"R": StockDirectoryMessage,
|
1579
|
-
b"H": StockTradingActionMessage,
|
1580
|
-
b"Y": RegSHOMessage,
|
1581
|
-
b"L": MarketParticipantPositionMessage,
|
1582
|
-
b"V": MWCBDeclineLeveMessage,
|
1583
|
-
b"W": MWCBStatusMessage,
|
1584
|
-
b"K": IPOQuotingPeriodUpdateMessage,
|
1585
|
-
b"J": LULDAuctionCollarMessage,
|
1586
|
-
b"h": OperationalHaltMessage,
|
1587
|
-
b"A": AddOrderNoMPIAttributionMessage,
|
1588
|
-
b"F": AddOrderMPIDAttribution,
|
1589
|
-
b"E": OrderExecutedMessage,
|
1590
|
-
b"C": OrderExecutedWithPriceMessage,
|
1591
|
-
b"X": OrderCancelMessage,
|
1592
|
-
b"D": OrderDeleteMessage,
|
1593
|
-
b"U": OrderReplaceMessage,
|
1594
|
-
b"P": NonCrossTradeMessage,
|
1595
|
-
b"Q": CrossTradeMessage,
|
1596
|
-
b"B": BrokenTradeMessage,
|
1597
|
-
b"I": NOIIMessage,
|
1598
|
-
b"N": RetailPriceImprovementIndicator,
|
1599
|
-
b"O": DLCRMessage,
|
1600
|
-
}
|
1
|
+
import struct
|
2
|
+
from dataclasses import make_dataclass
|
3
|
+
from typing import Dict, List, Tuple, Type
|
4
|
+
|
5
|
+
MESSAGES = b"SAFECXDUBHRYPQINLVWKJhO"
|
6
|
+
|
7
|
+
|
8
|
+
class MarketMessage(object):
|
9
|
+
"""
|
10
|
+
The TotalView ITCH feed is composed of a series of messages that describe orders added to, removed from, and executed
|
11
|
+
on Nasdaq as well as disseminate Cross and Stock Directory information.
|
12
|
+
This is the base class for all message type.
|
13
|
+
|
14
|
+
All Message have the following attributes:
|
15
|
+
- message_type: A single letter that identify the message
|
16
|
+
- description: Describe the message
|
17
|
+
- message_format: string format using to unpack the message
|
18
|
+
- message_pack_format: string format using to pack the message
|
19
|
+
- message_size: The size in bytes of the message
|
20
|
+
- timestamp: Time at which the message was generated (Nanoseconds past midnight)
|
21
|
+
- stock_locate: Locate code identifying the security
|
22
|
+
- tracking_number: Nasdaq internal tracking number
|
23
|
+
"""
|
24
|
+
|
25
|
+
message_type: bytes
|
26
|
+
description: str
|
27
|
+
message_format: str
|
28
|
+
message_pack_format: str
|
29
|
+
message_size: int
|
30
|
+
timestamp: int
|
31
|
+
stock_locate: int
|
32
|
+
tracking_number: int
|
33
|
+
price_precision: int = 4
|
34
|
+
|
35
|
+
def __repr__(self):
|
36
|
+
return repr(self.decode())
|
37
|
+
|
38
|
+
def set_timestamp(self, ts1: int, ts2: int):
|
39
|
+
"""
|
40
|
+
Reconstructs a 6-byte timestamp (48 bits) from two 32-bit unsigned integers.
|
41
|
+
|
42
|
+
This method combines the high 32 bits (`ts1`) and low 32 bits (`ts2`) into a single
|
43
|
+
64-bit integer to reconstruct the complete timestamp. For more details on how the
|
44
|
+
timestamp is processed and split, refer to the `split_timestamp()` method.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
ts1 (int): The high-order 32 bits (most significant 32 bits).
|
48
|
+
ts2 (int): The low-order 32 bits (least significant 32 bits).
|
49
|
+
"""
|
50
|
+
self.timestamp = ts2 | (ts1 << 32)
|
51
|
+
|
52
|
+
def split_timestamp(self) -> Tuple[int, int]:
|
53
|
+
"""
|
54
|
+
Splits a 6-byte timestamp (48 bits) into two 32-bit unsigned integers.
|
55
|
+
|
56
|
+
The ITCH protocol defines the timestamp as a **6-byte (48-bit) unsigned integer**.
|
57
|
+
Python's native `struct` module does not support 6-byte integers, so we manage
|
58
|
+
the timestamp as a 64-bit integer for simplicity. This method splits it into two
|
59
|
+
32-bit integers for easier handling, packing, and unpacking.
|
60
|
+
|
61
|
+
Process:
|
62
|
+
1. The timestamp is a 6-byte unsigned integer that we treat as a 64-bit integer
|
63
|
+
for ease of use (since Python handles 64-bit integers natively).
|
64
|
+
2. We extract the **high 32 bits** (most significant bits) and the **low 32 bits**
|
65
|
+
(least significant bits) using bitwise operations.
|
66
|
+
|
67
|
+
- The high 32 bits are extracted by shifting the 64-bit timestamp 32 bits to the right
|
68
|
+
(`self.timestamp >> 32`).
|
69
|
+
- The low 32 bits are isolated by subtracting the high 32 bits (shifted back to the left)
|
70
|
+
from the original 64-bit value (`self.timestamp - (ts1 << 32)`).
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
Tuple[int, int]: A tuple containing two integers:
|
74
|
+
- `ts1`: The high 32 bits (most significant 32 bits)
|
75
|
+
- `ts2`: The low 32 bits (least significant 32 bits)
|
76
|
+
|
77
|
+
Example:
|
78
|
+
If `self.timestamp = 0x123456789ABC` (in hex, which is 6 bytes long),
|
79
|
+
then:
|
80
|
+
ts1 = 0x12345678 # high 32 bits
|
81
|
+
ts2 = 0x9ABCDEF0 # low 32 bits
|
82
|
+
"""
|
83
|
+
ts1 = self.timestamp >> 32
|
84
|
+
ts2 = self.timestamp - (ts1 << 32)
|
85
|
+
return (ts1, ts2)
|
86
|
+
ts1 = self.timestamp >> 32
|
87
|
+
ts2 = self.timestamp - (ts1 << 32)
|
88
|
+
return (ts1, ts2)
|
89
|
+
|
90
|
+
def decode_price(self, price_attr: str) -> float:
|
91
|
+
precision = getattr(self, "price_precision")
|
92
|
+
price = getattr(self, price_attr)
|
93
|
+
if callable(price):
|
94
|
+
raise ValueError(f"Please check the price attribute for {price_attr}")
|
95
|
+
return price / (10**precision)
|
96
|
+
|
97
|
+
def decode(self, prefix: str = ""):
|
98
|
+
"""
|
99
|
+
Converts the message into a human-readable dataclass with all built-in fields.
|
100
|
+
- All bytes fields are converted to ASCII strings.
|
101
|
+
- Trailing spaces are stripped from ASCII fields (left-justified padded).
|
102
|
+
|
103
|
+
Args:
|
104
|
+
prefix : The prefix to the dataclass create from the Original message
|
105
|
+
"""
|
106
|
+
builtin_attrs = {}
|
107
|
+
for attr in dir(self):
|
108
|
+
if attr.startswith("__"):
|
109
|
+
continue
|
110
|
+
value = getattr(self, attr)
|
111
|
+
if "price" in attr and attr != "price_precision" and attr != "decode_price":
|
112
|
+
value = self.decode_price(attr)
|
113
|
+
if callable(value):
|
114
|
+
continue
|
115
|
+
if isinstance(value, (int, float, str, bool)):
|
116
|
+
builtin_attrs[attr] = value
|
117
|
+
elif isinstance(value, bytes):
|
118
|
+
try:
|
119
|
+
builtin_attrs[attr] = value.decode(encoding="ascii").rstrip()
|
120
|
+
except UnicodeDecodeError:
|
121
|
+
builtin_attrs[attr] = value
|
122
|
+
fields = [(k, type(v)) for k, v in builtin_attrs.items()]
|
123
|
+
|
124
|
+
decoded_class_name = f"{prefix}{self.__class__.__name__}"
|
125
|
+
DecodedMessageClass = make_dataclass(decoded_class_name, fields)
|
126
|
+
return DecodedMessageClass(**builtin_attrs)
|
127
|
+
|
128
|
+
def get_attributes(self, call_able=False) -> List[str]:
|
129
|
+
attrs = [attr for attr in dir(self) if not attr.startswith("__")]
|
130
|
+
if call_able:
|
131
|
+
return [a for a in attrs if callable(getattr(self, a))]
|
132
|
+
else:
|
133
|
+
return [a for a in attrs if not callable(getattr(self, a))]
|
134
|
+
|
135
|
+
|
136
|
+
class SystemEventMessage(MarketMessage):
|
137
|
+
"""
|
138
|
+
The system event message type is used to signal a market or data feed handler event.
|
139
|
+
|
140
|
+
Attributes:
|
141
|
+
- event_code: see ``itch.indicators.SYSTEM_EVENT_CODES``
|
142
|
+
"""
|
143
|
+
|
144
|
+
message_type = b"S"
|
145
|
+
description = "System Event Message"
|
146
|
+
message_format = "!HHHIc"
|
147
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
148
|
+
message_size = struct.calcsize(message_format) + 1
|
149
|
+
|
150
|
+
event_code: bytes
|
151
|
+
|
152
|
+
def __init__(self, message: bytes):
|
153
|
+
(
|
154
|
+
self.stock_locate,
|
155
|
+
self.tracking_number,
|
156
|
+
timestamp1,
|
157
|
+
timestamp2,
|
158
|
+
self.event_code,
|
159
|
+
) = struct.unpack(self.message_format, message[1:])
|
160
|
+
self.set_timestamp(timestamp1, timestamp2)
|
161
|
+
|
162
|
+
def pack(self):
|
163
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
164
|
+
message = struct.pack(
|
165
|
+
self.message_pack_format,
|
166
|
+
self.message_type,
|
167
|
+
self.stock_locate,
|
168
|
+
self.tracking_number,
|
169
|
+
timestamp1,
|
170
|
+
timestamp2,
|
171
|
+
self.event_code,
|
172
|
+
)
|
173
|
+
return message
|
174
|
+
|
175
|
+
|
176
|
+
class StockDirectoryMessage(MarketMessage):
|
177
|
+
"""
|
178
|
+
At the start of each trading day, Nasdaq disseminates stock directory messages for all active symbols in the Nasdaq
|
179
|
+
execution system.
|
180
|
+
|
181
|
+
Market data redistributors should process this message to populate the Financial Status Indicator (required displayfield) and
|
182
|
+
the Market Category (recommended display field) for Nasdaq listed issues
|
183
|
+
|
184
|
+
Attributes:
|
185
|
+
- stock : Denotes the security symbol for the issue in the Nasdaq execution system.
|
186
|
+
|
187
|
+
- market_category : see ``itch.indicators.MARKET_CATEGORY``.
|
188
|
+
|
189
|
+
- financial_status_indicator : see ``itch.indicators.FINANCIAL_STATUS_INDICATOR``.
|
190
|
+
|
191
|
+
- round_lot_size : Denotes the number of shares that represent a round lot for the issue
|
192
|
+
|
193
|
+
- round_lots_only : Indicates if Nasdaq system limits order entry for issue
|
194
|
+
(``b"Y": "Only round lots", b"N": "Odd and Mixed lots"``)
|
195
|
+
|
196
|
+
- issue_classification : Identifies the security class for the issue as assigned by Nasdaq.
|
197
|
+
see ``itch.indicators.ISSUE_CLASSIFICATION_VALUES``.
|
198
|
+
|
199
|
+
- issue_sub_type : Identifies the security sub-•-type for the issue as assigned by Nasdaq.
|
200
|
+
See ``itch.indicators.ISSUE_SUB_TYPE_VALUES``
|
201
|
+
|
202
|
+
- authenticity : Denotes if an issue or quoting participant record is set-•-up in Nasdaq systems in a live/production, test, or demo state.
|
203
|
+
Please note that firms should only show live issues and quoting participants on public quotation displays.
|
204
|
+
(``b"P"=Live/Production, b"T"=Test``)
|
205
|
+
|
206
|
+
- short_sale_threshold_indicator : Indicates if a security is subject to mandatory close-•-out of short sales under SEC Rule 203(b)(3):
|
207
|
+
b"Y": "Issue is restricted under SEC Rule 203(b)(3)"
|
208
|
+
b"N": "Issue is not restricted"
|
209
|
+
b" ": "Threshold Indicator not available"
|
210
|
+
|
211
|
+
- ipo_flag : Indicates if the Nasdaq security is set up for IPO release. This field is intended to help Nasdaq market
|
212
|
+
participant firms comply with FINRA Rule 5131(b):
|
213
|
+
b"Y": "Nasdaq listed instrument is set up as a new IPO security"
|
214
|
+
b"N": "Nasdaq listed instrument is not set up as a new IPO security"
|
215
|
+
b" ": "Not available"
|
216
|
+
|
217
|
+
- luld_ref : Indicates which Limit Up / Limit Down price band calculationparameter is to be used for the instrument.
|
218
|
+
Refer to [LULD Rule ](https://www.nasdaqtrader.com/content/MarketRegulation/LULD_FAQ.pdf) for details:
|
219
|
+
b"1": "Tier 1 NMS Stocks and select ETPs"
|
220
|
+
b"2": "Tier 2 NMS Stocks"
|
221
|
+
b" ": "Not available"
|
222
|
+
|
223
|
+
- etp_flag : Indicates whether the security is an exchange traded product (ETP):
|
224
|
+
b"Y": "Tier 1 NMS Stocks and select ETPs"
|
225
|
+
b"N": "Instrument is not an ETP"
|
226
|
+
b" ": "Not available"
|
227
|
+
|
228
|
+
- etp_leverage_factor : Tracks the integral relationship of the ETP to the underlying index.
|
229
|
+
Example: If the underlying Index increases by a value of 1 and the ETP's Leverage factor is 3, indicates the ETF will increase/decrease (see Inverse) by 3.
|
230
|
+
Leverage Factor is rounded to the nearest integer below, e.g. leverage factor 1 would represent leverage factors of 1 to 1.99.
|
231
|
+
This field is used for LULD Tier I price band calculation purpose.
|
232
|
+
|
233
|
+
- inverse_indicator : Indicates the directional relationship between the ETP and Underlying index:
|
234
|
+
b"Y": "ETP is an Inverse ETP "
|
235
|
+
b"N": "ETP is not an Inverse ETP "
|
236
|
+
Example: An ETP Leverage Factor of 3 and an Inverse value of "Y" indicates the ETP will decrease by a value of 3.
|
237
|
+
|
238
|
+
"""
|
239
|
+
|
240
|
+
message_type = b"R"
|
241
|
+
description = "Stock Directory Message"
|
242
|
+
message_format = "!HHHI8sccIcc2scccccIc"
|
243
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
244
|
+
message_size = struct.calcsize(message_format) + 1
|
245
|
+
|
246
|
+
stock: bytes
|
247
|
+
market_category: bytes
|
248
|
+
financial_status_indicator: bytes
|
249
|
+
round_lot_size: int
|
250
|
+
round_lots_only: bytes
|
251
|
+
issue_classification: bytes
|
252
|
+
issue_sub_type: bytes
|
253
|
+
authenticity: bytes
|
254
|
+
short_sale_threshold_indicator: bytes
|
255
|
+
ipo_flag: bytes
|
256
|
+
luld_ref: bytes
|
257
|
+
etp_flag: bytes
|
258
|
+
etp_leverage_factor: int
|
259
|
+
inverse_indicator: bytes
|
260
|
+
|
261
|
+
def __init__(self, message: bytes):
|
262
|
+
(
|
263
|
+
self.stock_locate,
|
264
|
+
self.tracking_number,
|
265
|
+
timestamp1,
|
266
|
+
timestamp2,
|
267
|
+
self.stock,
|
268
|
+
self.market_category,
|
269
|
+
self.financial_status_indicator,
|
270
|
+
self.round_lot_size,
|
271
|
+
self.round_lots_only,
|
272
|
+
self.issue_classification,
|
273
|
+
self.issue_sub_type,
|
274
|
+
self.authenticity,
|
275
|
+
self.short_sale_threshold_indicator,
|
276
|
+
self.ipo_flag,
|
277
|
+
self.luld_ref,
|
278
|
+
self.etp_flag,
|
279
|
+
self.etp_leverage_factor,
|
280
|
+
self.inverse_indicator,
|
281
|
+
) = struct.unpack(self.message_format, message[1:])
|
282
|
+
self.set_timestamp(timestamp1, timestamp2)
|
283
|
+
|
284
|
+
def pack(self):
|
285
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
286
|
+
message = struct.pack(
|
287
|
+
self.message_pack_format,
|
288
|
+
self.message_type,
|
289
|
+
self.stock_locate,
|
290
|
+
self.tracking_number,
|
291
|
+
timestamp1,
|
292
|
+
timestamp2,
|
293
|
+
self.stock,
|
294
|
+
self.market_category,
|
295
|
+
self.financial_status_indicator,
|
296
|
+
self.round_lot_size,
|
297
|
+
self.round_lots_only,
|
298
|
+
self.issue_classification,
|
299
|
+
self.issue_sub_type,
|
300
|
+
self.authenticity,
|
301
|
+
self.short_sale_threshold_indicator,
|
302
|
+
self.ipo_flag,
|
303
|
+
self.luld_ref,
|
304
|
+
self.etp_flag,
|
305
|
+
self.etp_leverage_factor,
|
306
|
+
self.inverse_indicator,
|
307
|
+
)
|
308
|
+
return message
|
309
|
+
|
310
|
+
|
311
|
+
class StockTradingActionMessage(MarketMessage):
|
312
|
+
"""
|
313
|
+
Nasdaq uses this administrative message to indicate the current trading status of a security to the trading
|
314
|
+
community.
|
315
|
+
|
316
|
+
Prior to the start of system hours, Nasdaq will send out a Trading Action spin. In the spin, Nasdaq will send out a
|
317
|
+
Stock Trading Action message with the “T” (Trading Resumption) for all Nasdaq--- and other exchange-•-listed
|
318
|
+
securities that are eligible for trading at the start of the system hours. If a security is absent from the pre-•-
|
319
|
+
opening Trading Action spin, firms should assume that the security is being treated as halted in the Nasdaq
|
320
|
+
platform at the start of the system hours. Please note that securities may be halted in the Nasdaq system for
|
321
|
+
regulatory or operational reasons.
|
322
|
+
|
323
|
+
After the start of system hours, Nasdaq will use the Trading Action message to relay changes in trading status for an
|
324
|
+
individual security. Messages will be sent when a stock is:
|
325
|
+
- Halted
|
326
|
+
- Paused*
|
327
|
+
- Released for quotation
|
328
|
+
- Released for trading
|
329
|
+
|
330
|
+
The paused status will be disseminated for NASDAQ---listed securities only. Trading pauses on non---NASDAQ listed securities
|
331
|
+
will be treated simply as a halt.
|
332
|
+
|
333
|
+
Attributes:
|
334
|
+
- stock : Stock symbol, right padded with spaces
|
335
|
+
- trading_state : Indicates the current trading state for the stock, see `itch.indicators.TRADING_STATES`
|
336
|
+
- reserved : Reserved
|
337
|
+
- reason : Trading Action reason, see `itch.indicators.TRADING_ACTION_REASON_CODES`
|
338
|
+
"""
|
339
|
+
|
340
|
+
message_type = b"H"
|
341
|
+
description = "Stock Trading Action Message"
|
342
|
+
message_format = "!HHHI8scc4s"
|
343
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
344
|
+
message_size = struct.calcsize(message_format) + 1
|
345
|
+
|
346
|
+
stock: bytes
|
347
|
+
trading_state: bytes
|
348
|
+
reserved: bytes
|
349
|
+
reason: bytes
|
350
|
+
|
351
|
+
def __init__(self, message: bytes):
|
352
|
+
(
|
353
|
+
self.stock_locate,
|
354
|
+
self.tracking_number,
|
355
|
+
timestamp1,
|
356
|
+
timestamp2,
|
357
|
+
self.stock,
|
358
|
+
self.trading_state,
|
359
|
+
self.reserved,
|
360
|
+
self.reason,
|
361
|
+
) = struct.unpack(self.message_format, message[1:])
|
362
|
+
self.set_timestamp(timestamp1, timestamp2)
|
363
|
+
|
364
|
+
def pack(self):
|
365
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
366
|
+
message = struct.pack(
|
367
|
+
self.message_pack_format,
|
368
|
+
self.message_type,
|
369
|
+
self.stock_locate,
|
370
|
+
self.tracking_number,
|
371
|
+
timestamp1,
|
372
|
+
timestamp2,
|
373
|
+
self.stock,
|
374
|
+
self.trading_state,
|
375
|
+
self.reserved,
|
376
|
+
self.reason,
|
377
|
+
)
|
378
|
+
return message
|
379
|
+
|
380
|
+
|
381
|
+
class RegSHOMessage(MarketMessage):
|
382
|
+
"""
|
383
|
+
In February 2011, the Securities and Exchange Commission (SEC) implemented changes to Rule 201 of the
|
384
|
+
Regulation SHO (Reg SHO). For details, please refer to [SEC Release Number 34-61595](https://www.sec.gov/files/rules/final/2010/34-61595.pdf).
|
385
|
+
In association with the Reg SHO rule change, Nasdaq will introduce the following Reg SHO Short Sale Price Test Restricted
|
386
|
+
Indicator message format.
|
387
|
+
|
388
|
+
For Nasdaq-listed issues, Nasdaq supports a full pre-•-opening spin of Reg SHO Short Sale Price Test Restricted
|
389
|
+
Indicator messages indicating the Rule 201 status for all active issues. Nasdaq also sends the Reg SHO
|
390
|
+
Short Sale Price Test Restricted Indicator message in the event of an intraday status change.
|
391
|
+
|
392
|
+
For other exchange-listed issues, Nasdaq relays the Reg SHO Short Sale Price Test Restricted Indicator
|
393
|
+
message when it receives an update from the primary listing exchange.
|
394
|
+
|
395
|
+
Nasdaq processes orders based on the most Reg SHO Restriction status value.
|
396
|
+
|
397
|
+
Attributes:
|
398
|
+
- stock : Stock symbol, right padded with spaces
|
399
|
+
- reg_sho_action : Denotes the Reg SHO Short Sale Price Test Restriction status for the issue at the time of the message dissemination:
|
400
|
+
b"0": "No price test in place"
|
401
|
+
b"1": "Reg SHO Short Sale Price Test Restriction in effect due to an intra-day price drop in security"
|
402
|
+
b"2": " Reg SHO Short Sale Price Test Restriction remains in effect"
|
403
|
+
"""
|
404
|
+
|
405
|
+
message_type = b"Y"
|
406
|
+
description = "Reg SHO Short Sale Price Test Restricted Indicator"
|
407
|
+
message_format = "!HHHI8sc"
|
408
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
409
|
+
message_size = struct.calcsize(message_format) + 1
|
410
|
+
|
411
|
+
stock: bytes
|
412
|
+
reg_sho_action: bytes
|
413
|
+
|
414
|
+
def __init__(self, message: bytes):
|
415
|
+
(
|
416
|
+
self.stock_locate,
|
417
|
+
self.tracking_number,
|
418
|
+
timestamp1,
|
419
|
+
timestamp2,
|
420
|
+
self.stock,
|
421
|
+
self.reg_sho_action,
|
422
|
+
) = struct.unpack(self.message_format, message[1:])
|
423
|
+
self.set_timestamp(timestamp1, timestamp2)
|
424
|
+
|
425
|
+
def pack(self):
|
426
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
427
|
+
message = struct.pack(
|
428
|
+
self.message_pack_format,
|
429
|
+
self.message_type,
|
430
|
+
self.stock_locate,
|
431
|
+
self.tracking_number,
|
432
|
+
timestamp1,
|
433
|
+
timestamp2,
|
434
|
+
self.stock,
|
435
|
+
self.reg_sho_action,
|
436
|
+
)
|
437
|
+
return message
|
438
|
+
|
439
|
+
|
440
|
+
class MarketParticipantPositionMessage(MarketMessage):
|
441
|
+
"""
|
442
|
+
At the start of each trading day, Nasdaq disseminates a spin of market participant position messages. The
|
443
|
+
message provides the Primary Market Maker status, Market Maker mode and Market Participant state for
|
444
|
+
each Nasdaq market participant firm registered in an issue. Market participant firms may use these fields to
|
445
|
+
comply with certain marketplace rules.
|
446
|
+
|
447
|
+
Throughout the day, Nasdaq will send out this message only if Nasdaq Operations changes the status of a
|
448
|
+
market participant firm in an issue.
|
449
|
+
|
450
|
+
Attributes:
|
451
|
+
- mpid : Denotes the market participant identifier for which the position message is being generated
|
452
|
+
- stock : Stock symbol, right padded with spaces
|
453
|
+
- primary_market_maker : Indicates if the market participant firm qualifies as a Primary Market Maker in accordance with Nasdaq marketplace rules
|
454
|
+
see ``itch.indicators.PRIMARY_MARKET_MAKER``
|
455
|
+
- market_maker_mode : Indicates the quoting participant's registration status in relation to SEC Rules 101 and 104 of Regulation M
|
456
|
+
see ``itch.indicators.MARKET_MAKER_MODE``
|
457
|
+
- market_participant_state : Indicates the market participant's current registration status in the issue
|
458
|
+
see ``itch.indicators.MARKET_PARTICIPANT_STATE``
|
459
|
+
"""
|
460
|
+
|
461
|
+
message_type = b"L"
|
462
|
+
description = "Market Participant Position message"
|
463
|
+
message_format = "!HHHI4s8sccc"
|
464
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
465
|
+
message_size = struct.calcsize(message_format) + 1
|
466
|
+
|
467
|
+
mpid: bytes
|
468
|
+
stock: bytes
|
469
|
+
primary_market_maker: bytes
|
470
|
+
market_maker_mode: bytes
|
471
|
+
market_participant_state: bytes
|
472
|
+
|
473
|
+
def __init__(self, message: bytes):
|
474
|
+
(
|
475
|
+
self.stock_locate,
|
476
|
+
self.tracking_number,
|
477
|
+
timestamp1,
|
478
|
+
timestamp2,
|
479
|
+
self.mpid,
|
480
|
+
self.stock,
|
481
|
+
self.primary_market_maker,
|
482
|
+
self.market_maker_mode,
|
483
|
+
self.market_participant_state,
|
484
|
+
) = struct.unpack(self.message_format, message[1:])
|
485
|
+
self.set_timestamp(timestamp1, timestamp2)
|
486
|
+
|
487
|
+
def pack(self):
|
488
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
489
|
+
message = struct.pack(
|
490
|
+
self.message_pack_format,
|
491
|
+
self.message_type,
|
492
|
+
self.stock_locate,
|
493
|
+
self.tracking_number,
|
494
|
+
timestamp1,
|
495
|
+
timestamp2,
|
496
|
+
self.mpid,
|
497
|
+
self.stock,
|
498
|
+
self.primary_market_maker,
|
499
|
+
self.market_maker_mode,
|
500
|
+
self.market_participant_state,
|
501
|
+
)
|
502
|
+
return message
|
503
|
+
|
504
|
+
|
505
|
+
class MWCBDeclineLeveMessage(MarketMessage):
|
506
|
+
"""
|
507
|
+
Informs data recipients what the daily Market-Wide Circuit Breaker (MWCB)
|
508
|
+
breach points are set to for the current trading day.
|
509
|
+
|
510
|
+
Attributes:
|
511
|
+
- level1_price : Denotes the MWCB Level 1 Value.
|
512
|
+
- level2_price : Denotes the MWCB Level 2 Value.
|
513
|
+
- level3_price : Denotes the MWCB Level 3 Value.
|
514
|
+
"""
|
515
|
+
|
516
|
+
message_type = b"V"
|
517
|
+
description = "Market wide circuit breaker Decline Level Message"
|
518
|
+
message_format = "!HHHIQQQ"
|
519
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
520
|
+
message_size = struct.calcsize(message_format) + 1
|
521
|
+
price_precision = 8
|
522
|
+
level1_price: float
|
523
|
+
level2_price: float
|
524
|
+
level3_price: float
|
525
|
+
|
526
|
+
def __init__(self, message: bytes):
|
527
|
+
(
|
528
|
+
self.stock_locate,
|
529
|
+
self.tracking_number,
|
530
|
+
timestamp1,
|
531
|
+
timestamp2,
|
532
|
+
self.level1_price,
|
533
|
+
self.level2_price,
|
534
|
+
self.level3_price,
|
535
|
+
) = struct.unpack(self.message_format, message[1:])
|
536
|
+
self.set_timestamp(timestamp1, timestamp2)
|
537
|
+
|
538
|
+
def pack(self):
|
539
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
540
|
+
message = struct.pack(
|
541
|
+
self.message_pack_format,
|
542
|
+
self.message_type,
|
543
|
+
self.stock_locate,
|
544
|
+
self.tracking_number,
|
545
|
+
timestamp1,
|
546
|
+
timestamp2,
|
547
|
+
self.level1_price,
|
548
|
+
self.level2_price,
|
549
|
+
self.level3_price,
|
550
|
+
)
|
551
|
+
return message
|
552
|
+
|
553
|
+
|
554
|
+
class MWCBStatusMessage(MarketMessage):
|
555
|
+
"""
|
556
|
+
Informs data recipients when a MWCB has breached one of the established levels
|
557
|
+
|
558
|
+
Attributes:
|
559
|
+
- breached_level : Denotes the MWCB Level that was breached:
|
560
|
+
b"1" = Level 1
|
561
|
+
b"2" = Level 2
|
562
|
+
b"3" = Level 3
|
563
|
+
"""
|
564
|
+
|
565
|
+
message_type = b"W"
|
566
|
+
description = "Market-Wide Circuit Breaker Status message"
|
567
|
+
message_format = "!HHHIc"
|
568
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
569
|
+
message_size = struct.calcsize(message_format) + 1
|
570
|
+
|
571
|
+
breached_level: bytes
|
572
|
+
|
573
|
+
def __init__(self, message: bytes):
|
574
|
+
(
|
575
|
+
self.stock_locate,
|
576
|
+
self.tracking_number,
|
577
|
+
timestamp1,
|
578
|
+
timestamp2,
|
579
|
+
self.breached_level,
|
580
|
+
) = struct.unpack(self.message_format, message[1:])
|
581
|
+
self.set_timestamp(timestamp1, timestamp2)
|
582
|
+
|
583
|
+
def pack(self):
|
584
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
585
|
+
message = struct.pack(
|
586
|
+
self.message_pack_format,
|
587
|
+
self.message_type,
|
588
|
+
self.stock_locate,
|
589
|
+
self.tracking_number,
|
590
|
+
timestamp1,
|
591
|
+
timestamp2,
|
592
|
+
self.breached_level,
|
593
|
+
)
|
594
|
+
return message
|
595
|
+
|
596
|
+
|
597
|
+
class IPOQuotingPeriodUpdateMessage(MarketMessage):
|
598
|
+
"""
|
599
|
+
Indicates the anticipated IPO quotation release time of a security.
|
600
|
+
|
601
|
+
Attributes:
|
602
|
+
- stock : Stock symbol, right padded with spaces
|
603
|
+
|
604
|
+
- ipo_release_time : Denotes the IPO release time, in seconds since midnight, for quotation to the nearest second.
|
605
|
+
NOTE: If the quotation period is being canceled/postponed, we should state that:
|
606
|
+
1. IPO Quotation Time will be set to 0
|
607
|
+
2. 2. IPO Price will be set to 0
|
608
|
+
|
609
|
+
- ipo_release_qualifier :
|
610
|
+
b"A": "Anticipated Quotation Release Time"
|
611
|
+
b"C": " IPO Release Canceled/Postponed"
|
612
|
+
b"A" value would be used when Nasdaq Market Operations initially enters the IPO instrument for release
|
613
|
+
b"C" value would be sued when Nasdaq Market Operations cancels or postpones the release of the new IPO instrument
|
614
|
+
|
615
|
+
- ipo_price : Denotes the IPO Price to be used for intraday net change calculations Prices are given in decimal format with 6 whole number
|
616
|
+
places followed by 4 decimal digits. The whole number portion is padded on the left with spaces; the decimal portion is padded on the right with zeroes. The decimal point is
|
617
|
+
implied by position, it does not appear inside the price field
|
618
|
+
"""
|
619
|
+
|
620
|
+
message_type = b"K"
|
621
|
+
description = "IPO Quoting Period Update Message"
|
622
|
+
message_format = "!HHHI8sIcI"
|
623
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
624
|
+
message_size = struct.calcsize(message_format) + 1
|
625
|
+
|
626
|
+
stock: bytes
|
627
|
+
ipo_release_time: int
|
628
|
+
ipo_release_qualifier: bytes
|
629
|
+
ipo_price: float
|
630
|
+
|
631
|
+
def __init__(self, message: bytes):
|
632
|
+
(
|
633
|
+
self.stock_locate,
|
634
|
+
self.tracking_number,
|
635
|
+
timestamp1,
|
636
|
+
timestamp2,
|
637
|
+
self.stock,
|
638
|
+
self.ipo_release_time,
|
639
|
+
self.ipo_release_qualifier,
|
640
|
+
self.ipo_price,
|
641
|
+
) = struct.unpack(self.message_format, message[1:])
|
642
|
+
self.set_timestamp(timestamp1, timestamp2)
|
643
|
+
|
644
|
+
def pack(self):
|
645
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
646
|
+
message = struct.pack(
|
647
|
+
self.message_pack_format,
|
648
|
+
self.message_type,
|
649
|
+
self.stock_locate,
|
650
|
+
self.tracking_number,
|
651
|
+
timestamp1,
|
652
|
+
timestamp2,
|
653
|
+
self.stock,
|
654
|
+
self.ipo_release_time,
|
655
|
+
self.ipo_release_qualifier,
|
656
|
+
self.ipo_price,
|
657
|
+
)
|
658
|
+
return message
|
659
|
+
|
660
|
+
|
661
|
+
class LULDAuctionCollarMessage(MarketMessage):
|
662
|
+
"""
|
663
|
+
Indicates the auction collar thresholds within which a paused security can reopen following a LULD Trading Pause.
|
664
|
+
Stock 11 8 Alpha Stock symbol, right padded with spaces
|
665
|
+
|
666
|
+
Attributes:
|
667
|
+
- stock : Stock symbol, right padded with spaces
|
668
|
+
- auction_collar_reference_price : Reference price used to set the Auction Collars
|
669
|
+
- upper_auction_collar_price : Indicates the price of the Upper Auction Collar Threshold
|
670
|
+
- lower_auctiin_collar_price : Indicates the price of the Lower Auction Collar Threshold
|
671
|
+
- auction_collar_extention : Indicates the number of the extensions to the Reopening Auction
|
672
|
+
"""
|
673
|
+
|
674
|
+
message_type = b"J"
|
675
|
+
description = "LULD Auction Collar"
|
676
|
+
message_format = "!HHHI8sIIII"
|
677
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
678
|
+
message_size = struct.calcsize(message_format) + 1
|
679
|
+
|
680
|
+
stock: bytes
|
681
|
+
auction_collar_reference_price: float
|
682
|
+
upper_auction_collar_price: float
|
683
|
+
lower_auction_collar_price: float
|
684
|
+
auction_collar_extention: int
|
685
|
+
|
686
|
+
def __init__(self, message: bytes):
|
687
|
+
(
|
688
|
+
self.stock_locate,
|
689
|
+
self.tracking_number,
|
690
|
+
timestamp1,
|
691
|
+
timestamp2,
|
692
|
+
self.stock,
|
693
|
+
self.auction_collar_reference_price,
|
694
|
+
self.upper_auction_collar_price,
|
695
|
+
self.lower_auction_collar_price,
|
696
|
+
self.auction_collar_extention,
|
697
|
+
) = struct.unpack(self.message_format, message[1:])
|
698
|
+
self.set_timestamp(timestamp1, timestamp2)
|
699
|
+
|
700
|
+
def pack(self):
|
701
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
702
|
+
message = struct.pack(
|
703
|
+
self.message_pack_format,
|
704
|
+
self.message_type,
|
705
|
+
self.stock_locate,
|
706
|
+
self.tracking_number,
|
707
|
+
timestamp1,
|
708
|
+
timestamp2,
|
709
|
+
self.stock,
|
710
|
+
self.auction_collar_reference_price,
|
711
|
+
self.upper_auction_collar_price,
|
712
|
+
self.lower_auction_collar_price,
|
713
|
+
self.auction_collar_extention,
|
714
|
+
)
|
715
|
+
return message
|
716
|
+
|
717
|
+
|
718
|
+
class OperationalHaltMessage(MarketMessage):
|
719
|
+
"""
|
720
|
+
The Exchange uses this message to indicate the current Operational Status of a security to the trading
|
721
|
+
community. An Operational Halt means that there has been an interruption of service on the identified
|
722
|
+
security impacting only the designated Market Center. These Halts differ from the “Stock Trading
|
723
|
+
Action” message types since an Operational Halt is specific to the exchange for which it is declared, and
|
724
|
+
does not interrupt the ability of the trading community to trade the identified instrument on any other
|
725
|
+
marketplace.
|
726
|
+
|
727
|
+
Nasdaq uses this administrative message to indicate the current trading status of the three market centers
|
728
|
+
operated by Nasdaq.
|
729
|
+
|
730
|
+
Attributes:
|
731
|
+
- stock : Denotes the security symbol for the issue in Nasdaq execution system
|
732
|
+
- market_code :
|
733
|
+
b"Q": "Nasdaq"
|
734
|
+
b"B": "BX"
|
735
|
+
b"X": "PSX"
|
736
|
+
|
737
|
+
- operational_halt_action :
|
738
|
+
b"H": "Operationally Halted on the identified Market"
|
739
|
+
b"T": "Operational Halt has been lifted and Trading resumed "
|
740
|
+
|
741
|
+
"""
|
742
|
+
|
743
|
+
message_type = b"h"
|
744
|
+
description = "Operational Halt"
|
745
|
+
message_format = "!HHHI8scc"
|
746
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
747
|
+
message_size = struct.calcsize(message_format) + 1
|
748
|
+
|
749
|
+
stock: bytes
|
750
|
+
market_code: bytes
|
751
|
+
operational_halt_action: bytes
|
752
|
+
|
753
|
+
def __init__(self, message: bytes):
|
754
|
+
(
|
755
|
+
self.stock_locate,
|
756
|
+
self.tracking_number,
|
757
|
+
timestamp1,
|
758
|
+
timestamp2,
|
759
|
+
self.stock,
|
760
|
+
self.market_code,
|
761
|
+
self.operational_halt_action,
|
762
|
+
) = struct.unpack(self.message_format, message[1:])
|
763
|
+
self.set_timestamp(timestamp1, timestamp2)
|
764
|
+
|
765
|
+
def pack(self):
|
766
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
767
|
+
message = struct.pack(
|
768
|
+
self.message_pack_format,
|
769
|
+
self.message_type,
|
770
|
+
self.stock_locate,
|
771
|
+
self.tracking_number,
|
772
|
+
timestamp1,
|
773
|
+
timestamp2,
|
774
|
+
self.stock,
|
775
|
+
self.market_code,
|
776
|
+
self.operational_halt_action,
|
777
|
+
)
|
778
|
+
return message
|
779
|
+
|
780
|
+
|
781
|
+
class AddOrderMessage(MarketMessage):
|
782
|
+
"""
|
783
|
+
An Add Order Message indicates that a new order has been accepted by the Nasdaq system and was added to the
|
784
|
+
displayable book. The message includes a day-•-unique Order Reference Number used by Nasdaq to track the order. Nasdaq
|
785
|
+
will support two variations of the Add Order message format.
|
786
|
+
"""
|
787
|
+
|
788
|
+
order_reference_number: int
|
789
|
+
buy_sell_indicator: bytes
|
790
|
+
shares: int
|
791
|
+
stock: bytes
|
792
|
+
price: float
|
793
|
+
|
794
|
+
|
795
|
+
class AddOrderNoMPIAttributionMessage(AddOrderMessage):
|
796
|
+
"""
|
797
|
+
This message will be generated for unattributed orders accepted by the Nasdaq system. (Note: If a firm wants to
|
798
|
+
display a MPID for unattributed orders, Nasdaq recommends that it use the MPID of “NSDQ”.)
|
799
|
+
|
800
|
+
Attributes:
|
801
|
+
- order_reference_number : The unique reference number assigned to the new order at the time of receipt.
|
802
|
+
- buy_sell_indicator : The type of order being added. b"B" = Buy Order. b"S" = Sell Order.
|
803
|
+
- shares : The total number of shares associated with the order being added to the book.
|
804
|
+
- stock : Stock symbol, right padded with spaces
|
805
|
+
- price : The display price of the new order. Refer to Data Types for field processing notes.
|
806
|
+
"""
|
807
|
+
|
808
|
+
message_type = b"A"
|
809
|
+
description = "Add Order - No MPID Attribution Message"
|
810
|
+
message_format = "!HHHIQcI8sI"
|
811
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
812
|
+
message_size = struct.calcsize(message_format) + 1
|
813
|
+
|
814
|
+
def __init__(self, message: bytes):
|
815
|
+
(
|
816
|
+
self.stock_locate,
|
817
|
+
self.tracking_number,
|
818
|
+
timestamp1,
|
819
|
+
timestamp2,
|
820
|
+
self.order_reference_number,
|
821
|
+
self.buy_sell_indicator,
|
822
|
+
self.shares,
|
823
|
+
self.stock,
|
824
|
+
self.price,
|
825
|
+
) = struct.unpack(self.message_format, message[1:])
|
826
|
+
self.set_timestamp(timestamp1, timestamp2)
|
827
|
+
|
828
|
+
def pack(self):
|
829
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
830
|
+
message = struct.pack(
|
831
|
+
self.message_pack_format,
|
832
|
+
self.message_type,
|
833
|
+
self.stock_locate,
|
834
|
+
self.tracking_number,
|
835
|
+
timestamp1,
|
836
|
+
timestamp2,
|
837
|
+
self.order_reference_number,
|
838
|
+
self.buy_sell_indicator,
|
839
|
+
self.shares,
|
840
|
+
self.stock,
|
841
|
+
self.price,
|
842
|
+
)
|
843
|
+
return message
|
844
|
+
|
845
|
+
|
846
|
+
class AddOrderMPIDAttribution(AddOrderMessage):
|
847
|
+
"""
|
848
|
+
This message will be generated for attributed orders and quotations accepted by the Nasdaq system.
|
849
|
+
|
850
|
+
Attributes:
|
851
|
+
- order_reference_number : The unique reference number assigned to the new order at the time of receipt.
|
852
|
+
- buy_sell_indicator : The type of order being added. “B” = Buy Order. “S” = Sell Order.
|
853
|
+
- shares : The total number of shares associated with the order being added to the book.
|
854
|
+
- stock : Stock symbol, right padded with spaces
|
855
|
+
- price : The display price of the new order. Refer to Data Types for field processing notes.
|
856
|
+
- attribution : Nasdaq Market participant identifier associated with the entered order
|
857
|
+
"""
|
858
|
+
|
859
|
+
message_type = b"F"
|
860
|
+
description = "Add Order - MPID Attribution Message"
|
861
|
+
message_format = "!HHHIQcI8sI4s"
|
862
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
863
|
+
message_size = struct.calcsize(message_format) + 1
|
864
|
+
|
865
|
+
attribution: bytes
|
866
|
+
|
867
|
+
def __init__(self, message: bytes):
|
868
|
+
(
|
869
|
+
self.stock_locate,
|
870
|
+
self.tracking_number,
|
871
|
+
timestamp1,
|
872
|
+
timestamp2,
|
873
|
+
self.order_reference_number,
|
874
|
+
self.buy_sell_indicator,
|
875
|
+
self.shares,
|
876
|
+
self.stock,
|
877
|
+
self.price,
|
878
|
+
self.attribution,
|
879
|
+
) = struct.unpack(self.message_format, message[1:])
|
880
|
+
self.set_timestamp(timestamp1, timestamp2)
|
881
|
+
|
882
|
+
def pack(self):
|
883
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
884
|
+
message = struct.pack(
|
885
|
+
self.message_pack_format,
|
886
|
+
self.message_type,
|
887
|
+
self.stock_locate,
|
888
|
+
self.tracking_number,
|
889
|
+
timestamp1,
|
890
|
+
timestamp2,
|
891
|
+
self.order_reference_number,
|
892
|
+
self.buy_sell_indicator,
|
893
|
+
self.shares,
|
894
|
+
self.stock,
|
895
|
+
self.price,
|
896
|
+
self.attribution,
|
897
|
+
)
|
898
|
+
return message
|
899
|
+
|
900
|
+
|
901
|
+
class ModifyOrderMessage(MarketMessage):
|
902
|
+
"""
|
903
|
+
Modify Order messages always include the Order Reference Number of the Add Order to which the update
|
904
|
+
applies. To determine the current display shares for an order, ITCH subscribers must deduct the number of shares
|
905
|
+
stated in the Modify message from the original number of shares stated in the Add Order message with the same
|
906
|
+
reference number. Nasdaq may send multiple Modify Order messages for the same order reference number and
|
907
|
+
the effects are cumulative. When the number of display shares for an order reaches zero, the order is dead and
|
908
|
+
should be removed from the book.
|
909
|
+
"""
|
910
|
+
|
911
|
+
order_reference_number: int
|
912
|
+
|
913
|
+
|
914
|
+
class OrderExecutedMessage(ModifyOrderMessage):
|
915
|
+
"""
|
916
|
+
This message is sent whenever an order on the book is executed in whole or in part. It is possible to receive several
|
917
|
+
Order Executed Messages for the same order reference number if that order is executed in several parts. The
|
918
|
+
multiple Order Executed Messages on the same order are cumulative.
|
919
|
+
|
920
|
+
By combining the executions from both types of Order Executed Messages and the Trade Message, it is possible to
|
921
|
+
build a complete view of all non-•-cross executions that happen on Nasdaq. Cross execution information is available in
|
922
|
+
one bulk print per symbol via the Cross Trade Message.
|
923
|
+
|
924
|
+
Attributes:
|
925
|
+
- order_reference_number : The unique reference number assigned to the new order at the time of receipt
|
926
|
+
- executed_shares : The number of shares executed
|
927
|
+
- match_number : The Nasdaq generated day unique Match Number of this execution.
|
928
|
+
The Match Number is also referenced in the Trade Break Message
|
929
|
+
|
930
|
+
"""
|
931
|
+
|
932
|
+
message_type = b"E"
|
933
|
+
description = "Add Order - Order Executed Message"
|
934
|
+
message_format = "!HHHIQIQ"
|
935
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
936
|
+
message_size = struct.calcsize(message_format) + 1
|
937
|
+
|
938
|
+
executed_shares: int
|
939
|
+
match_number: int
|
940
|
+
|
941
|
+
def __init__(self, message: bytes):
|
942
|
+
(
|
943
|
+
self.stock_locate,
|
944
|
+
self.tracking_number,
|
945
|
+
timestamp1,
|
946
|
+
timestamp2,
|
947
|
+
self.order_reference_number,
|
948
|
+
self.executed_shares,
|
949
|
+
self.match_number,
|
950
|
+
) = struct.unpack(self.message_format, message[1:])
|
951
|
+
self.set_timestamp(timestamp1, timestamp2)
|
952
|
+
|
953
|
+
def pack(self):
|
954
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
955
|
+
message = struct.pack(
|
956
|
+
self.message_pack_format,
|
957
|
+
self.message_type,
|
958
|
+
self.stock_locate,
|
959
|
+
self.tracking_number,
|
960
|
+
timestamp1,
|
961
|
+
timestamp2,
|
962
|
+
self.order_reference_number,
|
963
|
+
self.executed_shares,
|
964
|
+
self.match_number,
|
965
|
+
)
|
966
|
+
return message
|
967
|
+
|
968
|
+
|
969
|
+
class OrderExecutedWithPriceMessage(ModifyOrderMessage):
|
970
|
+
"""
|
971
|
+
This message is sent whenever an order on the book is executed in whole or in part at a price different from the
|
972
|
+
initial display price. Since the execution price is different than the display price of the original Add Order, Nasdaq
|
973
|
+
includes a price field within this execution message.
|
974
|
+
|
975
|
+
It is possible to receive multiple Order Executed and Order Executed With Price messages for the same order if that
|
976
|
+
order is executed in several parts. The multiple Order Executed messages on the same order are cumulative.
|
977
|
+
|
978
|
+
These executions may be marked as non-•-printable.
|
979
|
+
If the execution is marked as non-•-printed, it means that the shares will be included into a later bulk print (e.g., in the case of cross executions).
|
980
|
+
If a firm is looking to use the data in time-•-and-•-sales displays or volume calculations,
|
981
|
+
Nasdaq recommends that firms ignore messages marked as non- -- printable to prevent double counting.
|
982
|
+
|
983
|
+
Attributes:
|
984
|
+
- order_reference_number : The unique reference number assigned to the new order at the time of receipt
|
985
|
+
- executed_shares : The number of shares executed
|
986
|
+
- match_number : The Nasdaq generated day unique Match Number of this execution.
|
987
|
+
The Match Number is also referenced in the Trade Break Message
|
988
|
+
|
989
|
+
- printable : Indicates if the execution should be reflected on time and sales displays and volume calculations
|
990
|
+
b"N" = Non-Printable
|
991
|
+
b"Y" = Printable
|
992
|
+
|
993
|
+
- execution_price : The Price at which the order execution occurred. Refer to Data Types for field processing notes
|
994
|
+
"""
|
995
|
+
|
996
|
+
message_type = b"C"
|
997
|
+
description = "Add Order - Order Executed with Price Message"
|
998
|
+
message_format = "!HHHIQIQcI"
|
999
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1000
|
+
message_size = struct.calcsize(message_format) + 1
|
1001
|
+
|
1002
|
+
executed_shares: int
|
1003
|
+
match_number: int
|
1004
|
+
printable: bytes
|
1005
|
+
execution_price: float
|
1006
|
+
|
1007
|
+
def __init__(self, message: bytes):
|
1008
|
+
(
|
1009
|
+
self.stock_locate,
|
1010
|
+
self.tracking_number,
|
1011
|
+
timestamp1,
|
1012
|
+
timestamp2,
|
1013
|
+
self.order_reference_number,
|
1014
|
+
self.executed_shares,
|
1015
|
+
self.match_number,
|
1016
|
+
self.printable,
|
1017
|
+
self.execution_price,
|
1018
|
+
) = struct.unpack(self.message_format, message[1:])
|
1019
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1020
|
+
|
1021
|
+
def pack(self):
|
1022
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1023
|
+
message = struct.pack(
|
1024
|
+
self.message_pack_format,
|
1025
|
+
self.message_type,
|
1026
|
+
self.stock_locate,
|
1027
|
+
self.tracking_number,
|
1028
|
+
timestamp1,
|
1029
|
+
timestamp2,
|
1030
|
+
self.order_reference_number,
|
1031
|
+
self.executed_shares,
|
1032
|
+
self.match_number,
|
1033
|
+
self.printable,
|
1034
|
+
self.execution_price,
|
1035
|
+
)
|
1036
|
+
return message
|
1037
|
+
|
1038
|
+
|
1039
|
+
class OrderCancelMessage(ModifyOrderMessage):
|
1040
|
+
"""
|
1041
|
+
This message is sent whenever an order on the book is modified as a result of a partial cancellation.
|
1042
|
+
|
1043
|
+
Attributes:
|
1044
|
+
- order_reference_number : The reference number of the order being canceled
|
1045
|
+
- cancelled_shares : The number of shares being removed from the display size of the order as a result of a cancellation
|
1046
|
+
"""
|
1047
|
+
|
1048
|
+
message_type = b"X"
|
1049
|
+
description = "Order Cancel Message"
|
1050
|
+
message_format = "!HHHIQI"
|
1051
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1052
|
+
message_size = struct.calcsize(message_format) + 1
|
1053
|
+
|
1054
|
+
cancelled_shares: int
|
1055
|
+
|
1056
|
+
def __init__(self, message: bytes):
|
1057
|
+
(
|
1058
|
+
self.stock_locate,
|
1059
|
+
self.tracking_number,
|
1060
|
+
timestamp1,
|
1061
|
+
timestamp2,
|
1062
|
+
self.order_reference_number,
|
1063
|
+
self.cancelled_shares,
|
1064
|
+
) = struct.unpack(self.message_format, message[1:])
|
1065
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1066
|
+
|
1067
|
+
def pack(self):
|
1068
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1069
|
+
message = struct.pack(
|
1070
|
+
self.message_pack_format,
|
1071
|
+
self.message_type,
|
1072
|
+
self.stock_locate,
|
1073
|
+
self.tracking_number,
|
1074
|
+
timestamp1,
|
1075
|
+
timestamp2,
|
1076
|
+
self.order_reference_number,
|
1077
|
+
self.cancelled_shares,
|
1078
|
+
)
|
1079
|
+
return message
|
1080
|
+
|
1081
|
+
|
1082
|
+
class OrderDeleteMessage(ModifyOrderMessage):
|
1083
|
+
"""
|
1084
|
+
This message is sent whenever an order on the book is being cancelled. All remaining shares are no longer
|
1085
|
+
accessible so the order must be removed from the book.
|
1086
|
+
|
1087
|
+
Attributes:
|
1088
|
+
- order_reference_number : The reference number of the order being canceled
|
1089
|
+
"""
|
1090
|
+
|
1091
|
+
message_type = b"D"
|
1092
|
+
description = "Order Delete Message"
|
1093
|
+
message_format = "!HHHIQ"
|
1094
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1095
|
+
message_size = struct.calcsize(message_format) + 1
|
1096
|
+
|
1097
|
+
def __init__(self, message: bytes):
|
1098
|
+
(
|
1099
|
+
self.stock_locate,
|
1100
|
+
self.tracking_number,
|
1101
|
+
timestamp1,
|
1102
|
+
timestamp2,
|
1103
|
+
self.order_reference_number,
|
1104
|
+
) = struct.unpack(self.message_format, message[1:])
|
1105
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1106
|
+
|
1107
|
+
def pack(self):
|
1108
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1109
|
+
message = struct.pack(
|
1110
|
+
self.message_pack_format,
|
1111
|
+
self.message_type,
|
1112
|
+
self.stock_locate,
|
1113
|
+
self.tracking_number,
|
1114
|
+
timestamp1,
|
1115
|
+
timestamp2,
|
1116
|
+
self.order_reference_number,
|
1117
|
+
)
|
1118
|
+
return message
|
1119
|
+
|
1120
|
+
|
1121
|
+
class OrderReplaceMessage(ModifyOrderMessage):
|
1122
|
+
"""
|
1123
|
+
This message is sent whenever an order on the book has been cancel-•-replaced. All remaining shares from the
|
1124
|
+
original order are no longer accessible, and must be removed. The new order details are provided for the
|
1125
|
+
replacement, along with a new order reference number which will be used henceforth. Since the side, stock
|
1126
|
+
symbol and attribution (if any) cannot be changed by an Order Replace event, these fields are not included in the
|
1127
|
+
message. Firms should retain the side, stock symbol and MPID from the original Add Order message.
|
1128
|
+
|
1129
|
+
Attributes:
|
1130
|
+
- order_reference_number : The original order reference number of the order being replaced
|
1131
|
+
- new_order_reference_number : The new reference number for this order at time of replacement
|
1132
|
+
Please note that the Nasdaq system will use this new order reference number for all subsequent updates
|
1133
|
+
- shares : The new total displayed quantity
|
1134
|
+
- price : The new display price for the order. Please refer to Data Types for field processing notes
|
1135
|
+
"""
|
1136
|
+
|
1137
|
+
message_type = b"U"
|
1138
|
+
description = "Order Replace Message"
|
1139
|
+
message_format = "!HHHIQQII"
|
1140
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1141
|
+
message_size = struct.calcsize(message_format) + 1
|
1142
|
+
|
1143
|
+
new_order_reference_number: int
|
1144
|
+
shares: int
|
1145
|
+
price: float
|
1146
|
+
|
1147
|
+
def __init__(self, message: bytes):
|
1148
|
+
(
|
1149
|
+
self.stock_locate,
|
1150
|
+
self.tracking_number,
|
1151
|
+
timestamp1,
|
1152
|
+
timestamp2,
|
1153
|
+
self.order_reference_number,
|
1154
|
+
self.new_order_reference_number,
|
1155
|
+
self.shares,
|
1156
|
+
self.price,
|
1157
|
+
) = struct.unpack(self.message_format, message[1:])
|
1158
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1159
|
+
|
1160
|
+
def pack(self):
|
1161
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1162
|
+
message = struct.pack(
|
1163
|
+
self.message_pack_format,
|
1164
|
+
self.message_type,
|
1165
|
+
self.stock_locate,
|
1166
|
+
self.tracking_number,
|
1167
|
+
timestamp1,
|
1168
|
+
timestamp2,
|
1169
|
+
self.order_reference_number,
|
1170
|
+
self.new_order_reference_number,
|
1171
|
+
self.shares,
|
1172
|
+
self.price,
|
1173
|
+
)
|
1174
|
+
return message
|
1175
|
+
|
1176
|
+
|
1177
|
+
class TradeMessage(MarketMessage):
|
1178
|
+
match_number: int
|
1179
|
+
|
1180
|
+
|
1181
|
+
class NonCrossTradeMessage(TradeMessage):
|
1182
|
+
"""
|
1183
|
+
The Trade Message is designed to provide execution details for normal match events involving non-•-displayable
|
1184
|
+
order types. (Note: There is a separate message for Nasdaq cross events.)
|
1185
|
+
Since no Add Order Message is generated when a non-•-displayed order is initially received, Nasdaq cannot use the
|
1186
|
+
Order Executed messages for all matches. Therefore this message indicates when a match occurs between non---
|
1187
|
+
displayable order types.
|
1188
|
+
|
1189
|
+
A Trade Message is transmitted each time a non-•-displayable order is executed in whole or in part.
|
1190
|
+
It is possible to receive multiple Trade Messages for the same order if that order is executed in several parts.
|
1191
|
+
Trade Messages for the same order are cumulative.
|
1192
|
+
|
1193
|
+
Trade Messages should be included in Nasdaq time-•-and-•-sales displays as well as volume and other market statistics.
|
1194
|
+
Since Trade Messages do not affect the book, however, they may be ignored by firms just looking to build
|
1195
|
+
and track the Nasdaq execution system display.
|
1196
|
+
|
1197
|
+
Attributes:
|
1198
|
+
- order_reference_number : The unique reference number assigned to the order on the book being executed.
|
1199
|
+
- buy_sell_indicator : The type of non-display order on the book being matched b"B" = Buy Order, b"S" = Sell Order
|
1200
|
+
- shares : The number of shares being matched in this execution
|
1201
|
+
- stock : Stock Symbol, right padded with spaces
|
1202
|
+
- price : The match price of the order
|
1203
|
+
- match_number : The Nasdaq generated session unique Match Number for this trade
|
1204
|
+
The Match Number is referenced in the Trade Break Message
|
1205
|
+
"""
|
1206
|
+
|
1207
|
+
message_type = b"P"
|
1208
|
+
description = "Trade Message"
|
1209
|
+
message_format = "!HHHIQcI8sIQ"
|
1210
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1211
|
+
message_size = struct.calcsize(message_format) + 1
|
1212
|
+
|
1213
|
+
order_reference_number: int
|
1214
|
+
buy_sell_indicator: bytes
|
1215
|
+
shares: int
|
1216
|
+
stock: bytes
|
1217
|
+
price: float
|
1218
|
+
|
1219
|
+
def __init__(self, message: bytes):
|
1220
|
+
(
|
1221
|
+
self.stock_locate,
|
1222
|
+
self.tracking_number,
|
1223
|
+
timestamp1,
|
1224
|
+
timestamp2,
|
1225
|
+
self.order_reference_number,
|
1226
|
+
self.buy_sell_indicator,
|
1227
|
+
self.shares,
|
1228
|
+
self.stock,
|
1229
|
+
self.price,
|
1230
|
+
self.match_number,
|
1231
|
+
) = struct.unpack(self.message_format, message[1:])
|
1232
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1233
|
+
|
1234
|
+
def pack(self):
|
1235
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1236
|
+
message = struct.pack(
|
1237
|
+
self.message_pack_format,
|
1238
|
+
self.message_type,
|
1239
|
+
self.stock_locate,
|
1240
|
+
self.tracking_number,
|
1241
|
+
timestamp1,
|
1242
|
+
timestamp2,
|
1243
|
+
self.order_reference_number,
|
1244
|
+
self.buy_sell_indicator,
|
1245
|
+
self.shares,
|
1246
|
+
self.stock,
|
1247
|
+
self.price,
|
1248
|
+
self.match_number,
|
1249
|
+
)
|
1250
|
+
return message
|
1251
|
+
|
1252
|
+
|
1253
|
+
class CrossTradeMessage(TradeMessage):
|
1254
|
+
"""
|
1255
|
+
Cross Trade message indicates that Nasdaq has completed its cross process for a specific security. Nasdaq sends out
|
1256
|
+
a Cross Trade message for all active issues in the system following the Opening, Closing and EMC cross events.
|
1257
|
+
Firms may use the Cross Trade message to determine when the cross for each security has been completed.
|
1258
|
+
(Note: For the halted / paused securities, firms should use the Trading Action message to determine when an issue has been
|
1259
|
+
released for trading.)
|
1260
|
+
|
1261
|
+
For most issues, the Cross Trade message will indicate the bulk volume associated with the cross event. If the order
|
1262
|
+
interest is insufficient to conduct a cross in a particular issue, however, the Cross Trade message may show the
|
1263
|
+
shares as zero.
|
1264
|
+
|
1265
|
+
To avoid double counting of cross volume, firms should not include transactions marked as non-•-printable in time---
|
1266
|
+
and-•-sales displays or market statistic calculations.
|
1267
|
+
|
1268
|
+
Attributes:
|
1269
|
+
- shares : The number of shares being matched in this execution
|
1270
|
+
- stock : Stock Symbol, right padded with spaces
|
1271
|
+
- cross_price : The match price of the order
|
1272
|
+
- match_number : The Nasdaq generated session unique Match Number for this trade
|
1273
|
+
The Match Number is referenced in the Trade Break Message
|
1274
|
+
- cross_type : The Nasdaq cross session for which the message is being generated:
|
1275
|
+
b"O": "Nasdaq Opening Cross"
|
1276
|
+
b"C": "Nasdaq Closing Cross"
|
1277
|
+
b"H": "Cross for IPO and halted / paused securities"
|
1278
|
+
"""
|
1279
|
+
|
1280
|
+
message_type = b"Q"
|
1281
|
+
description = "Cross Trade Message"
|
1282
|
+
message_format = "!HHHIQ8sIQc"
|
1283
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1284
|
+
message_size = struct.calcsize(message_format) + 1
|
1285
|
+
|
1286
|
+
shares: int
|
1287
|
+
stock: bytes
|
1288
|
+
cross_price: float
|
1289
|
+
cross_type: bytes
|
1290
|
+
|
1291
|
+
def __init__(self, message: bytes):
|
1292
|
+
(
|
1293
|
+
self.stock_locate,
|
1294
|
+
self.tracking_number,
|
1295
|
+
timestamp1,
|
1296
|
+
timestamp2,
|
1297
|
+
self.shares,
|
1298
|
+
self.stock,
|
1299
|
+
self.cross_price,
|
1300
|
+
self.match_number,
|
1301
|
+
self.cross_type,
|
1302
|
+
) = struct.unpack(self.message_format, message[1:])
|
1303
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1304
|
+
|
1305
|
+
def pack(self):
|
1306
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1307
|
+
message = struct.pack(
|
1308
|
+
self.message_pack_format,
|
1309
|
+
self.message_type,
|
1310
|
+
self.stock_locate,
|
1311
|
+
self.tracking_number,
|
1312
|
+
timestamp1,
|
1313
|
+
timestamp2,
|
1314
|
+
self.shares,
|
1315
|
+
self.stock,
|
1316
|
+
self.cross_price,
|
1317
|
+
self.match_number,
|
1318
|
+
self.cross_type,
|
1319
|
+
)
|
1320
|
+
return message
|
1321
|
+
|
1322
|
+
|
1323
|
+
class BrokenTradeMessage(TradeMessage):
|
1324
|
+
"""
|
1325
|
+
The Broken Trade Message is sent whenever an execution on Nasdaq is broken. An execution may be broken if it is
|
1326
|
+
found to be “clearly erroneous” pursuant to [Nasdaq's Clearly Erroneous Policy](https://www.nasdaqtrader.com/Trader.aspx?id=ClearlyErroneous#:~:text=The%20terms%20of%20a%20transaction,or%20identification%20of%20the%20security.).
|
1327
|
+
A trade break is final; once a trade is broken, it cannot be reinstated.
|
1328
|
+
|
1329
|
+
Firms that use the ITCH feed to create time---and---sales displays or calculate market statistics should be prepared
|
1330
|
+
to process the broken trade message. If a firm is only using the ITCH feed to build a book, however, it may ignore
|
1331
|
+
these messages as they have no impact on the current book.
|
1332
|
+
|
1333
|
+
Attributes:
|
1334
|
+
- match_number : The Nasdaq Match Number of the execution that was broken.
|
1335
|
+
This refers to a Match Number from a previously transmitted Order Executed Message,
|
1336
|
+
Order Executed With Price Message, or Trade Message.
|
1337
|
+
"""
|
1338
|
+
|
1339
|
+
message_type = b"B"
|
1340
|
+
description = "Broken Trade Message"
|
1341
|
+
message_format = "!HHHIQ"
|
1342
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1343
|
+
message_size = struct.calcsize(message_format) + 1
|
1344
|
+
|
1345
|
+
def __init__(self, message: bytes):
|
1346
|
+
(
|
1347
|
+
self.stock_locate,
|
1348
|
+
self.tracking_number,
|
1349
|
+
timestamp1,
|
1350
|
+
timestamp2,
|
1351
|
+
self.match_number,
|
1352
|
+
) = struct.unpack(self.message_format, message[1:])
|
1353
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1354
|
+
|
1355
|
+
def pack(self):
|
1356
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1357
|
+
message = struct.pack(
|
1358
|
+
self.message_pack_format,
|
1359
|
+
self.message_type,
|
1360
|
+
self.stock_locate,
|
1361
|
+
self.tracking_number,
|
1362
|
+
timestamp1,
|
1363
|
+
timestamp2,
|
1364
|
+
self.match_number,
|
1365
|
+
)
|
1366
|
+
return message
|
1367
|
+
|
1368
|
+
|
1369
|
+
class NOIIMessage(MarketMessage):
|
1370
|
+
"""
|
1371
|
+
- Nasdaq begins disseminating Net Order Imbalance Indicators (NOII) at 9:25 a.m. for the Opening Cross and 3:50 p.m. for the Closing Cross.
|
1372
|
+
- Between 9:25 and 9:28 a.m. and 3:50 and 3:55 p.m., Nasdaq disseminates the NOII information every 10 seconds.
|
1373
|
+
- Between 9:28 and 9:30 a.m. and 3:55 and 4:00 p.m., Nasdaq disseminates the NOII information every second.
|
1374
|
+
- For Nasdaq Halt, IPO and Pauses, NOII messages will be disseminated at 1 second intervals starting 1 second after quoting period starts/trading action is released.
|
1375
|
+
- For more information, please see the [FAQ on Opening and Closing Crosses](https://www.nasdaqtrader.com/content/productsservices/trading/crosses/openclose_faqs.pdf).
|
1376
|
+
- Nasdaq will also disseminate an Extended Trading Close (ETC) message from 4:00 p.m. to 4:05 p.m. at five second intervals.
|
1377
|
+
- For more information, please see the [FAQ on Extended Trading Close](https://www.nasdaqtrader.com/content/productsservices/trading/After-Hour-Cross-FAQ-Factsheet-NAM.pdf).
|
1378
|
+
|
1379
|
+
Attributes:
|
1380
|
+
- paired_shares : The total number of shares that are eligible to be matched at the Current Reference Price.
|
1381
|
+
- imbalance_shares : The number of shares not paired at the Current Reference Price.
|
1382
|
+
- imbalance_direction : The market side of the order imbalance:
|
1383
|
+
b"B": "buy imbalance"
|
1384
|
+
b"S": "sell imbalance"
|
1385
|
+
b"N": "no imbalance"
|
1386
|
+
b"O": "Insufficient orders to calculate"
|
1387
|
+
b"P": "Paused"
|
1388
|
+
|
1389
|
+
- stock : Stock symbol, right padded with spaces
|
1390
|
+
- far_price : A hypothetical auction---clearing price for cross orders only. Refer to Data Types for field processing notes.
|
1391
|
+
- near_price : A hypothetical auction-•-clearing price for cross orders as well as continuous orders. Refer to Data Types for field
|
1392
|
+
- current_reference_price : The price at which the NOII shares are being calculated. Refer to Data Types for field processing notes.
|
1393
|
+
- cross_type : The type of Nasdaq cross for which the NOII message is being generated:
|
1394
|
+
b"O": "Nasdaq Opening Cross"
|
1395
|
+
b"C": "Nasdaq Closing Cross"
|
1396
|
+
b"H": "Cross for IPO and halted / paused securities"
|
1397
|
+
b"A": "Extended Trading Close"
|
1398
|
+
|
1399
|
+
- variation_indicator : This field indicates the absolute value of the percentage of deviation of
|
1400
|
+
the Near Indicative Clearing Price to the nearest Current Reference Price, see ``itch.indicators.``
|
1401
|
+
"""
|
1402
|
+
|
1403
|
+
message_type = b"I"
|
1404
|
+
description = "NOII Message"
|
1405
|
+
message_format = "!HHHIQQc8sIIIcc"
|
1406
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1407
|
+
message_size = struct.calcsize(message_format) + 1
|
1408
|
+
|
1409
|
+
paired_shares: int
|
1410
|
+
imbalance_shares: bytes
|
1411
|
+
imbalance_direction: bytes
|
1412
|
+
stock: bytes
|
1413
|
+
far_price: float
|
1414
|
+
near_price: float
|
1415
|
+
current_reference_price: float
|
1416
|
+
cross_type: bytes
|
1417
|
+
variation_indicator: bytes
|
1418
|
+
|
1419
|
+
def __init__(self, message: bytes):
|
1420
|
+
(
|
1421
|
+
self.stock_locate,
|
1422
|
+
self.tracking_number,
|
1423
|
+
timestamp1,
|
1424
|
+
timestamp2,
|
1425
|
+
self.paired_shares,
|
1426
|
+
self.imbalance_shares,
|
1427
|
+
self.imbalance_direction,
|
1428
|
+
self.stock,
|
1429
|
+
self.far_price,
|
1430
|
+
self.near_price,
|
1431
|
+
self.current_reference_price,
|
1432
|
+
self.cross_type,
|
1433
|
+
self.variation_indicator,
|
1434
|
+
) = struct.unpack(self.message_format, message[1:])
|
1435
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1436
|
+
|
1437
|
+
def pack(self):
|
1438
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1439
|
+
message = struct.pack(
|
1440
|
+
self.message_pack_format,
|
1441
|
+
self.message_type,
|
1442
|
+
self.stock_locate,
|
1443
|
+
self.tracking_number,
|
1444
|
+
timestamp1,
|
1445
|
+
timestamp2,
|
1446
|
+
self.paired_shares,
|
1447
|
+
self.imbalance_shares,
|
1448
|
+
self.imbalance_direction,
|
1449
|
+
self.stock,
|
1450
|
+
self.far_price,
|
1451
|
+
self.near_price,
|
1452
|
+
self.current_reference_price,
|
1453
|
+
self.cross_type,
|
1454
|
+
self.variation_indicator,
|
1455
|
+
)
|
1456
|
+
return message
|
1457
|
+
|
1458
|
+
|
1459
|
+
class RetailPriceImprovementIndicator(MarketMessage):
|
1460
|
+
"""
|
1461
|
+
Identifies a retail interest indication of the Bid, Ask or both the Bid and Ask for Nasdaq-•-listed securities.
|
1462
|
+
|
1463
|
+
Attributes:
|
1464
|
+
- stock : Stock symbol, right padded with spaces
|
1465
|
+
- interest_flag :
|
1466
|
+
b"B": "RPI orders available on the buy side"
|
1467
|
+
b"S": "RPI orders available on the sell side"
|
1468
|
+
b"A": "RPI orders available on both sides (buy and sell)"
|
1469
|
+
b"N": "No RPI orders available "
|
1470
|
+
"""
|
1471
|
+
|
1472
|
+
message_type = b"N"
|
1473
|
+
description = "Retail Interest message"
|
1474
|
+
message_format = "!HHHI8sc"
|
1475
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1476
|
+
message_size = struct.calcsize(message_format) + 1
|
1477
|
+
|
1478
|
+
stock: bytes
|
1479
|
+
interest_flag: bytes
|
1480
|
+
|
1481
|
+
def __init__(self, message: bytes):
|
1482
|
+
(
|
1483
|
+
self.stock_locate,
|
1484
|
+
self.tracking_number,
|
1485
|
+
timestamp1,
|
1486
|
+
timestamp2,
|
1487
|
+
self.stock,
|
1488
|
+
self.interest_flag,
|
1489
|
+
) = struct.unpack(self.message_format, message[1:])
|
1490
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1491
|
+
|
1492
|
+
def pack(self):
|
1493
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1494
|
+
message = struct.pack(
|
1495
|
+
self.message_pack_format,
|
1496
|
+
self.message_type,
|
1497
|
+
self.stock_locate,
|
1498
|
+
self.tracking_number,
|
1499
|
+
timestamp1,
|
1500
|
+
timestamp2,
|
1501
|
+
self.stock,
|
1502
|
+
self.interest_flag,
|
1503
|
+
)
|
1504
|
+
return message
|
1505
|
+
|
1506
|
+
|
1507
|
+
class DLCRMessage(MarketMessage):
|
1508
|
+
"""
|
1509
|
+
The following message is disseminated only for Direct Listing with Capital Raise (DLCR) securities.
|
1510
|
+
Nasdaq begins disseminating messages once per second as soon as the DLCR volatility test has successfully passed.
|
1511
|
+
|
1512
|
+
Attributes:
|
1513
|
+
- stock : Stock symbol, right padded with spaces.
|
1514
|
+
- open_eligibility_status : Indicates if the security is eligible to be released for trading (b"N": Not Eligible, b"Y": Eligible)
|
1515
|
+
- minimum_allowable_price : 20% below Registration Statement Lower Price
|
1516
|
+
- maximum_allowable_price : 80% above Registration Statement Highest Price
|
1517
|
+
- near_execution_price : The current reference price when the DLCR volatility test has successfully passed
|
1518
|
+
- near_execution_time : The time at which the near execution price was set
|
1519
|
+
- lower_price_range_collar : Indicates the price of the Lower Auction Collar Threshold (10% below the Near Execution Price)
|
1520
|
+
- upper_price_range_collar : Indicates the price of the Upper Auction Collar Threshold (10% above the Near Execution Price)
|
1521
|
+
"""
|
1522
|
+
|
1523
|
+
message_type = b"O"
|
1524
|
+
description = "Direct Listing with Capital Raise Message"
|
1525
|
+
message_format = "!HHHI8scIIIQII"
|
1526
|
+
message_pack_format = "!" + "c" + message_format[1:]
|
1527
|
+
message_size = struct.calcsize(message_format) + 1
|
1528
|
+
|
1529
|
+
stock: bytes
|
1530
|
+
open_eligibility_status: bytes
|
1531
|
+
minimum_allowable_price: float
|
1532
|
+
maximum_allowable_price: float
|
1533
|
+
near_execution_price: float
|
1534
|
+
near_execution_time: int
|
1535
|
+
lower_price_range_collar: float
|
1536
|
+
upper_price_range_collar: float
|
1537
|
+
|
1538
|
+
def __init__(self, message: bytes):
|
1539
|
+
(
|
1540
|
+
self.stock_locate,
|
1541
|
+
self.tracking_number,
|
1542
|
+
timestamp1,
|
1543
|
+
timestamp2,
|
1544
|
+
self.stock,
|
1545
|
+
self.open_eligibility_status,
|
1546
|
+
self.minimum_allowable_price,
|
1547
|
+
self.maximum_allowable_price,
|
1548
|
+
self.near_execution_price,
|
1549
|
+
self.near_execution_time,
|
1550
|
+
self.lower_price_range_collar,
|
1551
|
+
self.upper_price_range_collar,
|
1552
|
+
) = struct.unpack(self.message_format, message[1:])
|
1553
|
+
self.set_timestamp(timestamp1, timestamp2)
|
1554
|
+
|
1555
|
+
def pack(self):
|
1556
|
+
(timestamp1, timestamp2) = self.split_timestamp()
|
1557
|
+
message = struct.pack(
|
1558
|
+
self.message_pack_format,
|
1559
|
+
self.message_type,
|
1560
|
+
self.stock_locate,
|
1561
|
+
self.tracking_number,
|
1562
|
+
timestamp1,
|
1563
|
+
timestamp2,
|
1564
|
+
self.stock,
|
1565
|
+
self.open_eligibility_status,
|
1566
|
+
self.minimum_allowable_price,
|
1567
|
+
self.maximum_allowable_price,
|
1568
|
+
self.near_execution_price,
|
1569
|
+
self.near_execution_time,
|
1570
|
+
self.lower_price_range_collar,
|
1571
|
+
self.upper_price_range_collar,
|
1572
|
+
)
|
1573
|
+
return message
|
1574
|
+
|
1575
|
+
|
1576
|
+
messages: Dict[bytes, Type[MarketMessage]] = {
|
1577
|
+
b"S": SystemEventMessage,
|
1578
|
+
b"R": StockDirectoryMessage,
|
1579
|
+
b"H": StockTradingActionMessage,
|
1580
|
+
b"Y": RegSHOMessage,
|
1581
|
+
b"L": MarketParticipantPositionMessage,
|
1582
|
+
b"V": MWCBDeclineLeveMessage,
|
1583
|
+
b"W": MWCBStatusMessage,
|
1584
|
+
b"K": IPOQuotingPeriodUpdateMessage,
|
1585
|
+
b"J": LULDAuctionCollarMessage,
|
1586
|
+
b"h": OperationalHaltMessage,
|
1587
|
+
b"A": AddOrderNoMPIAttributionMessage,
|
1588
|
+
b"F": AddOrderMPIDAttribution,
|
1589
|
+
b"E": OrderExecutedMessage,
|
1590
|
+
b"C": OrderExecutedWithPriceMessage,
|
1591
|
+
b"X": OrderCancelMessage,
|
1592
|
+
b"D": OrderDeleteMessage,
|
1593
|
+
b"U": OrderReplaceMessage,
|
1594
|
+
b"P": NonCrossTradeMessage,
|
1595
|
+
b"Q": CrossTradeMessage,
|
1596
|
+
b"B": BrokenTradeMessage,
|
1597
|
+
b"I": NOIIMessage,
|
1598
|
+
b"N": RetailPriceImprovementIndicator,
|
1599
|
+
b"O": DLCRMessage,
|
1600
|
+
}
|