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 Strategy trait 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) digest

The 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 blake3 digest. The hash chain makes any divergence — or tampering — detectable in O(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 f64 for monetary values. Prices, sizes, and notionals are rust_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

CrateResponsibility
platform-coreDomain types, the pure reducer, the journal, state hashing
platform-feedMarket-data ingestion and normalization (consumes FerroFeed)
platform-executionOrder management and exchange adapters
platform-riskThe multi-stage risk gateway
platform-backtestThe replay engine
platform-monitoringMetrics, health, structured tracing
sdkThe stable Strategy trait — the strategy boundary
strategy-registryCompile-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.