Message Reference¶
Learning objectives
After reading this page you will understand:
- What a message is in the context of a distributed bus system and how it differs from a function call or shared data structure
- How messages are defined in real systems (schema registries, IDL, plain JSON) and the pragmatic trade-offs EduMatcher makes
- What ZeroMQ requires of a message — frames, encoding, topic filters
- Why ZeroMQ has no broker, and what that means for reliability and operational complexity compared with broker-based systems (Kafka, RabbitMQ, NATS)
- The full catalogue of messages used in EduMatcher, their fields, and which processes produce and consume each one
Where the rest of this page comes from
Everything below the narrative sections — the topic index, the record types
and one section per message — is generated from spec/messages/*.yaml
by pm-msgen generate, and pm-msgen check fails in CI when the page and
the spec disagree.
This file, 270-preamble.md, is the hand-written half: bus concepts,
transports and the CALF protocol, none of which the spec can state. Edit it
freely. To change anything about a message, edit its spec file.
Background — Messages in a Bus System¶
What is a message?¶
A function call passes data synchronously inside a process: the caller blocks until the callee returns. A message is an asynchronous unit of data sent between processes. The sender does not wait for a response; it hands the message to the transport layer and moves on.
Messages carry three things:
- Identity — what kind of event this is (the topic or message type)
- Payload — the data describing the event
- Routing metadata — information the transport needs to deliver it (addresses, topic filters, sequence numbers)
How message formats are defined in real systems¶
In production systems there are three common approaches to defining what a message looks like:
Schema registries (Avro, Protobuf, Thrift) : Messages are defined in an Interface Definition Language (IDL) file. A code generator produces serialisers and deserialisers for every target language. The registry enforces compatibility: a new field may be added but existing fields cannot be removed or re-typed without a version bump. Kafka and gRPC use this model.
Canonical JSON/XML schemas (JSON Schema, OpenAPI, AsyncAPI) : Message shapes are described in a human-readable document (like AsyncAPI for event-driven systems). Any process that speaks JSON can produce or consume a message without a code generator. The schema document is the contract; violations surface only at runtime unless you add a validation library.
Hardcoded structures : The simplest approach — message shape is implicit in the code that creates and reads it. No IDL, no registry, no generator. Fast to build, but schema drift is invisible until something breaks at runtime.
EduMatcher uses the hardcoded approach. Each message type is created
by a helper function in src/edumatcher/models/message.py (e.g.
make_order_new_msg, make_gateway_connect_msg) and decoded by decode().
Every field documented on this page is exactly what those functions produce.
This is ideal for a learning system — you can read the code and immediately
see the message — but a real exchange would use Protobuf or Avro to enforce
schema contracts across teams and languages.
What ZeroMQ requires of a message¶
ZeroMQ does not impose a message format. It sees messages as one or more opaque frames — byte arrays that are sent and received atomically as a group. It is up to the application to define what those bytes mean.
EduMatcher uses exactly two frames for every message:
frame[0] → topic string, UTF-8 encoded
e.g. b"order.ack.GW01"
frame[1] → JSON payload, UTF-8 encoded
e.g. b'{"order_id": "3f2a...", "accepted": true}'
frame[0] doubles as the PUB/SUB filter key. A subscriber that
registers for prefix "order.ack.GW01" will receive only messages whose
first frame starts with that string — all other messages are dropped by the
ZeroMQ layer before the application even sees them. This prefix-match filter
is evaluated in the kernel's socket buffer, not in Python, so it adds almost
no CPU overhead regardless of how many message types are on the bus.
ZeroMQ without a broker¶
Most messaging systems interpose a broker between producers and consumers:
The broker buffers messages, persists them to disk, routes them to the right queues, and handles consumer acknowledgements. Examples: RabbitMQ, Apache Kafka, NATS JetStream, AWS SQS.
ZeroMQ is brokerless. Producers connect directly to consumers (or to the engine in PUSH/PULL):
There is no third process in the middle. The advantages and disadvantages flow directly from that choice:
Advantages of no broker
| Advantage | Detail |
|---|---|
| Lower latency | No extra network hop; messages go directly from sender to receiver |
| Fewer moving parts | No broker process to install, configure, monitor, or restart |
| No single point of failure | The engine is the bus; if the engine is up, the bus is up |
| Simpler deployment | pip install pyzmq is the entire installation |
Disadvantages of no broker
| Disadvantage | Detail |
|---|---|
| No persistence | If a subscriber is down when a message is published, the message is gone forever |
| No guaranteed delivery | PUB/SUB drops messages to slow subscribers without warning |
| No replay | You cannot re-consume old messages; there is no commit log |
| Tight coupling on addresses | Producers must know the address of the engine; adding a new engine address requires reconfiguring all clients |
| No backpressure | A fast publisher can overwhelm a slow subscriber; the subscriber's receive buffer fills and messages are silently discarded |
For EduMatcher these trade-offs are acceptable: the system runs on localhost, sessions last hours not days, and correctness over time is handled by the GTC persistence layer rather than the message bus. For a real exchange, the audit trail would be written by a Kafka consumer (guaranteed delivery, infinite replay), and the matching engine would use a persisted queue for order intake.
Message structure¶
Every inter-process communication in EduMatcher is a two-frame ZeroMQ multipart message. ZeroMQ (ZMQ) is a high-performance messaging library; a "multipart message" is simply an ordered list of byte-array frames sent and received atomically:
| Frame | Content |
|---|---|
frame[0] |
Topic string (UTF-8) — used for PUB/SUB prefix filtering |
frame[1] |
JSON payload (UTF-8) |
Transport channels¶
| Channel | ZMQ pattern | Address | Direction |
|---|---|---|---|
| Order submission | PUSH → PULL | tcp://127.0.0.1:5555 |
Gateway → Engine |
| Event broadcast | PUB → SUB | tcp://127.0.0.1:5556 |
Engine → all subscribers |
| Drop copy | PUB → SUB | tcp://127.0.0.1:5557 |
Engine → drop-copy consumers |
The dedicated drop-copy channel is implemented in
src/edumatcher/engine/drop_copy.py and is documented in more detail on the
Drop Copy page.
flowchart LR
GW["pm-alf-console"] -- "PUSH\n:5555" --> ENG["pm-engine"]
SCH["pm-scheduler"] -- "PUSH\n:5555" --> ENG
ADM["pm-admin"] -- "PUSH\n:5555" --> ENG
ENG -- "PUB\n:5556" --> GW
ENG -- "PUB\n:5556" --> VW["pm-viewer"]
ENG -- "PUB\n:5556" --> ORD["pm-orders"]
ENG -- "PUB\n:5556" --> CLR["pm-clearing"]
ENG -- "PUB\n:5556" --> STS["pm-stats"]
ENG -- "PUB\n:5556" --> AUD["pm-audit"]
ENG -. "PUB\n:5557" .-> DC["Drop-copy\nconsumers"]
system.gateway_connect¶
Motivation: Enables explicit control/state synchronization so clients do not depend on timing of unsolicited events. Published by: Requesting client process (for example pm-alf-console, pm-admin, pm-viewer, pm-stats, bots, or API gateway) via PUSH :5555
Sent by an ALF gateway at startup to authenticate its gateway ID against
engine_config.yaml.
sequenceDiagram
participant GW as pm-alf-console
participant ENG as pm-engine
GW->>ENG: system.gateway_connect\n{gateway_id: "TRADER01"} (PUSH :5555)
alt ID is in gateways.alf
ENG-->>GW: system.gateway_auth.TRADER01\n{accepted: true, description: "..."} (PUB :5556)
Note over GW: Enters command loop
else ID not configured
ENG-->>GW: system.gateway_auth.TRADER01\n{accepted: false, reason: "Gateway not configured: TRADER01"}
Note over GW: Exits
end
| Field | Type | Description |
|---|---|---|
gateway_id |
string | Gateway identifier being requested (e.g. TRADER01) |
Reply: system.gateway_auth.{GW_ID}
| Field | Type | Description |
|---|---|---|
gateway_id |
string | Gateway identifier |
accepted |
boolean | true if ID is configured in gateways.alf |
reason |
string | Rejection reason when accepted=false |
description |
string | Optional configured description for the gateway |
When accepted=false, the gateway must terminate and MUST NOT submit orders.
order.new¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: Requesting client process (for example pm-alf-console, pm-admin, pm-viewer, pm-stats, bots, or API gateway) via PUSH :5555
Sent by a gateway to submit a new order for matching.
| Field | Type | Description |
|---|---|---|
id |
string (UUID) | Unique order identifier |
symbol |
string | Instrument ticker, e.g. MSFT |
side |
"BUY" | "SELL" |
Order side |
order_type |
string | MARKET, LIMIT, STOP, STOP_LIMIT, FOK, IOC, ICEBERG, TRAILING_STOP |
tif |
"DAY" | "GTC" | "ATO" | "ATC" | "FOK" |
Time-in-force |
quantity |
integer | Total order quantity |
remaining_qty |
integer | Unfilled quantity (equals quantity on submission) |
gateway_id |
string | Originating gateway identifier, e.g. TRADER01 |
timestamp |
float | Unix epoch (seconds) |
status |
string | Initial status, always "NEW" |
price |
float | null | Limit price (LIMIT, STOP_LIMIT, FOK, ICEBERG) |
stop_price |
float | null | Trigger price (STOP, STOP_LIMIT) |
visible_qty |
integer | null | Peak size for ICEBERG orders |
displayed_qty |
integer | null | Current visible slice (ICEBERG) |
trail_offset |
float | null | Offset from best price for TRAILING_STOP orders |
smp_action |
string | null | Self-match prevention: NONE, CANCEL_AGGRESSOR, CANCEL_RESTING, CANCEL_BOTH. null when the client omitted SMP=, in which case the engine resolves it to the gateway's configured gateways.alf[].smp_action default (else "NONE") before the order reaches the book — see Configuration — Gateway Fields |
client_tag |
string | absent | Optional client-supplied tag echoed back on every lifecycle event for this order (ack, fill, cancelled, expired). When present, subscribers can map events back to their submission without a FIFO scheme. |
arrival_seq |
integer | Engine-assigned monotonic arrival sequence that determines time priority within a price level. Not supplied by the client (0 on submission); populated by the engine and echoed in outbound order snapshots (see order.orders.{GW_ID}). |
oco_group_id |
string | null | Set once this order is linked into an OCO pair via order.oco; null on a plain submission |
combo_parent_id |
string | null | Parent ComboOrder.id when this order is a combo child leg; null for a standalone order |
leg_index |
integer | null | 0-based position within the parent combo's leg list; null for a standalone order |
origin |
"ORDER" | "QUOTE" | "IMPLIED" |
How this order entered the book: a direct order submission, a market-maker quote leg, or an engine-implied order |
quote_id |
string | null | Set when origin is "QUOTE", echoing the originating quote.new's identifier; null otherwise |
Valid field combinations by order type:
order_type |
price |
stop_price |
visible_qty |
trail_offset |
Notes |
|---|---|---|---|---|---|
MARKET |
— | — | — | — | Fills at best available; rejected if symbol halted |
LIMIT |
Required | — | — | — | Rests if no match; subject to collar check |
STOP |
— | Required | — | — | Triggers a market order when stop price touched |
STOP_LIMIT |
Required | Required | — | — | Triggers a limit order when stop price touched |
FOK |
Required | — | — | — | Fill fully immediately or cancel entirely |
IOC |
Optional | — | — | — | Fill as much as possible immediately, cancel remainder |
ICEBERG |
Required | — | Required | — | Shows only visible_qty; replenishes from hidden reserve |
TRAILING_STOP |
— | — | — | Required | Stop price follows best opposite-side price by trail_offset |
order.cancel¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: Requesting client process (for example pm-alf-console, pm-admin, pm-viewer, pm-stats, bots, or API gateway) via PUSH :5555
Sent by a gateway to cancel a resting order.
| Field | Type | Description |
|---|---|---|
order_id |
string (UUID) | ID of the order to cancel |
gateway_id |
string | Gateway that owns the order |
order.amend¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: Requesting client process (for example pm-alf-console, pm-admin, pm-viewer, pm-stats, bots, or API gateway) via PUSH :5555
Sent by a gateway to amend the price and/or quantity of a resting order.
| Field | Type | Description |
|---|---|---|
order_id |
string (UUID) | ID of the order to amend |
gateway_id |
string | Gateway that owns the order |
price |
float | absent | New limit price (omit to keep current) |
qty |
integer | absent | New total quantity (omit to keep current) |
At least one of price or qty must be present.
Priority rules:
- Quantity decrease only → priority preserved (timestamp unchanged)
- Price change or quantity increase → priority lost (new timestamp assigned)
Reply: order.amended.{GW_ID} on success, or order.ack.{GW_ID} with accepted=false on rejection.
quote.new¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: Requesting client process (for example pm-alf-console, pm-admin, pm-viewer, pm-stats, bots, or API gateway) via PUSH :5555
Sent by a market-maker gateway to submit or replace a two-sided quote. Role requirements and MM obligation controls are documented in Configuration - Role Privileges.
| Field | Type | Description |
|---|---|---|
gateway_id |
string | Originating gateway identifier |
symbol |
string | Instrument ticker |
quote_id |
string | absent | Optional client-provided quote label |
bid_price |
float | Bid price |
bid_qty |
integer | Bid quantity |
ask_price |
float | Ask price |
ask_qty |
integer | Ask quantity |
tif |
string | Quote leg time-in-force (DAY or GTC) |
Replies:
quote.ack.{GW_ID}quote.status.{GW_ID}
quote.ack.{GW_ID}¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: pm-engine via PUB :5556
Acknowledgement of a quote.new submission.
| Field | Type | Description |
|---|---|---|
quote_id |
string | Client-provided quote label (echoed from request) |
accepted |
boolean | true = accepted; false = rejected |
reason |
string | Rejection reason (empty string when accepted) |
bid_order_id |
string (UUID) | Order ID of the bid leg (present when accepted) |
ask_order_id |
string (UUID) | Order ID of the ask leg (present when accepted) |
quote.status.{GW_ID}¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: pm-engine via PUB :5556
Published when the quote's lifecycle state changes (e.g. a fill inactivates the quote or a cancel removes it).
| Field | Type | Description |
|---|---|---|
quote_id |
string | Client-provided quote label |
status |
string | New quote state (see values below) |
reason |
string | Additional context (e.g. halt reason); empty when not applicable |
Status values:
| Value | Meaning |
|---|---|
ACTIVE |
Quote successfully placed on the book (both legs resting) |
INACTIVE_BID_FILLED |
Bid leg filled; ask leg auto-cancelled |
INACTIVE_ASK_FILLED |
Ask leg filled; bid leg auto-cancelled |
CANCELLED |
Quote removed (explicit cancel, kill switch, or halt) |
quote.cancel¶
Motivation: Keeps order/quote lifecycle state synchronized between the initiating client and all interested subscribers. Published by: Requesting client process (for example pm-alf-console, pm-admin, pm-viewer, pm-stats, bots, or API gateway) via PUSH :5555
Cancel the active quote for one symbol.
| Field | Type | Description |
|---|---|---|
gateway_id |
string | Gateway identifier |
symbol |
string | Instrument ticker |
CALF TCP protocol (pm-md-gwy)¶
pm-md-gwy bridges the internal ZMQ bus documented above to a separate,
external-facing protocol: a newline-delimited UTF-8 text feed on TCP port
5570 (MSGTYPE|KEY=VALUE|KEY=VALUE\n lines), independent of ZMQ topics and
payload shapes. It is not just a passthrough of the messages above — it
normalises, re-sequences, and reshapes them into CALF's own message types.
| Channel | Message type | Wildcard (SYM=*) |
Baseline SNAP? |
Carries |
|---|---|---|---|---|
TOP |
MD |
Yes | Yes | Best bid/ask/last |
TRADE |
TRADE |
Yes | No | Individual trade prints |
STATE |
STATE |
Yes | Yes | Session/symbol state transitions |
INDEX |
IDX |
No | Yes | Index level updates |
DEPTH |
DEPTH |
No | Yes | Aggregated multi-level order book (Level 2) |
AUCTION |
AUCTION |
Yes | No | Auction uncross result (equilibrium price/qty, imbalance) |
CB |
CB |
No | Yes | Circuit-breaker halt/resume detail beyond STATE's coarse transition |
Client requests are HELLO (authenticate), SUB/UNSUB (subscribe/cancel),
RESUME (replay one stream from a known sequence — repeatable, one per
stream), SYMBOLS (ask which instruments exist), PING, and EXIT. Gateway replies include WELCOME, a baseline
SNAP per new stream on the five channels that have one, the seven message
types above, periodic HB heartbeats, and ERR on protocol/subscription
violations.
Two behaviours are easy to get wrong from the table alone:
MDis a delta, and an empty value is meaningful. Only changed fields are sent; an omitted field means unchanged. An explicitly emptyBID=orASK=means that book side is now empty and the client must discard the price rather than keep the last one it saw.RESUMEis per stream.LASTSEQdescribes one(CH, SYM)position, so a reconnecting client sends oneRESUMEper stream it was following. It is not a flag onHELLO.- Ask for the symbol universe; do not wait for it.
WELCOME|SYMBOLS=is optional and omitted entirely by a gateway started without a readable engine config. SendSYMBOLSafter the handshake, and read itsCOUNT— an empty universe omits the list rather than sending it empty. - Read
REF=before you format a price. It carries each symbol's display precision asSYM:DECtuples, on bothWELCOMEand theSYMBOLSreply, and is the only route a market data client has to that value. A gateway that predates the field omits it entirely, in which case assume2decimals — knowingly, because getting this wrong rounds every price the client shows.
The full protocol — every field table, the WELCOME/SNAP handshake,
sequence-gap detection and RESUME recovery, subscription limits, and the
complete error-code table — is maintained in one place to avoid drift:
Market Data Feed (CALF), with the normative
wire-format specification in the
CALF Protocol Reference.
See also¶
- Processes — which process subscribes to which topic prefix
- ALF Console — how participants receive fill, book, and risk events
- Commands —
ExchangeCommandClientmethods and their underlying message topics - Drop Copy — the separate :5557 socket for fill-only event feeds
- Risk Controls —
risk.*message payloads in detail - CALF TCP Protocol — external market-data feed message types
Topic index¶
Every topic in the system, and which process puts it on the wire.
| Topic | Family | Published by |
|---|---|---|
admin.action.{gateway_id} |
admin |
engine |
auction.indicative.{symbol} |
auction |
engine |
auction.result.{symbol} |
auction |
engine |
book.snapshot_request |
book |
stats, gateway |
book.{symbol} |
book |
engine |
circuit_breaker.extend.{symbol} |
circuit_breaker |
engine |
circuit_breaker.halt.{symbol} |
circuit_breaker |
engine |
circuit_breaker.resume.{symbol} |
circuit_breaker |
engine |
combo.ack.{gateway_id} |
structure |
engine |
combo.status.{gateway_id} |
structure |
engine |
depth.{symbol} |
book |
engine |
drop_copy.event.{gateway_id} |
drop_copy |
engine |
drop_copy.replay.{recipient_id} |
drop_copy |
engine |
execution_report (no bus topic) |
order |
gateway |
index.constituent_change |
index |
admin |
index.constituent_change_ack.{gateway_id} |
index |
index |
index.corp_action |
index |
admin |
index.corp_action_ack.{gateway_id} |
index |
index |
index.error.{gateway_id} |
index |
index |
index.history.{gateway_id} |
index |
index |
index.history_request |
index |
admin, api_gateway, gateway |
index.rebalance |
index |
api_gateway |
index.rebalance_ack.{gateway_id} |
index |
index |
index.update |
index |
index |
log.backfill.{sub_id} |
log |
log_server |
log.backfill_request |
log |
log_client |
log.error.{sub_id} |
log |
log_server |
log.event.{sub_id} |
log |
log_server |
log.lease_expired.{sub_id} |
log |
log_server |
log.notify.{sub_id} |
log |
log_server |
log.renew |
log |
log_client |
log.renew_ack.{sub_id} |
log |
log_server |
log.server_state |
log |
log_server |
log.status.{sub_id} |
log |
log_server |
log.status_request |
log |
log_client |
log.subscribe |
log |
log_client |
log.subscribe_ack.{sub_id} |
log |
log_server |
log.unsubscribe |
log |
log_client |
log.unsubscribe_ack.{sub_id} |
log |
log_server |
oco.ack.{gateway_id} |
structure |
engine |
oco.cancelled.{gateway_id} |
structure |
engine |
order.ack.{gateway_id} |
order |
engine |
order.amend |
order |
gateway |
order.amended.{gateway_id} |
order |
engine |
order.cancel |
order |
gateway |
order.cancelled.{gateway_id} |
order |
engine |
order.combo |
order |
gateway |
order.combo_cancel |
order |
gateway |
order.expired.{gateway_id} |
order |
engine |
order.fill.{gateway_id} |
order |
engine |
order.new |
order |
gateway |
order.oco |
order |
gateway |
order.oco_cancel |
order |
gateway |
order.orders.{gateway_id} |
order |
engine |
order.orders_request |
order |
admin, api_gateway, gateway |
quote.ack.{gateway_id} |
quote |
engine |
quote.cancel |
quote |
admin, api_gateway, gateway |
quote.new |
quote |
gateway |
quote.status.{gateway_id} |
quote |
engine |
risk.cancel_symbol |
risk |
admin, api_gateway |
risk.cancel_symbol_ack.{gateway_id} |
risk |
engine |
risk.circuit_breaker_halt_all |
risk |
admin |
risk.circuit_breaker_halt_all_ack.{gateway_id} |
risk |
engine |
risk.circuit_breaker_resume_all |
risk |
admin |
risk.circuit_breaker_resume_all_ack.{gateway_id} |
risk |
engine |
risk.kill_switch |
risk |
admin, api_gateway, gateway |
risk.kill_switch_ack.{gateway_id} |
risk |
engine |
risk.kill_switch_gateway |
risk |
api_gateway |
risk.kill_switch_gateway_ack.{gateway_id} |
risk |
engine |
risk.kill_switch_global |
risk |
api_gateway |
risk.kill_switch_global_ack.{gateway_id} |
risk |
engine |
risk.symbol_halt |
risk |
admin, api_gateway |
risk.symbol_halt_ack.{gateway_id} |
risk |
engine |
risk.symbol_resume |
risk |
admin, api_gateway |
risk.symbol_resume_ack.{gateway_id} |
risk |
engine |
session.state |
session |
engine |
session.transition |
session |
scheduler |
session.transition_ack.{gateway_id} |
session |
engine |
system.eod |
system |
engine |
system.gateway_auth.{gateway_id} |
system |
engine |
system.gateway_bye.{gateway_id} |
system |
engine |
system.gateway_connect |
system |
admin, api_gateway, gateway |
system.gateway_disconnect |
system |
admin, api_gateway, gateway |
system.gateways.{gateway_id} |
system |
engine |
system.gateways_request |
system |
admin, api_gateway |
system.halt_status.{gateway_id} |
system |
engine |
system.halt_status_request |
system |
api_gateway |
system.position_request |
system |
gateway |
system.position_snapshot.{gateway_id} |
system |
engine |
system.quote_bootstrap.{gateway_id} |
system |
engine |
system.quote_bootstrap_request |
system |
admin, api_gateway, gateway |
system.quote_legs.{gateway_id} |
system |
engine |
system.quote_legs_request |
system |
api_gateway, gateway |
system.reference.{gateway_id} |
system |
engine |
system.reference_reload |
system |
api_gateway |
system.reference_reload_ack.{gateway_id} |
system |
engine |
system.reference_request |
system |
api_gateway |
system.risk_state.{gateway_id} |
system |
engine |
system.risk_state_request |
system |
api_gateway |
system.session_schedule.{gateway_id} |
system |
engine |
system.session_schedule_request |
system |
admin, api_gateway |
system.session_state_request |
system |
admin, api_gateway, gateway, scheduler |
system.session_status.{gateway_id} |
system |
engine |
system.symbols.{gateway_id} |
system |
engine |
system.symbols_request |
system |
admin, api_gateway, gateway, stats |
system.volume.{gateway_id} |
system |
engine |
system.volume_request |
system |
admin |
trade.executed |
trade |
engine |
Family admin¶
Record types¶
AdminActionScope¶
What one admin command acted on, and what it did. Every field is optional because every action uses a different subset -- see the family header on why that is a stated limitation rather than a variant type. The name is inherited from the wire key rather than chosen: three of the seven fields are outcome counts rather than scope, which makes "AdminActionScope" a slightly generous reading of its own contents. It was kept because renaming the key is a wire change for every /admin/monitor client and buys nothing a reader can use.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
omitted when unset | max_len 16 | The instrument, when the command named one. kill_switch.self used to emit this as an explicit null when unscoped while its siblings omitted the key -- two spellings of one absence, and this is now the second. |
target_gateway_id |
string |
omitted when unset | max_len 32 | The participant acted upon, for kill_switch.gateway. |
level |
string |
omitted when unset | max_len 32 | The circuit-breaker rung, for circuit_breaker.trigger. Not an enum: the ladder is per-symbol configuration, so the value set differs per deployment. Bounded to match circuit_breaker.halt.level, which carries the same name onward. |
note |
string |
omitted when empty | max_len 256 | The operator's free-text reason, when one was supplied. Regime 4 to match risk.kill_switch's own note, which is where this value arrives from -- a field that omits on one message and emits "" on the next would be two answers to one question. |
cancelled_orders |
int |
omitted when unset | ge 0, unit dimensionless |
Outcome, on accepted kill-switch actions only. |
cancelled_quotes |
int |
omitted when unset | ge 0, unit dimensionless |
Outcome, on accepted kill-switch actions only. |
affected_gateways |
int |
omitted when unset | ge 0, unit dimensionless |
Outcome, on an accepted kill_switch.global only. |
admin.action.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to the admin monitor: one admin-gated command ran, who ran it, what it acted on, and whether it was accepted.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | The ADMIN caller. Topic-only; the body says the same thing as initiator_gateway_id. |
command_id |
string |
required | max_len 64 | |
initiator_gateway_id |
string |
required | max_len 32 | |
action |
enum: kill_switch.self, kill_switch.gateway, kill_switch.global, kill_switch.symbol, circuit_breaker.trigger, circuit_breaker.resume |
required | — | Which command ran. The six the engine publishes, enumerated rather than left a free string: a seventh admin command that forgets to declare itself here fails loudly at its first invocation, which is better than appearing in the monitor as a value no client renders. The values look like topics and are not: circuit_breaker.trigger is the action behind risk.symbol_halt, and there is no circuit_breaker.trigger topic anywhere. |
scope |
AdminActionScope |
required | — | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | Why it was rejected; "" on an accepted action. |
Note
This is the one topic in the system addressed to a gateway that is not for that gateway.
The suffix names the ADMIN caller so a monitor can filter by operator, but the event must never reach that caller's own private trading stream -- EngineClient._handle_event checks the prefix before the private/market-data split for exactly that reason, and ADMIN_ACTION_PREFIX is deliberately absent from PRIVATE_PREFIXES.
initiator_gateway_id repeats the topic suffix in the body.
That is redundant on the live wire and load-bearing off it: an event stored, forwarded or rendered without its topic still says who ran the command.
Publishing is a no-op without a command_id — with nothing to correlate against, a monitor record is an entry no client can tie to a request.
See also: risk.kill_switch, risk.symbol_halt, circuit_breaker.halt.{SYMBOL}
Family auction¶
auction.indicative.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all: where one symbol would uncross if the call phase ended now. Published repeatedly while an opening or closing auction collects orders.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
phase |
enum: OPENING_AUCTION, CLOSING_AUCTION |
required | — | Which call phase is running. These are the only two states models/session.py::is_auction_phase admits, and the producer returns early for every other one. |
eq_price |
float |
null when unset |
gt 0, unit display_price |
Indicative equilibrium price, or null if the book would not cross. |
eq_qty |
int |
required | ge 0, unit shares |
Quantity that would execute. Zero is a true reading and is always emitted, unlike eq_price, which has no zero. |
imbalance_side |
enum: BUY, SELL |
omitted when unset | — | Which side would be left unfilled. Absent when the book is balanced at the indicative price. |
imbalance_qty |
int |
required | ge 0, unit shares |
Surplus on imbalance_side; zero when balanced. |
Note
The difference from auction.result is tense.
That one reports what happened; this one reports what would happen if the phase ended now, and a client must not mistake the second for the first -- which is why md_gateway projects it to a CALF INDIC rather than an AUCTION.
eq_price is null when the book would not cross at all.
That is a real and informative state during a call phase -- nothing would trade yet -- and is not the same as a price of zero, so it is a null rather than an omission.
Field names are shared with circuit_breaker.extend's indicative deliberately.
A reopening auction and a scheduled one are the same mechanism, and a client that learned to read one should not have to learn the other.
See also: auction.result.{SYMBOL}, circuit_breaker.extend.{SYMBOL}
auction.result.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all: one symbol's uncross has completed. Published for every uncross, including the ones that printed nothing.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
eq_price |
float |
null when unset |
gt 0, unit display_price |
Uncross price, or null when there was no crossable interest. |
eq_qty |
int |
required | ge 0, unit shares |
Quantity executed; zero when nothing crossed. |
trades_count |
int |
required | ge 0, unit dimensionless |
How many trades the uncross printed. |
imbalance_side |
enum: BUY, SELL |
omitted when unset | — | Which side was left unfilled. Absent when the book was balanced at the uncross price. |
imbalance_qty |
int |
required | ge 0, unit shares |
Surplus on imbalance_side; zero when balanced. |
reason |
enum: SCHEDULED, REOPEN, RECOVERY, BACKSTOP |
required | — | Which of the four uncross paths produced this event. |
Note
reason says which uncross this was, because the four are otherwise indistinguishable to a consumer and a client cannot tell a circuit breaker reopening from the closing one: SCHEDULED - leaving an auction or other non-matching session phase REOPEN - a halted symbol reopening at the end of its halt RECOVERY - restored GTC orders uncrossed at engine startup BACKSTOP - the closing backstop forcing a still-halted symbol to reopen, printing at the corridor boundary rather than at the outlying equilibrium There is no persistent state to snapshot here, unlike TOP or DEPTH: every event is forwarded as its own independent CALF event.
See also: auction.indicative.{SYMBOL}, trade.executed
Family book¶
Record types¶
BookLevel¶
One aggregated price level. Iceberg orders contribute only their displayed quantity, so qty is what a viewer should show rather than what is actually resting.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
price |
float |
required | unit display_price |
|
qty |
int |
required | unit shares |
Aggregate visible size at this level. |
count |
int |
required | unit dimensionless |
How many orders make up the level. |
RecentTrade¶
One entry of the book's trade tape. A trimmed view of the public trade.executed print - the last five, carried with the snapshot so a viewer that has just subscribed has some history to draw.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
id |
string |
required | max_len 64 | |
symbol |
string |
required | max_len 16 | |
buy_order_id |
string |
required | max_len 64 | |
sell_order_id |
string |
required | max_len 64 | |
buy_gateway_id |
string |
required | max_len 32 | |
sell_gateway_id |
string |
required | max_len 32 | |
price |
float |
required | unit display_price |
|
quantity |
int |
required | unit shares |
|
timestamp |
float |
required | unit epoch_seconds |
Seconds, not nanoseconds: the snapshot divides by 1e9. |
book.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Broadcast an aggregated view of one instrument's order book, on a timer. What every viewer and terminal renders.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
tick_decimals |
int |
required | unit dimensionless |
The tick scale the display prices here were produced at. Subscribers that store prices exactly need it to convert back to integer ticks; without it they must guess, and guessing 2 for a 4-decimal symbol rounds the price away. |
bids |
list of BookLevel |
required | — | Descending by price. |
asks |
list of BookLevel |
required | — | Ascending by price. |
last_price |
float |
null when unset |
unit display_price |
Null until the instrument has traded. |
last_qty |
int |
null when unset |
unit shares |
|
last_buy_price |
float |
null when unset |
unit display_price |
|
last_sell_price |
float |
null when unset |
unit display_price |
|
recent_trades |
list of RecentTrade |
required | — | The last five prints, oldest first. |
Note
Every key is always present; the four last_* fields carry null on a book that has not traded.
The payload is OrderBook.snapshot() exactly.
See also: depth.{SYMBOL}, book.snapshot_request
book.snapshot_request¶
Published by: stats, gateway
Transport: engine_pub
Since: 1.0
Ask the engine to publish one symbol's book immediately.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 |
See also: book.{SYMBOL}
depth.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Book-depth metrics within a tolerance band of the last trade: how much size sits nearby, which way it leans, and what it costs to move.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
mid_price_ticks |
ticks |
required | unit ticks |
The last trade price, in ticks; the band is centred here. |
mid_price |
float |
required | unit display_price |
|
tolerance_ticks |
ticks |
required | unit ticks |
Half-width of the band, in ticks. |
bid_depth |
int |
required | unit shares |
Total resting size between mid - tolerance and mid. |
ask_depth |
int |
required | unit shares |
Total resting size between mid and mid + tolerance. |
imbalance |
float |
required | ge -1, le 1, unit dimensionless |
(bid - ask) / total, in [-1, 1]; positive means more bids. |
microprice |
float |
required | unit display_price |
Imbalance-weighted mid; falls back to mid_price. |
cost_to_move |
float |
required | unit money |
Display notional a buyer must spend to sweep every ask in the band. Summed in ticks and converted once, not per level. |
Note
Not published at all for a book with no last trade - depth_snapshot returns an empty dict and the engine skips it - so every field here is required rather than nullable.
See also: book.{SYMBOL}
Family circuit_breaker¶
circuit_breaker.halt.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all: one symbol has stopped trading. New orders rest rather than match until the halt ends, and every resting quote on the symbol has already been cancelled by the time this is published.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
trigger_price |
float |
null when unset |
gt 0, unit display_price |
The trade price that breached the level. Null on an ADMIN halt. |
reference_price |
float |
null when unset |
gt 0, unit display_price |
The price the breach was measured against. Null on an ADMIN halt. |
resume_at_ns |
int |
null when unset |
unit epoch_nanos |
When the current call phase ends. Null means indefinite: the halt lasts until an operator resumes it. ACE moves this on every extension, so a consumer that ignores circuit_breaker.extend will hold a value that has already passed. |
halt_source |
enum: CB, ADMIN |
omitted when unset | — | What put the symbol into the halt. Mirrors CircuitBreakerState.halt_source, which is None only while no halt is in effect -- so the key is present on every halt event. |
level |
string |
omitted when unset | max_len 32 | Which rung of the ladder fired -- a name from the symbol's circuit_breaker.levels config -- or ADMIN_ALL / ADMIN_SYMBOL for an operator halt. Not an enum: the ladder is configuration, so the value set differs per deployment. |
corridor_low |
float |
omitted when unset | gt 0, unit display_price |
Lower bound of the ACE reopening corridor. Absent when the halt has no corridor: either ACE is disabled or the halt began with no reference price to centre one on. |
corridor_high |
float |
omitted when unset | gt 0, unit display_price |
Upper bound of the ACE reopening corridor. Absent with the low. |
expansion |
int |
omitted when unset | ge 0, unit dimensionless |
Rungs of the expansion ladder consumed so far; 0 in the initial call phase. Absent whenever the corridor is, because a halt that cannot have a corridor can never widen one. |
Note
halt_source says what caused the halt, not how it will end: every halt ends in a reopening auction call, because LIMIT orders accumulate freely while a symbol is halted and resuming without an uncross would start continuous trading on a crossed book.
trigger_price and reference_price are the price that fired the breaker and the price it was measured against.
Both are null on an ADMIN halt, which fires on an operator's decision rather than on a price, and both may be null on a price-triggered halt that had no reference to latch.
resume_at_ns is null for an indefinite halt -- halt_all and a per-symbol halt named without a level both produce one, and it lasts until an explicit resume.
See also: circuit_breaker.extend.{SYMBOL}, circuit_breaker.resume.{SYMBOL}
circuit_breaker.extend.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all: the call phase ended with the indicative price outside the corridor, so the symbol stays halted, the corridor widens by one rung and a fresh call phase begins.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
indicative_price |
float |
required | gt 0, unit display_price |
Where the symbol would have reopened, outside the corridor. |
indicative_qty |
int |
required | gt 0, unit shares |
Quantity that would have executed. Always above zero: the producer only extends when the indicative uncross would have traded. |
imbalance_side |
enum: BUY, SELL |
omitted when unset | — | Which side is unfilled at the indicative price. Absent when the book is balanced there. |
resume_at_ns |
int |
required | unit epoch_nanos |
End of the new call phase. Always set; extend() computes it. |
corridor_low |
float |
required | gt 0, unit display_price |
Lower bound of the widened corridor. |
corridor_high |
float |
required | gt 0, unit display_price |
Upper bound of the widened corridor. |
expansion |
int |
required | gt 0, unit dimensionless |
Rungs consumed after this widening -- at least 1, since the event is published by the widening itself. |
Note
The symbol's state does not change here -- an extension is a continuation of the same halt -- so md_gateway deliberately does not re-emit a STATE event for it, only the moved corridor and resume time.
The corridor fields are the corridor after widening, and unlike on circuit_breaker.halt they are always present: this event can only be produced on a path that has just asserted a corridor exists.
indicative_price and indicative_qty are the imbalance indicator a real venue disseminates during a reopening.
They are what lets a participant supply the offsetting interest that resolves the halt, which only works while there is still time to act.
See also: circuit_breaker.halt.{SYMBOL}
circuit_breaker.resume.{symbol}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all: the symbol is trading again. It rejoins whatever the exchange is currently doing rather than returning to continuous trading -- a halt that expires near the close resumes into CLOSING_AUCTION or CLOSED.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
halt_source |
enum: CB, ADMIN |
omitted when unset | — | What had put the symbol into the halt that just ended. |
reason |
string |
omitted when empty | max_len 64 | Why the halt ended, when that is not simply "its call phase expired". Only the closing backstop sets it, to CLOSING_BACKSTOP. |
clamped |
bool |
omitted when unset | — | True when the print price was forced to the corridor boundary instead of the equilibrium. A client showing a clamped price as a discovered one would mislead. Absent on the three ordinary resumes, where no price is imposed at all -- which is a different statement from false, and worth keeping distinct. |
print_price |
float |
omitted when unset | gt 0, unit display_price |
The price the backstop uncross printed at. Absent when there was no crossing interest to print, and on the three ordinary resumes. |
Note
Four producers, two shapes.
Three of them -- ACE expiry, ADMIN resume-all and ADMIN per-symbol resume -- send symbol and halt_source alone.
The closing backstop sends three fields more, because it is the one resume where the reopening price was imposed rather than discovered: it prints at the corridor boundary for a symbol that could not reopen inside it, which can leave the book crossed by design.
The three extra fields are regime 3 and 4 rather than always-present nulls, so the three ordinary producers keep the two-key payload they have always sent.
normalise_cb_resume reads each through a falsy guard, so an absent key and an empty one are the same event to it.
See also: circuit_breaker.halt.{SYMBOL}
Family drop_copy¶
drop_copy.event.{gateway_id}¶
Published by: engine
Transport: drop_copy_pub
Since: 1.0
Engine to a participant's clearing broker, prime broker or in-house risk system: one execution, as it happens.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
seq |
int |
required | gt 0, unit dimensionless |
Process-wide monotone counter, starting at 1 so that 0 can mean "no events yet". Never resets while the engine lives. A recipient detects loss from a gap and a duplicate from a repeat, which is the whole reason the feed is sequenced. |
timestamp |
int |
required | unit epoch_nanos |
When the engine published the event (models/clock.py::now_ns). |
gateway_id |
string |
required | max_len 32 | The participant whose order executed. Carried in the body as well as in the topic, because drop_copy.replay names the recipient in its topic instead and a replayed event would otherwise not say whose fill it was. |
event_type |
enum: order.fill |
required | — | One value today. An enum rather than a free string so that a second event type is a spec change with a regenerated binding, rather than a new dict key no reader knows about -- DropCopyPublisher's own docstring promised "every fill and cancel" while only fills existed, which is how the gap went unnoticed. Section 27.3. |
order_id |
string |
required | max_len 64 | The resting or aggressing order this execution belongs to. |
symbol |
string |
required | max_len 16 | |
fill_qty |
int |
required | gt 0, unit shares |
|
fill_price |
float |
required | gt 0, unit display_price |
Display money, not ticks -- converted once in _publish_trade. |
liquidity_flag |
enum: MAKER, TAKER |
required | — | Derived from the trade's aggressor side: the aggressor is the TAKER and the resting side the MAKER. Exactly one of the two events a trade produces is TAKER. |
Note
Fed from the engine's single trade-publication path, so it covers every fill-producing flow -- new orders, quotes, combo legs, OCO legs, auction uncrosses, stop cascades and amend-rematches.
It was once wired only into the new-order loop, and quote and auction fills were invisible to clearing as a result.
This is a derived copy of order.fill.{GW_ID}, not the same message: it is sequenced, buffered for replay, carries the liquidity flag, and travels on a socket the trading gateway does not subscribe to.
The two are deliberately allowed to differ.
Every trade produces two of these, one per counterparty, so a recipient watching both sides of a matched pair sees the same execution twice under different gateway_ids.
See also: drop_copy.replay.{RECIPIENT_ID}, order.fill.{GW_ID}
drop_copy.replay.{recipient_id}¶
Published by: engine
Transport: drop_copy_pub
Since: 1.0
Engine to one named recipient: buffered events re-published on request, so a participant that reconnects mid-session can close its sequence gap.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
recipient_id |
string |
required | max_len 32 | Who asked for the replay. Topic-only — deliberately not in the body, which is byte-identical to the live event. |
seq |
int |
required | gt 0, unit dimensionless |
|
timestamp |
int |
required | unit epoch_nanos |
|
gateway_id |
string |
required | max_len 32 | Whose fill this was — not the recipient the topic names. |
event_type |
enum: order.fill |
required | — | |
order_id |
string |
required | max_len 64 | |
symbol |
string |
required | max_len 16 | |
fill_qty |
int |
required | gt 0, unit shares |
|
fill_price |
float |
required | gt 0, unit display_price |
|
liquidity_flag |
enum: MAKER, TAKER |
required | — |
Note
The body is byte-identical to the live event, including the original seq and timestamp -- a replayed fill is the same fill, not a new one.
Only the topic differs, and it names the recipient rather than the gateway, so two simultaneous replays do not interleave.
recipient_id is therefore not the same thing as gateway_id, which is why the body keeps carrying the latter.
There is no request message.
DropCopyPublisher.replay() is in-process only, callable from the engine and reachable by no protocol -- the module docstring described a drop_copy.replay_request that was never built.
Section 27.3.
See also: drop_copy.event.{GW_ID}
Family index¶
Record types¶
DaySummary¶
The session's open, high and low index level. All three arrive together or not at all: _update_day_ohlc sets open, high and low in one branch and _reset_for_new_session clears all three, so there has never been a state where one is known and another is not. As three flat keys under one guard that was a convention; as a nullable record it is unrepresentable otherwise, which is design section 16.2's whole argument.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
open |
float |
required | unit dimensionless |
The first level computed this session. |
high |
float |
required | unit dimensionless |
|
low |
float |
required | unit dimensionless |
HistoryRecord¶
One structural audit entry, replayed verbatim from pm-index's append-only JSONL archive. This is a union of five shapes discriminated by type, and the IDL has no variant construct (section 20.3). Every field the five do not share is therefore optional, and the spec cannot state "a CORP_ACTION always carries action and detail" - that rule lives in _handle_corp_action. What it can state is the field set, the units and the types, which is what every consumer needs: all six read the records with .get(key, default) and dispatch on type. The optional fields omit rather than null because the archive omits: a record written before this spec existed must read back and re-emit unchanged, or specifying the family would rewrite history.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
type |
enum: INIT, CORP_ACTION, ADD_CONSTITUENT, DELIST, REBALANCE |
required | — | The discriminator. IndexHistory.query drops any other value with a warning, so an unknown type never reaches the wire. |
timestamp |
float |
required | unit epoch_seconds |
|
index_id |
string |
required | max_len 32 | |
level |
float |
required | unit dimensionless |
The index level immediately after the event was applied. |
symbol |
string |
omitted when empty | max_len 16 | CORP_ACTION, ADD_CONSTITUENT and DELIST. |
action |
string |
omitted when empty | max_len 32 | CORP_ACTION only. |
detail |
string |
omitted when empty | max_len 128 | CORP_ACTION only; a rendered summary such as 'shares=1000'. |
old_divisor |
float |
omitted when unset | unit dimensionless |
Every type but INIT. |
new_divisor |
float |
omitted when unset | unit dimensionless |
Every type but INIT. |
base_value |
float |
omitted when unset | unit dimensionless |
INIT only. |
divisor |
float |
omitted when unset | unit dimensionless |
INIT only, and the odd one out: every other type reports the divisor as an old/new pair. Kept as written rather than normalised, because the archive on disk already says this. |
constituents |
list of string |
omitted when empty | — | INIT only. |
shares_outstanding |
int |
omitted when unset | unit shares |
ADD_CONSTITUENT only, and absent on records written before 5.2e: the handler used the share count to weight the constituent and then dropped it from the audit entry, so what a constituent was added with was durably recorded nowhere. Optional rather than required because the archive on disk still holds records without it. |
reference_price |
float |
omitted when unset | unit display_price |
ADD_CONSTITUENT only. |
symbols |
list of string |
omitted when empty | — | REBALANCE only: the symbols the batch actually applied to. |
RebalanceUpdate¶
One entry of a rebalance batch. Mechanically a SHARES_ISSUANCE corporate action, applied to every named existing constituent as one batch with a single recompute and publish rather than one round-trip per symbol.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
new_shares_outstanding |
int |
required | gt 0, unit shares |
index.update¶
Published by: index
Transport: engine_pub
Since: 1.0
pm-index to subscribers: the current level of one index, published on every constituent trade subject to a rate limit, and forced on a structural change or at end of day.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
index_id |
string |
required | max_len 32 | |
level |
float |
required | unit dimensionless |
|
aggregate_cap |
float |
required | unit money |
Sum of constituent market capitalisations. |
divisor |
float |
required | unit dimensionless |
Level = aggregate_cap / divisor. |
session_state |
string |
required | max_len 32 | Mirrors session.state; a plain string there and here. |
timestamp |
float |
required | unit epoch_seconds |
|
day |
DaySummary |
omitted when unset | — |
Note
day is absent before the first level of a session is computed and after _reset_for_new_session clears it.
All three consumers - alf_console's display, pm-stats' snapshot writer and md_gateway's CALF normaliser - read it with .get and test is not None, so absent and null are the same thing to every one of them.
See also: session.state
index.history_request¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Gateway or operator to pm-index: replay the structural audit log.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
index_id |
string |
required | max_len 32 | |
from_ts |
float |
required | unit epoch_seconds |
|
to_ts |
float |
required | unit epoch_seconds |
|
types |
list of string |
omitted when empty | — | Record types to include; omitted means all structural types. |
max_records |
int |
defaults to 10000 |
gt 0, unit dimensionless |
Note
pm-index's history is structural only - index creation, corporate actions, constituent changes, rebalances.
Level and end-of-day time-series history lives in pm-stats.
types is omitted when unset rather than sent as a default.
The hand-written builder defaulted it to four of the five structural types and silently dropped REBALANCE from every reply that took the default; the server's own default is the full set, and it cannot tell an omitted types from a deliberate one.
max_records keeps its default because the builder's value and the server's agree.
See also: index.history.{GW_ID}, index.error.{GW_ID}
index.history.{gateway_id}¶
Published by: index
Transport: engine_pub
Since: 1.0
pm-index to requestor: the matching audit records.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
index_id |
string |
required | max_len 32 | |
records |
list of HistoryRecord |
required | — | |
warnings |
list of string |
omitted when empty | — |
Note
warnings reports lines the archive could not parse and record types it did not recognise.
It is omitted when there are none, which is what the hand-written builder's if warnings: did.
See also: index.history_request
index.corp_action¶
Published by: admin
Transport: engine_pub
Since: 1.0
Operator to pm-index: apply a corporate action.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
action |
enum: SPLIT, CASH_DIVIDEND, SHARES_ISSUANCE |
required | — | |
index_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
gateway_id |
string |
required | max_len 32 | |
ratio_numerator |
int |
omitted when unset | gt 0, unit dimensionless |
SPLIT. |
ratio_denominator |
int |
omitted when unset | gt 0, unit dimensionless |
SPLIT. |
dividend_per_share |
float |
omitted when unset | gt 0, unit money |
CASH_DIVIDEND. |
new_shares_outstanding |
int |
omitted when unset | gt 0, unit shares |
SHARES_ISSUANCE. |
Note
The four parameters are action-specific and flat: SPLIT reads the two ratio fields, CASH_DIVIDEND reads dividend_per_share, SHARES_ISSUANCE reads new_shares_outstanding, and each is read with .get(key, 0) inside its own branch of _handle_corp_action.
A discriminated union would say that properly and the IDL has none - see design section 20.3 for why one was not built for a single family.
See also: index.corp_action_ack.{GW_ID}, index.error.{GW_ID}
index.constituent_change¶
Published by: admin
Transport: engine_pub
Since: 1.0
Operator to pm-index: add or delist a constituent.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
change_type |
enum: ADD, DELIST |
required | — | |
index_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
gateway_id |
string |
required | max_len 32 | |
shares_outstanding |
int |
omitted when unset | gt 0, unit shares |
ADD. |
initial_price |
float |
omitted when unset | gt 0, unit display_price |
ADD. |
Note
Both parameters belong to ADD and neither to DELIST, and the hand-written builder omitted each independently rather than as a pair - so unlike DaySummary they are two guards, not one, and stay flat.
See also: index.constituent_change_ack.{GW_ID}
index.rebalance¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to pm-index: set shares outstanding for several constituents in one batch.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
index_id |
string |
required | max_len 32 | |
gateway_id |
string |
required | max_len 32 | |
updates |
list of RebalanceUpdate |
required | min_items 1 | Never empty; the handler rejects an empty batch. |
command_id |
string |
omitted when empty | max_len 64 | Echoed on the ack so a caller can correlate. |
Note
The whole batch is validated before any of it is applied, so an invalid entry anywhere rejects all of it - the all-or-nothing guarantee the single-action handlers get for free by only ever doing one mutation.
See also: index.rebalance_ack.{GW_ID}
index.corp_action_ack.{gateway_id}¶
Published by: index
Transport: engine_pub
Since: 1.0
pm-index to requestor: the corporate action's outcome.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
timestamp |
float |
required | unit epoch_seconds |
|
index_id |
string |
omitted when empty | max_len 32 | |
level |
float |
omitted when unset | unit dimensionless |
|
divisor |
float |
omitted when unset | unit dimensionless |
Note
level and divisor are the recomputed values and are present only on acceptance; index_id is absent on the paths that reject before resolving one.
reason is always emitted, as "" on success, because the hand-written builder put it in the base payload rather than under a guard.
index.constituent_change_ack.{gateway_id}¶
Published by: index
Transport: engine_pub
Since: 1.0
pm-index to requestor: the constituent change's outcome.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
timestamp |
float |
required | unit epoch_seconds |
|
index_id |
string |
omitted when empty | max_len 32 | |
level |
float |
omitted when unset | unit dimensionless |
|
divisor |
float |
omitted when unset | unit dimensionless |
Note
Field for field the same payload as index.corp_action_ack, on its own topic.
Two topics rather than one because a caller waits on the specific reply to the command it sent, and commands/client.py names that topic when it registers the future.
index.rebalance_ack.{gateway_id}¶
Published by: index
Transport: engine_pub
Since: 1.0
pm-index to ADMIN: the batch's outcome.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
timestamp |
float |
required | unit epoch_seconds |
|
updated_symbols |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
index_id |
string |
omitted when empty | max_len 32 | |
level |
float |
omitted when unset | unit dimensionless |
|
divisor |
float |
omitted when unset | unit dimensionless |
|
command_id |
string |
omitted when empty | max_len 64 |
Note
updated_symbols is always emitted, as 0 on rejection: the builder puts it in the base payload beside accepted and reason, and a rejected batch applied nothing.
index.error.{gateway_id}¶
Published by: index
Transport: engine_pub
Since: 1.0
pm-index to requestor: the request could not be routed to an index at all.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | Always false; present so every reply has the same first key. |
reason |
string |
required | max_len 512 | |
timestamp |
float |
required | unit epoch_seconds |
Note
Distinct from a rejecting ack.
An unknown index_id means pm-index cannot know which ack topic the caller is waiting on, so it answers on the one topic every index caller subscribes to.
Once the index is known, a bad symbol or parameter comes back as accepted: false on the specific ack instead.
Family log¶
Record types¶
LogFilter¶
A row predicate, applied two ways by the server: evaluated in Python against a freshly persisted row on the live path, and compiled to a parameterised SQL WHERE clause for backfill. One definition with two evaluators is what guarantees a subscriber's backfill and its subsequent live stream contain the same kind of rows - a mismatch would show up as rows appearing or vanishing at the seam. Every field is optional; an empty filter matches everything.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
min_level |
string |
omitted when unset | max_len 16 | Lowest level to include, e.g. WARNING. |
processes |
list of string |
defaults to [] |
— | Process names to include; empty means all. |
loggers |
list of string |
defaults to [] |
— | Logger names to include; empty means all. |
sessions |
list of string |
defaults to [] |
— | Session ids to include; empty means all. |
contains |
string |
omitted when unset | max_len 256 | Substring the message must contain. |
exceptions_only |
bool |
defaults to False |
— |
LevelCount¶
How many rows of one level a NOTIFY subscription has buffered. This was a map on the wire - {"INFO": 3, "ERROR": 1} - which the IDL excludes deliberately (design section 15.4). The key was a value, and a list of records says so: the level is a field, not a key.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
level |
string |
required | max_len 16 | |
count |
int |
required | ge 0, unit dimensionless |
LogRow¶
One persisted log line, exactly the columns log_events stores. Carried by both log.event (live) and log.backfill (history) so a viewer sees one row shape at the seam between them.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
seq |
int |
required | unit dimensionless |
Monotonic server sequence; the cursor for backfill. |
client_ts |
string |
required | — | Producer wall-clock, ISO-8601 (e.g. 2026-07-29T10:00:00.000Z). |
server_ts |
string |
required | — | Server receive time, ISO-8601; the log_events columns are TEXT. |
process |
string |
required | max_len 64 | |
instance |
string |
required | max_len 64 | |
pid |
int |
required | unit dimensionless |
|
host |
string |
required | max_len 128 | |
session |
string |
required | max_len 64 | |
level |
string |
required | max_len 16 | |
logger |
string |
required | max_len 128 | |
module |
string |
required | max_len 128 | |
line |
int |
required | unit dimensionless |
|
has_exception |
bool |
required | — | |
truncated |
bool |
required | — | |
message |
string |
required | — |
SubscriptionStatus¶
One subscription's live counters, reported by log.status. This is the record that motivated allowing a record inside a record: it carries the subscription's own LogFilter. Flattening that into filter_min_level and friends is the a_b flattening section 16.2 argued against, and forbidding depth was a rule broader than its reason - what the generators cannot survive is a cycle, not a level.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
mode |
enum: STREAM, NOTIFY |
required | — | |
filter |
LogFilter |
required | — | |
lease_sec |
float |
required | unit dimensionless |
|
lease_remaining_sec |
float |
required | unit dimensionless |
|
age_sec |
float |
required | unit dimensionless |
|
pending_rows |
int |
required | unit dimensionless |
|
pending_count |
int |
required | unit dimensionless |
|
sent_rows |
int |
required | unit dimensionless |
|
sent_messages |
int |
required | unit dimensionless |
|
dropped_rows |
int |
required | unit dimensionless |
|
renewals |
int |
required | unit dimensionless |
log.subscribe¶
Published by: log_client
Transport: engine_pub
Since: 1.0
Subscriber to pm-log-srv: open or replace a leased stream.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
mode |
enum: STREAM, NOTIFY |
defaults to 'STREAM' |
— | STREAM pushes every row; NOTIFY pushes periodic counts. |
filter |
LogFilter |
omitted when unset | — | |
backfill_minutes |
int |
omitted when unset | unit dimensionless |
Replay this many minutes before the live stream starts. |
lease_sec |
int |
omitted when unset | unit dimensionless |
How long the subscription survives without a renew. |
notify_interval_ms |
int |
omitted when unset | unit dimensionless |
Note
Everything past sub_id and mode is omitted when unset rather than sent as a default, which is what the hand-written builder did - the server applies its own defaults and cannot tell an omitted lease_sec from one that happens to equal the default.
See also: log.renew, log.unsubscribe
log.renew¶
Published by: log_client
Transport: engine_pub
Since: 1.0
Lease keepalive. The liveness signal: a subscriber that stops renewing is dropped, which is how the server reclaims a viewer that went away without unsubscribing.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
timestamp |
float |
required | unit epoch_seconds |
See also: log.lease_expired.{SUB_ID}
log.unsubscribe¶
Published by: log_client
Transport: engine_pub
Since: 1.0
Close a subscription immediately rather than letting it lapse.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
timestamp |
float |
required | unit epoch_seconds |
log.backfill_request¶
Published by: log_client
Transport: engine_pub
Since: 1.0
Replay the last N minutes of history for one subscription.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
minutes |
int |
required | gt 0, unit dimensionless |
|
filter |
LogFilter |
omitted when unset | — | Defaults to the subscription's own filter when omitted. |
max_rows |
int |
omitted when unset | unit dimensionless |
See also: log.backfill.{SUB_ID}
log.status_request¶
Published by: log_client
Transport: engine_pub
Since: 1.0
Ask for subscription and server diagnostics.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
timestamp |
float |
required | unit epoch_seconds |
See also: log.status.{SUB_ID}
log.subscribe_ack.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
Confirm a subscription and echo the terms the server chose.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
accepted |
bool |
required | — | |
sub_id |
string |
required | max_len 64 | |
proto |
string |
required | max_len 16 | |
server |
string |
required | max_len 64 | |
mode |
enum: STREAM, NOTIFY |
required | — | |
filter |
LogFilter |
required | — | |
lease_sec |
float |
required | unit dimensionless |
|
renew_before_sec |
float |
required | unit dimensionless |
Renew sooner than this; half the lease. |
notify_interval_ms |
int |
required | unit dimensionless |
|
last_seq |
int |
required | unit dimensionless |
|
backfill_request_id |
string |
omitted when empty | max_len 64 | |
timestamp |
float |
required | unit epoch_seconds |
Note
The filter comes back parsed rather than as sent, so a subscriber can see what the server actually understood - which is where a lenient filter parse would otherwise hide a typo.
log.renew_ack.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
Confirm a keepalive and say how long the lease now has.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
accepted |
bool |
required | — | |
sub_id |
string |
required | max_len 64 | |
lease_sec |
float |
required | unit dimensionless |
|
expires_in_sec |
float |
required | unit dimensionless |
|
last_seq |
int |
required | unit dimensionless |
|
timestamp |
float |
required | unit epoch_seconds |
log.unsubscribe_ack.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
Confirm a close, or say there was nothing to close.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
accepted |
bool |
required | — | |
sub_id |
string |
required | max_len 64 | |
reason |
string |
omitted when empty | max_len 256 | |
timestamp |
float |
required | unit epoch_seconds |
log.status.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
Server and subscription diagnostics, on request.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
server |
string |
required | max_len 64 | |
proto |
string |
required | max_len 16 | |
subscribers |
int |
required | unit dimensionless |
|
active_backfills |
int |
required | unit dimensionless |
|
last_seq |
int |
required | unit dimensionless |
|
inbox_dropped |
int |
required | unit dimensionless |
|
subscription |
SubscriptionStatus |
null when unset |
— | |
timestamp |
float |
required | unit epoch_seconds |
Note
subscription is null when the requester has no live subscription - asking for status is legal without one, and null says "you have none" where an absent key would say "the server declined to tell you".
log.backfill.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
One chunk of replayed history for a subscription.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
request_id |
string |
required | max_len 64 | |
chunk |
int |
required | unit dimensionless |
|
rows |
list of LogRow |
required | — | |
row_count |
int |
required | unit dimensionless |
|
done |
bool |
required | — | |
total_sent |
int |
required | unit dimensionless |
|
truncated |
bool |
required | — | |
last_seq |
int |
required | unit dimensionless |
|
timestamp |
float |
required | unit epoch_seconds |
Note
Chunked because a backfill can be far larger than one message: done marks the last chunk, and truncated says the server stopped at max_rows rather than at the end of history.
The two are different answers to "why did it stop".
See also: log.backfill_request, log.event.{SUB_ID}
log.event.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
A batch of live rows for a STREAM subscription.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
rows |
list of LogRow |
required | min_items 1 | Never empty; the server skips a flush with nothing to send. |
row_count |
int |
required | unit dimensionless |
|
seq_from |
int |
required | unit dimensionless |
|
seq_to |
int |
required | unit dimensionless |
|
server_last_seq |
int |
required | unit dimensionless |
|
dropped |
int |
required | unit dimensionless |
Lifetime rows dropped for this subscription, not this batch. |
timestamp |
float |
required | unit epoch_seconds |
See also: log.notify.{SUB_ID}
log.notify.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
Periodic counts for a NOTIFY subscription: how much happened, without the rows.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
count |
int |
required | unit dimensionless |
|
levels |
list of LevelCount |
required | — | |
last_seq |
int |
required | unit dimensionless |
|
server_last_seq |
int |
required | unit dimensionless |
|
timestamp |
float |
required | unit epoch_seconds |
Note
levels was a map keyed by level name.
It is a list of records now - the key was a value, and design section 15.4 says a spec that appears to need a map is describing a message that should have been this.
log.lease_expired.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
The subscription was reaped for want of a renew.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sub_id |
string |
required | max_len 64 | |
reason |
string |
required | max_len 256 | |
lease_sec |
float |
required | unit dimensionless |
|
dropped_rows |
int |
required | unit dimensionless |
|
timestamp |
float |
required | unit epoch_seconds |
Note
Published on the off-chance the subscriber is alive but wedged: nothing about a crashed process is visible on a PUB socket, so this tells a client that is in fact listening that it must re-subscribe rather than wait for rows that will never come.
log.error.{sub_id}¶
Published by: log_server
Transport: engine_pub
Since: 1.0
A control request was rejected, with a machine-readable code.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
accepted |
bool |
required | — | Always false; present so every reply has the same first key. |
sub_id |
string |
required | max_len 64 | |
code |
string |
required | max_len 32 | |
reason |
string |
required | max_len 512 | |
timestamp |
float |
required | unit epoch_seconds |
log.server_state¶
Published by: log_server
Transport: engine_pub
Since: 1.0
Periodic server heartbeat and configuration, broadcast to everyone rather than addressed - it is how a viewer finds the server at all.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
server |
string |
required | max_len 64 | |
state |
enum: UP, DOWN |
required | — | |
proto |
string |
required | max_len 16 | |
pub_addr |
string |
required | max_len 128 | |
pull_addr |
string |
required | max_len 128 | |
subscribers |
int |
required | unit dimensionless |
|
active_backfills |
int |
required | unit dimensionless |
|
last_seq |
int |
required | unit dimensionless |
|
inbox_dropped |
int |
required | unit dimensionless |
|
default_lease_sec |
float |
required | unit dimensionless |
|
timestamp |
float |
required | unit epoch_seconds |
Family order¶
Record types¶
OcoLeg¶
One side of an OCO pair. It has no symbol or quantity of its own: both legs trade the same instrument in the same size, and the OCO carries those. That is what makes it a different record from a combo leg, which does own a symbol and a quantity.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
side |
enum: BUY, SELL |
required | — | |
order_type |
enum: MARKET, LIMIT, STOP, STOP_LIMIT, FOK, ICEBERG, IOC, TRAILING_STOP |
required | — | |
price |
ticks |
omitted when unset | unit ticks |
Limit price in engine ticks. Absent for a leg with none. |
stop_price |
ticks |
omitted when unset | unit ticks |
|
trail_offset |
ticks |
omitted when unset | unit ticks |
ComboLeg¶
One leg of a combo. Unlike an OcoLeg it owns a symbol and a quantity: the legs of a combo trade different instruments, in sizes that need not match. That is why the two are separate types rather than one shared leg - an early draft of design section 15 assumed they could be merged and was wrong.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
side |
enum: BUY, SELL |
required | — | |
order_type |
enum: MARKET, LIMIT, STOP, STOP_LIMIT, FOK, ICEBERG, IOC, TRAILING_STOP |
required | — | |
quantity |
int |
required | gt 0, unit shares |
|
price |
ticks |
null when unset |
unit ticks |
Limit price in engine ticks; null for a leg with none. |
stop_price |
ticks |
null when unset |
unit ticks |
|
smp_action |
enum: NONE, CANCEL_AGGRESSOR, CANCEL_RESTING, CANCEL_BOTH |
null when unset |
— | Null means the client did not specify SMP, which is distinct from an explicit NONE. Combo-level in the ALF protocols, so every leg carries the same value. |
OrderDisplay¶
One resting order as the engine reports it in an order.orders snapshot, in display units. It is Order.to_dict() with price, stop_price and trail_offset converted from ticks to display money and timestamp expressed in seconds - the projection order_to_display_dict builds so an operator reads prices in the same money the book shows, not raw ticks. Gateway_id is not included here; it is topic-only (part of the message topic as order.orders.{gateway_id}, not part of the record). The record contains the order state (id, symbol, side, etc.) exactly as Order.to_dict() produces, minus gateway_id; the eleven nullable ones ride as null when unset.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
id |
string |
required | max_len 64 | Engine order id; a UUID string. |
symbol |
string |
required | max_len 16 | |
side |
enum: BUY, SELL |
required | — | |
order_type |
enum: MARKET, LIMIT, STOP, STOP_LIMIT, FOK, ICEBERG, IOC, TRAILING_STOP |
required | — | |
tif |
enum: DAY, GTC, ATO, ATC |
required | — | |
quantity |
int |
required | gt 0, unit shares |
Total original quantity. |
remaining_qty |
int |
required | ge 0, unit shares |
Quantity yet to be filled. |
trail_offset |
float |
null when unset |
unit display_price |
TRAILING_STOP: trail distance, in display money. |
oco_group_id |
string |
null when unset |
max_len 64 | |
timestamp |
float |
required | ge 0, unit epoch_seconds |
Client-supplied submission time, in seconds. NOT the book's time priority key - see arrival_seq. |
status |
enum: NEW, PARTIAL, FILLED, CANCELLED, REJECTED, EXPIRED |
required | — | |
price |
float |
null when unset |
unit display_price |
Limit price in display money. Null for MARKET, which has none. |
stop_price |
float |
null when unset |
unit display_price |
STOP / STOP_LIMIT / TRAILING_STOP trigger. |
visible_qty |
int |
null when unset |
unit shares |
ICEBERG: fixed peak size. |
displayed_qty |
int |
null when unset |
unit shares |
ICEBERG: current visible slice on the book. |
smp_action |
enum: NONE, CANCEL_AGGRESSOR, CANCEL_RESTING, CANCEL_BOTH |
null when unset |
— | Self-match prevention. Null means the client did not specify SMP at all, distinct from an explicit NONE. See SmpAction's docstring. |
combo_parent_id |
string |
null when unset |
max_len 64 | |
leg_index |
int |
null when unset |
unit dimensionless |
Position in the parent combo's legs, 0-based. |
origin |
enum: ORDER, QUOTE, IMPLIED |
defaults to 'ORDER' |
— | Defaulted rather than nullable: to_dict always supplies ORDER. |
quote_id |
string |
null when unset |
max_len 64 | |
client_tag |
string |
null when unset |
max_len 64 | Client correlation tag, echoed on every lifecycle event. |
arrival_seq |
int |
defaults to 0 |
unit dimensionless |
Engine-assigned monotonic arrival sequence; 0 = unassigned. |
execution_report (no bus topic)¶
Published by: gateway
Transport: balf
Since: 1.0
Private per-order fill notification, sent to the gateway session that owns the order. Both sides of a match receive their own report.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
client_order_id |
int |
required | unit dimensionless |
Echoed from the original NEW_ORDER so a client can correlate. |
order_id |
int |
required | unit dimensionless |
Session-scoped BALF order id assigned by the gateway. A u64 on the wire, not a string - this is the field the reference example got wrong. |
fill_price |
float |
required | gt 0, unit display_price |
Execution price in display money. On the wire it is an i64 scaled by the fixed BALF PRICE_SCALE of 10^8, never by the instrument's tick_decimals. |
fill_qty |
int |
required | gt 0, unit shares |
Quantity matched in this event, not cumulatively. |
remaining_qty |
int |
required | ge 0, unit shares |
Unfilled quantity after this fill; zero means the order is done. |
timestamp_ns |
int |
required | ge 0, unit epoch_nanos |
Trade time in nanoseconds since the Unix epoch. |
symbol |
string |
required | max_len 8, pattern ^[A-Z0-9._]+$ |
Instrument, echoed for convenience; matches the original order. |
side |
enum: BUY, SELL |
required | — | Side of the filled order. |
status |
enum: PARTIAL, FILLED |
required | — | Whether this fill completed the order. Only these two values exist on BALF - there is no NEW or CANCELLED execution report. |
Note
Sent for every partial or full fill, so a single order may produce several.
remaining_qty reaching zero is what marks the order done; status FILLED says the same thing and the two must agree.
See also: trade.executed
order.ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Acknowledge acceptance or rejection of a new order, addressed to the gateway that submitted it.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
order_id |
string |
required | max_len 64 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 256 | Rejection detail; empty when accepted. |
symbol |
string |
omitted when unset | max_len 16 | |
side |
string |
omitted when unset | max_len 8 | |
order_type |
string |
omitted when unset | max_len 16 | |
tif |
string |
omitted when unset | max_len 8 | |
qty |
int |
omitted when unset | unit shares |
|
price |
float |
omitted when unset | unit display_price |
Absent for a MARKET order, which has no limit price. |
client_tag |
string |
omitted when unset | max_len 64 | |
oco_group_id |
string |
omitted when unset | max_len 64 | |
combo_parent_id |
string |
omitted when unset | max_len 64 | |
quote_id |
string |
omitted when unset | max_len 64 | |
leg_index |
int |
omitted when unset | unit dimensionless |
Note
The order-detail fields are present only when the engine had the order to hand; on a rejection before lookup they are absent.
reason is empty on an acceptance.
See also: order.fill.{GW_ID}, order.new
order.fill.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Private fill notification for one order, addressed to the gateway that owns it. The public counterpart is trade.executed.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
order_id |
string |
required | max_len 64 | |
fill_qty |
int |
required | unit shares |
|
fill_price |
float |
required | unit display_price |
|
remaining_qty |
int |
required | unit shares |
|
status |
string |
required | max_len 16 | |
symbol |
string |
omitted when unset | max_len 16 | |
side |
string |
omitted when unset | max_len 8 | |
order_type |
string |
omitted when unset | max_len 16 | |
tif |
string |
omitted when unset | max_len 8 | |
qty |
int |
omitted when unset | unit shares |
|
price |
float |
omitted when unset | unit display_price |
|
client_tag |
string |
omitted when unset | max_len 64 | |
oco_group_id |
string |
omitted when unset | max_len 64 | |
combo_parent_id |
string |
omitted when unset | max_len 64 | |
quote_id |
string |
omitted when unset | max_len 64 | |
leg_index |
int |
omitted when unset | unit dimensionless |
|
trade_ids |
list of string |
defaults to [] |
— | The public trade.executed id(s) that composed this fill event. Usually one; more than one when an aggressor swept several resting orders and the engine coalesced them into a single VWAP fill (H5/H6). Empty only for a fill with no trade behind it. Lets a reader link a private fill to the public trade tape without re-deriving the join. |
See also: trade.executed
order.cancelled.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Confirm that a resting order has been cancelled.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
order_id |
string |
required | max_len 64 | |
client_tag |
string |
omitted when unset | max_len 64 | |
oco_group_id |
string |
omitted when unset | max_len 64 | |
combo_parent_id |
string |
omitted when unset | max_len 64 | |
quote_id |
string |
omitted when unset | max_len 64 | |
leg_index |
int |
omitted when unset | unit dimensionless |
order.expired.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
A DAY order that never filled has expired at session end. Same shape as order.cancelled - the difference is who ended the order, not what the consumer needs to know about it.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
order_id |
string |
required | max_len 64 | |
client_tag |
string |
omitted when unset | max_len 64 | |
oco_group_id |
string |
omitted when unset | max_len 64 | |
combo_parent_id |
string |
omitted when unset | max_len 64 | |
quote_id |
string |
omitted when unset | max_len 64 | |
leg_index |
int |
omitted when unset | unit dimensionless |
order.amended.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Confirm an accepted amendment and report the resulting order.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
order_id |
string |
required | max_len 64 | |
price |
float |
null when unset |
unit display_price |
New limit price, or null for an order that has none. |
qty |
int |
required | unit shares |
|
remaining_qty |
int |
required | unit shares |
|
priority_reset |
bool |
required | — | True when the amendment lost the order its time priority. |
Note
price is nullable but always present: a MARKET order has no limit price, and the field says so with null rather than by being absent.
This is the one message in the group that emits null rather than omitting - it is what the hand-written builder did.
order.new¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Submit a new order to the matching engine. Sent over PUSH/PULL rather than the pub bus, but it carries a topic so the audit log can classify it alongside everything else.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
id |
string |
required | max_len 64 | Engine order id; a UUID string. |
symbol |
string |
required | max_len 16 | |
side |
enum: BUY, SELL |
required | — | |
order_type |
enum: MARKET, LIMIT, STOP, STOP_LIMIT, FOK, ICEBERG, IOC, TRAILING_STOP |
required | — | |
tif |
enum: DAY, GTC, ATO, ATC |
required | — | |
quantity |
int |
required | gt 0, unit shares |
Total original quantity. |
remaining_qty |
int |
required | ge 0, unit shares |
Quantity yet to be filled; equals quantity on submission. |
gateway_id |
string |
required | max_len 32 | |
trail_offset |
ticks |
null when unset |
unit ticks |
TRAILING_STOP: fixed distance to trail the market price. |
oco_group_id |
string |
null when unset |
max_len 64 | |
timestamp |
int |
required | ge 0, unit epoch_nanos |
Client-supplied submission time. NOT what the book uses for time priority - see arrival_seq. BALF has no timestamp field on NEW_ORDER, so balf_gwy stamps one at ingress. |
status |
enum: NEW, PARTIAL, FILLED, CANCELLED, REJECTED, EXPIRED |
required | — | Always NEW on submission; the enum is the full lifecycle. |
price |
ticks |
null when unset |
unit ticks |
Limit price in ticks. Null for MARKET, which has none. |
stop_price |
ticks |
null when unset |
unit ticks |
STOP / STOP_LIMIT / TRAILING_STOP trigger. |
visible_qty |
int |
null when unset |
unit shares |
ICEBERG: fixed peak size. |
displayed_qty |
int |
null when unset |
unit shares |
ICEBERG: current visible slice on the book. |
smp_action |
enum: NONE, CANCEL_AGGRESSOR, CANCEL_RESTING, CANCEL_BOTH |
null when unset |
— | Self-match prevention. Null means the client did not specify SMP at all, which is distinct from an explicit NONE: the engine resolves null to the gateway's configured default. See SmpAction's docstring. |
combo_parent_id |
string |
null when unset |
max_len 64 | |
leg_index |
int |
null when unset |
unit dimensionless |
Position in the parent combo's legs, 0-based. |
origin |
enum: ORDER, QUOTE, IMPLIED |
defaults to 'ORDER' |
— | Defaulted rather than nullable: from_dict supplies ORDER. |
quote_id |
string |
null when unset |
max_len 64 | |
client_tag |
string |
null when unset |
max_len 64 | Client correlation tag, echoed on every lifecycle event. |
arrival_seq |
int |
defaults to 0 |
unit dimensionless |
Engine-assigned monotonic arrival sequence; time priority is keyed on this, not on timestamp, so a back-dated payload cannot jump the queue. Zero means unassigned, which is what a submission carries. |
Note
The payload is exactly Order.to_dict().
Eleven fields are nullable and are emitted as null when unset rather than omitted - a MARKET order carries "price": null.
The engine's Order.from_dict reads absent and null alike, so a producer that omits them is still accepted.
See also: order.ack.{GW_ID}, order.cancel, order.amend
order.cancel¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Request cancellation of one resting order by id.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
order_id |
string |
required | max_len 64 | |
gateway_id |
string |
required | max_len 32 |
See also: order.cancelled.{GW_ID}
order.amend¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Request a price and/or quantity change to a resting order.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
order_id |
string |
required | max_len 64 | |
gateway_id |
string |
required | max_len 32 | |
price |
float |
omitted when unset | unit display_price |
New limit price; absent means the price is unchanged. |
qty |
int |
omitted when unset | unit shares |
New quantity; absent means the quantity is unchanged. |
Note
price and qty are omitted when not being changed, and the engine reads that absence as "leave this alone" - so unlike order.new these two DO take omit_when_none.
tests/test_messages.py pins the omission directly.
price here is display money, not the ticks that order.new carries.
See also: order.amended.{GW_ID}
order.combo_cancel¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Cancel a combo order and all of its resting child legs.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
combo_id |
string |
defaults to '' |
max_len 64 | Client-supplied combo label, not the internal UUID. |
gateway_id |
string |
defaults to '' |
max_len 32 |
See also: order.combo, order.cancel
order.combo¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Submit a combo: two or more orders on different instruments that the engine posts together and tracks as one aggregate.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
combo_id |
string |
required | max_len 64 | Client-supplied tracking label, not the engine's internal id. |
gateway_id |
string |
required | max_len 32 | |
combo_type |
enum: AON |
required | — | All-or-none: the combo completes only when every leg fills. |
tif |
enum: DAY, GTC, ATO, ATC |
required | — | |
legs |
list of ComboLeg |
required | min_items 2, max_items 10 | The child orders. The bounds below were previously enforced only by api_gateway's pydantic schema, which left the ALF console and gateway free to submit a one-legged combo. |
Note
This is the message the whole of design section 15 is about.
It was unspecifiable for three separate reasons, and each turned out to be the wire being wrong rather than the IDL being short: leg prices whose unit depended on their runtime type (15.2), engine lifecycle state riding on a client submission (15.4), and finally the ordinary need for nested and list[T] (15.5).
The payload below is `ComboOrder.
to_submission_dict()` and carries no engine state at all.
See also: combo.ack.{GW_ID}, order.combo_cancel
order.oco¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Submit a One-Cancels-Other pair: two orders on the same instrument, of which a fill on either cancels the other.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
oco_id |
string |
defaults to '' |
max_len 64 | Client-supplied label for the pair. |
gateway_id |
string |
defaults to '' |
max_len 32 | |
symbol |
string |
defaults to '' |
max_len 16 | Both legs trade this instrument. |
quantity |
int |
defaults to 0 |
unit shares |
Size of each leg; they are equal by construction. |
tif |
enum: DAY, GTC, ATO, ATC |
defaults to 'DAY' |
— | |
leg1 |
OcoLeg |
required | — | |
leg2 |
OcoLeg |
required | — |
Note
The first message in any spec to use a nested record.
Both legs are OcoLeg, and their prices are engine ticks - the gateway converts.
A leg omits a price it does not have rather than sending null, which is what the three producing gateways already do.
See also: oco.ack.{GW_ID}, order.oco_cancel
order.oco_cancel¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Cancel an OCO pair and both of its legs.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
oco_id |
string |
defaults to '' |
max_len 64 | Client-supplied OCO label. |
gateway_id |
string |
defaults to '' |
max_len 32 |
See also: order.oco, order.cancel
order.orders_request¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Caller to engine: the resting (unfilled, non-cancelled) orders a gateway currently has on the books, across all symbols.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Whose resting orders to return, and the reply's correlation key. |
See also: order.orders.{GW_ID}, system.symbols_request
order.orders.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: the gateway's resting orders in display units, one OrderDisplay record each. Empty when the gateway is unknown or flat.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped from the body by the default projection. |
orders |
list of OrderDisplay |
required | — | Resting orders, as the engine iterates its books. |
Note
gateway_id names the caller in the topic and is dropped from the body by the default projection, so the body is a single orders list - the same shape system.symbols uses.
See also: order.orders_request, order.new
Family quote¶
quote.new¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Market maker to engine: submit or replace a two-sided quote on one instrument. A quote is a bid and an ask posted as a pair; replacing one cancels the previous pair for that gateway and symbol.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
bid_price |
ticks |
required | gt 0, unit ticks |
|
bid_qty |
int |
required | gt 0, unit shares |
|
ask_price |
ticks |
required | gt 0, unit ticks |
|
ask_qty |
int |
required | gt 0, unit shares |
|
tif |
enum: DAY, GTC, ATO, ATC |
defaults to 'DAY' |
— | Applies to both legs; the engine reads it once. Same four values as models/order.py::TIF and order.combo's own tif - a quote's legs are ordinary orders once they rest. |
quote_id |
string |
omitted when empty | max_len 64 |
Note
Prices are integer ticks.
The engine rejects a float outright rather than converting it, because a display price of 150.0 accepted as 150 ticks would post the quote at 1/100th of the intended level on a two-decimal instrument - silent, and in the wrong direction for the side that gets hit.
quote_id is the client's own handle, echoed on every ack and status event.
It is optional: a gateway that submits one quote per symbol can identify it by symbol alone, and the hand-written builders omitted the key entirely rather than sending "".
See also: quote.ack.{GW_ID}, quote.cancel, quote.status.{GW_ID}
quote.cancel¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Market maker to engine: pull the active quote on one symbol.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 |
Note
Addressed by symbol rather than by quote_id: a gateway has at most one active quote per instrument, so the pair identifies it.
That is also why quote_id is optional on submission.
See also: quote.new
quote.ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to market maker: the quote was accepted or rejected.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
quote_id |
string |
defaults to '' |
max_len 64 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
bid_order_id |
string |
defaults to '' |
max_len 64 | |
ask_order_id |
string |
defaults to '' |
max_len 64 |
Note
The two order ids are the engine's handles for the resting legs, and are what ties a subsequent order.fill to the quote that produced it.
Both are always emitted, as "" on rejection - the hand-written builder put them in the base payload, and a rejected quote rests nothing.
quote.status.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to market maker: the quote left the book, and why.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
quote_id |
string |
defaults to '' |
max_len 64 | |
status |
enum: ACTIVE, INACTIVE_BID_FILLED, INACTIVE_ASK_FILLED, CANCELLED |
required | — | Mirrors models/quote.py::QuoteState. |
reason |
string |
defaults to '' |
max_len 512 |
Note
The two INACTIVE_* states say which side was hit, which a market maker needs in order to re-quote the other one.
They are distinct states rather than one INACTIVE plus a side field because models/quote.py::QuoteState is what the engine actually holds, and a wire that renames its own state machine is a translation nobody asked for.
See also: quote.ack.{GW_ID}
Family risk¶
risk.kill_switch¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Gateway or admin to engine: cancel a gateway's open risk-bearing exposure. The gateway is NOT halted - it may submit again as soon as the ack arrives.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
defaults to '' |
max_len 16 | Scope to one instrument; "" cancels across all of them. |
note |
string |
omitted when empty | max_len 256 | Free-text reason, recorded on the admin monitor. |
command_id |
string |
omitted when empty | max_len 64 | Echoed on the ack. A kill-switch ack carries no natural identifier - unlike the symbol acks, which carry symbol - so without this two concurrent mass cancels for one gateway are indistinguishable once both acks are in flight. |
Note
symbol scopes the cancel to one instrument.
It is always emitted, as "" for the whole-gateway case, because the handler reads it as if symbol_filter: - empty and absent mean the same thing to it, and the hand-written builder always sent the key.
gateway_id names whose exposure is cancelled, not who asked.
This message only ever acts on the caller's own gateway; use risk.kill_switch_gateway for one participant acting on another.
See also: risk.kill_switch_ack.{GW_ID}, risk.kill_switch_gateway
risk.kill_switch_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: what the kill switch cancelled.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
cancelled_orders |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
cancelled_quotes |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
command_id |
string |
omitted when empty | max_len 64 |
Note
The counters are always emitted, as 0 on rejection: the hand-written builder put them in the base payload beside accepted, and a rejected kill switch cancelled nothing.
reason is likewise always present, as "" on success.
risk.kill_switch_gateway¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: cancel every order and quote belonging to one named participant.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | The ADMIN caller; the ack is addressed to this id. |
target_gateway_id |
string |
required | max_len 32 | Whose orders and quotes are cancelled. |
note |
string |
omitted when empty | max_len 256 | |
command_id |
string |
omitted when empty | max_len 64 |
Note
The one message in this group where the caller and the affected gateway are allowed to differ.
gateway_id is the ADMIN making the request - it is what the role and connection checks run against, and what addresses the ack - while target_gateway_id is whose exposure is cancelled.
Two fields rather than one because they are two different participants, which is exactly what risk.kill_switch cannot express.
See also: risk.kill_switch, risk.kill_switch_global
risk.kill_switch_gateway_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: what the gateway-targeted kill switch cancelled.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
target_gateway_id |
string |
defaults to '' |
max_len 32 | |
reason |
string |
defaults to '' |
max_len 512 | |
cancelled_orders |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
cancelled_quotes |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
command_id |
string |
omitted when empty | max_len 64 |
Note
target_gateway_id is echoed in the body, unlike gateway_id, which the topic already carries.
The two are different participants here, so an ack naming only the topic's id would not say who was actually acted on.
risk.kill_switch_global¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: cancel every resting order and quote, for every gateway. The full-market emergency stop.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | The ADMIN caller; the ack is addressed to this id. |
note |
string |
omitted when empty | max_len 256 | |
command_id |
string |
omitted when empty | max_len 64 |
Note
Distinct from risk.circuit_breaker_halt_all, which halts trading but leaves resting orders in place.
This one cancels them outright, and does not halt anything - a gateway may submit again immediately.
See also: risk.circuit_breaker_halt_all
risk.kill_switch_global_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: what the market-wide kill switch cancelled.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
cancelled_orders |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
cancelled_quotes |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
affected_gateways |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
command_id |
string |
omitted when empty | max_len 64 |
Note
affected_gateways is what distinguishes this ack from the other two: the same two counters, plus how many participants they were spread across.
risk.symbol_halt¶
Published by: admin, api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: halt trading on one instrument.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
level |
string |
omitted when empty | max_len 32 | A configured circuit-breaker level; omitted halts indefinitely. |
note |
string |
omitted when empty | max_len 256 | |
command_id |
string |
omitted when empty | max_len 64 |
Note
With level naming one of the symbol's configured circuit_breaker.levels, the halt runs through the same CircuitBreakerState.activate() state machine a price-triggered halt uses - so it gets a real resume_at_ns and ACE corridor, and the normal circuit-breaker tick resumes it.
Without one the halt is indefinite, cleared only by an explicit risk.symbol_resume.
The engine rejects a level for a symbol that has no circuit breaker configured.
See also: risk.symbol_resume, risk.circuit_breaker_halt_all
risk.symbol_halt_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: the per-symbol halt's outcome.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
symbol |
string |
defaults to '' |
max_len 16 | |
reason |
string |
defaults to '' |
max_len 512 | |
cancelled_quotes |
int |
defaults to 0 |
ge 0, unit dimensionless |
Quotes pulled because the instrument stopped trading. |
command_id |
string |
omitted when empty | max_len 64 |
Note
symbol is echoed in the body.
It is what tells two concurrent halt acks for one gateway apart - the reason risk.kill_switch needed a command_id and this did not.
risk.symbol_resume¶
Published by: admin, api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: resume trading on one halted instrument.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
note |
string |
omitted when empty | max_len 256 | |
command_id |
string |
omitted when empty | max_len 64 |
See also: risk.symbol_halt
risk.symbol_resume_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: the per-symbol resume's outcome.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
symbol |
string |
defaults to '' |
max_len 16 | |
reason |
string |
defaults to '' |
max_len 512 | |
command_id |
string |
omitted when empty | max_len 64 |
Note
No counters: a resume cancels nothing.
The one ack in this family that carries only an outcome and the symbol it applies to.
risk.cancel_symbol¶
Published by: admin, api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: cancel every resting order for one instrument, across every gateway.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
note |
string |
omitted when empty | max_len 256 | |
command_id |
string |
omitted when empty | max_len 64 |
Note
The instrument-scoped counterpart to risk.kill_switch's gateway-scoped sweep: that one cancels one gateway across all symbols, this one cancels one symbol across all gateways.
Neither halts anything - trading may resume immediately.
See also: risk.symbol_halt, risk.kill_switch
risk.cancel_symbol_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: what the symbol-wide mass cancel removed.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
symbol |
string |
defaults to '' |
max_len 16 | |
reason |
string |
defaults to '' |
max_len 512 | |
cancelled_orders |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
cancelled_quotes |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
command_id |
string |
omitted when empty | max_len 64 |
risk.circuit_breaker_halt_all¶
Published by: admin
Transport: engine_pub
Since: 1.0
ADMIN to engine: halt trading on every known symbol.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
Note
Halts, and leaves resting orders in place.
risk.kill_switch_global is the other market-wide stop and does the opposite - it cancels everything and halts nothing.
The pair is the reason both exist.
Carries no note and no command_id, on either side.
See the block comment above this group.
See also: risk.circuit_breaker_resume_all, risk.kill_switch_global
risk.circuit_breaker_halt_all_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: how wide the market-wide halt reached.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
halted_symbols |
int |
defaults to 0 |
ge 0, unit dimensionless |
|
cancelled_quotes |
int |
defaults to 0 |
ge 0, unit dimensionless |
risk.circuit_breaker_resume_all¶
Published by: admin
Transport: engine_pub
Since: 1.0
ADMIN to engine: resume every symbol halted by the market-wide halt.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: risk.circuit_breaker_halt_all
risk.circuit_breaker_resume_all_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: how many symbols came back.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
resumed_symbols |
int |
defaults to 0 |
ge 0, unit dimensionless |
Note
No cancelled_quotes counterpart: resuming pulls nothing.
The halt ack and the resume ack are deliberately different shapes because they report different things, not one shape with a field left at zero.
Family session¶
Record types¶
NextTransition¶
The transition after this one: what the session moves to, and when. Present only when the scheduler drove the transition, since it alone knows the day's timetable. A manual or admin-driven transition carries no next - deliberately, because the schedule says what should happen while the engine decides what does, and a countdown derived from the timetable alone would tick toward a transition nobody will perform. The two fields are one record rather than two optional keys because neither is meaningful alone: a phase without a time cannot be counted down to, and a time without a phase does not say what happens.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
state |
string |
required | max_len 32 | The session state being moved to. |
at |
string |
required | max_len 32 | UTC ISO-8601 instant of the transition. |
ReplyTo¶
Where to send the outcome of a command, and under what correlation id. Supplied by an interactive requester that wants to know what happened. pm-scheduler omits it: it drives the timetable, has nobody to report back to, and the public session.state broadcast already says what occurred. A record rather than two optional keys for the same reason as NextTransition - a command_id with no gateway to answer on is undeliverable, and a gateway with no command_id cannot be correlated.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
command_id |
string |
required | max_len 64 | |
gateway_id |
string |
required | max_len 32 |
session.state¶
Published by: engine
Transport: engine_pub
Since: 1.0
Broadcast the engine's current session state to every subscriber. The most widely consumed topic in the system.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
state |
string |
required | max_len 32 | The session state now in effect. |
prev_state |
string |
omitted when empty | max_len 32 | The state departed from; absent on the first broadcast. |
next |
NextTransition |
omitted when unset | — |
Note
prev_state is omitted when empty rather than emitted as "", which is what the hand-written payload did.
next is present only on a scheduler-driven transition.
See also: session.transition
session.transition¶
Published by: scheduler
Transport: engine_pub
Since: 1.0
Scheduler or operator to engine: request a state change.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
to_state |
string |
required | max_len 32 | |
next |
NextTransition |
omitted when unset | — | Describes the transition after this one, so the engine can publish a countdown target. A manual transition omits it, which clears any stale target the engine was holding. |
reply_to |
ReplyTo |
omitted when unset | — |
See also: session.transition_ack.{GW_ID}, session.state
session.transition_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to the requesting gateway: the outcome of a transition request.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
command_id |
string |
required | max_len 64 | |
accepted |
bool |
required | — | |
to_state |
string |
defaults to '' |
max_len 32 | |
reason |
string |
defaults to '' |
max_len 256 | Rejection detail; empty when accepted. |
Note
Addressed rather than broadcast, because a command_id belongs to whoever issued it - putting it on the public session.state topic would hand every subscriber another operator's correlation id.
It also closes a silent failure: a request the engine discarded previously produced no reply at all, so a caller could not tell a rejection from a timeout.
Family structure¶
combo.ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to gateway: a combo submission was accepted or rejected.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
combo_id |
string |
required | max_len 64 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 |
Note
Three scalars.
It carried a full ComboOrder.to_dict() state dump until the submission, event and persistence shapes were separated in 5.1c - and no consumer had ever read it: alf_console, alf_gwy, pm-stats and the api_gateway event stream all take only these three.
Design section 15.4 records that removal.
See also: order.combo, combo.status.{GW_ID}
combo.status.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to gateway: a combo moved to a new lifecycle state.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
combo_id |
string |
required | max_len 64 | |
status |
enum: PENDING, PARTIALLY_MATCHED, MATCHED, FAILED, CANCELLED, REJECTED |
required | — | Mirrors models/combo.py::ComboStatus. |
reason |
string |
omitted when empty | max_len 512 | Why a terminal status was reached; absent on the happy path. |
Note
PENDING is never published - it is the state a combo is created in, so the first event a client sees is always a transition out of it.
reason replaced a details map carrying exactly one key, always "reason", which both consumers unwrapped on arrival.
It is omitted when empty, which is what the map's if reason else None guard did.
See also: combo.ack.{GW_ID}
oco.ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to gateway: an OCO pair was accepted or rejected.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
oco_id |
string |
required | max_len 64 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | |
order_id_1 |
string |
defaults to '' |
max_len 64 | The first leg's engine order id. |
order_id_2 |
string |
defaults to '' |
max_len 64 | The second leg's engine order id. |
Note
The two order ids are always emitted, as "" on rejection: the hand-written builder put them in the base payload rather than under a guard, and a rejected pair has no orders to name.
They are what lets a client tie the pair to the two single-order acks that follow.
See also: order.oco, oco.cancelled.{GW_ID}
oco.cancelled.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to gateway: one leg of an OCO pair was cancelled because the other was actioned.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
oco_id |
string |
required | max_len 64 | |
cancelled_order_id |
string |
required | max_len 64 | |
reason |
string |
defaults to '' |
max_len 512 |
Note
Distinct from order.cancelled, which says an order is gone.
This says why - the sibling filled or was cancelled - and names the pair, which an order-level event cannot.
See also: oco.ack.{GW_ID}, order.cancelled.{GW_ID}
Family system¶
Record types¶
SymbolInfo¶
One tradable instrument, as the engine sees it for a given caller. What used to be one entry of symbols and one entry of symbol_meta, which were always the same instrument written twice. The market-maker fields are resolved per caller, not per symbol: the engine layers the gateway's own policy, the global per-symbol policy and the gateway's per-symbol override before publishing. Two gateways asking at the same moment get different values for the same instrument, which is why they travel on this reply rather than in the reference bundle.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
tick_decimals |
int |
required | ge 0, le 9, unit dimensionless |
The tick scale, as the integer exponent the engine actually holds. Same field, same name and same meaning as book.tick_decimals and system.eod's. It replaces tick_size, which was this value passed through 10 ** -n on the way out and round(-log10(x)) on the way back in by two consumers, and read under a third spelling by a third. |
enforce_mm_obligation |
bool |
omitted when unset | — | Whether this caller carries a market-making obligation on this instrument. Absent when the caller is not a configured gateway -- regime 3 rather than false, because "no obligation configured" and "obligation explicitly disabled" are different answers and the console renders them differently. |
mm_max_spread_ticks |
int |
omitted when unset | ge 0, unit ticks |
Widest quote that satisfies the obligation. Absent with it. |
mm_min_qty |
int |
omitted when unset | ge 0, unit shares |
Smallest quote that satisfies the obligation. Absent with it. |
prev_close |
float |
omitted when unset | unit display_price |
Previous session's closing reference price, from persisted book stats. Absent on an instrument that has never closed, which is a real state on the first day of a deployment. |
Collar¶
Price-band configuration. The two bands CollarConfig reads; a deployment writing other keys under collar: in its YAML had them carried onto the wire and read by nothing, and they no longer travel.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
static_band_pct |
float |
required | ge 0, unit percent |
|
dynamic_band_pct |
float |
required | ge 0, unit percent |
CircuitBreakerLevel¶
One rung of a symbol's circuit-breaker ladder, as configured. name is the string circuit_breaker.halt.level and admin.action.scope.level carry onward, and is bounded here to match them.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
name |
string |
required | max_len 32 | |
price_shift_pct |
float |
required | gt 0, unit percent |
|
halt_duration_ns |
int |
required | ge 0, unit duration_nanos |
How long a halt at this rung lasts. A duration, not an instant -- the distinction epoch_nanos could not make, and the reason the unit registry gained duration_nanos in this phase. See design section 28.7. |
SymbolCircuitBreaker¶
A symbol's configured circuit-breaker ladder and its lookback.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
reference_window_ns |
int |
required | ge 0, unit duration_nanos |
Lookback window for the reference price. A duration. |
levels |
list of CircuitBreakerLevel |
required | — |
ReferenceSymbol¶
One instrument's static configuration. Distinct from SymbolInfo, which is the same instrument as a caller sees it: this record is identical for every caller and changes only on reload, which is why the bundle can be cached and hashed and the symbols reply cannot.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | Was the map key. Section 19.2. |
tick_decimals |
int |
required | ge 0, le 9, unit dimensionless |
As on SymbolInfo; tick_size is gone from here too. |
level |
string |
omitted when unset | max_len 32 | Which risk-control level this symbol resolves to, naming an entry of risk.levels. Absent on a symbol with no level configured. |
collar |
Collar |
omitted when unset | — | Both bands or neither -- section 16.2's combination. |
circuit_breaker |
SymbolCircuitBreaker |
omitted when unset | — |
RiskLevel¶
One named risk-control level. Was a map entry keyed by name; section 19.2's shape, with the key as a field.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
name |
string |
required | max_len 32 | |
collar |
Collar |
omitted when unset | — | The level's default bands, which a symbol may override. Absent when the level configures none -- previously an empty collar object, which said the same thing in more bytes. |
ReferenceRisk¶
The risk-control ladder as configured, and which rung is the default.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
default_level |
string |
omitted when unset | max_len 32 | Names an entry of levels. Absent when none is configured. |
levels |
list of RiskLevel |
required | — |
SessionTimes¶
The trading day's clock, as five wall-clock times. Carried by system.session_schedule and, nested inside ReferenceSchedule, by system.reference -- one shape declared once rather than two declarations that can drift apart. The values are strings because that is what the config file holds and what every consumer renders. Nullable individually because a partial schedule: block is a legal config.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
pre_open |
string |
null when unset |
max_len 32 | |
opening_auction_start |
string |
null when unset |
max_len 32 | |
continuous_start |
string |
null when unset |
max_len 32 | |
closing_auction_start |
string |
null when unset |
max_len 32 | |
closing_auction_end |
string |
null when unset |
max_len 32 |
ReferenceSchedule¶
The venue's calendar configuration: whether sessions run at all, which country's holidays they observe, and the clock itself. schedule is nested rather than flattened beside its two siblings, which is a change to GET /reference/schedule. The alternative was declaring SessionTimes's five fields a second time inline, and a shape described twice is the drift section 1 is about.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
sessions_enabled |
bool |
required | — | |
country |
string |
omitted when unset | max_len 2 | ISO 3166-1 alpha-2, for the holiday calendar. Absent when unset. |
schedule |
SessionTimes |
null when unset |
— | Null when the deployment configures no schedule: block. Regime 2 rather than 3: both readers pass the bundle through verbatim to a caller, and a key that is sometimes absent is harder to consume than one that is sometimes null. |
IndexDefinition¶
One index as configured -- its membership and its starting level, not its current one. GET /history/index-daily serves the live value; see index.rebalance.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
id |
string |
required | max_len 32 | |
description |
string |
defaults to '' |
max_len 128 | |
base_value |
float |
required | unit dimensionless |
Index points. dimensionless to match index.yaml. |
constituents |
list of string |
required | — |
EodBookLevel¶
One aggregated price level of a closing book. The same three fields as book.BookLevel, redeclared because records are family-scoped and the IDL has no cross-family reference -- see design section 28.5 on why that stays a known duplication rather than becoming a shared-types construct on the strength of one instance. price is required here. BookLevelPayload, the hand-written dataclass this replaces, made it optional; the only producer is OrderBook.snapshot(), which has never emitted a level without one.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
price |
float |
required | unit display_price |
|
qty |
int |
required | unit shares |
|
count |
int |
required | unit dimensionless |
EodBook¶
One symbol's closing book. A deliberately trimmed book.book_snapshot: SystemEodPayload.from_dict has always dropped last_qty, last_buy_price, last_sell_price and recent_trades from the snapshot it is handed, and this record says so rather than leaving it to a from_dict a reader has to go and find.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
tick_decimals |
int |
required | ge 0, le 9, unit dimensionless |
The scale these display prices were produced at. |
bids |
list of EodBookLevel |
required | — | Descending by price. |
asks |
list of EodBookLevel |
required | — | Ascending by price. |
last_price |
float |
omitted when unset | unit display_price |
Absent on an instrument that never traded. |
HaltedSymbol¶
One currently-halted instrument. The circuit-breaker detail is present only when a circuit breaker is what halted it: an ADMIN halt sets the flag without a breaker behind it, so the three fields travel together or not at all -- section 16.2's combination, expressed as three regime-3 fields rather than a record because they are three independent CALF-side values and no reader holds them as a unit (section 26.2's reasoning, second application).
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
resume_at_ns |
int |
omitted when unset | unit epoch_nanos |
When the halt lapses. Null on an indefinite halt. |
level |
string |
omitted when unset | max_len 32 | The rung that triggered it. Bounded to match the ladder. |
halt_source |
string |
omitted when unset | max_len 32 |
Position¶
One instrument the gateway is not flat in. Only non-zero net positions are reported, so an empty list means flat everywhere.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
net_qty |
int |
required | unit shares |
Positive is long, negative is short. Never zero. |
avg_cost |
float |
required | ge 0, unit display_price |
Volume-weighted entry price; 0.0 when unknown. |
ActiveQuote¶
One active two-sided quote, with both legs' live order state. What a market maker reads on reconnect to find out what it already has resting. A leg whose order is gone reports MISSING with zero quantities rather than being omitted: the quote still exists as far as the engine's index is concerned, and a bootstrap that silently dropped one side would let a bot re-quote into its own resting order.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
quote_id |
string |
required | max_len 64 | |
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
required | max_len 16 | |
state |
string |
required | max_len 32 | |
bid_order_id |
string |
required | max_len 64 | |
ask_order_id |
string |
required | max_len 64 | |
bid_price |
float |
null when unset |
unit display_price |
|
ask_price |
float |
null when unset |
unit display_price |
|
bid_qty |
int |
required | ge 0, unit shares |
|
ask_qty |
int |
required | ge 0, unit shares |
|
bid_remaining_qty |
int |
required | ge 0, unit shares |
|
ask_remaining_qty |
int |
required | ge 0, unit shares |
|
bid_status |
string |
required | max_len 32 | |
ask_status |
string |
required | max_len 32 |
QuoteLeg¶
One live leg of an active quote, with its order's current state.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
quote_id |
string |
required | max_len 64 | |
order_id |
string |
required | max_len 64 | |
symbol |
string |
required | max_len 16 | |
leg_side |
enum: BUY, SELL |
required | — | Which side of the quote this leg is. Exactly two producers. |
price |
float |
omitted when unset | unit display_price |
Leg limit price in display money. A quote leg is always a priced limit order; null only if the resting order's price is somehow unavailable. |
qty |
int |
required | ge 0, unit shares |
|
remaining |
int |
required | ge 0, unit shares |
|
filled |
int |
required | ge 0, unit shares |
|
status |
string |
required | max_len 32 | |
quote_status |
string |
required | max_len 32 |
QuoteLegSnapshot¶
A leg as it stood when its quote left the book. No live qty/remaining here in the sense the name suggests -- these are the final values, recorded at removal, because once an order leaves the book its state is not available anywhere in the engine.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
order_id |
string |
required | max_len 64 | |
qty |
int |
required | ge 0, unit shares |
|
remaining |
int |
required | ge 0, unit shares |
|
filled |
int |
required | ge 0, unit shares |
|
status |
string |
required | max_len 32 |
RecentQuote¶
One recently-removed quote, from the engine's bounded per-gateway inactivation history. A quote-level summary rather than a per-leg one. Does not survive an engine restart.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
quote_id |
string |
required | max_len 64 | |
symbol |
string |
required | max_len 16 | |
bid_order_id |
string |
required | max_len 64 | |
ask_order_id |
string |
required | max_len 64 | |
quote_status |
string |
required | max_len 32 | |
reason |
string |
required | max_len 64 | Why it was removed. quote_status is derived from this. |
removed_at_ns |
int |
required | unit epoch_nanos |
|
bid_leg |
QuoteLegSnapshot |
null when unset |
— | Null when the quote had no bid leg to record. |
ask_leg |
QuoteLegSnapshot |
null when unset |
— |
LiveCircuitBreaker¶
A symbol's circuit breaker as it stands right now. The live counterpart to ReferenceSymbol.circuit_breaker, which is the configuration.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
halted |
bool |
required | — | |
reference_price |
float |
null when unset |
unit display_price |
|
trigger_price |
float |
null when unset |
unit display_price |
|
triggered_level |
string |
null when unset |
max_len 32 | |
expansion_index |
int |
null when unset |
ge 0, unit dimensionless |
|
corridor_low |
float |
null when unset |
unit display_price |
Flat, and named without the corridor. box it used to sit in. _corridor_payload returns {corridor_low, corridor_high, expansion} and risk_state nested that under a key called corridor, so the wire read corridor.corridor_low -- the prefix and the box saying the same thing twice. The same helper is splatted flat into circuit_breaker.halt, so one producer was emitting two shapes of one value. Flat here matches the event. See design 29.2. |
corridor_high |
float |
null when unset |
unit display_price |
|
corridor_expansion |
int |
null when unset |
ge 0, unit dimensionless |
The ACE expansion count that goes with the bounds above. Distinct from expansion_index, which is always a real integer; this one is null exactly when the corridor is. |
resume_at_ns |
int |
null when unset |
unit epoch_nanos |
SymbolRiskState¶
One symbol's live risk state. Was a map entry keyed by symbol -- section 19.2's shape for the twelfth time.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
collar_reference_price |
float |
omitted when unset | unit display_price |
Absent on a symbol with no collar configured. |
circuit_breaker |
LiveCircuitBreaker |
omitted when unset | — | Absent on a symbol with no circuit breaker configured. |
GatewayInfo¶
One configured participant, and whether it is connected right now.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
id |
string |
required | max_len 32 | |
role |
string |
required | max_len 32 | |
description |
string |
defaults to '' |
max_len 128 | |
connected |
bool |
required | — |
SymbolVolume¶
One instrument's traded volume so far today. Was a map entry keyed by symbol; the key is a field now, as everywhere else in this family.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
symbol |
string |
required | max_len 16 | |
qty |
int |
required | ge 0, unit shares |
|
value |
float |
required | ge 0, unit money |
Traded notional, rounded to two decimals by the producer. |
trades |
int |
required | ge 0, unit dimensionless |
system.gateway_connect¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Gateway to engine: authenticate this participant. Sent over PUSH/PULL rather than the pub bus; it carries a topic so the audit log can classify it alongside everything else.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Who is connecting. The engine's PULL socket is a boundary of its own -- section 22.3 -- so this is clamped on arrival. |
See also: system.gateway_auth.{GW_ID}, system.gateway_disconnect
system.gateway_auth.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all subscribers: a participant's connection was accepted or rejected. The PUB-side answer to system.gateway_connect, and the widest-read message in this half -- five consumers structurally, and it is in PRIVATE_PREFIXES.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
accepted |
bool |
required | — | |
reason |
string |
defaults to '' |
max_len 512 | Why it was rejected; "" on acceptance. Regime 1, because GatewayAuthPayload.to_dict has always emitted the key. |
description |
string |
defaults to '' |
max_len 128 | The gateway's configured display name. Regime 1, as above. |
Note
gateway_id is in the topic AND the body, and every consumer reads it from the body -- balf_gwy dispatches on the topic suffix and then reads the payload's copy.
So the field is listed in include: explicitly.
Section 26.4: include: all means "every field except the topic parameters", and taking the default here would have dropped the key from the wire with pm-msgen check still passing.
See also: system.gateway_connect, system.gateway_bye.{GW_ID}
system.gateway_disconnect¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Gateway to engine: I am leaving cleanly. PUSH/PULL, like gateway_connect; the engine republishes it as gateway_bye so PUB subscribers hear about it at all.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
reason |
string |
defaults to '' |
max_len 512 |
See also: system.gateway_bye.{GW_ID}, system.gateway_connect
system.gateway_bye.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all subscribers: a participant has disconnected. The PUB-side counterpart to gateway_auth. The inbound system.gateway_disconnect is a PULL message and never reaches subscribers, so without this broadcast clearing could not close the matching session.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
reason |
string |
defaults to '' |
max_len 512 |
Note
gateway_id is in the topic and the body, and clearing -- the only structural reader -- takes it from the body.
Enumerated for the same reason as gateway_auth.
See also: system.gateway_disconnect, system.gateway_auth.{GW_ID}
system.eod¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to all subscribers, once, before shutdown: the closing book of every instrument. Four consumers -- clearing, index, stats and the RALF gateway -- which is the widest fan-out in this half, and the reason the record is declared rather than left to a from_dict.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
books |
list of EodBook |
required | — | One per instrument the engine had a book for, unordered. |
Note
A broadcast with no request: nothing asks for end of day, the engine announces it.
That is why it has no _request sibling and why it is in this half rather than 6.1f's request/reply pairs.
See also: book.{SYMBOL}, session.state
system.symbols_request¶
Published by: admin, api_gateway, gateway, stats
Transport: engine_pub
Since: 1.0
Caller to engine: which instruments are tradable, and on what terms for me.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Both the correlation key for the reply topic and the identity the market-maker fields are resolved against. |
See also: system.symbols.{GW_ID}
system.symbols.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: the tradable instruments, with the tick scale and this caller's market-making terms for each.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped from the body by the default projection. |
symbols |
list of SymbolInfo |
required | — | Sorted by symbol, as the engine iterates its books. |
Note
The default projection is right here: gateway_id names the caller in the topic and has never been in the body.
Verified against the producer rather than assumed -- section 26.4 is the case where assuming cost five wires a field.
One collection, not two.
The old payload carried symbols as a list of strings beside symbol_meta as a map from those same strings to their metadata, built in the same loop; nine readers joined them back together.
The join is gone.
See also: system.symbols_request, system.reference.{GW_ID}
system.reference_request¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
Any caller to engine: the compiled reference-data bundle. Static configuration only -- nothing that changes during a session.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Correlation key only. The API gateway passes an API key here for read-only callers, since the bundle does not vary by caller and this only has to be unique. |
See also: system.reference.{GW_ID}, system.reference_reload
system.reference.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: every piece of static venue configuration in one round trip -- tick scales, risk levels, circuit-breaker ladders, index definitions and the calendar -- with a hash that changes when any of it does.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 64 | Topic-only; dropped from the body by the default projection. Bounded at 64 rather than 32 because the API gateway passes an API key here for read-only callers. |
symbols |
list of ReferenceSymbol |
required | — | Sorted by symbol. Was a map keyed by it. |
risk |
ReferenceRisk |
required | — | |
indexes |
list of IndexDefinition |
required | — | Already a list of records before this phase. |
schedule |
ReferenceSchedule |
required | — | |
config_version |
string |
null when unset |
max_len 64 | Truncated SHA-256 of the bundle. Null before a config is loaded, which is the only remaining difference between the two states the old two-shape reply used to distinguish. Regime 2: every consumer compares it, so the key has to be there to compare. |
Note
The whole payload was a dict[str, Any] passed to encode unread.
One producer builds it -- _rebuild_reference_cache -- with five fixed top-level keys, so it was a record nobody had written down.
ONE SHAPE, ALWAYS.
Before an engine config is loaded the reply used to be {"config_version": null} and nothing else: a second payload shape for the same topic, which every slicing endpoint compensated for with a .get(key, {}) default.
The bundle is now always complete, with empty collections and a null version, and the compensating defaults go with it.
REST-VISIBLE.
api_gateway/routers/reference.py returns slices of this bundle verbatim, so reference.symbols becoming a list of records and reference.schedule gaining a level of nesting change GET /reference/symbols and GET /reference/schedule.
Sanctioned, and the better JSON in both cases -- a list of objects each carrying its own symbol is what a client can iterate without knowing the keys.
260-api-gateway.md moves with it.
See also: system.reference_request, system.reference_reload_ack.{GW_ID}, system.symbols.{GW_ID}
system.reference_reload¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: re-read static reference data from disk. Deliberately narrower than a startup load -- it never re-seeds quotes, creates or removes books, or touches session and halt state, so a reload that changed the symbol set is rejected rather than partially applied.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
command_id |
string |
required | max_len 64 | Correlated by the ack. Read off the wire unclamped before this phase, and quoted straight into a bounded ack field -- section 22.3's silent non-answer, four reply paths. |
See also: system.reference_reload_ack.{GW_ID}, system.reference.{GW_ID}
system.reference_reload_ack.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: the reload verdict, and the new configuration hash when it took.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped from the body by the default projection. |
command_id |
string |
required | max_len 64 | |
accepted |
bool |
required | — | |
config_version |
string |
omitted when unset | max_len 64 | Present on acceptance. |
reason |
string |
omitted when unset | max_len 512 | Present on rejection. |
Note
The default projection is right: gateway_id is topic-only and has never been in the body.
config_version and reason are the two halves of the verdict and never travel together -- an accepted reload carries the version, a rejected one carries the reason.
See also: system.reference_reload
system.session_state_request¶
Published by: admin, api_gateway, gateway, scheduler
Transport: engine_pub
Since: 1.0
Caller to engine: what session are we in, without advancing it. The scheduler is the dominant producer -- it asks rather than assuming, because it is not the only thing that can move the session.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: system.session_status.{GW_ID}, session.state
system.session_status.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: the current session state, on request. The polled answer to the session.state broadcast, for a caller that has just started and missed the last transition.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped from the body by the default projection. |
state |
string |
required | max_len 32 | Same values as session.state.state. |
sessions_enabled |
bool |
required | — | False when the deployment runs continuously with no session schedule at all, which makes state advisory. |
Note
Default projection: gateway_id is topic-only.
state is a plain bounded string rather than an enum, matching session.state.state -- the value set lives in the session machine, and enumerating it in two specs would be two places to update.
See also: session.state, system.session_state_request
system.session_schedule_request¶
Published by: admin, api_gateway
Transport: engine_pub
Since: 1.0
Operator to engine: the configured session schedule.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: system.session_schedule.{GW_ID}, system.reference.{GW_ID}
system.session_schedule.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to operator: the trading day's clock as configured. The same SessionTimes record system.reference carries, which is what forced these two topics into one phase.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped from the body by the default projection. |
sessions_enabled |
bool |
required | — | |
schedule |
SessionTimes |
null when unset |
— | Null when no schedule: block is configured. |
Note
Default projection: gateway_id is topic-only.
Both readers -- commands/client.py and GET /admin/schedule -- pass the payload through to a caller without touching a key, so nothing structural constrains the presence regime here; schedule is regime 2 to match the copy inside ReferenceSchedule rather than because a reader needs it.
It was schedule or {} before, so an unconfigured venue sent an empty object where it now sends null.
See also: system.session_schedule_request, system.reference.{GW_ID}
system.halt_status_request¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
Any process to engine: which instruments are halted now.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: system.halt_status.{GW_ID}, circuit_breaker.halt.{SYMBOL}
system.halt_status.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: every currently-halted instrument, with the breaker state behind it where a breaker is what halted it. The polled answer to the circuit_breaker.halt broadcast, for a caller that has just started and missed it.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
halted |
list of HaltedSymbol |
required | — |
Note
An empty list means nothing is halted, which is the normal reply.
The three optional fields are present together or not at all: an ADMIN halt sets the flag with no breaker behind it.
See also: system.halt_status_request, system.risk_state.{GW_ID}
system.position_request¶
Published by: gateway
Transport: engine_pub
Since: 1.0
Gateway to engine: what am I holding.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Both the correlation key and the account being asked about. A gateway can only ask about itself: the handler answers from _gateway_positions[gateway_id] and nothing else. |
See also: system.position_snapshot.{GW_ID}
system.position_snapshot.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to gateway: per-symbol net position and average cost, for the asking gateway only.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
positions |
list of Position |
required | — |
Note
An unauthenticated or unknown gateway gets an empty list rather than a rejection -- flat and not-a-gateway are the same answer here, which is deliberate: the alternative tells an unauthenticated caller whether an id exists.
This pair has no consumer in src/ at all.
It is exercised only by tests/test_position_snapshot.py, which is section 27.4's shape -- a capability exercised only by its own tests.
It is specified rather than removed because unlike drop_copy.replay_request it is fully implemented on both sides and reachable by any gateway; what it lacks is a caller, not an implementation.
Recorded here so the next phase to touch it knows the difference.
See also: system.position_request, trade.executed
system.quote_bootstrap_request¶
Published by: admin, api_gateway, gateway
Transport: engine_pub
Since: 1.0
Market maker to engine, on reconnect: what quotes do I already have resting. Without it a bot cannot tell a fresh start from a reconnect and will quote into its own orders.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
defaults to '' |
max_len 16 | Narrows the reply to one instrument. "" means all. |
See also: system.quote_bootstrap.{GW_ID}, system.quote_legs_request
system.quote_bootstrap.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to market maker: the active quotes it already holds.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
quotes |
list of ActiveQuote |
required | — |
See also: system.quote_bootstrap_request, quote.status.{GW_ID}
system.quote_legs_request¶
Published by: api_gateway, gateway
Transport: engine_pub
Since: 1.0
Operator or market maker to engine: the per-leg detail behind this gateway's quotes, live and recently removed. What QLEGS asks for.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | |
symbol |
string |
defaults to '' |
max_len 16 | Narrows the reply to one instrument. "" means all. |
show |
enum: ACTIVE, RECENT, ALL |
defaults to 'ALL' |
— | Which half to return. ACTIVE is live legs, RECENT is the removal history, ALL is both. Enumerated rather than a free string because the handler branches on exactly these three and treats anything else as ACTIVE -- a typo currently answers, quietly, with the wrong half. |
See also: system.quote_legs.{GW_ID}, system.quote_bootstrap_request
system.quote_legs.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to caller: live quote legs, recently-removed quotes, or both.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
legs |
list of QuoteLeg |
required | — | Live legs. Empty unless show was ACTIVE or ALL. |
show_requested |
enum: ACTIVE, RECENT, ALL |
required | — | Echo of the request's show, so replies can be told apart. |
complete |
bool |
required | — | False when the reply could not fully answer what was asked. Always true today; kept because the recent-history buffer is bounded and a truncated answer needs a way to say so. |
recent |
list of RecentQuote |
required | — | Removal history. Empty unless show was RECENT or ALL. |
Note
legs and recent are both always present, empty when the requested half does not include them.
Regime 4 would be the IDL's default instinct -- absent and [] are the same value to alf_gwy, the only structural reader -- but GET /quotes/legs returns this payload verbatim, and a REST client should not have to guess whether a key exists.
[] is a true statement here rather than an invented one: both halves always mean something on this message.
show_requested echoes what was asked, so a caller that pipelined two requests can tell the replies apart.
See also: system.quote_legs_request
system.risk_state_request¶
Published by: api_gateway
Transport: engine_pub
Since: 1.0
ADMIN to engine: the live collar and circuit-breaker state of every symbol that has either configured, halted or not.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: system.risk_state.{GW_ID}, system.reference.{GW_ID}
system.risk_state.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to ADMIN: live risk state per symbol. The counterpart to reference.risk, which is the static definitions, and to halt_status, which is only the symbols currently halted.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
symbols |
list of SymbolRiskState |
required | — | Sorted by symbol. Was a map keyed by it. |
Note
The fourth symbols field in this family and the fourth different thing: a list of SymbolInfo on system.symbols, of ReferenceSymbol on reference, of SymbolRiskState here and of SymbolVolume on volume.
Same name, four types, no relationship -- which is why a find-and-replace across them would be the worst available mistake.
See also: system.risk_state_request, system.halt_status.{GW_ID}
system.gateways_request¶
Published by: admin, api_gateway
Transport: engine_pub
Since: 1.0
Operator to engine: which participants exist, and who is on.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: system.gateways.{GW_ID}, system.gateway_auth.{GW_ID}
system.gateways.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to operator: every configured participant with its role and current connection status. The polled counterpart to the gateway_auth / gateway_bye broadcasts.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
gateways |
list of GatewayInfo |
required | — | Sorted by id. Empty when no config is loaded. |
See also: system.gateways_request, system.gateway_bye.{GW_ID}
system.volume_request¶
Published by: admin
Transport: engine_pub
Since: 1.0
Operator to engine: how much has traded today.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 |
See also: system.volume.{GW_ID}, trade.executed
system.volume.{gateway_id}¶
Published by: engine
Transport: engine_pub
Since: 1.0
Engine to operator: traded quantity, notional and trade count, per instrument and exchange-wide.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
gateway_id |
string |
required | max_len 32 | Topic-only; dropped by the default projection. |
symbols |
list of SymbolVolume |
required | — | Sorted by symbol. Was a map keyed by it. |
total_qty |
int |
required | ge 0, unit shares |
|
total_value |
float |
required | ge 0, unit money |
|
total_trades |
int |
required | ge 0, unit dimensionless |
Note
The totals are carried rather than left to the caller to sum.
That is redundant on the wire and load-bearing off it: they are the engine's own running counters, not a sum of the rows, so a caller adding up symbols would silently disagree with the engine about any instrument whose book was removed mid-session.
See also: system.volume_request
Family trade¶
trade.executed¶
Published by: engine
Transport: engine_pub, calf
Since: 1.0
Public print of a completed match. The authoritative record of what traded, consumed by statistics, clearing, index and market data.
| Field | Type | Presence | Rules | Description |
|---|---|---|---|---|
id |
string |
required | max_len 64, pattern ^[0-9]+$ |
Engine trade counter, unique within one engine run only - it restarts at 1 on every launch. Assigned by models/trade.py::Trade.create from a process-local itertools.count. |
symbol |
string |
required | max_len 16, pattern ^[A-Z0-9._]+$ |
Instrument the match occurred in. |
buy_order_id |
string |
required | max_len 64 | Resting or aggressing order id on the buy side. |
sell_order_id |
string |
required | max_len 64 | Resting or aggressing order id on the sell side. |
buy_gateway_id |
string |
required | max_len 32 | Gateway that submitted the buy order. |
sell_gateway_id |
string |
required | max_len 32 | Gateway that submitted the sell order. |
price |
float |
required | gt 0, unit display_price |
Execution price in display money, already converted from ticks by the publisher. Contrast trade_log.price, which is ticks - the mismatch this unit declaration exists to make reviewable. |
quantity |
int |
required | gt 0, unit shares |
Matched quantity. |
aggressor_side |
enum: BUY, SELL, AUCTION |
required | — | Side that removed liquidity. AUCTION when both sides rested, which happens on an uncross print where there is no true aggressor. |
timestamp |
float |
required | unit epoch_seconds |
Match time in Unix epoch seconds. The engine divides its nanosecond clock by 1e9 at publish time. |
tick_decimals |
int |
defaults to 2 |
ge 0, le 8, unit dimensionless |
Decimal scale for price; 1 tick = 10^-tick_decimals. |
Note
aggressor_side is AUCTION for uncross prints, where both sides rested and there is no true aggressor.
See also: book.{SYMBOL}, order.fill.{GW_ID}