diff --git a/README.md b/README.md index 8b67b57..f9a32de 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ These profiles depend on artifacts produced by `CryptoLivePoolPipelines` before | Profile | Name | Notes | | --- | --- | --- | -| `crypto_live_pool_rotation` | Crypto Live Pool Rotation | runtime-enabled trend-following rotation that consumes the ordered live pool published by CryptoLivePoolPipelines. Runtime code may gate and size trades inside that pool, but monthly selection and order remain upstream. | +| `crypto_live_pool_rotation` | Crypto Live Pool Rotation | runtime-enabled trend-following rotation that consumes the ordered live pool published by CryptoLivePoolPipelines. The legacy `crypto_leader_rotation` profile remains an alias for compatibility. Runtime code may gate and size trades inside that pool, but monthly selection and order remain upstream. | ### Research-only candidates diff --git a/README.zh-CN.md b/README.zh-CN.md index 0306bf5..e115f96 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -31,7 +31,7 @@ CryptoStrategies 是 QuantStrategyLab 的加密货币策略包。为 Binance 执 | Profile | 名称 | 说明 | | --- | --- | --- | -| `crypto_live_pool_rotation` | Crypto Live Pool Rotation | 消费 CryptoLivePoolPipelines 发布的有序 live pool 的趋势轮动策略。运行时代码可以在该池内做交易门控和仓位 sizing,但月度选池和排序属于上游。 | +| `crypto_live_pool_rotation` | 加密领涨轮动 | 消费 CryptoLivePoolPipelines 发布的有序 live pool 的趋势轮动策略。兼容旧 profile `crypto_leader_rotation`;运行时代码可以在该池内做交易门控和仓位 sizing,但月度选池和排序属于上游。 | ### 研究侧候选 diff --git a/pyproject.toml b/pyproject.toml index 6218bab..a2470b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "crypto-strategies" -version = "0.4.7" +version = "0.4.8" description = "Shared crypto strategy catalog and implementations" readme = "README.md" requires-python = ">=3.11" diff --git a/src/crypto_strategies/__init__.py b/src/crypto_strategies/__init__.py index a57ad70..0791522 100644 --- a/src/crypto_strategies/__init__.py +++ b/src/crypto_strategies/__init__.py @@ -1,6 +1,8 @@ from .catalog import ( STRATEGY_CATALOG, STRATEGY_DEFINITIONS, + CRYPTO_LEADER_ROTATION_PROFILE, + CRYPTO_LIVE_POOL_ROTATION_PROFILE, get_strategy_catalog, get_strategy_definition, get_strategy_definitions, @@ -16,6 +18,8 @@ __all__ = [ "STRATEGY_CATALOG", "STRATEGY_DEFINITIONS", + "CRYPTO_LEADER_ROTATION_PROFILE", + "CRYPTO_LIVE_POOL_ROTATION_PROFILE", "PLATFORM_RUNTIME_ADAPTERS", "get_strategy_catalog", "get_strategy_definition", diff --git a/src/crypto_strategies/catalog.py b/src/crypto_strategies/catalog.py index b719e76..8907147 100644 --- a/src/crypto_strategies/catalog.py +++ b/src/crypto_strategies/catalog.py @@ -15,6 +15,8 @@ ) CRYPTO_LIVE_POOL_ROTATION_PROFILE = "crypto_live_pool_rotation" +CRYPTO_LEADER_ROTATION_PROFILE = "crypto_leader_rotation" +CRYPTO_LIVE_POOL_ROTATION_ALIASES = (CRYPTO_LEADER_ROTATION_PROFILE,) CRYPTO_CANONICAL_REQUIRED_INPUTS = frozenset( { @@ -74,7 +76,7 @@ canonical_profile=CRYPTO_LIVE_POOL_ROTATION_PROFILE, display_name="Crypto Live Pool Rotation", description="Trend-following crypto rotation with staged entries, degradation controls, and cash parking.", - aliases=(), + aliases=CRYPTO_LIVE_POOL_ROTATION_ALIASES, cadence="daily", asset_scope="liquid_crypto_assets", benchmark="BTC", diff --git a/src/crypto_strategies/manifests/__init__.py b/src/crypto_strategies/manifests/__init__.py index 8769419..640e0f8 100644 --- a/src/crypto_strategies/manifests/__init__.py +++ b/src/crypto_strategies/manifests/__init__.py @@ -8,6 +8,7 @@ domain="crypto", display_name="Crypto Live Pool Rotation", description="Trend-following crypto rotation with staged entries, degradation controls, and cash parking.", + aliases=("crypto_leader_rotation",), required_inputs=frozenset( { "market_prices", diff --git a/tests/test_catalog.py b/tests/test_catalog.py index bdbec11..9793c5e 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -4,8 +4,10 @@ from crypto_strategies import get_strategy_definitions from crypto_strategies.catalog import ( CRYPTO_CANONICAL_REQUIRED_INPUTS, + CRYPTO_LEADER_ROTATION_PROFILE, CRYPTO_LIVE_POOL_ROTATION_PROFILE, get_strategy_definition, + get_strategy_metadata, ) from crypto_strategies.runtime_adapters import BINANCE_PLATFORM, get_platform_runtime_adapter @@ -34,6 +36,14 @@ def test_known_profile_resolves(self): "crypto_strategies.strategies.crypto_live_pool_rotation.rotation", ) + def test_legacy_leader_rotation_profile_resolves_to_live_pool_rotation(self): + definition = get_strategy_definition(CRYPTO_LEADER_ROTATION_PROFILE) + metadata = get_strategy_metadata(CRYPTO_LEADER_ROTATION_PROFILE) + + self.assertEqual(definition.profile, CRYPTO_LIVE_POOL_ROTATION_PROFILE) + self.assertEqual(metadata.canonical_profile, CRYPTO_LIVE_POOL_ROTATION_PROFILE) + self.assertIn(CRYPTO_LEADER_ROTATION_PROFILE, metadata.aliases) + def test_runtime_adapter_covers_canonical_inputs(self): definition = get_strategy_definition(CRYPTO_LIVE_POOL_ROTATION_PROFILE) adapter = get_platform_runtime_adapter(CRYPTO_LIVE_POOL_ROTATION_PROFILE, platform_id=BINANCE_PLATFORM) diff --git a/tests/test_contract_governance.py b/tests/test_contract_governance.py index 85caa3c..37f7536 100644 --- a/tests/test_contract_governance.py +++ b/tests/test_contract_governance.py @@ -89,6 +89,7 @@ def test_manifest_matches_catalog_definition_and_metadata(self) -> None: self.assertEqual(manifest.profile, definition.profile) self.assertEqual(manifest.domain, definition.domain) self.assertEqual(manifest.display_name, metadata.display_name) + self.assertEqual(manifest.aliases, metadata.aliases) self.assertEqual(manifest.required_inputs, definition.required_inputs) self.assertEqual(dict(manifest.default_config), dict(definition.default_config)) @@ -106,6 +107,7 @@ def test_entrypoint_manifest_matches_catalog_when_importable(self) -> None: self.assertEqual(entrypoint.manifest.profile, definition.profile) self.assertEqual(entrypoint.manifest.domain, definition.domain) self.assertEqual(entrypoint.manifest.display_name, metadata.display_name) + self.assertEqual(entrypoint.manifest.aliases, metadata.aliases) self.assertEqual(entrypoint.manifest.required_inputs, definition.required_inputs) def test_strategy_index_rows_expose_expected_contract_fields(self) -> None: