Commit Graph

391 Commits

Author SHA1 Message Date
jgrusewski
ff7a81ceeb feat(D2.1): capture real IBKR fills->exec_fills (strategy-tagged) + ibkr_account_nav snapshot (best-effort, paper-only)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:32:37 +02:00
jgrusewski
784cd27949 fix(D1): gate sim_curve_ret write with backtest_summary (same-basis invariant)
Move the recompute-replay sim_curve_ret persist to after the `if sm is None: return`
guard so it is gated on the SAME condition as the reconciliation-gate backtest_summary
write. A too-short replay (< 2 rows, no gate ref) now writes NEITHER, never a degenerate
sim curve with no matching gate ref. Adds test_insufficient_replay_writes_neither_ref_nor_sim_curve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:20:48 +02:00
jgrusewski
3532671892 feat(D1): /paper/sim sources any book's curve by backtest.kind (adds IBKR multistrat/vrp)
Generalizes the Backtest page beyond the Bybit-only venue: bybit_4edge/bybit_4edge_levered
keep reading the precomputed bybit_sleeve_ret table; the IBKR paper strategies (multistrat,
vrp) now read their own precomputed recompute-replay curve from a new sim_curve_ret table,
written nightly by _persist_track_backtest_ref from the SAME rows the reconciliation-gate
ref is derived from. New sim_curves.sim_returns_for dispatches on registry_backtest_kind;
app.py wires an _ibkr_measured_context render path (reusing the existing pure
simulate_naive_eqwt engine) alongside the unchanged Bybit measured-cost path, with
plain-language pill labels for the two IBKR books.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:13:27 +02:00
jgrusewski
fd5d97bc94 fix(cockpit): give the bybit_4edge sleeve breakdown its own display names
crypto_tstrend's standalone STRATEGY_REGISTRY entry was retired in Phase 0b
Task 4, but the still-live crypto_tstrend book SLEEVE in the bybit_4edge
per-edge breakdown (Overview + /paper) shared the same string id, so its
display-name lookup blanked to the raw id and linked to a dead
/strategy/crypto_tstrend (404).

Add _SLEEVE_DISPLAY_NAMES next to _DEFAULT_BYBIT_SLEEVES in
bybit_book_eval.py — the sleeve breakdown's own display-name source,
independent of STRATEGY_REGISTRY, so a future forward-track retirement can
never blank a live sleeve again. _deploy_individual_edges() now sources
display_name from that map and adds a `linkable` field (true only when the
sleeve id is also a real registry entry); deploy_constituent_row renders the
/strategy link only when linkable, plain text otherwise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:11:31 +02:00
jgrusewski
70708ca8a7 fix(vrp): freeze the LAST COMPLETE OPRA session via bounded step-back
The vrp health axis flagged vrp=STALE because the OPRA freeze
(xsp_option_bars) was stuck: vrp_nav froze as_of=today, but today's
session is only partially available intraday, so the freeze failed with
`422 data_schema_not_fully_available` (distinct from the C1a clamp's
`data_end_after_available_end`). That made the freeze timing-dependent —
it could only succeed in a narrow post-settlement window and otherwise
silently degraded to recompute-off-stale-frozen.

Make it robust and timing-independent:
- databento: normalize the `data_schema_not_fully_available` 422 into the
  single catchable DatabentoRangeUnavailable (surgical — only that 422;
  any other client error propagates raw). Expose
  last_available_option_date() as the step-back's starting candidate.
- assets: new _freeze_latest_session entry point walks back one TRADING
  day (skip Sat/Sun) from OPRA's available-end until a COMPLETE session
  freezes, bounded to 5 steps; returns/logs the frozen session date.
  Never freezes a partial session's marks.
- No silent degradation: if no complete session is found in the bound it
  raises DatabentoRangeUnavailable; the existing C1b wrapper in vrp_nav
  catches it → recompute off frozen, and the health axis keeps vrp STALE
  (loud), never a quiet no-op.

