Skip to main content

Streaming Orders into NATS JetStream

Overview​

This document explains how to configure and use NATS JetStream as a data warehouse for Ember.

Install and Configure NATS​

To learn how to install NATS server with JetStream enabled, see the NATS documentation. JetStream must be enabled on the server (for example, nats-server -js).

Configure Ember​

Streams​

Since Ember version 1.16, you can set up a warehouse service to export data into NATS. The service creates the following objects if they are missing:

  • EmberMessages stream: All trading flow messages, including trading requests (order submissions, cancellations, modifications) and trading events (order ACKs/NACKs, cancellation ACKs/NACKs, fills, etc.). You can configure a filter to reduce this stream to trade events only.
  • EmberOrders stream: Order snapshots. By default it contains one record with the final state of each order (see Updatable Orders).
  • EmberMetadata-<region>-<tenant> key-value bucket: The term key holds the current Ember journal term.

Configuration​

Add the following section to $EMBER_HOME/ember.conf. The data export mode is controlled by the live parameter.

warehouse {
nats { # unit id, you will use it when you run the app, it might be any
live = true # the process does not wait for new data and exits if false

messages = [
${template.warehouse.nats.messages} { # loader which loads order messages
filter.settings.inclusion = null # EVENTS or TRADES, null means no filter

loader.settings {
servers = "nats://nats-1:4222,nats://nats-2:4222" # comma-separated NATS servers
region = EU # US, EU, AP or AU
streamMaxAge = 30d # stream retention
streamMaxBytes = 100G # stream size
}
}
]

orders = [
${template.warehouse.nats.orders} { # loader which loads orders
loader.settings {
servers = "nats://nats-1:4222,nats://nats-2:4222"
region = EU
streamMaxAge = 30d
streamMaxBytes = 10G
}
}
]
}
}

Loader settings:

SettingDefaultDescription
serversnats://localhost:4222Comma-separated list of NATS servers
regionnot setRegion of this Ember instance: US, EU, AP or AU. When not set, _ is used
usernamenot setUser name for user/password authentication
passwordnot setPassword for user/password authentication
streamMaxAgeunlimitedMaximum age of records in the stream, for example 30d
streamMaxBytesunlimitedMaximum size of the stream, for example 100G
maxPendingAcks1024Number of asynchronous publications awaiting acknowledgement before the loader waits for them

Stream limits are applied when a stream is created and to an existing stream on startup. Limits that are not specified keep their current values (or NATS defaults).

Updatable Orders​

By default the EmberOrders stream receives one snapshot per order when the order reaches its final state (completely filled, canceled, expired, or rejected).

Set storeActiveOrders = true to publish a snapshot on every order change: order submission, acknowledgement, each fill, replace, restatement, and the final state. NATS streams are append-only, so every change is appended as a new record and the stream contains the complete history of each order.

warehouse {
nats {
storeActiveOrders = true # publish every order change

orders = [
${template.warehouse.nats.orders} {
loader.settings {
servers = "nats://nats-1:4222"
region = EU
}
}
]
}
}
note

This mode applies to orders only. The EmberMessages stream always contains every trading message.

Start Exporting Data​

Run Ember's data-warehouse script with a single argument that specifies the NATS data warehouse:

export EMBER_HOME=/deltix/emberhome
/deltix/ember/bin/data-warehouse nats

In Batch Mode, when the live parameter is set to false, this script exits as soon as all recent data is exported. If you re-run the script, it appends any new data that Ember accumulated since the last export: on startup each loader reads the last record of its stream and continues from the next journal sequence.

You can inspect the exported data using the NATS CLI:

nats stream ls
nats stream subjects EmberMessages
nats stream view EmberOrders
nats kv get EmberMetadata-EU-_ term

Data Format​

Subjects​

EmberMessages.term.<term>.r.<region>.t.<tenant>.<MessageType>
EmberOrders.term.<term>.r.<region>.t.<tenant>
TokenDescription
term.<term>Ember journal term (journal creation time). A new journal starts a new term and restarts sequence numbers
r.<region>Configured region, or _ when the region is not set
t.<tenant>Tenant. Tenants are not supported yet, so the value is always _
<MessageType>Ember message type of the record, for example OrderNewRequest

Stream names match the subject root. Subject keys follow the conventions of the C2 protocol: r for region, t for tenant, and _ for no value.

Headers​

HeaderDescription
Ember-SequenceEmber journal sequence of the record. For orders, the sequence of the last order event (close sequence)
Nats-Msg-Id<region>:<term>:<sequence> (with a :<n> suffix for records that share a sequence). JetStream discards duplicates within its duplicate window
Nats-Expected-StreamTarget stream name. NATS rejects the record if its subject belongs to another stream

Record Format​

Records are encoded with Protocol Buffers (proto3):

  • EmberMessages records are EmberMessage messages.
  • EmberOrders records are EmberOrder messages.

The schema file deltix/ember/warehouse/nats/warehouse.proto (package deltix.ember.warehouse.nats) is included in the deltix-ember-data-warehouse-nats-<version>.jar library of the Ember distribution. Use it to generate a client for your language. The schema imports google/type/decimal.proto from the Google common protos.

Field encoding:

Ember typeProtobuf typeDescription
Decimalgoogle.type.DecimalExact decimal value as a string, for example "5000.25". Values are not rounded
AlphanumericstringDecoded text, for example "CME" for sourceId
Timestampint64Unix epoch time. Nanoseconds for timestamp and original_timestamp of messages, milliseconds for all other timestamps
EnumerationenumEmber value with an enum-name prefix, for example SIDE_BUY for Side.BUY.
Textstring
Integerint32, int64

Messages Stream​

