B8.1 · dive 3 — price-time by mint order
Price-time priority, without a comparator.
A matching engine resolves two crossing orders by price-time priority: best price
first, and within a price, the order that arrived first. This platform's order book gets the second half for
free. Exchange.OrderBook (PROPOSED, pure) is a price ladder per side — an ordered tree
keyed by price, gb_trees per the house pattern — each level a FIFO resolved by branded mint
order, "so price-time priority falls out of the id law rather than a comparator."
The order book is PROPOSED — the trading engine on top of a real substrate
(docs/trading/), not a shipped module yet. It claims no fill count and no match latency; those
land at its rung. The substrate beneath it is real and hardened: EchoCache.Ring is shipped
Elixir in the live umbrella, the convergence target of the rung-gated EchoMQ program
(docs/echo_mq/emq.roadmap.md), which names the trading platform as its downstream consumer; the
mint-order law the book leans on is committed — Appendix F's order theorem, held in SQLite, Valkey, and
Postgres COLLATE "C" alike.
ORD-a1
(older) then ORD-a3 (newer) — and one at 101, ORD-a2. An incoming buy crosses;
the matcher fills best price first, and within 100 the smaller (older) mint id first. The comparator falls
out of the id law.The ladder, and why mint order is the comparator
docs/trading/trading.specs.md · the pure bookThe book is a price ladder per side: an ordered tree keyed by price (gb_trees),
the bids descending and the asks ascending, so the best price is a tree edge. Within one price level, orders
queue in a FIFO — and the FIFO key is the branded Snowflake stamped at admission. Mint order is
arrival order, by construction, so "first at this price" is "smallest id at this price," and the same order
holds in SQLite, Valkey, and Postgres COLLATE "C" alike by Appendix F's order theorem.
# Exchange.OrderBook (PROPOSED): a price ladder per side # asks :: gb_trees keyed by price; each level a FIFO of branded ids # best ask = the leftmost price (a tree edge), no scan # within a level: oldest = smallest mint id = first filled
The consequence is a design with one ordering, not two: the sequence, the sort, the cursor, and the within-level priority are the same fourteen bytes, paid for once at mint. A separate sequence counter beside each level would be a second source of truth to keep in step with the first; the id removes it. This is the same move the ingress made in dive 1 — the chase sequence is the id — applied to the resting book.
The open-order indexes ride the branded tree
docs/trading/trading.specs.md · EchoData.BrandedTreeOpen-order indexes per account ride EchoData.BrandedTree — namespaced
gb_trees with first/2, last/2, and page_after/4, "the
oldest-first and pagination shapes a book needs already its verbs." A book asks for the oldest open order, or
the next page after a cursor, and the branded tree answers in creation order without a sort step — the same
page_after/4 the ingress consumer uses to resume from an id cursor in dive 1. Snapshots for
projections freeze through the CHAMP / FrozenIndex family rather than copying mutable maps.
So the book and the ingress share one ordering verb. The id is the cursor on the ring, the FIFO key in the ladder, and the pagination key in the open-order index — three uses of the fourteen bytes the mint already stamped, with no comparator and no second counter anywhere in the engine.
The single-writer reconcile (a PROPOSED gate)
docs/trading/trading.specs.md · TRD.2, design-voicedThe master invariant binds the two halves of the engine: one instrument's book is mutated by exactly one process — the sole drainer of its Ring — and the Ring's accepted count reconciles exactly with the Book's applied count plus the counted drops. Crossing orders produce fills; the publishes that were accepted equal the applies plus the drops, with nothing lost in between and nothing applied twice.
publishes = applies + drops — the TRD.2 gate's shape. This is a
PROPOSED gate: it reports the reconcile arithmetic, never a measured throughput. It becomes a
committed record at its rung.accepted = applied + dropped — the shape
the harness will check at the Book's rung. No throughput is asserted here.This is the TRD.2 gate, and it is PROPOSED — "the single-writer property under concurrent publishes" the design will gate at the rung that ships the Book. The reconcile is the shape the harness will check, not a number already on the record. The acknowledge-after-append rule is the spec's law: a command accepted then crashed before its events are appended is lost and the caller knows nothing, so the gate reconciles publishes against applies-plus-drops precisely to make that boundary visible.
The seat the engine fills — the ring, real and quoted
echo/apps/echo_cache/lib/echo_cache/ring.ex · B4.3 recordThe reconcile stands on a real, hardened seat. EchoCache.Ring — shipped Elixir
at echo/apps/echo_cache/lib/echo_cache/ring.ex, the convergence target of the rung-gated EchoMQ
program (/echomq, docs/echo_mq/emq.roadmap.md) that names the trading
platform as its downstream consumer — already proves the admission half of the arithmetic: the accepted count
and the counted drops are real today, its committed behavior on the B4.3 record:
G4 full ok -- the bound held its shape: 64 accepted, 136 refused with :dropped and counted -- never blocked, never overwritten -- then the release drained all 64 and publish 201 landed and applied: a storm bends the lane's at-most-once contract no further than the contract already bends G1 surface ok -- the ring's surface is whole -- ... a fresh ring stands at occupancy 0 PASS 6/6
The accepted count, the counted drops, and the drained applies are the three terms the
PROPOSED Book's reconcile will balance — the ring's record fixes the ingress side today
(64 accepted, 136 refused); the Book's rung will fix the apply side. That is the two-layer
discipline in one paragraph: the seat is real, shipped, and quoted; the trading engine on top — the
Exchange.* writer that fills it — is PROPOSED and design-voiced; and no fill count, match
latency, or throughput is claimed for an engine that has run no rung yet, while the substrate beneath it
already has its numbers. The next module, B8.2 · The Log and the Ledger, carries the facts
these fills emit into the journal — Chapter 4.4's posture as its subject.
References
sources · relatedSources
- The LMAX Disruptor (technical paper)sequences as the coordination primitive — the ordering the book's mint-keyed FIFO takes onto branded ids
- Fowler — The LMAX Architecturethe single-writer matching processor the reconcile gates
- Chassaing — Functional Event Sourcing Deciderthe events the reconcile counts are decide's outputs, not commands
Related
- B8.1 — The Enginethe module hub; the engine's two questions
- B8.1.1 — The Disruptor Seatthe ingress whose accepted/dropped counts the reconcile balances
- B8.1.2 — The Deciderthe pure core the book shells out to
- B4.3 — The Single Writer and the Ringthe as-built ring this dive quotes, in its home chapter
- B4 — EchoCachethe chapter the ring and tables live in
- Functional Programming in Elixirthe gb_trees and CHAMP cores the book is built on