TDD: step-back-past-partial, weekend-skip, bound-exhausted-raises, 422
normalization (both chain + bars paths), unrelated-error-propagates,
last_available_option_date caching. Full suite green (one pre-existing,
unrelated seed-dependent gauntlet flake).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:45:48 +02:00
jgrusewski
01e437816b fix(forward-health): close no-anchor silent-HEALTHY hole; fix BROKEN docstring scope
The "no forward data yet" carve-out in compute_health let a reconciliation-gated
recompute track that never produced data since inception (empty freeze table / no
forward_summary, old anchor t0 or no active anchor at all) read HEALTHY the same
way a genuinely brand-new track does. Thread the active forward_anchor.t0 through
evaluate_health so compute_health can tell "just inceptioned" (anchor within the
stale threshold -> HEALTHY) apart from "broken from inception" or "never anchored"
(-> STALE), closing the silent-degradation hole without false-alarming new tracks.

Also corrects the module docstring: BROKEN only covers per-track compute failures,
not the migrate/all_summaries/all_backtest_summaries/xsp_freeze_max_date reads
evaluate_health hoists outside the per-track try (those fail the whole
cockpit_forward Dagster asset instead).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:13:13 +02:00
jgrusewski
d070ec9127 feat(B): runtime staleness/health axis — kill silent rot (red badge + ERROR log)
vrp rotted for 2 weeks because a data failure degraded QUIETLY (recompute off a
frozen-but-stale table, only a log line) and the gate can't tell a stale track
from a healthy WAIT. Add a HEALTH axis SEPARATE from the gate so ANY live/gated
track that stops updating surfaces LOUDLY on day 1.

forward_health.py (new): compute_health (pure state machine) + evaluate_health,
called by cockpit_forward AFTER ingest. States {HEALTHY, STALE(age), NO_REF,
BROKEN}, precedence BROKEN>STALE>NO_REF>HEALTHY:
- STALE: a `recompute` track whose source as_of OR its freeze table's max date
  (vrp → xsp_option_bars) is > Settings.stale_after_business_days (default 3)
  business days behind. The freeze axis catches the exact vrp mode: forward
  "updates" yet the frozen input is stale.
- NO_REF: a reconciliation-gated recompute track with no resolvable backtest
  reference (the gate can never reconcile) — the M6 static invariant made a real
  runtime check.
- record_mode carve-out: `observed` twins (vrp_exec/…) are externally-driven and
  legitimately DORMANT when execution is suspended — never staleness/no-ref
  alarmed (won't false-alarm the intentionally-dormant reference_only tracks).
- BROKEN: a per-track health-compute error is surfaced, never swallowed.

Storage: new strategy_health table (create_all — idempotent, Postgres-safe, no
ALTER), replace-all each run so a recovered track clears its stale row.
Rendering: FleetRow/StrategyDetail gain health/health_stale_days/health_reason;
a LOUD red health_badge (STALE 14d / NO REF / BROKEN) on Overview, the all-tracks
table, and the detail header — visually impossible to confuse with a WAIT.
cockpit_forward + evaluate_health log at ERROR with the unhealthy list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:00:34 +02:00
jgrusewski
3302ca5d04 fix(C2): make every *_nav failure-isolating so the gate is never skipped
cockpit_forward reads purely from the DB (forward_summary) and its deps are
ordering-only, so a skipped upstream stalls the whole gate/promotion/allocation
pipeline. Close the two pre-tracker offenders so no *_nav asset raises on a
data-provider failure:

- multistrat_nav: the snapshot_yahoo_closes loop runs OUTSIDE _run_paper_tracker;
  a Yahoo outage now logs ERROR and degrades to a no-op {forward_days:0,...}
  return (record unchanged) instead of raising and skipping the gate.
- _run_paper_tracker: broaden the caught set to also isolate the databento
  provider error base (BentoError/BentoClientError, imported lazily since
  databento is an optional extra). Bare Exception is NOT caught — genuine
  strategy-math bugs (KeyError/ValueError) still propagate loudly.

With every expected data-provider failure mode isolated, cockpit_forward always
runs (deps kept for ordering). See report for the decouple-approach rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:36:48 +02:00
jgrusewski
afe488e6aa fix(C1): clamp OPRA query end to dataset range + vrp_nav freeze resilience
vrp_nav hard-failed every night: _freeze_day runs OUTSIDE _run_paper_tracker
and databento 422'd (data_end_after_available_end) because end=as_of+1day
exceeded OPRA's ~22:30-UTC available end at the 23:30 freeze. The raised
BentoClientError killed the asset before run_track/set_anchor, zeroing vrp's
forward anchor AND skipping the downstream cockpit_forward gate.

C1a: DatabentoDataProvider now fetches the OPRA available end via
metadata.get_dataset_range (cached once per instance) and clamps the exclusive
query `end` down to it in fetch_option_chain/fetch_option_chain_range/
fetch_option_bars, so no query requests past what OPRA has published. An empty
window (start >= clamped end) raises catchable DatabentoRangeUnavailable, never
a raw 422.

C1b: vrp_nav wraps _freeze_day so any freeze failure logs a WARNING and
continues to _run_paper_tracker, which recomputes VrpStrategy off the FROZEN
xsp_option_bars table — guarantees a forward row/anchor/ref even on a day the
freeze fails, and never skips the gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:34:57 +02:00
jgrusewski
375cd108bc feat(0b): remove the vestigial dual-venue PaperRepo abstraction (Task 7f)
Task 7e only renamed venue="binance" -> "legacy" to pass the substring guard;
the dual-venue repo machinery was left in place even though bybit is the ONLY
live venue. PaperRepo now hardcodes every discriminated read/write to a
_BYBIT_VENUE = "bybit" module constant instead of a constructor venue param
(removed) -- reads/writes ONLY the bybit rows, so the orphaned legacy/binance
rows from the retired combined-crypto book (Task 7d) are never touched.
nav_summary's venue arg is dropped the same way (every caller always passed
"bybit"). cockpit_models keeps the venue column (dropping it from the PK is a
separate prod schema migration, documented in the report for the merge
runbook) with reworded comments reflecting the single-venue reality.

