ITCH RTL implementation
Table of Contents
Introduction #
ITCH
is a message protocol used in the application layer of
financial exchanges that implement the ITCH/OUCH
feeds.
It is part of the exchange’s direct data feed, a low latency feed between the exchange’s servers and a client’s trading infrastructure.
This project is a synthesizable Verilog
implementation of an ITCH
protocol
message parser, used on the client end of the link.
Although multiple exchanges use ITCH
in their data feeds, the format of these
messages varies. In order to reduce the amount of additional work required to
add support for new exchanges, the majority of this RTL
is procedurally
generated.
By default this module supports NASDAQ
’s TotalView ITCH 5.0
message format.
RTL implementation of the ITCH protocol decoder.
ITCH #
The ITCH
protocol is an integral part of the direct exchange data feed.
This protocol delineates a series of exchange-specific binary messages that
convey essential exchange status information, including the tracking of orders,
administrative messages, and exchange event notifications. It is exclusively
used for outbound market data feeds and does not support order entry.
The messages comprising the ITCH
protocol are delivered via MoldUDP64
packets,
which ensure proper sequencing and tracking.
It is important to note that there is no universal implementation of ITCH
;
instead, each exchange defines its own message formats. For instance, NASDAQ
’s
version of ITCH
is known as TotalView ITCH
, and the Australian Securities
Exchange (ASX
) uses ASX ITCH
.
Message format #
Messages are of defined length based on their Message Type
.
I will be using the TotalView ITCH version 5.0
message format in the following examples.
The modules used in the following examples will utilize an 8
-byte payload with for UDP->MoldUPD64
.
There is a predefined format for each Message Type
with predefined fields.
For example, the System Event
message, used to signal a market or data feed
handler event, has a Message Type
of 0x53
(S
in ASCII), a total length of 12
bytes, and follows the following format :
The fields can belong to one of the following four types :
Unsigned integer
: This is the most common type, used for message integer fields, and it is represented in big endian.Price
: Integer fields that need to be converted to a fixed point decimal format. There are two sub-type forPrice
:Price(4)
with4
decimal places andPrice(8)
with8
.ASCII
: Text fields that are left-justified and padded on the right with spaces.Timestamp
: A6
-byte unsigned integer representing the number of nanoseconds elapsed since midnight.
Automatic generation #
Because there is no single ITCH
protocol message format, and because the
RTL
code for the decoder is painfully repetitive, I have decided to automatically
generate the majority of the Verilog
code for this module.
XML #
The ITCH
message format is described in an XML
file.
This XML
file is also used to generate the code for the C
ITCH
library
associated with this project. This library is used in the HFT
project’s self-checking test bench
and in my custom tools.
The following is the description of the System Event
message :
<Struct name="system_event" len="12" id="S" database="true">
<Field name="message_type" offset="0" len="1" type="char_t"/>
<Field name="stock_locate" offset="1" len="2" type="u16_t"/>
<Field name="tracking_number" offset="3" len="2" type="u16_t"/>
<Field name="timestamp" offset="5" len="6" type="u48_t"/>
<Field name="event_code" offset="11" len="1" type="eSystemEvent"/>
</Struct>
Struct.name
: message typeSturct.len
: total length in bytes of this messageStruct.id
: ascii code for this message typeStruct.database
: identifies if we should include thisStruct
in our generation, currently unusedField.name
: name of the fieldField.offset
: field start position, offset in bytes from the start of the messageField.len
: length in bytes of this fieldField.type
: type of this field, unused byITCH
module, used byC
libraries to indicate how to manipulate the data.
This XML
was originally authored by the github user doctorbigtime
for his own message parser written in Rust
.
All credits for this XML
belong to him.
Python script #
The itch_msg_to_rtl.py
Python
script reads this XML
and translates the
outlined message formats into Verilog
code.
These generated sequences are then written into multiple Verilog
files in the
gen
folder.
Include in module #
Our main module assembles the code by using the include
directive to include
the generated code files.
Architecture #
This module is a message decoder, internally it works by accumulating message bytes, identifying the message type and routing the message field data to the wires associated with the message type.
Internally, the message decoder accumulates the message bytes
received from the MoldUDP64
module into the internal data_q
and ov_data_q
flops. The contents of the data_q
flops are connected to the corresponding
outbound decoded message fields.
The number of bytes received for the current message is tracked by the
data_cnt_q
counter. By examining the first byte of each message, we can
identify the Message Type
and determine the number of expected bytes to
collect.
The cycle after the entire message has been received, the validity
signal on the ITCH
outbound interface corresponding to this Message Type
is asserted.
Overlap #
I refer to an overlap
as a case
where, from the perspective of the MoldUDP64
module, the last bytes of a
message and the first bytes of a new message are transmitted within the same
UDP->MoldUDP64
payload.
The overlapping bytes are the first bytes of this new message.
Due to choices made regarding how to handle overlap
cases, the ITCH
module has two inbound interfaces by which it
can accept new message bytes.
In order to avoid corrupting the value of the byte count data_cnt_q
and the
flopped data data_q
for the finishing message, data pertaining to these
overlapping bytes will be stored in dedicated flops ov_data_q
and ov_cnt_q
,
and merged with the remainder of its message in the following cycle.
Interfaces #
Input interfaces #
The inbound interface is connected to the MoldUDP64
module and used to
receive message bytes.
Message interface #
The message interface
is the standard interface used to transmit all
message bytes, with the exception of the overlapping bytes.
input valid_i,
input start_i,
input [KEEP_LW-1:0] len_i,
input [AXI_DATA_W-1:0] data_i,
valid_i
: signals the validity of data on this interfacestart_i
: signals the start of a new messagelen_i
: length of the valid data in bytesdata_i
: data bytes
Overlap interface #
The overlap interface
is used exclusively for transmitting the
overlapping bytes. Due to the conditions in which an overlap occurs, there is
no need for a start
signal, as the start is implied when we have a valid
overlap.
input ov_valid_i,
input [OV_KEEP_LW-1:0] ov_len_i,
input [OV_DATA_W-1:0] ov_data_i,
ov_valid_i
: an overlap has occurred, data on this interface is valid. Implies the start of a new messageov_len_i
: length of the valid data in bytesov_data_i
: overlapping bytes
Output interfaces #
The output of this module is intended to be connected to a trading algorithm.
There are two output interfaces. The first is the standard output interface, which becomes valid once all the bytes of the message have been received.
The second interface is optional, and is an early interface used to identify the type of the message currently being received, and which message fields have received all their data.
To include this interface, declare the EARLY
macro.
Standard interface #
Standard outbound decoder interface, contains fully decoded messages.
output logic itch_<message_type>_v_o,
output logic [<field length>-1:0] itch_<message_type>_<field_name>_o,
...
output logic [<field length>-1:0] itch_<message_type>_<field_name>_o,
itch_<message_type>_v_o
: valid signal, a message of<message_type>
has been fully receiveditch_<message_type>_<field_name>_o
: message field
Early interface #
This optional early
interface is used to start triggering decisions as soon as
individual data fields have been fully received, eliminating the need to wait for
the complete reception of all the message bytes.
output logic itch_<message_type>_early_v_o,
output logic itch_<message_type>_<field_name>_early_v_o,
...
output logic itch_<message_type>_<field_name>_early_v_o,
itch_<message_type>_early_v_o
: valid signal, decoding a message of type<message_type>
.itch_<message_type>_<field_name>_early_v_o
: valid signal, all bytes of<field_name>
have been received. When used, it should only be used when the associated early<message_type>
valid signal is high. The field data bytes will be on the standard interface.
Example #
In the following example, the ITCH
module with an early
interface is
decoding two messages. The first is a 21
-byte-long Snapshot
message, and
the second is a 39
-byte-long anonymous Add Order
message.
data_cnt_q
in
yellow that is using decimal.Since our Snapshot
message is 21
bytes long, and since, in our MoldUDP64
packet, there is a 2
bytes long lenght field before the start of each new
message’s data bytes, the first byte of the Add Order
message will overlap.
Due to this, the start of the Add Order
message will be sent through the
overlap interface. We can observe that the data_cnt_q
counter will delay
accounting for this overlapping byte by one cycle, giving us the needed time to finish
processing the previous Snapshot
message.