Skip to main content

create_architect_trade_id

Function create_architect_trade_id 

Source
pub fn create_architect_trade_id(
    ts_event: UnixNanos,
    price: Price,
    quantity: Quantity,
    aggressor_side: AggressorSide,
) -> Result<TradeId>
Expand description

Creates a [TradeId] for an AX market-data trade.

AX publishes no trade identifier for market data: GET /trades and the market-data WebSocket both carry only ts, tn, s, p, q, and d, and tn is the nanosecond component of the timestamp rather than a sequence number. The composed timestamp alone is not unique either, because one aggressor sweeping several levels reports multiple prints at an identical ts and tn. Across 100 sandbox trades on 2026-07-25, GBPUSD-PERP yielded only 64 distinct timestamps.

The identity is therefore the composed timestamp plus a digest over the price, quantity, and aggressor side, which separated 99 of those 100 prints. Two prints identical in all five fields remain indistinguishable; nothing the venue publishes separates them. A sandbox run on 2026-07-25 observed exactly that, two JPYUSD-PERP prints agreeing on timestamp, price, quantity, and side, so the residual is real rather than theoretical at roughly 1 to 4 percent of prints.

That residual is accepted because a duplicate here cannot reach an execution. Live fills carry the venue’s own identifiers, fill.trade_id on REST and execution.tid on the orders WebSocket, and the backtest matching engine mints its own trade IDs with a per-timestamp counter. This function is called only from the two market-data parse_trade_tick functions, so a duplicate is a market-data fidelity limit and never an execution or position-accounting risk. Only a consumer that itself deduplicates on [TradeId] is affected; nothing in the Nautilus data path compares them.

Both transports must call this so the same trade fetched historically and received live gets one identity. The digest covers the parsed [Price] and [Quantity] rather than the wire text, because AX sends the same price as "1.339700000000" over REST and in a shorter form over the WebSocket.

Parity relies on both transports reporting the aggressor side. GET /trades always does, and no sandbox WebSocket trade has omitted it, but AxMdTrade::d is modelled as optional and an omitted side resolves to [AggressorSide::NoAggressor], which would not match the REST identity for that trade.

The result is exactly 36 characters, which is [TradeId]’s maximum, so a timestamp beyond 19 digits (year 2262) returns an error rather than silently truncating.

§Errors

Returns an error if the composed identity is not a valid [TradeId].