Commit Graph

668 Commits

Author SHA1 Message Date
0ed2cfdff3 fix(exec): UCITS live-run 478/10349 + false-EXECUTED (Mon 2026-07-20)
The first live UCITS paper run on bizworx reported EXECUTED/exit-0 but was
degraded: the IEF->CBU0 leg got 0 fills and DBMF hung Submitted.

- Error 478 (contract conflict): the ISIN-qualified UCITS contract kept a
  localSymbol/secId conflicting with the pinned conId (requested CBU0 vs
  canonical CSBGU0). Strip secId/secIdType/localSymbol after resolve and route
  by conId + SMART.
- Error 10349 (TIF preset): the bare MarketOrder had no TIF so the account
  preset forced DAY + cancel/reconfirm. Set explicit tif="DAY".
- False-success (the dangerous one): plan.executed counted Cancelled/Submitted
  legs as placed, and exec-record booked on any non-blocked run. Now executed
  requires EVERY leg filled; a degraded run prints DEGRADED, exits 1, and
  skips exec-track booking so the reconciliation gate stays WAIT (never books a
  phantom rebalance) — real money errs false-WAIT.

Tests: new test_ucits_exec_fill_status.py + degraded cases in test_execution.py
and test_execute_multistrat_b2a.py (TDD failing-first). 523 exec/forward/gate
tests green, mypy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 09:20:47 +00:00
a00001e826 research(em-asia): Stage-1 LOO marginal-Sharpe kill-switch — China/India KILLED
Backtest-only gate (no forward wiring) for the China/India diversifier question,
decided from ~7y of real Yahoo data. Reuses the SSOT marginal_sharpe primitive +
multistrat.book_series verbatim; cost model = max(vol-norm re-sizing turnover,
monthly-rebalance floor) so a quiet co-crasher can't survive at ~0 modeled cost.

Result (10bp UCITS, 2019-08..2026-07): CNYA.L marginal Sharpe -0.099, NDIA.L
-0.048 — both co-crash the book (+0.23 corr) and worsen with cost. KILL both:
the accessible UCITS wrapper carries EM beta, not the mainland inefficiency
(direct A-shares/NSE closed to the EU-retail entity). Same co-crash death as VRP
and unlock. Do NOT wire the em_asia forward sleeve.

Gate is generic — reusable for any single-instrument diversifier candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:56:30 +00:00
04305a1c72 feat(orchestration): fold bybit precompute into a Dagster asset (K8sRunLauncher-ready)
Move _persist_bybit_book + _BYBIT_BOOK_SIDS to application/bybit_book_persist.py
(single source of truth; breaks the cli<->assets coupling). Add a new nightly
asset bybit_book_precompute (deps=[bybit_warehouse_refresh]) that wraps the
same helper backtest-refs/bybit-persist-sleeve-ret run — writing bybit_sleeve_ret
+ the report-kind gate refs in one compute-once pass. Tagged
dagster-k8s/config 6Gi req / 8Gi limit so K8sRunLauncher runs it in its own
right-sized Job (never the 4Gi daemon). Wired into combined_book_job + defs;
tests updated (13->14 assets + presence + wiring). CLI commands left intact
(deleted at the cron layer later). 60 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 09:36:05 +00:00
jgrusewski
38b8ff26d2 feat(exec): multistrat self-cleans its OWN superseded lines (fixes lingering IDTM)
The IEF->CBU0 UCITS remap (515117d, 2026-07-16 12:28) left the paper account
holding IDTM (the old thin Dist line, bought on the 07-15/16 09:00 runs under the
prior IEF->IDTM config). The rebalancer only ever traded its TARGET symbols
(_plan iterates targets.items()), so it never sold the orphaned IDTM — it lingered
in the account NLV and (correctly) tripped the stray-holdings warning.

Fix — scoped, safe self-cleaning:
- superseded_holdings(): the symbols multistrat ITSELF previously traded (per its
  own exec_fills) that are still held but no longer a target — priced at the last
  fill. Scoped to multistrat's own fills, so a position ANOTHER strategy holds on
  the SHARED IBKR account is never touched.
- plan_and_record: adds them at target weight 0 (+ LSEETF/USD spec so a UCITS line
  resolves by symbol to SELL) before the rebalance. Best-effort: a scan failure
  never blocks a real rebalance.
- _plan: a full EXIT (weight 0 on a held line) ALWAYS trades — hysteresis damps
  churn, it must not strand a position we're deliberately closing.