Each record contains one trading message. The message type is the last token of the subject and the type field of the record:

  • Requests: OrderNewRequest, OrderReplaceRequest, OrderCancelRequest, OrderStatusRequest.
  • Events: OrderPendingNewEvent, OrderNewEvent, OrderRejectEvent, OrderPendingCancelEvent, OrderCancelEvent, OrderCancelRejectEvent, OrderPendingReplaceEvent, OrderReplaceEvent, OrderReplaceRejectEvent, OrderTradeReportEvent, OrderTradeCancelEvent, OrderTradeCorrectEvent, OrderStatusEvent, OrderRestateEvent.

OrderDiscardRequest and OrderTradeAckEvent messages are not exported.

EmberMessage fields:

NameTypeDescription
typeMessageTypeMessage type, for example MESSAGE_TYPE_ORDER_NEW_REQUEST
sequenceint64Ember journal sequence of the message
sequence_numberint32Sequence number of the event reported by execution venue
timestampint64Message timestamp, nanoseconds
original_timestampint64Timestamp reported by execution venue, nanoseconds
source_idstringMessage source
destination_idstringMessage destination
order_idstringOrder ID assigned by order source
original_order_idstringOriginal order ID in cancel-replace chain
correlation_order_idstringID of the first order in order-replace chain
parent_source_idstringSource of parent order
parent_order_idstringID of parent order
request_idstringRequest ID
external_order_idstringOptional order ID assigned by execution venue
event_idstringEvent ID
reference_event_idstringUsed to identify previously communicated event
quote_idstringOptional ID of quote targeted by order
order_statusOrderStatusOrder status
symbolstringInstrument symbol
instrument_typeInstrumentTypeInstrument type
currencystringOrder currency
exchange_idstringOptional destination exchange for outbound message and source exchange for inbound messages
trader_idstringID of trader
trader_groupstringGroup of trader
accountstringOrder account
clearing_accountstringOrder clearing account
sideSideOrder side
time_in_forceTimeInForceOrder time in force
expire_timeint64Order expiration time
quantityDecimalOrder quantity
min_quantityDecimalMinimum quantity to execute
display_quantityDecimalMinimum quantity to display on exchange floor
order_typeOrderTypeOrder type
limit_priceDecimalLimit price
stop_priceDecimalStop price
peg_differenceDecimalPeg offset for pegged order types
average_priceDecimalAverage execution price
cumulative_quantityDecimalCumulative executed quantity
remaining_quantityDecimalRemaining order quantity
trade_priceDecimalOrder trade price
trade_quantityDecimalOrder trade quantity
commissionDecimalTrade commission
commission_currencystringTrade commission currency
discretion_offsetDecimalDiscretion offset
counter_party_source_idstringSource of the other side of the trade
counter_party_order_idstringOrder ID of the other side of the trade
settlement_dateint64Trade settlement date
settlement_typestringSettlement type
trade_dateint64Trade date
reasonstringReason of cancel and reject events
vendor_reject_codeint32Vendor specific reject code
deltix_reject_codeint32Reject code in Deltix classification
multi_leg_reporting_typeMultiLegReportingTypeIdentifies single-leg trade or whole contract trade of multi-legged security
aggressor_sideAggressorIndicatorTrade aggressor side
order_unknownboolFlag used by order cancel reject events
cancel_typeCancelTypeEnum used by cancel events
exec_restatement_reasonOrderRestateReasonTypeUsed by order restate events to classify restate type
flagsint32Bitmask containing various order flags
user_datastringUser-provided order tag
module_keystringUsed to identify strategy that placed the order
portfolio_keystringUsed to identify portfolio this order belongs to
partystringDescribes business entity involved in trade
clearing_brokerstringClearing broker
attributesrepeated AttributeCustom attributes of the message: int32 key and optional string value

Orders Stream​

Each record contains one order snapshot (see Updatable Orders).

EmberOrder fields:

NameTypeDescription
open_sequenceint64Sequence of new order request
close_sequenceint64Sequence of the last order event
source_idstringOrder source
destination_idstringOrder destination
order_idstringOrder ID assigned by order source
correlation_order_idstringID of the first order in order-replace chain
parent_source_idstringSource of parent order
parent_order_idstringID of parent order
external_order_idstringOptional order ID assigned by execution venue
accountstringOrder account
clearing_accountstringClearing account
trader_idstringID of trader
trader_groupstringGroup of trader
quote_idstringOptional ID of quote targeted by order
symbolstringInstrument symbol
instrument_typeInstrumentTypeInstrument type
exchange_idstringOptional destination exchange for outbound message and source exchange for inbound messages
currencystringOrder currency
sideSideOrder side
time_in_forceTimeInForceOrder time in force
expire_timeint64Order expiration time
order_statusOrderStatusOrder state at the time of the snapshot
open_timeint64Order submission time
close_timeint64Order completion time
order_typeOrderTypeOrder type
limit_priceDecimalLimit price
stop_priceDecimalStop price
peg_differenceDecimalPeg offset for pegged order types
quantityDecimalOrder quantity
display_quantityDecimalMinimum quantity to display on exchange floor
min_quantityDecimalMinimum quantity to execute
cumulative_quantityDecimalCumulative executed quantity
total_commissionDecimalTotal trade commission
commission_currencystringTrade commission currency
average_priceDecimalAverage execution price
user_datastringUser-provided order tag
module_keystringUsed to identify strategy that placed the order
portfolio_keystringUsed to identify portfolio this order belongs to
partystringDescribes business entity involved in trade
clearing_brokerstringClearing broker
settlement_dateint64Settlement date
reasonstringReason for cancel or reject events
deltix_reject_codeint32Reject code in Deltix classification
vendor_reject_codeint32Vendor specific reject code
attributesrepeated AttributeCustom order attributes: int32 key and optional string value. Written only with storeCustomAttributes = true, see How the Attributes field is formed