diff --git a/src/fxhnt/adapters/orchestration/assets.py b/src/fxhnt/adapters/orchestration/assets.py index dd08708..a3fb64c 100644 --- a/src/fxhnt/adapters/orchestration/assets.py +++ b/src/fxhnt/adapters/orchestration/assets.py @@ -208,6 +208,23 @@ def crypto_tstrend_nav(context: AssetExecutionContext) -> dict: # type: ignore[ ) +@asset +def stablecoin_rotation_nav(context: AssetExecutionContext) -> dict: # type: ignore[type-arg] + """Paper-forward stablecoin peg-reversion (short-rich daily treasury rotation, Binance spot).""" + from fxhnt.adapters.data.binance_spot_history import BinanceSpotHistory + from fxhnt.application.stablecoin_strategy import StableRotationForward + + def load_panel() -> dict: # type: ignore[type-arg] + h = BinanceSpotHistory() + return {s: h.daily_close(s) for s in ("FDUSDUSDT", "TUSDUSDT", "USDPUSDT")} + + return _run_paper_tracker( + context, "stablecoin_rotation_nav", + lambda: StableRotationForward(load_panel), + "stablecoin_rotation_state", + ) + + @asset def unlock_nav(context: AssetExecutionContext) -> dict: # type: ignore[type-arg] """Paper-forward token-unlock dilution shorts (market-neutral): short insider/investor cliff names @@ -224,7 +241,7 @@ def unlock_nav(context: AssetExecutionContext) -> dict: # type: ignore[type-arg return _run_paper_tracker(context, "unlock_nav", lambda: UnlockShortForward(cal), "unlock_state") -@asset(deps=[combined_forward_nav, sixtyforty_nav, xsfunding_nav, unlock_nav, crypto_tstrend_nav]) +@asset(deps=[combined_forward_nav, sixtyforty_nav, xsfunding_nav, unlock_nav, crypto_tstrend_nav, stablecoin_rotation_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 c523df5..afd0cc4 100644 --- a/src/fxhnt/adapters/orchestration/definitions.py +++ b/src/fxhnt/adapters/orchestration/definitions.py @@ -11,6 +11,7 @@ from fxhnt.adapters.orchestration.assets import ( crypto_tstrend_nav, futures_bars, sixtyforty_nav, + stablecoin_rotation_nav, unlock_nav, xsfunding_nav, ) @@ -19,7 +20,7 @@ combined_book_job = define_asset_job( name="combined_book_forward_job", selection=[ crypto_bars, futures_bars, combined_forward_nav, sixtyforty_nav, - xsfunding_nav, unlock_nav, crypto_tstrend_nav, + xsfunding_nav, unlock_nav, crypto_tstrend_nav, stablecoin_rotation_nav, cockpit_forward, ], ) @@ -35,7 +36,7 @@ daily_combined_book_schedule = ScheduleDefinition( defs = Definitions( assets=[ crypto_bars, futures_bars, combined_forward_nav, sixtyforty_nav, - xsfunding_nav, unlock_nav, crypto_tstrend_nav, + xsfunding_nav, unlock_nav, crypto_tstrend_nav, stablecoin_rotation_nav, cockpit_forward, ], jobs=[combined_book_job], diff --git a/tests/integration/test_orchestration_definitions.py b/tests/integration/test_orchestration_definitions.py index a35dd29..a0d614d 100644 --- a/tests/integration/test_orchestration_definitions.py +++ b/tests/integration/test_orchestration_definitions.py @@ -24,9 +24,10 @@ def test_definitions_load_with_assets_and_schedule() -> None: assert "xsfunding_nav" in asset_keys, f"missing xsfunding_nav asset: {asset_keys}" assert "unlock_nav" in asset_keys, f"missing unlock_nav asset: {asset_keys}" assert "crypto_tstrend_nav" in asset_keys, f"missing crypto_tstrend_nav asset: {asset_keys}" - # 8 assets: crypto_bars, futures_bars, combined_forward_nav, sixtyforty_nav, - # xsfunding_nav, unlock_nav, crypto_tstrend_nav, cockpit_forward - assert len(asset_keys) == 8, f"expected 8 assets, got {len(asset_keys)}: {asset_keys}" + assert "stablecoin_rotation_nav" in asset_keys, f"missing stablecoin_rotation_nav: {asset_keys}" + # 9 assets: crypto_bars, futures_bars, combined_forward_nav, sixtyforty_nav, + # xsfunding_nav, unlock_nav, crypto_tstrend_nav, stablecoin_rotation_nav, cockpit_forward + assert len(asset_keys) == 9, f"expected 9 assets, got {len(asset_keys)}: {asset_keys}" # --- schedule present with the right cron --- # defs.schedules is a list[ScheduleDefinition] (or None when empty) @@ -44,5 +45,5 @@ 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", "xsfunding_nav", "unlock_nav", "sixtyforty_nav", "crypto_tstrend_nav"} + expected_upstream = {"combined_forward_nav", "xsfunding_nav", "unlock_nav", "sixtyforty_nav", "crypto_tstrend_nav", "stablecoin_rotation_nav"} assert expected_upstream <= upstream, f"cockpit_forward missing upstream: {expected_upstream - upstream}" diff --git a/tests/integration/test_stablecoin_wiring.py b/tests/integration/test_stablecoin_wiring.py new file mode 100644 index 0000000..6cefcd8 --- /dev/null +++ b/tests/integration/test_stablecoin_wiring.py @@ -0,0 +1,10 @@ +def test_asset_importable(): + from fxhnt.adapters.orchestration.assets import stablecoin_rotation_nav + assert stablecoin_rotation_nav is not None + + +def test_definitions_include_stablecoin(): + from fxhnt.adapters.orchestration.assets import stablecoin_rotation_nav + from fxhnt.adapters.orchestration.definitions import combined_book_job, defs + assert stablecoin_rotation_nav in defs.assets + assert combined_book_job is not None