Messaging Subject Map: YU17-otc-rates
Every inherited NATS subject is carried forward with its existing contract, including the YU16
entries below. YU17 adds no subject, removes none and renames none (NFR-OTC06). A swap booking
travels the Aeron cluster ingress, not NATS, and produces no trade, no order-lifecycle transition
and no position; so the /trades, /orders and per-account families carry nothing new.
The one observable difference is content, on risk.extract.ready: the announcement gains four
fields naming the second artifact.
The Aeron Cluster channels and the gateway FIX endpoint are inherited from YU12 unchanged, with one
added commandType and one added egress ack kind on the existing templates.
Subject Familiesβ
-
/trades- producer:
trade-service(legacy REST path); YU12 adds the Aeron Cluster leader's trade-egress bridge (TradeNatsPublisher) as the primary producer; every booked trade (KIND_TRADE_BOOKEDon the deterministic apply stream) is published here so the cluster's fills reachtrade-processorβ the SQL DB β the/accounts/*/trades+/positionsUI feeds. Not the gateway egress (best-effort, submitting-session-only). Leader-only (no follower dupes); at-least-once, keyed bytradeSeq+side sotrade-processor(Trade JPA id) dedups. Enabled byTRADE_BRIDGE_NATS_URL. See ADR-048. - consumer:
trade-processor - delivery:
point-to-point - wildcard:
no - scope:
global - payload:
NatsEnvelope<TradeOrder>(typemust equalTradeOrder);payload={id, state, security, quantity, price, accountId, side}with stamped execution price - note (YU16): Treasury rows carry face in
quantityand fraction-of-par inprice.
- producer:
-
/accounts/<accountId>/trades- producer:
trade-processor - consumer: frontend trade blotter stream,
tick-storecapture - delivery:
broadcast - wildcard:
no(consumed with wildcard/accounts/*/trades) - scope:
per-account - payload: processed trade (includes
price) - note (YU16): may carry
state: RejectedwithrejectionReason/sourceOrderId; a rejected trade is never followed by a position message.
- producer:
-
/accounts/<accountId>/positions- producer:
trade-processor - consumer: frontend position blotter stream
- delivery:
broadcast - wildcard:
no - scope:
per-account - payload: position snapshot (includes
averageCostBasis) - note (YU16): Treasury rows carry face in
quantityand a fraction-of-par average at 6 dp.
- producer:
-
pricing.<TICKER>- producer:
price-publisher - consumer: frontend valuation streams,
trade-processor's EOD closing-price source,tick-storecapture, YU12 feed adapter (sequences conflated ticks as cluster ingress) - delivery:
broadcast - wildcard:
yes(pricing.*) - scope:
per-ticker - payload: market tick (
price,openPrice,closePrice,asOf,source) - note (YU16): see "YU16 change" below for Treasury payload extension.
- producer:
-
/accounts/<accountId>/orders- producer:
order-matcher - consumer: frontend account order blotter stream,
execution-algo-enginefill tracking (subscribes to NATS's catch-all>and filters client-side by subject prefix/suffix, since the literal/-separated subject has no.-token position for a real NATS wildcard) - delivery:
broadcast - wildcard:
no(no NATS-native wildcard is possible on this subject shape at all) - scope:
per-account - payload:
NatsEnvelopewrapping an order lifecycle event (orderId,status,remainingQuantity,limitPrice,lastExecutionPrice) underpayload
- producer:
-
/orders- producer:
order-matcher - consumer: frontend admin order blotter stream
- delivery:
broadcast - wildcard:
no - scope:
global - payload: order lifecycle event (
orderId,accountId,status,remainingQuantity,limitPrice)
- producer:
-
eod.prices.ready(JetStream, streamTRADERX_EOD)- producer:
trade-processor - consumer:
position-service(durable consumereod-pnl) - delivery:
durable point-to-point(JetStream file storage; redelivered until acked) - wildcard:
no - scope:
global - payload: gate event (
sessionDate,version,instrumentCount,publishedAtMillis)
- producer:
-
eod.pnl.done(JetStream, streamTRADERX_EOD)- producer:
position-service - consumer:
risk-extract(durable consumerrisk-extract, inherited from YU15) - delivery:
durable point-to-point(JetStream file storage; redelivered until acked) - wildcard:
no - scope:
global - payload: completion event (
sessionDate,version,accountsMarked,accountsHalted,completedAtMillis)
- producer:
-
algo.events.>(JetStream, streamTRADERX_ALGO_ENGINE)- producer:
execution-algo-engine - consumer:
execution-algo-engine(durable consumeralgo-engine-state, itself; no other subscriber in this state) - delivery:
durable point-to-point(JetStream file storage; explicit ack after applying to in-memory state, so a crash between append and ack redelivers) - wildcard:
yes(subject per event carriesalgo.events.<parentOrderId>) - scope:
global - payload: parent-order lifecycle event (
type,parentOrderId, plus type-specific fields)
- producer:
-
traderx.control.security.deltas(JetStream, streamTRADERX_CONTROL_SECURITY)- producer:
reference-data(outbox publisher, inherited from YU04) - consumer:
order-matcherrisk replica bootstrap (ControlFeedSubscriber) - delivery:
durable broadcast(JetStream file storage) - wildcard:
no - scope:
global - payload: security delta (
ticker,companyName) - note (YU16): content only; the ten new instrument keys flow as ordinary rows; the snapshot
bootstrap URL defaults to
/instruments/control-snapshotat this state's layer. Stream and subject names unchanged (TD-CDM02).
- producer:
-
risk.extract.cut- producer:
order-matcher-clusterleader only - consumer:
risk-extract - delivery:
broadcast(core NATS; one message per extract) - wildcard:
no - scope:
global - payload: the canonical position cut as US-ASCII text; a
#cutheader carryingschema,seq,sessionDateEpochDay,priceVersion,rows, then one row per(accountId, security)with quantity, average cost ticks, contract multiplier, and last trade price ticks. Unchanged in YU16 (#cut schema=1); the cut is engine state.
- producer:
-
risk.extract.ready- producer:
risk-extract - consumer: the external pricing/risk engine
- delivery:
broadcast - wildcard:
no - scope:
global - payload: delivery record;
schema(2 from this state),uri,consensusSequence,sessionDate,priceSnapshotVersion,rows,sha256,cutSha256,quiesceWitnessSequence
- producer:
FIX session endpoint (non-NATS)β
| Endpoint | Transport | Producer -> Consumer | Payload | Scope |
|---|---|---|---|---|
| gateway FIX port | FIX 4.4 / TCP (point-to-point session) | FIX initiator <-> fix-gateway acceptor | FIX 4.4 messages: A/0/1/2/3/4/5 session-level; D/F/H in; 8/9 out | cluster-internal |
The acceptor terminates on the gateway tier; the session survives BLP leader changes.
Aeron Cluster channels (non-NATS, inherited from YU12/YU15)β
| Endpoint | Transport | Producer -> Consumer | Payload | Scope |
|---|---|---|---|---|
| cluster ingress UDP | Aeron Cluster client protocol | gateway tier / feed adapter / risk-extract -> leader consensus module | SBE InputEventMessage ingress commands; SBE RiskExtractMessage (template 8) | cluster |
| cluster egress UDP | Aeron Cluster client protocol | leader -> gateway tier / feed adapter / risk-extract | committed admission responses, session events, marker acks | cluster |
| consensus/log UDP | Aeron Cluster consensus protocol | leader <-> followers | log replication, votes, heartbeats, commit positions | member pods |
| catch-up/snapshot UDP | Aeron Archive + cluster catch-up | serving member -> rejoining member | snapshot retrieval and committed-log replay | member pods |
YU16 change: pricing.<instrumentKey> carries Treasury payload extensionsβ
The subject, producer, consumers and every inherited field are unchanged; Treasury payloads add
assetClass, cleanPrice (fraction of par, equal to price),
priceSemantics: "CLEAN_FRACTION_OF_PAR", ytmPercent, yieldConvention, dayCount, quoteTimestamp (= asOf),
maturityDate, matured, simulated, officialSeedCleanPrice. The binary companion
pricing-tick-bin.<instrumentKey> carries round(fraction Γ 1e6) for a Treasury; six-decimal
precision; the inherited 3-dp rounding remains the equity/option contract. A matured Treasury's
payloads are suppressed. UST-* keys contain no ., so each remains one NATS token.
YU17 change: risk.extract.ready announces two artifactsβ
The subject, producer, consumer, delivery and every inherited field are unchanged. The payload
gains contractsSchema, contractsUri, contracts (the row count) and contractsSha256. Both
artifacts share the existing consensusSequence, sessionDate and cutSha256: one cut, one
instant, two files (ADR-064).
YU17 change: risk.extract.cut carries a second sectionβ
The subject and its single-message delivery are unchanged. The cut moves to schema 2 and appends a
#contracts section after the position rows; every line above the marker is byte-identical to what
schema 1 rendered for the same state. The section is present even when the portfolio holds no
swaps, so a consumer can never read an absent section as an empty one.
YU17: the swap path uses no subject at allβ
POST /swaps β Aeron cluster ingress (TYPE_SWAP_BOOK, template 1) β egress ack
(KIND_SWAP_BOOKED, kind 102). No NATS subject is involved in booking a swap, because a swap
produces nothing the read model carries: no trade to bridge, no order lifecycle to publish, no
position to project.
YU18 local bundle componentβ
YU18 adds no messaging subjects. Its CLI consumes completed files explicitly and does not subscribe to risk.extract.ready. The inherited entries above are preserved verbatim as lineage documentation; their historical descriptions are not delivery guarantees made by the bundle CLI.
The optional local .ready.json receipt contains the unchanged risk.extract.ready payload and precedes the notification. The bridge consumes receipt files, not the NATS subject; missed notifications therefore do not prevent local receipt discovery.
Optional GCS staging adds no messaging subject. It accepts a locally captured completion event or explicitly selected archive URI. Archive mode never emits a ready receipt from object presence alone.