From 94da2c6a6b96c48b057c32f46014e9e3770c8b5a Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Fri, 19 Jun 2026 07:50:19 +0200 Subject: [PATCH] feat(xsfunding): register xsfunding paper track + Dagster asset in daily book Co-Authored-By: Claude Opus 4.8 (1M context) --- src/fxhnt/adapters/orchestration/assets.py | 15 ++++++++++++++- src/fxhnt/adapters/orchestration/definitions.py | 3 +++ src/fxhnt/registry.py | 5 +++++ .../integration/test_orchestration_definitions.py | 8 +++++--- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/fxhnt/adapters/orchestration/assets.py b/src/fxhnt/adapters/orchestration/assets.py index 429e73f..734f631 100644 --- a/src/fxhnt/adapters/orchestration/assets.py +++ b/src/fxhnt/adapters/orchestration/assets.py @@ -242,8 +242,21 @@ def eqfactor_tilt_nav(context: AssetExecutionContext, eqfactor_scores: dict) -> return {"forward_days": st.forward_days, "last_date": st.last_date} +@asset +def xsfunding_nav(context: AssetExecutionContext) -> dict: # type: ignore[type-arg] + """Paper-forward cross-sectional crypto funding-dispersion (executable construction): + live delta-neutral funding+basis book through the gate.""" + from fxhnt.adapters.data.binance_xsfunding_live import BinanceXsFundingLive + from fxhnt.application.xsfunding_strategy import XsFundingForward + from fxhnt.application.forward_tracker import ForwardTracker + + st = ForwardTracker(XsFundingForward(BinanceXsFundingLive()), f"{_data_dir()}/xsfunding_state.json").step() + context.log.info(f"xsfunding_nav: {st.forward_days}d through {st.last_date}") + return {"forward_days": st.forward_days, "last_date": st.last_date} + + @asset(deps=[combined_forward_nav, sixtyforty_nav, multistrat_nav, gd_nav, funding_nav, poc_nav, - eqfactor_long_nav, eqfactor_tilt_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.""" from fxhnt.application.forward_ingest import ingest_forward_state diff --git a/src/fxhnt/adapters/orchestration/definitions.py b/src/fxhnt/adapters/orchestration/definitions.py index fb447c6..0361449 100644 --- a/src/fxhnt/adapters/orchestration/definitions.py +++ b/src/fxhnt/adapters/orchestration/definitions.py @@ -17,6 +17,7 @@ from fxhnt.adapters.orchestration.assets import ( multistrat_nav, poc_nav, sixtyforty_nav, + xsfunding_nav, ) combined_book_job = define_asset_job( @@ -25,6 +26,7 @@ combined_book_job = define_asset_job( crypto_bars, futures_bars, combined_forward_nav, sixtyforty_nav, multistrat_nav, gd_nav, funding_nav, poc_nav, eqfactor_scores, eqfactor_long_nav, eqfactor_tilt_nav, + xsfunding_nav, cockpit_forward, ], ) @@ -42,6 +44,7 @@ defs = Definitions( crypto_bars, futures_bars, combined_forward_nav, sixtyforty_nav, multistrat_nav, gd_nav, funding_nav, poc_nav, eqfactor_scores, eqfactor_long_nav, eqfactor_tilt_nav, + xsfunding_nav, cockpit_forward, ], jobs=[combined_book_job], diff --git a/src/fxhnt/registry.py b/src/fxhnt/registry.py index c4a3abc..273f99f 100644 --- a/src/fxhnt/registry.py +++ b/src/fxhnt/registry.py @@ -45,4 +45,9 @@ STRATEGY_REGISTRY: dict[str, dict] = { "venue": "us-equity", "state_file": "eqfactor_tilt_state", "gate_spec": {"min_days": 60, "min_total_return": 0.0, "min_sharpe": 0.3}, }, + "xsfunding": { + "display_name": "Crypto funding — cross-sectional", "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_orchestration_definitions.py b/tests/integration/test_orchestration_definitions.py index 3fd8edd..4406aa2 100644 --- a/tests/integration/test_orchestration_definitions.py +++ b/tests/integration/test_orchestration_definitions.py @@ -21,9 +21,11 @@ def test_definitions_load_with_assets_and_schedule() -> None: eqfactor = {"eqfactor_long_nav", "eqfactor_tilt_nav"} assert eqfactor <= asset_keys, f"missing equity-factor assets: {eqfactor - asset_keys}" assert "eqfactor_ls_nav" not in asset_keys, "eqfactor_ls_nav must be retired (B3b)" - # B2.1: the shared scores asset is wired in (fetched once, consumed by the sleeves) — 12 total + # XSFP T3: the cross-sectional crypto funding sleeve is wired into the graph + 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) == 12, f"expected 12 assets, got {len(asset_keys)}: {asset_keys}" + assert len(asset_keys) == 13, f"expected 13 assets, got {len(asset_keys)}: {asset_keys}" # --- schedule present with the right cron --- # defs.schedules is a list[ScheduleDefinition] (or None when empty) @@ -41,7 +43,7 @@ def test_definitions_load_with_assets_and_schedule() -> None: cockpit_def = repo.assets_defs_by_key[AssetKey("cockpit_forward")] deps = cockpit_def.asset_deps[AssetKey("cockpit_forward")] upstream = {k.to_user_string() for k in deps} - expected_upstream = {"combined_forward_nav"} | paper | eqfactor + expected_upstream = {"combined_forward_nav", "xsfunding_nav"} | paper | eqfactor assert expected_upstream <= upstream, f"cockpit_forward missing upstream: {expected_upstream - upstream}" # --- each equity-factor nav sleeve depends on the shared eqfactor_scores asset (fetched once) ---