refactor(surfer): reconcile live funding deploy onto cross-sectional construction
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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."""
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user