Documentation
This directory contains the design and architecture documentation
for an algorithmic-trading system written in OCaml. The project
streams market data, runs indicator-and-strategy pipelines, and
routes signals through a broker-agnostic order layer to live
brokers (Finam, BCS) or a paper simulator. For installation,
quickstart and CLI reference see the top-level
README.md.
Why OCaml
OCaml is a practical, industrially-proven functional language designed by researchers and used where correctness matters more than hype. Three things drove the choice for this system:
- Speed + reliability. OCaml compiles to efficient native code comparable to C/C++ on numeric workloads, yet runs a GC and never segfaults. A backtest that touches millions of bars stays predictable both in throughput and memory.
-
A type system that models the domain. Variants, records, and
module signatures make invariants explicit: an
Ordercan beNew | Partially_filled | Filled | …but never an invalid in-between state; aSignalcarries itsInstrumentby construction. The compiler refuses to build code that contradicts the model — the same property that keeps Jane Street's trading stack safe under pressure. - Formal-methods ecosystem. OCaml is the implementation language behind proof assistants like Coq/Rocq and F* — the same tools that verify cryptographic libraries and proof-oriented languages (Lean, F*). If a piece of the risk layer ever needs mechanical verification, the path is open and well-trodden. OCaml has a rich ecosystem of automated formal verification (Gospel, Cameleer, Ortac, QCheck-STM, CFML, Why3, Coq/Rocq).
The language is developed by INRIA (French national research institute) and stewarded by the OCaml Software Foundation; fintech companies like Jane Street drive much of the industrial tooling. It's "a language by scientists for scientists" that also pays professional bills.
For a broader personal take see Why I chose OCaml as my primary language by Xavier Van de Woestyne.
Why OCaml pairs well with LLM-assisted development
LLMs are stochastic code generators — every line is a guess shaped by training, and the only way to converge on correct output is a fast, specific feedback loop. OCaml gives exactly that, more than most languages:
- Compiler as reviewer. The OCaml type-checker rejects mismatched variants, wrong arities, missing cases in pattern matches, and signature/implementation drift — usually in one pass, with precise line-level errors. An LLM loop that "compile → read errors → fix" converges in seconds, not minutes.
-
Types that force modeling, not just annotation. Writing a
.mlisignature or a variant type ahead of implementation pins down the shape of the problem. An LLM that reads a good signature produces code that already lines up with the domain; the few remaining mistakes are caught at compile time rather than at runtime. - Exhaustiveness and totality. Pattern-match warnings turn "forgot a case" into a compile error. When an LLM adds a new variant constructor, every downstream match lights up — no silent fall-throughs, no runtime surprises.
-
Functional paradigm controls complexity. Immutability and
pure functions by default make code easier to reason about both
for humans and for statistical models: side effects are visible
in types (
Eio,Mutex), data flows from input to output without hidden global state, and composition is the primary reuse mechanism instead of inheritance. An LLM asked to extend a small pure function rarely breaks unrelated modules. - Pathway to formal verification. As components stabilize, the same OCaml ecosystem (Coq/Rocq, F*, QuickCheck-style generators) lets humans — and, increasingly, LLM-assisted workflows — prove key properties rather than hope they hold. That upper bound on "how much correctness is achievable" is a real ceiling other mainstream languages don't offer.
The net effect: the model-level reasoning the type system demands nudges LLM output toward well-factored designs, and the compiler's fast, specific feedback catches the rest.
Architecture
Long-form essays on how the system is structured and why. Read in order for a tour; read individually for a specific concern.
- Overview — hexagonal layers, module map, data flow from bar to order.
-
Domain model — core types
(
Instrument,Candle,Order,Signal,Portfolio) and the invariants they carry. -
State machine: Step + Pipeline
— the shared transducer
BacktestandLive_engineboth drive. -
Streams — functional pipeline over
Seq.t, with anEio.Streamadapter as the only push/pull boundary. -
Reservations ledger — cash and
position accounting across the broker-latency gap:
reserve → commit_fillwithavailable_cashgating Risk checks. - Live engine — how streaming bars become orders; Paper wiring; reconciliation with the broker.
-
Testing strategy — unit, component,
differential; mirroring
lib/intest/unit/. -
Order flow — footprint analysis on
the public trade tape: the
order_flowBC, its aggregate lifecycle, the per-venue trade relay, tick-replay backtest, and what is proved versus tested. -
Gradient-boosted trees — pure-OCaml
inference over LightGBM text-dump models; training pipeline;
how
Gbt_strategyplugs into the engine. -
Logistic regression —
lightweight classifier as a gating function for the
Composite.Learnedpolicy; SGD + L2 in ~70 lines. - Triple-barrier labelling — path-sensitive, volatility-adaptive label derivation for supervised training; the intended alternative to "sign of forward return" when the strategy will trade TP/SL brackets.
How-to guides
Step-by-step walkthroughs for common tasks. Unlike the architecture docs (which explain why), these focus on how: concrete commands, expected output, troubleshooting.
- Train and deploy a GBT strategy — end-to-end pipeline from historical bars to a live-running model.
-
Train and deploy a logistic gate —
in-process OCaml training for the
Composite.Learnedpolicy; no Python, weights are a 10-scalar array. - Triple-barrier vs threshold labels: A/B comparison — practical workflow for deciding whether path-sensitive labelling actually helps your model, with tuning guidance and pitfalls.
-
Backtest a footprint strategy and record a live tape
— synthetic-tape backtest, recording a real tape with the broker
probes (
--record), and replaying it offline (backtest --tape). -
Work with Gospel specifications —
running
dune build @gospel, adding new contracts, working around Gospel 0.3.1 limitations; Phase 0 wiring and roadmap to Ortac/Why3.
Decision records (ADR)
Short, dated notes capturing architecture decisions and their
rationale. See adr/README.md for the template
and chronological index.
Module reference
Auto-generated API documentation (from .mli files via odoc)
lives at dune build @doc output, typically published to GitHub
Pages. This repository uses .mli files as the primary guardrail
against leaking implementation details — every domain module has
one.
Conventions
-
Code examples are copied from the repo at the time of
writing; exact signatures may drift. Check
lib/when in doubt. - ASCII diagrams where possible; Mermaid where ASCII is too cramped (GitHub renders Mermaid inline).
- Cross-links use relative paths so documents resolve both on GitHub and in local preview.