PLUTUS · ENGINE ROOM // PAPER ONLY · NO LIVE CAPITAL
ENGINEERED BY LEOPARD DATA

The Build Log

What was built, in order, with the gate each phase had to pass — and what each one got wrong before it passed.

Late August 2026 — design before code

Twenty-four design documents before an engine existed: the system design, the engine's daily algorithm, the trade lifecycle, the resolver and probability engines, the backtester, the storage split and blob layout, the schema and its standards, health monitoring, the news pipeline, portfolio allocation, the strategy catalog. A review on 2026-08-30 found the design build-ready with a short list of genuine gaps, and recommended an order: the news archiver first, because a rolling data window was destroying a month of history per month of delay; the backtester second, because the design's own text says to build it first.

Three live probes against the FMP API retired open questions across four documents — and corrected a wrong belief that the news archive reached 2019. It reaches exactly 365 rolling days.

2026-09-01 — Phase 1: the news archiver

gate passed
days 364 × stock + general items 627,754 size 114 MB calls 2,881 time 23.5 min gaps 0 verify: four random days re-fetched → byte-identical learned grades-latest-news allows page=0 only — a snapshot feed, not a history

Blob-only, so it respected the standing rule of no migrations. It also stood up the first cloud resource — one storage account — and the foundation half of the Pulumi estate, gated behind a deployEstate flag so the VM and database wait for Phase 4. The archiver.

2026-09-02 — the schema review, and Phase 0

decided

A question from Allan — "I don't like having 39 JSON columns" — became a column-by-column review: nine were real documents, thirteen were typed facts in disguise, ten were per-day payloads that belonged in blob, seven were named DetailJson because nobody had decided. Thirty changed. The rework was loaded into a real MySQL 8, which caught two defects the file never could: twenty-five foreign-key names over the 64-character limit, and a seed row with six values instead of seven.

Then the four Phase 0 decisions: build the schema; a maintained market calendar measured from 8,456 real SPY bar dates with zero disagreements against the NYSE rules; backtest rows in a Docker MySQL from the first run; and a per-resolver recompute cadence — Allan's own refinement over both options offered.

The review · The calendar

2026-09-03 — Phase 2: the backtester

gate passed

The contracts first — IStrategy, MarketState, TargetWeights, absence with a reason — then the point-in-time assembler, the bar store, the fill simulator, the day loop, the tear sheet, the trial registry, and three strategies. Twelve contract tests, including one that probes the assembler out of order to try to make it hand over a future bar.

attempt 1 FAIL benchmark truncated at 5,000 rows; trial number leaked into the tear sheet attempt 2 PASS ΔCAGR −0.02 pp · multiple −0.73 % · rerun identical coin-flip CAGR 8.77 % loses 2 pp to SPY with 2,014 %/yr turnover — a control behaving like one dollar-mag7CAGR 30.95 % SURVIVOR-UNIVERSE · ABSENT:macro confident garbage, and the stamps say so

Gate 2 in detail · The backtester

The gates
Gate 1365 blobs, gap-free, idempotentpassed
Gate 2spy-hold matches SPY total return; rerun identicalpassed
Gate 3migration applies; run reproduces with a row; calendar ≡ seednext
Gate 4five unattended days; survives a mid-cycle killlater
A rule about gates

A gate that fails is not renegotiated downward. If spy-hold cannot match SPY, the answer is to fix the chain, not to widen the tolerance. Both of tonight's failures were fixed; neither tolerance moved.