Skip to main content

Binary-FIX (SBE + SOFH + FIXP) — Conformance & Certification

Senticore's binary-FIX gateway is a CME iLink-3-class stack:

  • SBE (Simple Binary Encoding) — fixed-layout binary messages from the published schema (backend/protocol/sbe-fix/schema/senticore-orderentry-1.0.xml, schemaId=1).
  • SOFH (Simple Open Framing Header) — 6-byte big-endian frame delimiter.
  • FIXP (FIX Performance session layer) — binary Recoverable-flow session (Negotiate / Establish / Sequence / RetransmitRequest / Retransmission / Terminate).

Application messages (templateId 1–12) and FIXP session messages (templateId 100–111) ride the same SOFH stream, distinguished by templateId.

Wire framing

SOFH (6 bytes, BIG-endian):
u32 message_length # total bytes INCLUDING these 6
u16 encoding_type # 0x5BE0 = SBE little-endian
SBE message header (8 bytes, LITTLE-endian):
u16 blockLength # fixed root block size
u16 templateId
u16 schemaId # = 1
u16 version # = 1
<root block: blockLength bytes> [ <repeating groups> ] [ <var-data: u16 length + bytes> ]

schemaId is fixed forever. version is bumped only for additive, backward-compatible changes (append trailing fields, grow blockLength; old decoders skip via blockLength). Breaking changes ship as a new schemaId on a new port, with both running during migration.

Session authentication

Credentials are presented once, at FIXP Negotiate, in the credentials var-field:

apiKey:apiSecret:apiPassphrase:accountHex

verified against your issued agent credential (the same FixLogon credential as tag=value FIX). After Establish there is no per-order signing — the session is the authenticated identity. Every order's embedded account field must equal the authenticated account (cross-account orders are rejected). Credentials travel in cleartext inside TLS; the gateway refuses plaintext in guarded environments.

Golden vectors

The reference encoder (scripts/fixp_conformance_smoke.py) and the Rust codec (backend/gateway/fixp/tests/golden.rs) emit these byte-identical full-frame (SOFH-wrapped) hex vectors. Your codec must reproduce them exactly. Canonical inputs: account = 0x000102…13, orderId = 0xAB×32, sessionId = 0x11×16, timestamp = 1700000000000.

MessagetemplateIdgolden hex (SOFH-framed)
NewOrderSingle1000000885be07a00010001000100434f4e462d31…010201000000
ExecutionReport10000000cb5be0bb000a0001000100abab…0101010000
Negotiate1000000002d5be01900640001000100111111…030400434f4e46
Establish103000000385be02800670001000100111111…01000…00
EstablishmentAck104000000365be02800680001000100111111…0100…00
Sequence106000000165be008006a00010001000100000000000000

(Full untruncated vectors are in EXPECTED_HEX in the Python script and EXPECTED_* in golden.rs.)

Reference client

Stdlib-only Python, runnable in restricted certification environments:

# 1) prove your/our encoder is byte-compatible with the Rust codec
python scripts/fixp_conformance_smoke.py verify-golden

# 2) drive a live handshake + order against a gateway
python scripts/fixp_conformance_smoke.py session \
--host fix-bin.example --port 9879 --tls \
--api-key spk_… --api-secret sps_… --api-passphrase spp_… \
--account 0xabc… --market 1 --side bid --price 1000000 --qty 1

Certification checklist

An MM is certified once it passes:

  1. Encodingverify-golden green (all golden vectors byte-identical).
  2. Handshake — Negotiate → NegotiationResponse, Establish → EstablishmentAck; Recoverable flow required (non-Recoverable is rejected).
  3. Order lifecycle — NewOrderSingle → ExecutionReport drop-copy; OrderCancelRequest and OrderCancelReplaceRequest by OrderID and by OrigClOrdID.
  4. Idempotency — a duplicate ClOrdID placement is rejected (BusinessMessageReject).
  5. Recovery — on a sequence gap the server emits RetransmitRequest; a client RetransmitRequest is answered with Retransmission + replayed frames.
  6. LivenessSequence keepalives are exchanged on the negotiated interval; a silent connection is closed after the grace window.
  7. Rate limits — sustained order flow stays within the per-account budget (shared MM limiter).

Current support matrix

CapabilityStatus
NewOrderSingle / Cancel / CancelReplace (by OrderID or OrigClOrdID)
ExecutionReport drop-copy
Duplicate-ClOrdID rejection
FIXP Negotiate/Establish/Sequence/RetransmitRequest/Retransmission/Terminate
OrderMassCancelRequest (cancel-all / per-market / per-side)
Cancel-on-disconnect (resting orders swept on any teardown)
Reject-on-unsupported (no silent semantic downgrade)
Source-IP allowlist / mTLS admission control
OrderStatusRequest reply⏳ no reply yet (track via drop-copy)
Cross-connection resume (ResumeRegistry)⏳ not yet wired

Cancel-on-disconnect & mass-cancel (MM risk controls)

  • Cancel-on-disconnect is armed per credential (cancelOnDisconnect on the agent). When enabled, every resting order placed on the session is registered, and on ANY connection teardown — clean close, timeout, network drop, or error — all of that account's resting orders are swept. A dropped TCP connection never leaves a live quote stack in the book.
  • OrderMassCancelRequest cancels all open orders for the account in one message — optionally filtered to specific markets and/or a side — fanned out as qty=0 quote-replaces by the engine. This is the low-latency panic-kill; you do not enumerate orders client-side.
  • Liveness as a soft dead-man's-switch: stop sending Sequence keepalives and the session dies on the negotiated interval, which triggers the cancel-on-disconnect sweep.