README.md: Exchange8:Margin
1. Summary
Exchange8:Margin is the asynchronous, stateful derivatives-risk engine of the
exchange8 vertical (the capital-markets tier of the Sentin8 platform). It owns
the net-position ledger per (environment, account, contract) for futures,
perpetuals, and options; revalues that ledger against mark/index prices
(mark-to-market); computes initial and maintenance margin and decides
liquidations; applies perpetual funding; prices options with the
Black–Scholes–Merton closed form (European) and the Cox-Ross-Rubinstein
binomial tree (American, early exercise); and runs the futures/expiry
settlement state machine (open → expiring → settled, cash or physical).
The risk core is a set of pure Rust modules (services/margin/src/margin.rs,
greeks.rs, expiry.rs): value types and pure functions with no clock, no
tokio, and no database dependency. The service binary wraps them in the
Sentin8 platform shell (sentin8::shell): axum HTTP/ TLS surface,
--print-registration self-registration, DB pool, health endpoint, Shield
(declarative policy) admin API mount, mimalloc allocator, and graceful drain.
The HTTP data surface is the RLS-scoped query API for positions, margin
accounts, and liquidations plus RFW admin aliases; cycle scheduling (margin
cycles, MTM runs, funding, expiry) is delegated to Sentin8:Cron. Durable event
delivery rides the platform planes: the local margin.outbox relays to
Sentin8:Bus and margin.audit_journal pushes to Sentin8:Sys.
2. Motivation
- Different latency profile, different failure model than pre-trade risk. Exchange8:Risk is a stateless microsecond pure-function gate; margin, by contrast, must maintain positions over time, revalue them continuously, close breached positions, and never lose a funding or liquidation decision. Merging the two would force one latency budget on two incompatible workloads — the platform therefore splits them (margin design §1).
- Defensible account risk under audit. Average-entry position math, realized/unrealized P&L, maintenance-requirement checks and shortfall quantification must be provable, table-tested pure functions. A margin check that cannot be replayed bit-exactly is a dispute waiting to happen.
- Options are Greeks-shaped, not notional-shaped. Option risk limits act on delta-equivalent notional, gamma, and vega exposure — a different dimension than notional-based pre-trade checks, computed from one shared Greeks shape so portfolio netting stays exact.
- Expiry must be exact and idempotent. A contract settles once, at one settlement price (the observation-window average), with cash P&L or physical delivery obligations; a crashed settle must replay to the identical result.
- No bespoke infrastructure. Identity, TLS, policy administration, event
durability, audit delivery, and scheduling are all platform services
(Sentin8:Auth,
sentin8-tls, Shield, Sentin8:Bus, Sentin8:Sys, Sentin8:Cron); the binary only owns the derivatives-risk domain.
3. Architecture & High-Level Design
The engine is event-driven and stateful; the HTTP surface is query-only (RLS-scoped views over the margin ledger). Position accounting, margin, MTM, liquidation, funding, Greeks, and expiry are pure modules; the shell owns registration, the database pool, health, the Shield admin API, the outbox relay, and the audit pusher.
The shell (main.rs) boots the DB pool (EXCHANGE8_MARGIN_DATABASE_URL), the
Shield boot load (fail-closed on unparsable policy text), the query router,
the shared policy-admin mount, the margin.outbox relay, and the
margin.audit_journal pusher. The risk modules — margin.rs (position
ledger, margin checks, funding, liquidation), greeks.rs (BSM Greeks +
portfolio netting + limits), expiry.rs (CRR binomial + expiry/settlement
state machine) — are free of tokio, DB, and clock dependencies, which is what
makes unit testing and idempotent expiry exact.
4. Key Traits and Data Structures
Concept and data-structure overview (the authoritative shapes live in the source modules); signatures are illustrative, not API dumps.
- Position ledger (
Position) — signed net quantity, volume-weighted average entry, and accumulated realized P&L in raw fixed-point units.apply_fillhandles the three fill cases: addition (weighted average in absolute-quantity space), reduction (realizes P&L against the average, keeps it), and flip (realizes the whole old position, rebuilds the average from the new price). A close to flat resets the entry and keeps the realized P&L. - Margin account (
MarginState) — posted equity plus the per-account maintenance and initial margin basis points. Requirement is|qty| × mark × bps/10⁴; effective equity isequity + unrealized(mark)(cash-settled variation); the check yields either Ok or Liquidate with the quantified shortfall. - Funding (
funding_payment) — pure perpetuals funding:clamp(premium_index, ±cap) × qty × mark / 10⁴, sign flowing through the signed quantity — longs pay when the premium index is positive, shorts receive, and the sign flips below zero. - Liquidation plan (
Liquidation,evaluate_liquidation) — for a breached non-flat account: close quantity|qty|, close side opposite the position (a long is closed by selling, a short by buying), the shortfall, and the mark the check ran against. The plan is the input to the market close order destined for Exchange8:ME. - Greeks engine (
GreeksInput,Greeks) — Black–Scholes–Merton closed form for European calls/puts: theoretical price plus delta, gamma, vega, theta, rho per unit of underlying. Degenerate inputs (non-positive spot, strike, vol, tau) are rejected, never defaulted. The normal CDF is an erf-based rational approximation (Abramowitz–Stegun), with the √2 argument conversion handled explicitly. - Portfolio netting (
GreeksLeg,PortfolioGreeks,aggregate_greeks) — portfolio-level Greeks: underlying legs contribute±qtydelta; option legs contributeqty × multiplier × per-unit Greek. Netting is per account, not per leg. - Options risk check (
GreeksLimits,check_greeks) — delta-equivalent notional (delta × underlying price), gamma, and vega exposure limits per account, evaluated on absolute values so short books trip the same limits. - CRR binomial (
BinomialParams,crr_price_american) — Cox-Ross-Rubinstein backward induction with early exercise (max(intrinsic, continuation)at every node); the American-exercise premium path the BSM closed form cannot price. Greeks come from finite differences of the tree and reuse the sameGreeksshape as the closed form. - Expiry engine (
ExpiryEngine,ExpiryState,ContractSettlement) — the futures/expiry lifecycle:enter_expiryopens the window (expiring),observeaccrues settlement-price observations, andsettlecomputes the arithmetic-mean settlement price, then either cash P&L per account or physical delivery obligations (long receives, short delivers).settleis idempotent (a repeated call returns the recorded settlement) and refuses to run without observations.
5. Rationale, Trade-offs, and Alternatives
- Pattern choices.
- Pure core + thin shell. All risk math lives in pure value types; the shell owns I/O, identity, persistence, and scheduling delegation. Risk formulas are the auditable surface of the venue — embedding them in request handlers or DB tasks would make them untestable and un-replayable.
- Outbox-first events. Margin decisions (calls, liquidations, funding) are
staged in
margin.outboxin the same transaction as the domain write and relayed idempotently to Sentin8:Bus (idempotency key = outbox row id), so a Bus outage delays but never drops or duplicates a risk event. - Fixed-point integrity. Prices, quantities, P&L, and notional stay in
raw fixed-point (
i128/NUMERIC) in the engine and the schema; floating point is confined to the Greeks module's transcendental math, where it is reported per unit, never stored as money.
- Dispatching. Option pricing splits by nature, not by strategy object: European contracts use the BSM closed form, American exercise uses the CRR tree — a closed enum over a closed set of models. Dynamic dispatch would put a vtable between the risk cycle and its pricing for no extensibility benefit, while the enum keeps each pricer pure and property-testable (parity, convergence, exercise-bound).
- Concurrency. The service is deliberately mostly stateless: axum request tasks serve the RLS-scoped query surface, and the durable workers (outbox relay, audit pusher, policy hot-reload consumer, stat emitter) are shell-owned tokio tasks. Cycle scheduling lives in Sentin8:Cron rather than an in-process loop — one scheduler for the whole platform, no per-instance duplication across replicas, and retries survive instance drain.
- Rejected alternatives.
- In-process scheduler: a second scheduling engine would double-fire cycles across replicas and fight the captain's registry; Sentin8:Cron owns scheduling by platform rule.
- Event-sourced rebuild as the only position state: the margin design
reconciles the event-sourced position ledger against the trade stream, but
the durable table (
margin.contract_position) is the serving state — rebuilding a live book from the trade stream on every start would couple margin availability to replay latency. - Float pricing in the margin engine: basis-point fractions fit fixed
integer math (
value × bps / 10⁴) with exact reproducibility; floats would make margin checks non-deterministic across platforms. - SPAN/portfolio libraries today: the contract master declares all four margin models, but the shipped engine computes the bps-based (isolated/ cross) requirement; a SPAN implementation is a future design decision, not an embedded dependency.
6. Internal Implementation & Invariants (Private Design)
- Position fill math. Addition weights the entry in absolute-quantity
space so the sign never cancels:
avg' = (avg×|old| + px×|add|)/(|old|+|add|). A reduction realizesper_unit × close_abs × qty.signum()(the sign makes the same formula cover short covers), keeps the average, and resets it only at flat. A flip realizes the whole old position once, then rebuildsavg_entryfrom the crossing price. - Margin check order. Requirement is computed from
|qty| × mark × bps, then compared to effective equity (posted equity + unrealized variation). The check outcome carries the requirement, and on breach the shortfall — the liquidation engine prices the close from the plan, not from a re-derivation. - Funding sign convention.
funding_paymentreturns the amount charged to the account: a long (positive qty) with a positive clamped premium pays (negative result); a short receives. The tablemargin.funding_ratekeeps premium index, cap, and clamp-appliedrate_bpsso every applied rate is reproducible. - Liquidation side.
close_is_buyis true exactly when the position is short; the plan'sclose_qtyis the absolute position. Idempotency for the liquidation engine ridesmargin.liquidationrows; thestatecheck permitspending/submitted/filled/failedfor the submit → fill cycle of the close order. - Greeks numerical hygiene. The
erfrational approximation (A&S 7.1.26) carries a residual of about1e-9at zero — tests assert against reference values at that tolerance, and the CDF argument is scaled byFRAC_1_SQRT_2(multiplying by the constant, then by nothing else — the inverted conversion is a documented bug class). Put–call parity and delta parity are asserted exactly in tests; gamma/vega equality across calls and pins the closed form. - CRR tree.
u = e^{σ√dt},d = 1/u, risk-neutral probability clamped to[0,1]; terminal payoffs then backward induction with early exercise at every node; finite differences use0.1%spot bump,1/100of tau,1e-4sigma and rate bumps. The European case converges to the BSM closed form within tree tolerance, which is asserted. - Expiry settlement. The settlement price is the arithmetic mean of
observation-window ticks; cash settlement books
qty × (settlement − entry)per account; physical settlement records signed delivery obligations (long receives, short delivers). On settle, the contract terminalizes toSettled, the position snapshot is dropped, and a second settle returns the recorded result (idempotent; unknown contract or empty window →None). - RLS posture. Every tenant table (
margin.*) carriesENABLE+FORCE ROW LEVEL SECURITYwithsentin8_context_initialized() AND env_id = sentin8_current_env_id()policies; handlers set the context on the same transaction that queries (SELECT sentin8_set_tenant_context($1), always awaited) and add explicitWHERE env_id = $1clauses as defense-in-depth. - Invariants always true: P&L realization is conserved (Σ realized from
reductions equals the flip/realization math; no drift); requirement scales
monotonically in
|qty|, mark, and bps; funding never exceeds the clamp; a flat account can never be in a liquidation plan;settleis idempotent per contract; every durable table is RLS-forced and only reachable through a set tenant context; the Shield scope-mirror set covers every action the handlers evaluate (wired by tests, no-set/no-match → deny).
7. Unresolved Questions / Future Designs
- Outbound event envelopes. The exact
exchange8:margin:{resource}:{operation}event strings and payload mapping for calls, liquidations, funding, and position updates onmargin.outboxare the open writer surface (the schema, relay, and workloads are declared; the writers ride with the cycle wiring). - Inbound consumption wiring. Trade/index/settlement/corporate-action
consumption workloads are registered (
sentin8:bus:consume) but the concrete consumer task topology and consumer groups are open. - Liquidation close-order submission. The close plan is computed and the
margin.liquidationstate machine declared; the market-order submission to Exchange8:ME, retry/escalation, and thefailedescalation path are open. - SPAN / portfolio margin. The
margin_modelcolumn declares four models; the engine implements the bps-based (isolated/cross) requirement — SPAN and portfolio margin are design-level open questions. - Index-source binding. Which
margin.contractindex feed drives funding and expiry settlement (constituents, outlier rejection, staleness guards) is owned by Exchange8:VFeeds and not yet bound here. - Funding posting rails. Funding is a posting, not a trade; the fiat (Bank8:Ledger) / custody (crypto) rail integration for moving value between longs and shorts is open.
- Greeks snapshot writers.
margin.greeks_snapshotis a declared cache for the options-layer risk checks and the dashboard; the writer cadence (per tick / per cycle) and the implied-vol surface feed are open. - RFW admin page mounts. The declared pages
(
/v1/admin/ui/positions|accounts|liquidations) are registered at enrollment; theGET /v1/admin/ui/...page mounts and the accounts data endpoint naming (/v1/admin/accountsvs the mounted/v1/admin/margin-accounts) are open. - Physical-delivery workflow. The expiry engine records delivery obligations; the custody/CCP handoff downstream belongs to Exchange8:Settlement and is not wired here.
- Margin-call lifecycle.
margin.margin_callis an append-only event table; a margin-call lifecycle (acknowledgment, expiry, escalation rules) is an exchange-policy decision pending product rules.
Design attribution: exchange8:margin and its underlying concepts were designed by Hamid Jahanzad Hamid@Jahanzad.com.