3. The engine¶
The engine is the thing you trade from. It owns the parts of trading that are difficult to get right and expensive to rebuild: sizing decisions, order handling, venue connectivity, capital accounting, and survival under fault. A strategy sits on top and decides what to do. Neither owns the other.
This separation is the organising principle of the engine. Everything below is infrastructure a strategy consumes and does not implement, and everything a strategy chooses is described in section 4. The engine makes no assumption about what that choice looks like. It provides four layers.
3.1 Layer one: the Capital Oracle¶
The oracle is the sizing brain. It answers two questions for each configured asset: should the engine be trading this right now, and at what size and spacing.
It answers them from the asset's own recorded history. Conventional sizing asks a person how much drawdown they can tolerate, a guess made once, usually under optimism, and never revisited. The oracle measures the worst decline the asset has actually produced, derives a funding target from it, and verifies by replay that a given size and spacing can survive that decline. The tolerance comes from the asset and is re-derived on every pass.
Reference quantities. From the asset's merged price history three references are
derived. max_drawdown_pct is the single worst peak-to-trough percentage decline in
the entire series, measured from a running peak of closes to the deepest subsequent
low. Recovery is irrelevant, so an unrecovered collapse counts at full depth. ath is
the all-time high and atl the all-time low, both taken from intrabar extremes.
Runway math. With current the live price:
realized_dd = (ath - current) / ath
runway_pct = max_drawdown_pct * target_survival
floor_price = ath * (1 - runway_pct)
remaining_drop = (current - floor_price) / current
Worked example: an asset with an all-time high of 100k whose worst historical
decline is 92%, with target_survival 0.80, has a runway of 73.6% and a floor at
26.4k. At a current price of 70k the asset sits 30% below its peak, and a ladder
working that funded depth must survive a further fall of roughly 43.6% before reaching
the floor.
Three regimes follow from the arithmetic. Normal, where price trades above the floor, funds the remaining drop. Floor extension, where price lives at or beyond the deepest drawdown ever recorded, extends the funding target down to the all-time low. Unprecedented lows, where price trades at the deepest drawdown and at or below the all-time low, apply maximum conservatism: minimum quantity, maximum spacing.
An aggressiveness factor, the ratio of realized decline to maximum drawdown, biases parameter selection toward tighter spacing and larger size the deeper an asset sits in its runway. Shallow positions resolve conservatively. The bias never overrides survival requirements or bounds.
Survival model. Candidate parameters are replayed over the merged history, charging venue fees on both sides of every fill, to find the fraction of the funded depth the ladder survives before the allocated quote would be exhausted. A value of 1.0 or above means the requirement is met. Because the replay is a geometric walk over a fixed spacing, the surviving depth has a closed form and the search does not need to simulate rung by rung.
Parameter search. The search sweeps spacing across the configured bounds, and for each spacing sweeps size, early-exiting on the first miss because survival is monotonic in quantity. Three outcomes are possible. Unreachable, where no candidate meets the funded-depth requirement, falls back to the conservative corner of minimum quantity and maximum spacing. Reachable selects the largest size at the tightest spacing that holds the survival target, biased by aggressiveness. Surplus, where the aggressive corner clears the target with quote to spare, takes maximum quantity at minimum spacing. Branch flips are instant on recompute, with no hysteresis.
The decision record. Each pass emits one record per asset, active or not. Exactly four of its fields form the strategy-facing contract:
active bool
grid_interval float
buy_qty float
sell_qty float
These values are generated by one code path and consumed everywhere, and no second copy exists in the engine. Values are emitted for inactive assets too, for visibility, pool accounting, and immediate reactivation eligibility. They are raw floats; tick rounding, lot rounding, and minimum order size are all applied at the exchange layer. Every other field on the record is observability and lives in logs, the dashboard, and the standalone executable, never in the strategy-facing contract.
There is no configuration fallback path. A strategy that could read its own sizing from configuration would have two sources of truth, and the two would disagree the first time an operator edited the config while the oracle was running.
Allocation and priority. Quote pools are per venue and never cross venues. A pool equals the exchange balance, and quantities tied up in resting orders count against it. Allocation walks assets in configuration order, first entry highest priority, and sizes each against the entire remaining availability, so a lower-priority asset is never starved while quote exists for it. When a higher-priority need cannot fit, a cancellation cascade cancels lower-priority resting buys until it does, possibly many lesser orders to satisfy one greater. Capital returning to the pool from any source triggers re-evaluation, so a cancelled asset resumes on the same event if quote now covers it.
The normative specification of the oracle, including the full variable set, the search algorithm, and the multi-strategy allocation rules, is in the Oracle specification under Using Dio.
3.2 Layer two: the order path¶
The order path takes an order a strategy has decided to place and gets it onto the venue correctly, without that decision having been second-guessed. It handles price rounding, lot sizing, minimum order sizes, in-flight deduplication, time-in-force recovery, and the accounting that keeps resting orders and reserved capital consistent with what the exchange actually holds.
Order construction. Every order carries a post-only attribute, set by the strategy as it constructs the order, and the venue adapter is free to honour it or not. Whether a venue can enforce a passive order is an adapter-level capability and it differs between venues. The matrix is in section 6.1, and the reasoning behind it is in section 3.3.
Crossing. An order that would cross the book is normally prevented before it is built, by price clamping: a buy price is clamped down to the current bid, and a sell price is lifted to the ask on every venue that reports one. The engine separately tests for a resting or evicted sell at or below its intended buy price. That is a wash-trade hazard, a different failure from crossing the book, and it is checked on its own terms. Where an order still crosses, the outcome depends on the venue. Where the venue enforces post-only it rejects the order; the engine absorbs that rejection, cancels the dead order, arms time-in-force recovery, and retries on a later cycle. The default grid never converts a rejected order into a market order. Overlay strategies can explicitly submit IOC orders; that is a strategy choice, not an implicit conversion.
Fees. The maker fee is fetched live, cached with a time-to-live, and refreshed on a cycle. The buy-side base credit is recorded net of fee where a venue charges in base currency, the sell-side profit figure is computed net of fees, and the oracle's survival replay charges fees on both sides of every fill when it decides whether a parameter set is viable.
Capital guards. Effective exposure limits are all balance-derived: a latched low-capital flag when quote is insufficient to fund a full lot, an affordability test against quote net of what resting buys already hold, atomic per-exchange reservation so one asset's allocation cannot race another's, and cross-venue quote reservation so pools stay separate. The live guards are documented in the Product specification under Using Dio.
3.3 Layer three: connectivity¶
Venue adapters translate the engine's abstract order book interface into native API calls, handle lot sizing, fees, tick rules, and settlement conventions, and normalize market data back to the engine. Balance reporting is normalized to a tradeable figure, so quantities committed to resting orders are excluded and sizing always reasons over what the exchange would actually accept.
Two properties of a venue determine what a strategy can ask of it, and both are adapter capabilities. The per-venue matrix is in section 6.1.
Passive-order support. Whether a venue honours the post-only flag, and therefore whether a strategy's passive intent is enforced by the exchange or left to the engine's own price clamping.
Oracle support. The oracle reads an asset's history through a registry that is separate from the live trading interface, so a venue can trade without providing history. Not every adapter implements it, and the standalone tuning executable reports which do.
The five supported venues, why each is supported, and the transport mechanics of each adapter are in section 6.
3.4 Layer four: the runtime¶
The runtime keeps the other three layers running unattended.
Domain isolation. Each trading asset runs in its own OCaml 5 domain. Order executors, feeds, and strategy execution for one asset cannot block another, and a fault in one does not propagate.
Event-driven execution. Everything is triggered by events: price ticks, order fills, cancellations, balance updates, and oracle decision publications. There is no polling loop.
Supervision. The oracle runs as a supervised module, heartbeated on every published pass. A health monitor restarts it automatically if it stalls, and domains resume from the next published pass. Faults become restarts, and the engine keeps running.
Persistence. Accumulation state, pending order levels, and position reconciliation survive restarts, so a deployment or a crash does not lose tracking.
Capital accounting. A single accounting layer owns the invariants that every other layer depends on: grace windows for settlement lag, ghost-order detection, balance freshness, pending deduplication, and reservation of quote and base across assets and venues. Keeping this in one place lets several strategies share a venue balance without racing each other.
The component-level architecture, including the supervisor, feed parsing, order executor, error handling, logging, and persistence internals, is specified in the Product specification under Using Dio.