Next UCITS rebalance (09:00) closes IDTM and buys CBU0; the warning clears. Also
prevents future mapping-change legacy from silently accreting in the NLV.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:30:00 +02:00
jgrusewski
713424a89d feat(fund): multistrat_levered — observe-only levered shadow of the ETF book + financing sensitivity band
The multistrat ETF book is an UNDER-levered risk-parity book: it runs at ~5.7%
vol vs its own 10% target because leverage is capped at 1x (MAXLEV=1.0), leaving
return on the table. Add a return-defined observe-only shadow (cf.
bybit_4edge_levered) that levers the SAME series to the 10% vol target minus the
HONEST financing drag on the borrowed leg.

- LeveredShadowStrategy (paper_strategies.py): L = min(max_lev, target_vol/vol),
  floored at 1x; levered_ret = L·r − (L−1)·financing/252. Never executed.
- multistrat_levered_nav asset (deps=multistrat_nav) publishes its forward track
  + basis-matched backtest ref like the base; registered in the nightly job.
- multistrat_levered registry entry + SIM_BOOKS (Backtest switcher).
- financing_bps=530 GROUNDED in IBKR's real USD schedule (verified 2026-07-18:
  Fed funds 3.63% + Pro tiered spread → 5.83% <$100k / ~5.3% $100k-1M).