Test fallout: deleted test_paper_repo_venue.py (tested the now-removed
dual-venue isolation) and 3 "does-not-touch-binance" tests that constructed
PaperRepo(venue="binance"); mechanically dropped the venue kwarg everywhere
else. Added a guard assertion that PaperRepo.__init__ has no venue param.
Full suite green (1853 passed); binance substring guard still 0 hits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 15:20:57 +02:00
jgrusewski
68970a793f feat(0b): true Binance 0% — remove the crypto_pit Binance fetch/ingest/research surface + substring guard (keep Databento futures + shared TrendRunner) 2026-07-14 14:57:08 +02:00
jgrusewski
62357d6cc2 feat(0b): remove vestigial registry state_file field (DB anchor is the forward SSOT)
Step-1 finding: migration_builders.py and the migrate-forward-anchors CLI
(cli.py:427) already carry their own local strategy_id -> state_file basename
map (track_builders()) from an earlier Task-8 pass -- neither reads
entry["state_file"] off STRATEGY_REGISTRY, so removing the field doesn't
touch them. Only three direct consumers needed changes:
 - registry.py: dropped "state_file" from all 11 entries + the header comment.
 - assets.py: _run_paper_tracker's state_file param was unused at steady
   state (its own docstring said so) -- dropped it + updated its 3 call
   sites (sixtyforty_nav, multistrat_nav, vrp_nav).
 - a handful of tests asserted on the registry's state_file key; updated to
   drop those assertions, and added tests/unit/test_no_registry_state_file.py
   as a permanent guard.

The JSON ForwardTracker + its .bak crash-recovery, and migrate-forward-anchors
itself, are untouched -- still the one-time JSON->DB seed / deployment tool.
2026-07-14 10:38:15 +02:00
jgrusewski
0be7eb2fc6 feat(0b): retire the dead Binance-era combined-crypto /paper book (redundant with bybit_4edge); close the 7c Major 2026-07-14 10:21:53 +02:00
jgrusewski
5fc25a428b feat(0b): finish Binance-0% — repoint worst-basis to Bybit; drop vestigial stablecoin display + dormant MomentumVrp (Task 7c) 2026-07-14 09:48:00 +02:00
jgrusewski
994bb59641 test: parallelize (pytest-xdist -n auto) + optimize the slow tests
- pytest-xdist added to dev deps; addopts='-n auto' runs the ~1940-test suite across all
  cores (~6-9min -> ~1.5min); registered a 'slow' marker for a serial '-m "not slow"' loop.
