The equal-weight cap+renormalise was algebraically a no-op — n=1 got 100% weight despite a 50% cap.
Cap without renormalising: when 1/n > max_strategy_weight the book holds cash rather than over-
concentrating; common case (1/n <= cap) is plain equal weight. + tests that exercise the binding cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sorted(kept) makes the leave-one-out bench choice stable across process runs (a set's iteration order
is PYTHONHASHSEED-dependent) — a reproducibility fix for a capital-allocation function. Docstring no
longer claims compute_allocation/target_capital, which land in later tasks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AllocationPolicy + hash (T1) → pure helpers full-history-vol/equal-weight-marginal-prune/killswitch
(T2) → compute_allocation ex-ante composition + anti-reactive invariant (T3) → target_capital hard
ceiling (T4) → strategy_allocation table+repo (T5) → nightly records the allocation from A's records
+ B1 deploy set (T6). TDD, ex-ante only (full-history vol), no capital moved (that is C).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Core of the allocation plane (layer 4, plane 2) on the data-driven-pipeline north-star.
Versioned in-code AllocationPolicy + a pure compute_allocation: equal-weight the marginal-Sharpe-
gate survivors, ex-ante vol-target leverage from the FULL-history vol (never trailing — the
anti-reactive invariant that honours the 3x-rejected reactive de-sizing conclusion: ex-ante K +
rare killswitch), a rare latching aggregate killswitch, and target_capital that caps total
deployed gross at a HARD human capital_ceiling (the one human gate, in the versioned policy).
Recorded + policy-version-stamped. No capital moved (that is C). Builds on A + B1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
book_empty was `not book` (the aggregated returns dict), so deploy members present but with no
overlapping forward_nav data (e.g. a just-re-inceptioned deploy track) read as "first edge" and
AUTO-PROMOTED with no correlation check — a one-way false-promote on a capital-adjacent gate.
Now `not book_sids` (membership): no deploy members → promote (true first edge); members present
but no data → indeterminate → WITHHOLD, per spec. + regression test.
Also neutralizes the real registry's deploy-tier tracks in two pre-existing promotion tests that
were unknowingly relying on the old inversion (they didn't seed nav data for the real deploy book).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Memoize nav_history per sid and precompute the deploy-member series once outside the candidate
loop, instead of re-reading every deploy member's history per candidate — restores the "LIGHT,
no heavy recompute" contract when more than one candidate reaches PASS. Behavior unchanged
(candidate still excluded from its own book; backstop semantics identical).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
evaluate_promotions now promotes a promote_on_pass candidate at gate==PASS only when its daily-return
correlation to the equal-weight deploy book (registry deploy-tier ∪ promoted, from A's records, minus
the candidate) is < POLICY.promotion_max_corr. Empty book (first edge) passes; short overlap or corr
above threshold withholds (logged). Catches a human mis-flag of a correlated edge with data. One-way
promotion unchanged; no capital.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ForwardSummaryRow gains nullable policy_version + policy_hash; upsert_summary stamps them
(keyword args, default None). migrate() additively ensures the two columns on an EXISTING
forward_summary table on BOTH dialects (Postgres ADD COLUMN IF NOT EXISTS; sqlite PRAGMA-guarded
ADD COLUMN) — create_all does not add columns to an existing table, so a persistent DB (prod
Postgres rows, or a lagging test sqlite) would otherwise be missing them (fixed a regression
where the ORM SELECT referenced the absent column).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
evaluate_reconciliation_gate + evaluate_gate now take a ResolvedPolicy (resolve_policy(POLICY,
gate_spec)); the module constants (MIN_FORWARD_DAYS / RECON_* ) + gate.py inline defaults are
removed — thresholds now come from the single GatePolicy. Verdict logic + reason strings
unchanged → byte-identical verdicts (full suite 1873 green proves it). _gate_verdict resolves once.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First plane of the policy layer (layer 4) on the data-driven-pipeline north-star.
Consolidates the scattered gate defaults (reconciliation_gate module constants + gate.py
inline literals + registry gate_spec) into one in-code, versioned GatePolicy; resolves the
effective per-track policy at one point; stamps each verdict with policy_version + a
content-derived policy_hash (auditability + forced version-bump); and formalizes promotion
as gate==PASS AND human promote_on_pass flag AND an automatic correlation backstop (< threshold
vs the equal-weight deploy book from A's records). Byte-identical verdicts under current
thresholds. No capital (deploy-tier is tracking; capital is B2). Builds on sub-project A.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CRITICAL: add combined to migration_builders so its anchor is seeded — the pre-branch forward_summary
row would otherwise trip the lost-anchor guard and crash combined_forward_nav every nightly
- hash accuracy: bybit cost_bps mirrors Settings default (10.0); multistrat instruments derive from
FUND_INSTRUMENTS (no duplicate) so a return-affecting change actually re-inceptions
- sixtyforty documented as a benchmark exempt from strict PIT (spec §4)
- observed mode: xsfunding consumes the engine's UTC _today; re-inception resets carried book-state
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ForwardIngestService.ingest now wraps each tracker's DB read/gate-compute/write in try/except+continue
so a failure in an earlier (research) track can't abort the batch and silently freeze the deploy-tier
real-capital gate. Adds a fault-isolation test + a direct test for the DB-backed _sleeve_return_series
(and dropped its now-unused data_dir param).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
forward_ingest now evaluates the gate from the DB record the engine writes (all_summaries +
nav_history) instead of reading *_state.json via ForwardStateReader; _sleeve_return_series reads
nav_history too. ingest_forward_state drops state_dir. state_reader.py + its test deleted — the
DB anchor + record are the single source of truth (spec sub-project A). Gate verdict logic unchanged.
Docstrings updated to reflect the engine path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
combined_forward_nav now runs CombinedBookStrategy through forward_engine.run_track (DB anchor +
recompute) — the last track off the JSON ForwardTracker. The former ForwardTracker-level drawdown
killswitch overlay is intentionally dropped (a track throttle, not part of the edge; vestigial
research track, not capital). record_mode observed->recompute (version 2) -> clean re-inception.
Removes now-unused build_combined_forward_nav / CombinedBookForwardTracker registration in
migration_builders.py + the test_orchestration_assets.py test (its only exerciser). Also drops the
"combined" entry from the one-time migrate-forward-anchors builder map (record_mode is now
recompute, so the observed lambda:None placeholder would crash it; combined re-inceptions cleanly
through run_track with no JSON left to migrate).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_seeded_paper_repo seeded its synthetic sleeve returns from random.Random(hash(sleeve)), but
hash() is PYTHONHASHSEED-salted, so the vol-targeted-vs-raw assertion tripped for a small
fraction of process salts under the full suite (passed in isolation). Seed from zlib.crc32
instead — stable across processes → the test is now deterministic. Pre-existing flake, unrelated
to sub-project A; fixed to keep the suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restores the safety invariant the legacy ForwardTracker enforced (raise on state loss rather
than reset the OOS clock to day 0 + discard the record — 3 prod resets "burned real capital").
run_track now raises when there is no active anchor BUT prior forward history exists (lost
anchor row), while a genuine first run still inceptions. Protects the deploy-tier tracks Task 8b
routed onto the engine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
build_{bybit_4edge,bybit_4edge_levered,positioning}_forward_nav now run their (recompute)
strategy through forward_engine.run_track (DB anchor + recompute) instead of the JSON
ForwardTracker, so the deploy-tier Bybit book + positioning sleeve record to the DB SSOT
like the other tracks. Asset callers thread repo/today/at; tests re-pointed to the DB.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- an accumulated date absent from the recompute now counts as divergence (status=diverged,
missing_dates) instead of silently staying "match" — fixes the false-match when PIT history
is incomplete (the multistrat/YahooPitClient shape the reconcile exists to catch)
- recompute window bounded to [T0, today] (today param was unused -> window was [T0, inf))
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
migrate_track seeds each track's anchor from its *_state.json inception (preserve-T0),
reconciles recompute tracks vs the accumulated nav (divergence surfaced, never silently
overwritten), and imports observed tracks' booked log verbatim. migration_builders maps
each registry id to its (state_file, build_strategy); `fxhnt migrate-forward-anchors` CLI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_run_paper_tracker now runs each track through forward_engine.run_track (anchor +
recompute/observed dispatch, DB-backed) instead of the JSON ForwardTracker, keeping
the transient-error tolerance. Returns forward_days/last_date/reinception.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- replace_nav purges stale pre-t0 rows on re-inception (nav cache == current record; fixes
polluted nav_history / wrong dashboard headline_t0 after a definition change)
- record_mode validated before any anchor mutation (a bad mode no longer persists a broken anchor)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- set_anchor self-guards the single-active-anchor invariant (archive prior active on re-inception)
- append_record/record_series validate + normalize ISO-8601 dates (fail loud on malformed)
- ordering test now inserts out-of-order; removed dead test imports/vars; clean Session import
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Planning surfaced that not all active tracks are recomputable: xsfunding books a
live funding snapshot with no reproducible full-history series (and combined carries
latching kill-state). Add a per-track record_mode: `recompute` (anchor + PIT data,
the majority) vs `observed` (anchor + append-only immutable booked log + minimal
carried book-state in DB). Observed removes the hand-editable PVC JSON for those
tracks and is the same machinery sub-project C reuses for execution fills.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Foundation of the unified data-driven pipeline: replace mutable, hand-editable
tracker JSON state with an immutable (T0, definition_hash) anchor in Postgres;
recompute the forward record over [T0, today] from PIT warehouse data each run;
auto-re-inception on declared-definition change. Migration preserves existing T0s
and reconciles (divergence = drift, surfaced not silenced). Yahoo closes get PIT
snapshots (recompute stability); execution fills scoped out to sub-project C.
Data-driven mechanics, human-gated capital — A does not touch capital.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The multistrat fund book dropped BTC-USD (FUND_INSTRUMENTS = 5 pure-equity ETFs),
but the registry display_name still read "SPY/IEF/GLD/PDBC/DBMF + BTC" — the cockpit
showed the new numbers (Sh 1.63, 0/20) under a stale name that contradicted them.
Name now matches the book.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The multistrat book had BTC-USD as 1 of 6 streams — redundant (crypto exposure
already comes from the Bybit book) AND the main source of correlation between the
equity and crypto legs. Measured (437d overlap): dropping BTC cuts multistrat<->crypto
daily-return correlation +0.284 -> +0.175, lifts multistrat's own Sharpe 1.23 -> 1.46,
and the combined portfolio Sharpe ~1.95 -> 2.14. Nothing lost (crypto exposure stays
via the Bybit book); the Alpaca leg is now pure equities (no crypto order).
- multistrat: FUND_INSTRUMENTS = the 5 pure-equity ETFs (SPY/IEF/GLD/PDBC/DBMF);
book_series/target_weights/_build parameterized by instrument list (default = the
original 6, faithful port intact). MultiStratStrategy takes an instruments arg.
- multistrat_nav + execute-multistrat use FUND_INSTRUMENTS (BTC symbol map dropped).
Full suite green (1838). Re-inceptions the multistrat track (return series changed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a track flagged `promote_on_pass` clears its reconciliation gate, the nightly
promotes it research→deploy — one-way + persistent, so it becomes fund-tier
deliberately once and never flickers with the daily gate.
- registry: multistrat gets promote_on_pass=True. Its diversification vs the crypto
book was validated out-of-band (measured +0.284 daily-return corr over 437d → a
real diversifier; combined Sharpe ~2.06 > 1.8). Correlation = the tested/deliberate
judgment; gate PASS = the automated trigger.
- persistence: strategy_promotion table + ForwardNavRepo.promote / promoted_strategy_ids.
- DashboardService.fleet(): effective tier = deploy if promoted (overrides registry).
- forward_ingest.evaluate_promotions(dsn): light (reads gate_status + the flag),
called by cockpit_forward after ingest. One-way; never reverts on a later dip.
Tested: promotes flagged+PASS, not unflagged, not while WAIT, one-way persistent,
fleet tier flips. Full suite green (1837).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Binance is no longer an active execution venue (deploy = Bybit, equities = Alpaca).
FleetRow.reference_only = venue not in the active set {bybit-perp, alpaca-paper},
computed in DashboardService. The All-forward-tracks table shows a subtle bordered
"ref" tag next to the venue for reference-only tracks — the Binance discovery tracks
(crypto_tstrend/unlock/xsfunding/stablecoin/combined) + the glbx 60/40 benchmark —
so it is clear at a glance which tracks are observed-only vs traded live.
Verified in a real browser against the prod DB: ref on the 6 Binance/glbx tracks,
none on the 4 Bybit/Alpaca tracks. Full suite green (1833).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a flat, undimmed section listing EVERY registered forward track (deploy +
research, incl. the headline book) in one table: name/venue/PAPER badge, tier,
basis-matched backtest ref (Sh/CAGR or — when none), and the live forward gate
(days/min + PASS/WAIT/GO). Complements the deploy/research grouped sections (the
research group stays dimmed) — the research tracks were at 62% opacity and read as
"missing"; this gives the complete picture at a glance.
Also passes `fleet` into the cockpit template context (it was computed but not
passed — the new loop rendered empty until fixed; caught by local browser test).
Verified locally in a real browser against the prod DB: all 10 tracks render.
Full suite green (1833).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reconciliation gate's _has_gaps assumed calendar-continuous booking (crypto is
24/7). A weekday-only US-equity NAV (multistrat) has Fri->Mon 3-day steps + holiday
long weekends (<=4d) — NORMAL, but the gate would flag them as an execution gap at
day 20 -> perpetual false-WAIT (never reconciles). Now _has_gaps takes max_gap_days
from gate_spec (default 1 = crypto, unchanged); multistrat sets 4. A step of
0/negative (duplicate/out-of-order) is still always a hole.
Surfaced verifying the just-added multistrat gate track (14/20 building, real ref
CAGR 9.9% / Sharpe 1.48). Full suite green (1833).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-adds multistrat_nav (retired in the 2026-06-20 cockpit cleanup) as a forward-
tracked strategy so the TradFi book runs the SAME honest gate as the crypto edges
— the diversification-Sharpe path (uncorrelated equity leg beside crypto).
- multistrat_nav asset: _run_paper_tracker(MultiStratStrategy(YahooDailyClient()),
persist_backtest_ref=True). Yahoo covers all 6 instruments incl BTC-USD natively.
persist_backtest_ref publishes the basis-matched ref (full book_series via
advance(None,{})) so the gate reconciles for real (Fix A/B), not a hollow PASS.
- registry: multistrat entry, reconciliation gate, min_forward_days=20.
- wired into the nightly combined_book_forward_job + cockpit_forward deps.
Full suite green (1832); asset count 20 -> 21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Puts the adaptive multi-strat ETF book on the clean execution port:
- multistrat.target_weights(closes): exposes the final-day per-instrument weights
(L*tw*volnorm_scale) that book_series applies but only collapses into a return.
Refactored _volnorm via a shared _volnorm_scale so book_series stays byte-identical.
Tested: sum(w*R[last]) reconstructs book_series's last booked return exactly.
- ExecutionService.rebalance_weights(weights, prices): a direct pre-computed-weights
entry beside rebalance(book), sharing one _plan engine (gates + fractional sizing).
Caller owns data<->broker symbol mapping.
- AlpacaBroker crypto-aware: a slash symbol (BTC/USD) -> time_in_force gtc; equities
stay day. So the book's BTC-USD leg trades on Alpaca crypto alongside the 5 ETFs.
- CLI fxhnt execute-multistrat (maps BTC-USD -> BTC/USD); the alpaca-rebalancer
CronJob now runs it (suspended until paper keys).
Full suite green (1832); +5 tests. Alpaca-crypto position-symbol form validated on
first paper run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>