Enterprise clean-rebuild (no foxhunt code). Hexagonal/ports-and-adapters: pure domain (gauntlet math, strategies, backtest, models) | ports (DataProvider, repositories) | adapters (Yahoo data, SQLAlchemy operational [Postgres/SQLite], DuckDB analytical) | application (ResearchService, DI) | CLI composition root. Gauntlet-first: Deflated Sharpe (Bailey-LdP) built + falsification-tested (kills best-of-N-on-noise, keeps real premium). Full vertical slice runs end-to-end on real data: data -> strategy(trend) -> backtest(net of costs) -> IS/OOS gauntlet -> persistence. 4/4 tests green. Postgres+DuckDB split, pydantic contracts, typed, DRY via one-contract-per-port. ADR + README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.8 KiB
ADR 0001 — Foundational architecture
Status: accepted · Date: 2026-06-09
Context
fxhnt is an agentic platform that systematically discovers, backtests and OOS-validates trading strategies across many markets — and runs the survivors live (multiple strategies at once). The value is NOT a secret edge; it is breadth + discipline + automation. The #1 existential risk is multiple-testing: a fast search over thousands of (strategy × market) combos manufactures false positives unless the statistics correct for the full search. This must be designed-in, not bolted-on.
Decisions
-
Hexagonal / ports-and-adapters (clean architecture).
domain/— pure business logic (gauntlet math, strategies, backtest, portfolio). No I/O.ports/— abstract contracts (DataProvider, Broker, repositories).adapters/— concrete infra (Yahoo/Databento data, IBKR broker, SQLAlchemy/DuckDB stores).application/— use-case services that orchestrate the domain via ports (dependency-injected).cli.py— the single composition root that wires concrete adapters.- Rationale: swappable infra, isolated testability, DRY (one contract per port, no copy-paste adapters).
-
Gauntlet first. The Deflated-Sharpe validation engine (Bailey & López de Prado) is built and falsification-tested (must kill best-of-N-on-noise, keep a real premium) BEFORE any search is built on top.
n_trials/sr_variancecarry the full search size into the verdict. -
Persistence split: Postgres (operational) + DuckDB (analytical). Relational store for runs, verdicts, the survivor library, positions, trades; columnar store for market data + backtest timeseries. Both behind repository ports; SQLAlchemy makes the operational store DB-agnostic (SQLite for dev/test, Postgres in production via
FXHNT_OPERATIONAL_DSN). -
Contracts via pydantic; numeric value objects via frozen dataclasses. Serializable, validated models cross boundaries; numpy-holding objects (PriceSeries, BacktestResult) stay in-memory.
-
Clean rebuild (no foxhunt code). A pristine codebase; proven ideas are re-implemented cleanly.
Non-negotiable principles
domain/imports nothing infrastructural (enforced by review/structure).- Every strategy must declare a structural
rationaleto register (the gauntlet requires one). - Conservative validation defaults (DSR ≥ 0.95 over the full search; OOS must hold).
Build order (so we never ship a POC pretending to be an app)
gauntlet → contracts/domain → data adapter → persistence → application slice → execution layer (multi-strategy) → discovery/agentic search → portfolio assembly → live execution. This pass delivers a full vertical slice (data → strategy → backtest → gauntlet → persistence).