- equity_backtest_runner liquidity fixture: trimmed the 3000-bar (8yr) LIVELONG/ILLIQUID
  histories to 800 bars (still >> min_history=300, multi-year for peak-yearly ranking) —
  the peak-yearly aggregation over 8yr was the cost: 50s/30s/19s tests -> ~5.8s each,
  assertions unchanged.
- paper_sim perf guards: made them parallel-robust (the wall-clock <1.6s bound flaked under
  -n auto CPU contention). full-history guard relaxed to <10s (it targets an O(D²) blowup =
  orders of magnitude, not a constant factor); cheap-reapply made RELATIVE (< full/10, ratio
  is contention-invariant). Both marked slow.

Full suite 1940 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 00:51:52 +02:00
jgrusewski
86aba147c9 feat(0b): remove Binance data + cross-venue research backends — crypto is 100% Bybit
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 00:30:20 +02:00
jgrusewski
af68419909 feat(0b): cockpit is Bybit-only — remove the Binance venue toggle + paper book + sim compare view (Task 7a)
Removes the cockpit's (web-layer) use of Binance: the /paper venue toggle + Binance paper book view, the /paper/replay venue toggle, and the sim's binance_combined book + Binance-vs-Bybit compare view (_sim_compare.html deleted). Bybit is now the cockpit's only paper-book venue. Backend Binance data/research modules (compare_measured_cost.py, data adapters) are untouched -- Task 7b deletes those.
2026-07-13 23:16:07 +02:00
jgrusewski
fb9564c798 feat(0b): remove dead Binance execution surface (binance_ccxt + crypto-rebalance/positions/flatten CLI)
Binance is deprecated as a broker; kept only as a DATA source (Task 7 removes
that). Deletes binance_ccxt.py, crypto_execution.py (no non-Binance consumer),
the crypto-rebalance/crypto-positions/crypto-flatten CLI commands, and the
execution-only BinanceSettings class + field. Adds a narrow guard test.
2026-07-13 22:35:33 +02:00
jgrusewski
4cf6de17ee feat(0b): remove dead Alpaca execution; IBKR is the sole equity executor (Broker port kept)
Alpaca's rebalancer CronJob has been suspended with 401'd keys since Task 4's
venue consolidation; IBKR paper is already the live forward-track venue for
the multi-strat book. This removes the dead code/config/manifests instead of
leaving them to rot, and relabels the multistrat/multistrat_exec registry
venues from the vestigial "alpaca-paper" to "ibkr-equity" (they execute on
IBKR paper, not Alpaca — exec_venue itself is still computed dynamically at
runtime).

- Delete src/fxhnt/adapters/broker/alpaca.py, AlpacaSettings + the `alpaca`
  config field; move the one real setting it carried (min_order_notional,
  a generic "skip sub-min-notional dust" floor for any future
  fractional-capable broker) onto ExecutionSettings.
- Remove the `--broker` option and its alpaca branches from `execute` and
  `execute-multistrat` (cli.py) — IBKR is now implicit; IbkrBroker was
  already the module-level import, so the branch was pure dead code once
  Alpaca is gone.
- Delete infra/k8s/jobs/fxhnt-alpaca-rebalancer.yaml and
  infra/k8s/secrets/alpaca-credentials.yaml; delete the superseded
  infra/k8s/services/multistrat-rebalancer.yaml (PVC + NetworkPolicy +
  CronJob), replaced by fxhnt-ibkr-rebalancer. Drop the now-invalid
  `--broker ibkr` argument from fxhnt-ibkr-rebalancer.yaml and
  fxhnt-ucits-rebalancer.yaml (the flag no longer exists).
- Delete tests/integration/test_alpaca_broker.py (tested the deleted
  adapter directly); add tests/unit/test_no_alpaca.py as a standing guard
  (no "alpaca" substring anywhere in src/fxhnt + IbkrBroker still imports).
- Update test_registry_multistrat_exec.py + test_cockpit_ssot.py (venue
  label) and test_cli_execute_helpers.py (drop the broker_name kwarg and
  AlpacaBroker fake, use IbkrBroker instead) to match.

