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/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
+ }