- leverage_financing_sensitivity() + a cockpit BAND on the levered book's
  Backtest view: levered return/vol/Sharpe across 1..6.83% + the break-even
  (= the book's 7.7% return), so the verdict is never hostage to one rate.

Honest finding it surfaces: at any realistic IBKR retail rate (~5-7%) levering
this modest-return book LOWERS risk-adjusted return (Sharpe 1.35 -> ~0.9); it
only pays at institutional financing (~1-3%). The financing drag is what makes
the shadow honest (without it, free leverage would look like 13.5%/Sharpe 1.35).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:15:55 +02:00
jgrusewski
f6f1c9082b chore(cockpit): strip jargon text blocks + hard-coded values from the pages
Following the /paper blurb removal, sweep the remaining pages:
- Backtest (sim.html): drop the paragraph-long verdict banner; keep a one-line
  plain subtitle ("a what-if on capital & period at real trading cost").
- Measured-cost caption (_sim_result.html): visible text now plain
  ("real trading cost, per coin — fee + spread · avg drag X%"); the technical
  detail (L1 quoted spread / current-snapshot caveat / "not a slider") moves to
  the hover tooltip. IBKR book caption likewise plain.
- Hard-coded "vol-targeted @ 20%" label (Overview, paper_crypto, strategy) ->
  "each shown on its own" (drops the hard-coded 20% + the jargon).

Tests updated to the new plain copy; technical strings that stay in tooltips
(real L1 quoted spread, the snapshot caveat) are still asserted there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:46:51 +02:00
jgrusewski
ebd31d7c07 chore(cockpit): drop the orientation blurb on /paper — the two cards say it
The Paper-books landing led with a paragraph-long verdict banner ("Two paper
accounts, one job..."). The two book cards below already convey account value,
holdings and status, so the blurb was redundant text. Keep just the h1 + a
one-line subtitle. Removes the two tests that asserted the banner copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:36:06 +02:00
jgrusewski
9e3ff64e1d feat(cockpit): warn when the IBKR account holds a symbol outside the book
Verifying the IBKR side surfaced an IBIT (bitcoin ETF) position in the DU paper
account on 2026-07-14 — a legacy leftover from the pre-UCITS US book (exec_fills
has zero IBIT fills; the current multistrat only ever traded the 5 UCITS lines).
It was liquidated in the 07-15 UCITS transition, but until then it sat silently
in the account NLV, unattributed to any book.

Add a stray-holdings guard: `_expected_book_symbols()` derives the allowed set
from the SAME two SSOTs the executor uses (multistrat.FUND_INSTRUMENTS + the
UCITS map) — never a hardcoded duplicate. Each holding gets an `in_book` flag;
StrategyDetail carries `stray_holdings` (held, nonzero-qty symbols outside the
set). The holdings view now renders a red "Position outside the book: <syms>"
banner + a ⚠ on the offending rows, so legacy/stray ballast can't inflate the
NLV unnoticed. [] = clean (no warning).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:29:38 +02:00
jgrusewski
fd2316b1e8 fix(cockpit): Backtest state panel said "active sleeves" for a coin count
Verifying the Backtest page: the scrub state panel labelled n_active "active
sleeves", but n_active counts the instruments HELD that day — the Bybit book's
~64-coin liquid universe (compare_measured_cost sets n_active=[n_coins]*len),
the ETF book's ~5 ETFs — NOT the book's sleeves (its 4 edges). So a 4-edge book
read as having "64 sleeves". Relabelled "active positions", accurate for both
the coin book and the ETF book.

Regression test pins "active positions" (and asserts "active sleeves" absent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:56:06 +02:00
jgrusewski
b1ceee39a5 fix(cockpit): Overview real-trades return was a naked % under the "allocated" column
Verifying the Overview: the IBKR "↳ real trades" sub-row put the real account's
cumulative return (e.g. -0.01%) bare in the borrowed "allocated" column — on
desktop it read as an allocation, and on mobile the card reflow labelled it
"allocated: -0.01%" (actively wrong; it's a return, not capital allocated).

The data was correct and reconciled (real -0.01% vs plan +0.56% => "0.6% behind
plan"); only the labelling was off. Mirrored the exec-twin row's self-labelling:
the return now renders "real -0.01%" inline, and every sub-row cell carries an
accurate data-label (real return / trades / vs plan / cost) so the mobile card
reflow shows the true meaning, not the borrowed column name.

Test pins the labelled form ("real +3.10%"), so the label can't silently drop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:46:03 +02:00
jgrusewski
8bd87e8ab1 fix(cockpit): honest FAIL verdict + plain UCITS holdings on the strategy detail
Two inconsistencies found verifying the IBKR /strategy/multistrat page:

1. A FAILED backtest with a high Sharpe (multistrat: Sharpe 1.7, gate FAIL)
   rendered a GREEN "Validated edge ... backtest passed" banner directly above
   the FAIL badge — the Sharpe>=1.0 fallback overrode the explicit FAIL. The
   verdict now respects the badge: an explicit FAIL is an amber "Backtest
   didn't clear the gate ... treat it as unproven", never a green "passed".
   The backtest Sharpe figure also goes neutral-grey (not alarming red) when
   unvalidated — the number itself (1.7) isn't bad, it just didn't validate.

2. The UCITS lines the EU DU account actually holds (CSPX/CBU0/IDTM/IGLN/ICOM)
   weren't in ASSET_DESCRIPTIONS, so the holdings "what it is" column read
   "other". Mapped each to its plain description (US stocks / US Treasuries /
   Gold / Commodities), same as its US cousin.

Regression tests: a passed=False + Sharpe 1.7 detail renders amber
"didn't clear the gate" and never "backtest passed"/"Validated edge"; UCITS
tickers resolve to plain descriptions, never "other". Verified desktop+mobile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:31:27 +02:00
jgrusewski
ce464f82f4 fix(cockpit): IBKR paper card was falsely "not connected" — it is live via UCITS
The IBKR paper account (DU9600528) IS connected and trading: the armed
fxhnt-ucits-rebalancer (0 9 * * 1-5) books the multi-strat ETF leg through
UCITS lines (EU KID/PRIIPs blocks the US-ETF leg, whose rebalancer is
correctly suspended). Prod already rendered it ($1,029,621, 5 holdings).

Two self-inflicted "not connected" artifacts fixed:
- scripts/dev_cockpit_local.py did NOT wire ibkr_account_repo, so the LOCAL
  dev cockpit always showed the IBKR card empty regardless of real data —
  a misleading local-verification artifact. Now wires every repo prod wires.
- paper.html's new orientation banner hardcoded "not connected yet" (written
  from that stale local view). Made it data-aware (keyed on ibkr_view) so it
  states the live NLV + holdings when trading, "not connected yet" only when
  genuinely empty — it can't drift from the card beneath it.

Adds a regression assertion: connected account => banner says "live now,
trading through UCITS" and never "not connected yet".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:11:41 +02:00
jgrusewski
bd5f820697 fix(cockpit): restore guarded exec-twin card + align its two tests to the restored behavior
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:46:06 +02:00
jgrusewski
d5f871af0a fix(cockpit): DB-name fallback for non-registry sids + update strategy-detail tests to the redesign
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:42:30 +02:00
jgrusewski
a71737eeca refactor(cockpit): ONE name resolver — fold display-name overrides into the registry SSOT
Root cause of the 'half' inconsistency: two name paths disagreed — the fleet
(dashboard_service) read the raw DB strategy_registry.display_name (seeded from
the registry), while other views used display_name()'s _OVERRIDES map (checked
first). So multistrat showed 'Adaptive multi-strat book (SPY/IEF/GLD/PDBC/DBMF)'
in the fleet and 'Multi-asset ETF portfolio' elsewhere. Fix: fold the overrides
INTO the registry display_name (multistrat->'ETF Portfolio', vrp->'Options income
(put spreads)'), delete _OVERRIDES, and route the fleet/detail through the ONE
display_name() resolver (registry SSOT, always fresh — no seed-lag). Every
surface now shows the same name. Locally verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:05:09 +02:00
jgrusewski
048e5ca26d fix(cockpit): name the deploy book 'Bybit 4-edge book' consistently on every surface
Trim the registry display_name (drops the (naive eq-wt, forward) parenthetical;
exec/levered keep their qualifiers), rename the /paper/crypto h2 (was 'Bybit
paper book') and the sim pill (was 'Bybit 4-edge'). Overview/paper/sim/strategy
all render 'Bybit 4-edge book' now (locally verified); old names gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 13:53:03 +02:00
jgrusewski
70d584251f fix(equity): carry ADV across the return series (capacity = current-liquidity)
The UCITS volume history (~90-309d) is far shorter than the multistrat return
series (~441d). The per-day-ADV cost model zeroed the pre-volume-history days
(scale_hit) -> degenerate series -> partial-sizing guard dropped the whole book.
Capacity is a CURRENT-liquidity concept: dollar_adv now carries the nearest
trailing-median ADV across every close date (forward-fill + back-fill), so old
return days are sized at the nearest available liquidity, not zeroed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 13:02:34 +02:00
jgrusewski
cd5203a33c feat(equity): backfill-ucits-volume-ibkr CLI + source-flag read in equity provider
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:49:30 +02:00
jgrusewski
ee8694dffb feat(equity): IBKR pit-volume store + historical_daily_volume + ucits_volume_source flag
Task 1 of the IBKR UCITS volume source plan: adds ibkr_pit_volume (first-seen-
frozen, mirrors yahoo_pit_volume), IbkrBroker.historical_daily_volume
(reqHistoricalData TRADES, integration-only, never raises), and the
ucits_volume_source config flag (default yahoo, additive ibkr path). Task 2
(CLI + provider read) and Task 3 (CronJob) are separate follow-ons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:42:59 +02:00
jgrusewski
515117de2c feat(equity): swap IEF->CBU0 (liquid treasury UCITS) + partial-sizing coverage (use as much as tradeable)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:28:25 +02:00
jgrusewski
0c4ffc11fc fix(equity): reseed UCITS volumes in equity integration test + fail loud on unknown regime
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:53:58 +02:00
jgrusewski
c2dd057ce1 feat(equity): regime-aware ADV/spread (ucits default | us) for the capacity+cost model
equity_allocation_inputs now sources each US sleeve's dollar-ADV + half-spread from the
instrument the EU account actually trades: "us" uses the US ticker's own volume +
ETF_HALF_SPREAD_BPS (byte-identical to before this task); "ucits" (the settings default)
uses the UCITS-mapped ticker's volume (US close as price proxy) + that line's
half_spread_bps. Returns (book_series/target_weights) stay US in both regimes. Added an
optional spread_bps override to equity_honest_returns/equity_capacity_curve so the regime
spread dict can override the module-constant lookup without mutating it. record_allocation
threads regime from settings.allocation.equity_execution_regime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:44:39 +02:00
jgrusewski
1ad41f5145 feat(equity): ingest UCITS LSE volume + regime flag + UCITS spread config
Task 1 of the UCITS re-base plan: UcitsListing gains yahoo_ticker (defaults
to f"{ticker}.L") and half_spread_bps (CSPX 1.5, IDTM 2.0, IGLN 2.0, ICOM 8.0,
DBMF 30.0); AllocationSettings gains equity_execution_regime ("ucits" default
| "us" post-MiFID-II professional). backfill-etf-volume and the multistrat_nav
nightly asset now also snapshot each UCITS line's Yahoo LSE volume, isolated
per-symbol so a thin/absent line (DBMF.L) can't break ingestion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:28:36 +02:00
jgrusewski
514f17df6a fix(equity): annualized Sharpe for the ADV ceiling + ADV-coverage guard
Whole-branch review: the equity ceiling used per-period (raw daily) Sharpe vs
the annualized capacity_min_sharpe=1.0 -> ~19x too small -> ceiling 0 ->
multistrat sized to $0 under the default policy (a silent no-op; tests masked
it with min_sharpe=-10). Now uses the crypto path's annualized sharpe_of. Also
guard against an empty/partial ADV store (pre-backfill) emitting a garbage
zero-capacity series instead of falling back to the nav record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:08:03 +02:00
jgrusewski
797462bdf7 feat(equity): equity_allocation_inputs provider merged into record_allocation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:48:27 +02:00
jgrusewski
dde51b8641 feat(equity): real ETF cost model + ADV capacity curve (equity_honest.py)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:33:49 +02:00
jgrusewski
3f2c114241 feat(equity): ingest ETF volume (PIT) + backfill-etf-volume CLI
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:23:09 +02:00
jgrusewski
90cc809456 refactor(alloc): load-once/reprice honest inputs (panels loaded once, repriced per AUM)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:55:37 +02:00
jgrusewski
86e0035d46 fix(alloc): stale promotion of a de-declared constituent must not re-enter deploy_sids (double-count guard)
Whole-branch review: promoted_strategy_ids() is a never-deleted persisted set;
removing promote_on_pass from book constituents (xsfunding/unlock) must also
retract them from the deploy allocation, else the book + its constituents are
allocated twice. Enforce the invariant in code, not the runbook.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:22:19 +02:00
jgrusewski
c4102c7319 feat(fund-config): max_venue_weight knob (fxhnt fund-config set --max-venue-weight)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:11:07 +02:00
jgrusewski
bc83fbdbe7 feat(alloc): max_venue_weight per-venue gross cap (default 1.0 = no-op)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:03:59 +02:00
jgrusewski
6ac1325d36 feat(alloc): switch crypto deploy target to the bybit_4edge book (75% deploy vs 18% per-sleeve)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:58:01 +02:00
jgrusewski
8b887af153 test(alloc): repoint funding-filter test to positioning (bybit_4edge now research-tier)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:52:29 +02:00
jgrusewski
98dda4e835 feat(alloc): per-sleeve crypto allocation (promote xsfunding/unlock; bybit_4edge->reference)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:47:26 +02:00
jgrusewski
c1d1cb80fb feat(fund-config): nightly allocation reads fund_config for equity + policy (target_vol/kelly)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:30:34 +02:00
jgrusewski
d2353ced52 feat(fund-config): fxhnt fund-config show|set CLI + seed on migrate-cockpit
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:23:54 +02:00
jgrusewski
b5a2b48de0 feat(fund-config): single-row fund_config SSOT (read/write/fallback/validate/seed)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:20:17 +02:00
jgrusewski
6abf2a8289 fix(alloc): killswitch on forward record (not backtest), ISO-normalize honest keys, unviable-capacity=0 sentinel 2026-07-16 00:07:48 +02:00
jgrusewski
efc304e3f9 feat(alloc): capacity policy knobs (min_sharpe, equity_default_ceiling) + ceiling shown next to the curve (spec 6/8) 2026-07-15 23:45:42 +02:00
jgrusewski
242277db30 feat(alloc): record_allocation sizes from honest-reference + caps at capacity; graceful store injection (spec 4b/4c) 2026-07-15 23:41:10 +02:00
jgrusewski
5c5171efc5 feat(alloc): honest-reference series+ceiling provider per deploy strategy (spec 4c/5) 2026-07-15 23:32:54 +02:00
jgrusewski
a27fc717e6 feat(alloc): compute_allocation weights by inverse-vol (capped) not equal-weight 2026-07-15 23:24:01 +02:00
jgrusewski
41913da027 feat(alloc): capacity ceiling from an honest capacity curve 2026-07-15 23:18:28 +02:00
jgrusewski
74ae34a28b feat(alloc): inverse-vol (risk-parity) base weights 2026-07-15 23:16:39 +02:00
jgrusewski
b6e3f24ed2 fix(honest-ref): impact calibration (400bps + turnover-basis), edge-4 sign, book union-fill, wire G4, leverage-ceiling guard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:35:57 +02:00
jgrusewski
dd55f331c3 feat(honest-ref): research report + CLI; consolidate spread reader to PaperRepo SSOT (spec 9) 2026-07-15 22:13:30 +02:00
jgrusewski
91f25215f3 feat(honest-ref): candidate edge #4 causal pump detector + 3-gate honest-cost eval (spec 8) 2026-07-15 22:03:33 +02:00
jgrusewski
d1cba19133 feat(honest-ref): leverage ceiling vs real DD + G4 anti-reactive gate (spec 7) 2026-07-15 21:58:46 +02:00
jgrusewski
255d5baa81 feat(honest-ref): gates G1 survives / G2 LOO / G3 beats-concentration (spec 6) 2026-07-15 21:54:56 +02:00
jgrusewski
65a9ae1f90 feat(honest-ref): risk-parity weights + book aggregation (spec Phase 2) 2026-07-15 21:49:36 +02:00