orderbook — a fast, embeddable limit order book & matching engine in Go
What is this?
orderbook is a Go library you drop into your own project to run a<br>central limit order book (CLOB) and matching engine —<br>the core of an exchange, a trading simulator, a game economy, or any system where<br>buyers and sellers meet at a price. It is a focused, dependency-light core you can<br>go get today, with a small, stable API.
It’s not a hosted service or a full exchange (no accounts, custody, or settlement) —<br>it’s the engine. Everything you see on this page is powered by that exact engine.
First, what is an order book?
If you’ve never seen one, here it is from the ground up.
People post offers. A bid is a buy order (“I’ll buy 5 at 100”). An ask is a sell order (“I’ll sell 3 at 101”).
They’re sorted by price. Best bid (highest a buyer will pay) and best ask (lowest a seller will accept) sit in the middle. The gap between them is the spread .
Size at each price is depth. Lots of size = a deep, liquid market that’s hard to move. Little size = thin, and prices swing.
That whole ladder is the order book — a live list of everyone’s intentions, updating constantly. →
The ladder on the right is a real book from this engine. Best bid/ask are highlighted; the spread is the gap in the middle.
PRICESIZE
spread —mid —
How matching works
When a new order can trade, the engine matches it by price–time priority :<br>best price first, and among equal prices, first-come-first-served. Walk through it —<br>each step runs on the real engine.
PRICESIZE
spread —last —
▶ Start<br>Reset
Press Start to build a book, rest an order, and send a market order that matches.
Maker — a limit order that rests and provides liquidity.
Taker — a market/crossing order that removes it, paying the spread.
Trades print at the maker’s price. Big orders “walk the book” and pay more (slippage).
Everything it does
The order-type and market-integrity surface real venues ship — all in the core.
Order types
Market & Limit
Stop & Stop-limit
Iceberg (hidden reserve, auto-refill)
Post-only (maker-guaranteed)
Pegged (to bid / ask / mid + offset)
OCO / bracket (one-cancels-other)
Trailing stop
Matching & priority
Price–time priority (FIFO)
Pro-rata allocation mode
Trades at the maker price
Self-trade prevention (5 modes)
Time-in-force: GTC · IOC · FOK
Call-auction uncross (open/close)
Market integrity
Pre-trade risk caps (fat-finger, dust, per-account)
Anti-spoofing: min resting time, order-to-trade ratio
Mark-price step + depth bounds; self-output guardrail
Surveillance: marking-close, ramping, pinging, cross-book
Halt / cancel-only states; enforcing gateway
Grounded in a threat model ↗
Market data
L1 top-of-book
L2 aggregated depth
L3 / MBO (order-by-order)
Snapshots + sequence numbers
Trade tape (time & sales)
Why it’s production-grade
Money is never a float
Prices and quantities are int64 ticks and lots — a per-symbol<br>Instrument converts decimals only at the boundary. No binary rounding<br>error creeping into balances, and an overflow guard rejects any order whose notional<br>would wrap.
Deterministic & replayable
The same ordered input always produces byte-identical trades and state. No wall-clock<br>or RNG in the matching path — so you can record a session and replay it exactly (verified<br>by a record→replay→identical-digest test).
Fast, with real numbers
O(1) best bid/ask, O(1) cancel, O(log n) inserts via a map + binary-search price<br>ladder. Measured: 6 ns top-of-book reads, ~2.8M match round-trips/s<br>per core, and 0 allocations on the submit/cancel/match hot path. Benchmarks run<br>in CI on every push.
Tested hard
Race, fuzz, soak, and replay-recovery suites under the race detector, plus<br>invariant checks (the book never crosses, quantity is conserved, FIFO holds). Green<br>on every push.
Embeddable & layered
The core (types · orderbook · matching) is<br>dependency-light and imports nothing above it. Drop it into your service; add only what<br>you need on top.
How it handles things
Each side is a map[price]→level plus a sorted price ladder; each level is<br>a FIFO queue (that’s time priority). One writer per book keeps it correct; you scale<br>out across symbols. IDs and timestamps are injected where determinism requires it.
Use it in your project
Install:
go get github.com/intrepidkarthi/orderbook/pkg/matching<br>Create an engine, submit orders, read the book:
eng := matching.NewEngine(matching.DefaultConfig("BTC-USD"))
// A resting sell (maker) — integer ticks & lots<br>ask, _ := types.NewOrder("alice", "BTC-USD", types.SideSell,<br>types.OrderTypeLimit, 30000, 5,<br>types.TIFGoodTillCancel)<br>eng.Process(ask)
// A crossing buy (taker) — trades against it<br>bid, _ := types.NewOrder("bob", "BTC-USD", types.SideBuy,<br>types.OrderTypeLimit, 30000, 3,<br>types.TIFGoodTillCancel)<br>res := eng.Process(bid)
for _, t := range res.Trades {<br>fmt.Printf("%d @ %d\n", t.Quantity, t.Price) // 3 @ 30000<br>best, qty, _ := eng.BestAsk() // 30000 x 2 left
Runnable examples:
go...