From 17fa0d03948c3c9f86352b7431d8cb266ac90b8d Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Sat, 20 Jun 2026 10:37:27 +0200 Subject: [PATCH] refactor(surfer): reconcile live funding deploy onto cross-sectional construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cockpit ran TWO funding tracks: 'funding' (absolute 5bp-hurdle, long-only, faithful port of crypto_funding_paper.py) and 'xsfunding' (cross-sectional long-top/short-bottom, delta-neutral). Investigation (regime-switch backtest) showed the absolute-hurdle variant sits idle ~60% of days and was flat through 2022, while the cross-sectional construction harvests funding DISPERSION, trades continuously, and made +71% in the 2022 backtest (LUNA/FTX deleveraging dispersion). The two were a confusing parallel where the conservative one masqueraded as the dormant crown jewel. Retire the absolute 'funding' track (asset funding_nav, registry entry, job membership) and promote 'xsfunding' as the canonical funding deploy (relabelled 'Crypto funding harvest (cross-sectional, delta-neutral)'). FundingCarryStrategy stays in paper_strategies.py (tested, on the shelf) — only the live deploy changes. Tests updated (12 assets now); full suite 499 passed, mypy clean on changed lines. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/fxhnt/adapters/orchestration/assets.py | 16 ++++++---------- src/fxhnt/adapters/orchestration/definitions.py | 5 ++--- src/fxhnt/registry.py | 10 ++++------ tests/integration/test_dashboard_service.py | 2 +- .../test_orchestration_definitions.py | 8 +++++--- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/fxhnt/adapters/orchestration/assets.py b/src/fxhnt/adapters/orchestration/assets.py index 6630b13..249b70b 100644 --- a/src/fxhnt/adapters/orchestration/assets.py +++ b/src/fxhnt/adapters/orchestration/assets.py @@ -183,15 +183,11 @@ def gd_nav(context: AssetExecutionContext) -> dict: # type: ignore[type-arg] ) -@asset -def funding_nav(context: AssetExecutionContext) -> dict: # type: ignore[type-arg] - """Paper-forward crypto funding harvest (Binance public futures API, no key).""" - from fxhnt.adapters.data.binance_funding import BinanceFundingClient - from fxhnt.application.paper_strategies import FundingCarryStrategy - - return _run_paper_tracker( - context, "funding_nav", lambda: FundingCarryStrategy(BinanceFundingClient()), "funding_paper_state" - ) +# NOTE: the absolute-hurdle `funding_nav` track was RETIRED 2026-06-20 — its 5bp absolute long-only +# hurdle sits idle ~60% of the time (flat in 2022) and was a misleading parallel to the cross-sectional +# `xsfunding_nav`, which IS the deployed funding edge (harvests dispersion, always active, +71% in the +# 2022 backtest). The faithful-port FundingCarryStrategy stays in paper_strategies.py (tested) for the +# record; the live funding deploy is now exclusively cross-sectional. See xsfunding_nav below. @asset @@ -258,7 +254,7 @@ def xsfunding_nav(context: AssetExecutionContext) -> dict: # type: ignore[type- ) -@asset(deps=[combined_forward_nav, sixtyforty_nav, multistrat_nav, gd_nav, funding_nav, poc_nav, +@asset(deps=[combined_forward_nav, sixtyforty_nav, multistrat_nav, gd_nav, poc_nav, eqfactor_long_nav, eqfactor_tilt_nav, xsfunding_nav]) def cockpit_forward(context: AssetExecutionContext) -> None: """Normalize tracker state files and upsert rows + summary into the operational cockpit DB.""" diff --git a/src/fxhnt/adapters/orchestration/definitions.py b/src/fxhnt/adapters/orchestration/definitions.py index 0361449..3c08e37 100644 --- a/src/fxhnt/adapters/orchestration/definitions.py +++ b/src/fxhnt/adapters/orchestration/definitions.py @@ -11,7 +11,6 @@ from fxhnt.adapters.orchestration.assets import ( eqfactor_long_nav, eqfactor_scores, eqfactor_tilt_nav, - funding_nav, futures_bars, gd_nav, multistrat_nav, @@ -24,7 +23,7 @@ combined_book_job = define_asset_job( name="combined_book_forward_job", selection=[ crypto_bars, futures_bars, combined_forward_nav, - sixtyforty_nav, multistrat_nav, gd_nav, funding_nav, poc_nav, + sixtyforty_nav, multistrat_nav, gd_nav, poc_nav, eqfactor_scores, eqfactor_long_nav, eqfactor_tilt_nav, xsfunding_nav, cockpit_forward, @@ -42,7 +41,7 @@ daily_combined_book_schedule = ScheduleDefinition( defs = Definitions( assets=[ crypto_bars, futures_bars, combined_forward_nav, - sixtyforty_nav, multistrat_nav, gd_nav, funding_nav, poc_nav, + sixtyforty_nav, multistrat_nav, gd_nav, poc_nav, eqfactor_scores, eqfactor_long_nav, eqfactor_tilt_nav, xsfunding_nav, cockpit_forward, diff --git a/src/fxhnt/registry.py b/src/fxhnt/registry.py index 273f99f..4913e1d 100644 --- a/src/fxhnt/registry.py +++ b/src/fxhnt/registry.py @@ -10,11 +10,9 @@ STRATEGY_REGISTRY: dict[str, dict] = { "venue": "binance+glbx", "state_file": "fxhnt_combined_forward", "gate_spec": {"min_days": 20, "min_total_return": 0.0, "min_sharpe": 0.5}, }, - "funding": { - "display_name": "Crypto funding harvest (delta-neutral)", "sleeve": "carry", - "venue": "binance", "state_file": "funding_paper_state", - "gate_spec": {"min_days": 28, "min_total_return": 0.0, "min_sharpe": 0.0}, - }, + # NOTE: the absolute-hurdle "funding" track was RETIRED 2026-06-20 (idle ~60% of days, flat 2022); + # the canonical funding deploy is the cross-sectional "xsfunding" below (harvests dispersion, always + # active, +71% in the 2022 backtest). The faithful-port harness stays in paper_strategies.py. "multistrat": { "display_name": "Multi-strategy book", "sleeve": "multi", "venue": "glbx", "state_file": "multistrat_state", @@ -46,7 +44,7 @@ STRATEGY_REGISTRY: dict[str, dict] = { "gate_spec": {"min_days": 60, "min_total_return": 0.0, "min_sharpe": 0.3}, }, "xsfunding": { - "display_name": "Crypto funding — cross-sectional", "sleeve": "crypto-funding", + "display_name": "Crypto funding harvest (cross-sectional, delta-neutral)", "sleeve": "crypto-funding", "venue": "binance-perp", "state_file": "xsfunding_state", "gate_spec": {"min_days": 60, "min_total_return": 0.0, "min_sharpe": 0.5}, }, diff --git a/tests/integration/test_dashboard_service.py b/tests/integration/test_dashboard_service.py index 8ea4d5c..0930f41 100644 --- a/tests/integration/test_dashboard_service.py +++ b/tests/integration/test_dashboard_service.py @@ -41,7 +41,7 @@ def test_fleet_joins_registry_to_summary() -> None: def test_fleet_includes_registry_entries_without_data() -> None: svc = DashboardService(_seeded()) fleet = svc.fleet() - funding = next(f for f in fleet if f.strategy_id == "funding") + funding = next(f for f in fleet if f.strategy_id == "xsfunding") assert funding.days == 0 and funding.gate_status == "WAIT" # no data yet -> shown, not errored diff --git a/tests/integration/test_orchestration_definitions.py b/tests/integration/test_orchestration_definitions.py index 4406aa2..fcd1860 100644 --- a/tests/integration/test_orchestration_definitions.py +++ b/tests/integration/test_orchestration_definitions.py @@ -12,10 +12,12 @@ def test_definitions_load_with_assets_and_schedule() -> None: asset_keys = {k.to_user_string() for k in repo.assets_defs_by_key} assert {"crypto_bars", "futures_bars", "combined_forward_nav", "cockpit_forward"} <= asset_keys # B1: the paper-track assets are wired into the graph alongside the B0 four - # (crossvenue retired — falsified 3x as a max-min statistical artifact; only the 5 remain) - paper = {"sixtyforty_nav", "multistrat_nav", "gd_nav", "funding_nav", "poc_nav"} + # (crossvenue retired — falsified 3x as a max-min statistical artifact; + # funding_nav retired 2026-06-20 — absolute-hurdle variant idle ~60%, superseded by xsfunding) + paper = {"sixtyforty_nav", "multistrat_nav", "gd_nav", "poc_nav"} assert paper <= asset_keys, f"missing paper-track assets: {paper - asset_keys}" assert "crossvenue_nav" not in asset_keys, "crossvenue_nav must be retired (falsified 3x)" + assert "funding_nav" not in asset_keys, "funding_nav retired — superseded by cross-sectional xsfunding" # B2: the equity-factor forward sleeves are wired into the graph alongside the B1 paper tracks # (eqfactor_ls retired per B3b — no edge in backtest; only long + tilt forward-track now) eqfactor = {"eqfactor_long_nav", "eqfactor_tilt_nav"} @@ -25,7 +27,7 @@ def test_definitions_load_with_assets_and_schedule() -> None: assert "xsfunding_nav" in asset_keys, f"missing xsfunding_nav asset: {asset_keys}" # B2.1: the shared scores asset is wired in (fetched once, consumed by the sleeves) — 13 total assert "eqfactor_scores" in asset_keys, f"missing eqfactor_scores asset: {asset_keys}" - assert len(asset_keys) == 13, f"expected 13 assets, got {len(asset_keys)}: {asset_keys}" + assert len(asset_keys) == 12, f"expected 12 assets, got {len(asset_keys)}: {asset_keys}" # --- schedule present with the right cron --- # defs.schedules is a list[ScheduleDefinition] (or None when empty)