How pricing works
One price, two engines. A central limit order book and an LMSR market maker share a single source of truth — so a quote and a fill can never disagree.
Drazill is a hybrid market. Every market runs both:
- a central limit order book (CLOB) — peer-to-peer limit and market orders with price-time priority, and
- an LMSR automated market maker (AMM) — Hanson’s Logarithmic Market Scoring Rule, always-on liquidity that prices off the shares outstanding.
The important part — and what makes Drazill’s engine unusual — is that these are not two
prices. They share one canonical price state, so the book and the AMM can never
diverge. This page explains that model. It is a description of the shipped design
(docs/decisions/0001-unified-price-state.md), not a re-derivation — the numbers below are
verified against the production pricing function by a committed script.
Price is probability
Each outcome has a price in [0, 1], quoted as a decimal string. That price is the
market’s implied probability for the outcome, and across a market’s outcomes the prices sum
to exactly 1. A market at 0.63 is a 63% implied chance.
The AMM computes prices from the LMSR formula over the outcome share vector amm_shares
(q) and a liquidity parameter b:
b sets the depth: a larger b means more liquidity and less price impact per share; a
smaller b means thinner liquidity and larger impact. The market maker’s worst-case loss
is bounded at b · ln(N) for N outcomes (app/services/lmsr_service.py).
One canonical state (no book-vs-AMM arbitrage)
The single source of truth is the LMSR quantity vector amm_shares together with a
snapshotted effective liquidity parameter b_eff. Every outcome’s stored price is a
derived projection of that state — never set independently:
For every outcome, at the end of every price-moving transaction,
outcome.price == lmsr_prices(amm_shares, b_eff)andsum(outcome.price) == 1exactly.
Both engines maintain that one state:
- An AMM trade moves
amm_sharesdirectly, then prices are re-derived. - A book (CLOB) trade executes peer-to-peer at the maker price, then rebases
amm_sharesso the LMSR-implied price of the traded outcome equals that execution price, redistributing the rest of the simplex proportionally.
Because book fills rebase the shared state, a later AMM trade always prices off the
already-moved state. There is no stale second price to arbitrage — the defect this
design was built to remove. (Self-trades do not move price, closing the wash-to-move-the-mark
vector.) The full design
rationale is ADR 0001, in the repo at docs/decisions/0001-unified-price-state.md.
Worked example
A fresh binary market (Yes / No) with liquidity parameter b = 100:
Buying 100 Yes shares moves Yes from 50% to 73.11%. That figure is exactly
e^(100/100) / (e^(100/100) + e^(0/100)) = e / (e + 1) ≈ 0.73106, rounded onto the coherent
4-decimal simplex so the two prices still sum to exactly 1.
This example is not hand-computed prose — it is verified in CI by
scripts/docs/lmsr_worked_example.py, which recomputes it with the engine’s own formula
and (where the app dependencies are present) asserts the production lmsr_prices function
returns these exact values. If the pricing model changes, that gate fails and this page
must be updated.
Money rounds in the platform’s favour
At every wallet boundary, money is quantized to cents in the house’s favour: a buy cost
rounds up, sell proceeds round down, and fees round up, with the sub-cent remainder retained
by the platform. A peer-to-peer book trade quantizes the traded notional once so
buyer_debit == seller_credit + total_fee exactly (conservation). These are enforcement
details of .cursor/rules/money-rounding.mdc; they are described here, not re-specified —
integrators should treat the quote and receipt the API returns as authoritative.
Related
- Order lifecycle — how an order becomes a fill.
- Fees — the maker/taker schedule applied on top.
- Market data — reading prices, the book, and the trade tape.

