Anvil
Runtime architecture
Anvil's three-layer workspace, the runtime event flow, the determinism model, and how the strategy boundary is enforced at compile time.
Anvil is a Rust Cargo workspace organized into three strictly separated layers.
The boundaries between them are enforced by the compiler and by
cargo deny check bans — not by convention or code review.
Three layers
Layer 3 Strategies proprietary logic
│ emits OrderIntent only
▼
Layer 2 SDK the Strategy trait
│ no execution handles here
▼
Layer 1 Platform reducer · journal · risk- Layer 1 — Platform (strategy-agnostic). Core types and the reducer, the event-sourced journal, market-data ingestion, order management and exchange adapters, the risk gateway, the replay engine, and monitoring. None of it knows what a strategy does.
- Layer 2 — SDK. A single
Strategytrait and a compile-time strategy registry. Strategies receive state and emit order intents; they never hold an execution handle or reach an exchange directly. - Layer 3 — Strategies. Proprietary logic, selected at compile time via Cargo features. Swappable and testable, with the platform underneath unchanged.
Runtime event flow
Every input to the runtime becomes a journaled event before it affects state.
inputs market data, intents,
│ risk decisions, fills/acks
▼
JOURNAL append-only · hash-chained
│
▼
REDUCER s' = f(s, e) · pure, no I/O
│
▼
STATE + blake3(s) digestThe reducer is the only place state changes. It is a pure function of the current state and a single event: identical state plus identical event always produces identical next state and an identical hash. The journal is written before state is advanced, so a crash can never leave applied-but-unjournaled state.
Determinism model
Determinism is a property Anvil enforces and verifies:
- No ambient time or I/O in the core. Strategies and the reducer read time
and randomness as explicit inputs (seeded
rand_chacha), never the wall clock. - Stable ordering. Events are processed in journal order; the core avoids nondeterministic iteration (e.g. unordered hash-map traversal) on the hot path.
- State hashing. After each applied event, state folds into a
blake3digest. The hash chain makes any divergence — or tampering — detectable inO(1). - Replay verification. On shutdown, Anvil replays the entire run from the journal and halts if any replayed hash differs from the live hash. Backtest, paper, live, and recovery are the same code path.
Type safety
The domain model forbids the foot-guns that quietly corrupt trading state:
- No raw
f64for monetary values. Prices, sizes, and notionals arerust_decimal-backed newtypes — exact arithmetic, no silent rounding drift. #![deny(unsafe_code)]across every crate in the workspace.- Money, instrument, side, and venue are distinct types; mixing them is a compile error, not a runtime surprise.
Crate responsibilities
| Crate | Responsibility |
|---|---|
platform-core | Domain types, the pure reducer, the journal, state hashing |
platform-feed | Market-data ingestion and normalization (consumes FerroFeed) |
platform-execution | Order management and exchange adapters |
platform-risk | The multi-stage risk gateway |
platform-backtest | The replay engine |
platform-monitoring | Metrics, health, structured tracing |
sdk | The stable Strategy trait — the strategy boundary |
strategy-registry | Compile-time strategy selection |
Risk architecture
Order intents are proposals, not commands. Each one crosses the risk gateway — position, exposure, rate, and sanity stages run in a fixed, deterministic order, and a single rejection stops the intent and is recorded to the journal with its reason. Because the SDK seam means strategy code never holds an execution handle, there is no path that skips the gateway.
See the Strategy SDK for the intent boundary and the Reference for the event and journal contracts.