MANUAL cluster ops (not run by CI, record only):
  kubectl delete cronjob/fxhnt-alpaca-rebalancer secret/alpaca-credentials -n foxhunt
  kubectl delete cronjob/multistrat-rebalancer networkpolicy/multistrat-rebalancer pvc/multistrat-state -n foxhunt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:17:04 +02:00
jgrusewski
d7a9c32690 feat(0b): retire crypto_tstrend standalone track + stablecoin_rotation (not tradeable on Bybit); book sleeve unchanged
Neither is tradeable on Bybit: crypto_tstrend as a standalone Binance-perp forward
track was a -0.21 marginal-Sharpe drag/crash-amplifier, and stablecoin_rotation's
FDUSD/USDP pairs aren't listed on Bybit. Removes the two *_nav assets, their
registry entries, the orphaned migration_builders builders, and the
Binance-only StableRotationForward wrapper (stablecoin_runner.py's
StableReversionRunner stays -- it's still live via the Bybit stablecoin eval
paths). The crypto_tstrend SLEEVE inside the bybit_4edge deploy book
(_DEFAULT_BYBIT_SLEEVES, Bybit data) is untouched.

Updates dependent tests: deletes 5 whose subject (the retired asset/registry
entry/module) no longer exists, and swaps the retired sid for a still-registered
one (unlock/xsfunding/sixtyforty) in tests that only used crypto_tstrend as a
generic example sid.
2026-07-13 21:56:23 +02:00
jgrusewski
14f9894482 feat(0b): migrate unlock to a Bybit single-sleeve forward track
Reuses build_bybit_sleeve_forward_nav (Task 1). unlock_nav drops the standalone
Binance UnlockShortForward(UnlockCalendarLive) path, runs the single unlock sleeve
on bybit_features through the deterministic engine, loading the unlock calendar via
load_unlock_events (the same loader the Bybit book uses). unlock joins _BYBIT_BOOK_SIDS
+ bybit_report_sid_kwargs (ref via _persist_bybit_book); registry venue=bybit-perp,
backtest.kind=report. _DEFAULT_BYBIT_SLEEVES/deploy book untouched. Regression lock
now requires the unlock ref too. Full suite 2057 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:51:59 +02:00
jgrusewski
5a3fca0de3 test(0b): tighten backtest-refs regression lock to require xsfunding ref (task 1 review minor)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:29:43 +02:00
jgrusewski
ae3b906f64 test(backtest): cockpit shows xsfunding gate reconciling once its ref exists (bug A verified)
End-to-end test at the real production seam CockpitBacktestRefProvider(repo).reference(sid, days)
-> evaluate_reconciliation_gate: proves the reconciliation gate is permanently stuck in the
"no backtest reference to reconcile against" WAIT with no backtest_summary row, and un-sticks
(reconciles to PASS) once one is persisted (Tasks 2/6's fix for bug A).
2026-07-13 12:59:37 +02:00
jgrusewski
83a0e29894 fix(backtest): unify bybit-book scheduled writer — backtest-refs --all writes bybit_sleeve_ret + refs via shared compute-once helper (no cockpit-data regression) 2026-07-13 12:37:49 +02:00
jgrusewski
c38f3372ab feat(backtest): route nightly refs through backtest_ref + scheduled resourced backtest-refs CronJob (bug B)
Refactor _persist_track_backtest_ref (assets.py) to delegate to the unified backtest_ref dispatcher
instead of re-implementing the recompute-replay path inline, and wire xsfunding's missing sleeve-kind
ref through it (bug A's asset half) using the Binance-scoped feature store. Replace the manual
fxhnt-bybit-sleeve-ret Job with a weekly-scheduled fxhnt-backtest-refs CronJob running
`fxhnt backtest-refs --all` (bug B) so the report-kind refs (bybit_4edge/bybit_4edge_levered/positioning)
no longer depend on a human remembering to apply a one-off Job.
2026-07-13 12:12:47 +02:00
jgrusewski
b8bd43480d perf(vrp): batched per-month OPRA backfill — ~1h vs ~45h (2 calls/month vs ~6-8/day)
The per-day backfill did ~6-8 OPRA API round-trips/day (~26k over 2013->), all latency
-> ~45h. Rewrote it batched: fetch_option_chain_range (one range definition/month, deduped)
+ one range ohlcv fetch/month (band-bounded via a coarse median-strike forward, wide enough
for intra-month moves incl. COVID), then every trading day processed LOCALLY (parity forward
+ band + upsert). Extracted the forward/band logic into pure helpers (_forward_from_marks,
_band_contracts, _band_rows_from_marks) shared by the per-day path (_xsp_forward_estimate,
_freeze_xsp_slice — refactored, behaviour unchanged, tests green) and _backfill_month. CLI
iterates per-month. ~1h, ~$70-100 (bulk pulls a wider slice than the tight daily band).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:50:19 +02:00
jgrusewski
1b3dd58610 perf(vrp): fetch OPRA chain once/day (_freeze_day) — was double-fetched, doubling cost
_xsp_forward_estimate and _freeze_xsp_slice each independently called fetch_option_chain
(definition) per day, doubling OPRA definition cost on every nightly vrp_nav run AND the
2013-> backfill (~$75 -> ~$37 definition). New _freeze_day fetches the chain ONCE, derives
the parity forward from it, and freezes the band from the SAME chain. Both helpers now accept
an optional pre-fetched chain (default-fetch preserved, so existing callers/tests unaffected).
All 3 callers (vrp_nav, plan_and_record_vrp, backfill-xsp-opra) use _freeze_day. Cuts the
2013-> backfill from ~$90 to ~$55.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:52:35 +02:00
jgrusewski
972332ba52 test(vrp): cockpit renders the vrp sleeve detail (local verify) 2026-07-13 01:10:31 +02:00
jgrusewski
4c49d94b45 refactor(vrp): batch forward-estimate mark fetch + fix stale band comment (review Minors) 2026-07-12 23:54:05 +02:00
jgrusewski
e0db586bbf feat(vrp): vrp_nav asset (freeze OPRA PIT slice -> recompute + real backtest ref) + wiring
Freezes puts (spread band) + near-ATM calls (for the strategy's parity forward) into
xsp_option_bars, then recomputes the vrp track off the frozen table. _xsp_forward_estimate
uses min(sorted(...)) for cross-process determinism. Wires vrp_nav into the nightly job,
Definitions.assets, and cockpit_forward deps. (Controller committed after self-verifying:
subagent completed the work + augmentations but returned the malformed monitor-wait status.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 23:41:10 +02:00
jgrusewski
4a1d91885d test(cockpit): render assertion -> resolved paper ceiling $1M (not hardcoded $35k)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:59:22 +02:00
jgrusewski
31bc726053 fix(cockpit): overview ceiling reads resolved policy (paper/live), not hardcoded $35k; ruff import-sort
Whole-branch Important: dashboard showed the stale hardcoded ceiling while the engine sizes against
the resolved (paper $1M / live $35k) ceiling. Task-5 Minor: ruff I001 import-sort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:51:15 +02:00
jgrusewski
689fd3672e feat(alloc): record_allocation uses resolve_allocation_policy (paper/live ceiling, v2 hash)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:38:25 +02:00
jgrusewski
e8f0dd6c4e chore(cleanup): decommission legacy combined-book forward-track (CLI + modules + cron)
The 'combined' crypto-momentum book was retired; its legacy forward-tracking path is
now dead (the fxhnt-forward-cronjob was long since replaced by the Dagster deterministic
engine, and is already gone from the cluster). Removed: the 'forward-track' CLI command +
_FUTURES_UNIVERSE, the combined_book / combined_book_factory / combined_book_source
modules, CombinedBookStrategy + CombinedBookForwardTracker (KEEPING the load-bearing
ForwardTracker base used by the recompute engine + bybit tracks), the combined_book_*
config fields, and 5 combined-book-dedicated test files. MomentumVrpStrategy kept (a
tested strategy primitive, not a combined_book module).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 11:11:40 +02:00
jgrusewski
391400f233 chore(strategy): retire vestigial 'combined' crypto-momentum book from nightly + registry
The broad-Binance-universe momentum book (venue binance+glbx) was a survivorship/
microcap mirage superseded by the isolated crypto_tstrend edge + bybit_4edge. Removed
the combined_forward_nav Dagster asset (+ its cockpit_forward/paper_book_snapshot deps),
the registry entry (DB registry row auto-prunes via _seed_registry), the dashboard
headline preference, and the dead migration builder. CombinedBook modules retained
(still used by the forward-track CLI). Test fixtures repointed combined -> crypto_tstrend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 10:54:07 +02:00
jgrusewski
58e39955ba feat(cockpit): surface recorded exec venue in Executed-reality badge (fallback to configured) 2026-07-11 01:36:54 +02:00
jgrusewski
185e504028 refactor(dagster): remove bybit exec assets/job/schedule (leg now CLI-driven)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 01:23:34 +02:00
jgrusewski
146e1733f3 test(cockpit): cover latest_testnet_nav + executed-reality detail block (review)
Adds two test cases:
1. Test that latest_testnet_nav() returns the most-recent row by run_date
2. Test that the Executed reality detail block renders with exec/modeled return figures

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:24:57 +02:00
jgrusewski
7dd4a813e4 feat(cockpit): detail page funding / allocation / executed-reality blocks
Adds the per-edge lifecycle blocks (funding health, allocation, executed
reality) to the strategy detail page, plus a minimal latest_testnet_nav()
reader on BybitTestnetRepo to surface mean exec slippage for the bybit
executed twin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:20:18 +02:00
jgrusewski
e15b648f24 feat(cockpit): overview capital meter + allocation/funding columns + nested exec twin
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:13:55 +02:00
jgrusewski
6899c2787f feat(cockpit): mobile bottom-tab nav + funding/meter/exec CSS tokens
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:09:11 +02:00
jgrusewski
94674ddfe6 feat(cockpit): read-model surfaces allocation, funding, exec-vs-modeled pairing
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:02:00 +02:00
jgrusewski
05f0b1439e feat(cockpit): repo reads for allocation + funding rows
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:58:04 +02:00
jgrusewski
370c0be80b test(bybit): seed bybit_4edge B2a allocation in testnet job-wiring fixtures
bybit_testnet_reconcile now sizes plan_orders to the B2a/B3 envelope
(min(current_allocation, equity)) instead of raw equity (prior commit).
These pre-existing fixtures never seeded a bybit_4edge allocation row, so
the envelope collapsed to 0 and 6 of these tests started asserting on a
flattened (zero-order) book. Seed an allocation well above the fake
equity by default so envelope == equity, preserving the sizing these
tests were written against.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:33:35 +02:00
jgrusewski
371f795161 feat(bybit): B2a/B3-envelope-driven testnet leg + record bybit_4edge_exec observed track (C2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:33:31 +02:00
jgrusewski
9f0322c177 feat(bybit): record_bybit_exec_track — book the executed crypto track via A run_track (C2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:22:59 +02:00
jgrusewski
2713d739ca feat(allocation): filter B2a input through B3 funding status + record snapshot
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 01:59:59 +02:00
jgrusewski
7515f088ec feat(persistence): strategy_funding audit table + repo (B3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 01:56:19 +02:00
jgrusewski
02317a14ab fix(exec): skip recording on blocked rebalance; alias exec track's reconciliation ref to modeled multistrat (C1)
A blocked RebalancePlan places zero orders but the account keeps its prior positions, so
plan_and_record must not advance the multistrat_exec_pos basis or book a forward_record in that
case — otherwise every subsequent executed return is computed against a phantom target book that
was never placed. Also wires CockpitBacktestRefProvider to alias multistrat_exec -> multistrat so
the reconciliation gate actually reconciles the executed track against the modeled book's
basis-matched backtest reference, as the registry comment already (incorrectly) claimed. Plus a
doc comment clarifying positions_after is the intended target basis, and type-annotated locals in
compute_exec_return to keep the inferred return type float instead of Any.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 00:12:17 +02:00
jgrusewski
e96f4c7eb0 fix(exec): next-run exec-return basis is the TARGET book, not the pre-rebalance account read (C1)
plan_and_record read positions_after from the account BEFORE this run's rebalance, so the stored
multistrat_exec_pos was the prior book — every future exec-return was computed one rebalance-cycle
stale (an equity-accounting off-by-one). Now the target book (weights·nlv/price) is computed inside
plan_and_record after the rebalance; the positions_after param + the CLI's stale account read are gone.
+ a test that the stored book is the target book.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:58:47 +02:00