7. Architecture¶
Section 3 described what the engine's four layers do. This chapter covers how they are built: the library structure, the concurrency model, and the operational surface.
Dio is built on OCaml 5.2, compiled with OxCaml's flambda2 backend, with true parallelism via domains. Each trading asset runs in its own isolated domain with lock-free communication, and latency stays in the microsecond range across concurrent strategies.
7.1 Implementation properties¶
- Domain-per-asset isolation. Each trading pair runs in its own OCaml 5 domain. Strategies, order executors, and exchange feeds are fully isolated with lock-free messaging.
- Lock-free communication. The tick bus and event registry use atomic compare-and-set operations for zero-contention reads. Writers perform CAS loops on shared state.
- Copy-on-write caches. Read-mostly maps are sharded, and each shard is an immutable map published through its own atomic cell. A lookup reads one shard and allocates nothing; a write copies only the shard it lands in. Readers can therefore read instrument metadata, per-venue store registries, and per-symbol event queues from every trading domain while another domain is writing.
- Bounded order indexes. The per-venue order-identifier maps are capped and evicted oldest-first, with admission tracked separately from map membership so that an identifier retired out of band and later re-inserted is not queued twice. Capacity is enforced from the first insert, not only once startup ingestion finishes.
- Ring buffer order queuing. Orders are queued through fixed-capacity ring buffers with per-asset mutexes, ensuring bounded memory usage and deterministic latency.
- In-flight deduplication. Both order placements and amendments are deduplicated via composite keys (symbol + side + quantity + price), preventing duplicate submissions.
- Optional venue capabilities. Funding/position/margin context and quote reservation are registered as optional exchange-interface capabilities. A venue that does not have them simply leaves them unregistered; strategy code reads them through the exchange registry, never through a global store.
- Event-driven overlays. An overlay strategy runs in the same asset domain as the primary strategy and is cycled by book, perp top-of-book, position, and funding events. Resting overlay orders are trailed by amending the venue order on those events; there is no timer-based overlay loop.
- Microsecond latency profiling. The engine records per-domain percentile latencies (p50, p90, p95, p99, p999) in microseconds, providing real-time visibility into performance.
- Graceful shutdown. The engine handles SIGINT/SIGTERM for orderly shutdown, tearing down feeds and cleaning up sockets with a 3-second forced-exit timeout.
7.2 Layering¶
The engine is one wrapped library organized into layers with a one-way dependency direction, enforced by directory structure:
strategy files (.strategy / .json) -> protocol (parse, validate, compile, run)
protocol -> actions (registered capabilities)
actions -> core (shared decision and accounting)
core, actions -> platform (accounting invariants)
The core layer holds the decision logic the script interpreter reproduces; a
behavioural-equivalence harness and trace differ exist to keep the config-driven path
and the reference path in agreement. The platform layer owns cross-cutting accounting
invariants that both layers must respect: grace windows, ghost-order detection, balance
freshness, pending deduplication, and cross-exchange quote reservation.
domain_spawner, the order supervisor, and the replay tooling consume the library only
through its public interface, so the layering holds in practice.
Below the strategy library sit the shared runtime libraries, and their dependency
direction is one-way as well. logging sits at the bottom, because everything logs.
data_structures sits below it and depends on nothing at all: it holds the copy-on-write
map that the caching layer is built from, and both logging and latency_profiling need
that type, so it cannot live in concurrency alongside the rest of the concurrency
primitives. concurrency depends on all three.
7.3 Dashboard¶
A standalone TUI dashboard binary connects to the running engine over a Unix domain socket and displays:
- Uptime, sentiment index, per-venue connectivity status
- Heap size, GC statistics, memory fragmentation
- Per-asset holdings, mid-price, accumulated positions, pending distances, unrealized value; perp positions also show PnL and the distance to the nearest order that would reduce the position
- Live capital-oracle decisions: active verdict, resolved spacing, buy and sell sizes, survival diagnostics
- Per-domain latency percentiles (p50, p90, p95, p99, p999) in microseconds
- Domain running/stopped status, restart count, and last restart age
The snapshot payload also carries a caches section: occupancy, capacity, hit rate, and
eviction counters for every cache that registered a provider, which includes the per-venue
order indexes. The terminal dashboard does not render this section yet; it is there for
external tooling reading the socket.
The dashboard operates out-of-process for crash isolation and auto-reconnects when the engine restarts, so it survives an engine fault and can still show what the fault was.
The component-level architecture, including the supervisor, feed parsing, order executor, error handling, logging, and persistence layers, is specified in the Product specification under Using Dio.