From d250cd8fa3302d2fe72d5377559de469b5e0c50f Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Wed, 3 Jun 2026 20:00:42 +0800 Subject: [PATCH] Use upgraded crypto live pool profile --- .github/workflows/main.yml | 2 +- application/cycle_service.py | 2 +- degraded_mode_support.py | 2 +- docs/binance_platform_rename_checklist.md | 4 +-- docs/operator_runbook.md | 8 +++--- main.py | 6 ++-- notify_i18n_support.py | 4 +-- research/backtest.py | 2 +- runtime_config_support.py | 11 +------- runtime_support.py | 2 +- scripts/print_strategy_switch_env_plan.py | 11 ++------ strategy_registry.py | 2 +- .../cycle_replay/pool_input_snapshot.json | 2 +- tests/test_cycle_service.py | 16 +++++------ tests/test_main_runtime_error_notification.py | 6 ++-- tests/test_notify_i18n.py | 2 +- tests/test_rotation_strategy.py | 4 +-- tests/test_runtime_config_support.py | 25 ++++++++--------- tests/test_runtime_support.py | 4 +-- tests/test_runtime_workflow_shared_config.sh | 2 +- tests/test_strategy_core.py | 2 +- tests/test_strategy_loader.py | 4 +-- tests/test_strategy_runtime.py | 28 +++++++++---------- tests/test_trend_pool_loading.py | 25 ++++++++--------- trend_pool_support.py | 12 ++++---- 25 files changed, 84 insertions(+), 104 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6cd2e4e0..42318c69 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -106,7 +106,7 @@ jobs: env: BINANCE_API_KEY: ${{ secrets.BINANCE_API_KEY }} BINANCE_API_SECRET: ${{ secrets.BINANCE_API_SECRET }} - STRATEGY_PROFILE: ${{ vars.STRATEGY_PROFILE || 'crypto_leader_rotation' }} + STRATEGY_PROFILE: ${{ vars.STRATEGY_PROFILE || 'crypto_live_pool_rotation' }} TG_TOKEN: ${{ secrets.TG_TOKEN }} GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }} NOTIFY_LANG: ${{ vars.NOTIFY_LANG }} diff --git a/application/cycle_service.py b/application/cycle_service.py index 441f00a4..354879ab 100644 --- a/application/cycle_service.py +++ b/application/cycle_service.py @@ -248,7 +248,7 @@ def run_live_cycle( platform="binance", deploy_target=os.getenv("LOG_DEPLOY_TARGET", "vps"), service_name=os.getenv("SERVICE_NAME", "binance-platform"), - strategy_profile=str(getattr(runtime, "strategy_profile", "") or os.getenv("STRATEGY_PROFILE", "crypto_leader_rotation")), + strategy_profile=str(getattr(runtime, "strategy_profile", "") or os.getenv("STRATEGY_PROFILE", "crypto_live_pool_rotation")), run_id=str(getattr(runtime, "run_id", "") or ""), extra_fields={ "dry_run": bool(getattr(runtime, "dry_run", False)), diff --git a/degraded_mode_support.py b/degraded_mode_support.py index 2f15f68e..169c3604 100644 --- a/degraded_mode_support.py +++ b/degraded_mode_support.py @@ -72,7 +72,7 @@ def resolve_trend_pool_source( ) messages.extend(last_good_result.get("errors", [])) - configured_path = get_strategy_artifact_env("STRATEGY_ARTIFACT_FILE", "TREND_POOL_FILE") + configured_path = get_strategy_artifact_env("STRATEGY_ARTIFACT_FILE") file_candidates = build_strategy_artifact_file_candidates( configured_path=configured_path, default_candidates=get_default_live_pool_candidates(default_live_pool_legacy_path), diff --git a/docs/binance_platform_rename_checklist.md b/docs/binance_platform_rename_checklist.md index b943ffa4..39626841 100644 --- a/docs/binance_platform_rename_checklist.md +++ b/docs/binance_platform_rename_checklist.md @@ -19,7 +19,7 @@ This checklist records the completed runtime repository rename from `BinanceQuan - Current runtime model: Oracle Cloud / VPS hosted self-hosted runner - Current runtime trigger: external scheduler calling GitHub `workflow_dispatch` - Current strategy domain: `crypto` -- Current live profile: `crypto_leader_rotation` +- Current live profile: `crypto_live_pool_rotation` ## Why this rename is different from Cloud Run repos @@ -88,7 +88,7 @@ These can be updated in the repo during the rename change: - GCP project id `binancequant` - Firestore database name / location - strategy domain `crypto` -- current profile `crypto_leader_rotation` +- current profile `crypto_live_pool_rotation` ## Blocking condition diff --git a/docs/operator_runbook.md b/docs/operator_runbook.md index f88fa448..f369f92d 100644 --- a/docs/operator_runbook.md +++ b/docs/operator_runbook.md @@ -88,7 +88,7 @@ Degraded mode: - Source is `last_known_good`, `local_file`, or `static` - New trend buys are paused by default -- Set `STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED=1` only if you intentionally want degraded-mode entries; `TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED=1` remains a compatibility alias for the current live profile +- Set `STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED=1` only if you intentionally want degraded-mode entries Interpretation: @@ -98,13 +98,13 @@ Interpretation: ## Strategy Artifact Settings -Use the generic `STRATEGY_ARTIFACT_*` names for new crypto strategies. The older `TREND_POOL_*` names are accepted only as compatibility aliases for `crypto_leader_rotation`. +Use the generic `STRATEGY_ARTIFACT_*` names for crypto strategy artifacts. Primary settings: -- `STRATEGY_PROFILE`: live profile selector; current supported value is `crypto_leader_rotation` +- `STRATEGY_PROFILE`: live profile selector; current supported value is `crypto_live_pool_rotation` - `STRATEGY_ARTIFACT_FIRESTORE_COLLECTION`: upstream artifact collection, default `strategy` -- `STRATEGY_ARTIFACT_FIRESTORE_DOCUMENT`: upstream artifact document, default `CRYPTO_LEADER_ROTATION_LIVE_POOL` +- `STRATEGY_ARTIFACT_FIRESTORE_DOCUMENT`: upstream artifact document, default `CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL` - `STRATEGY_ARTIFACT_FILE`: local fallback artifact path - `STRATEGY_ARTIFACT_MAX_AGE_DAYS`: freshness window for upstream `as_of_date` - `STRATEGY_ARTIFACT_ACCEPTABLE_MODES`: comma-separated accepted upstream modes diff --git a/main.py b/main.py index 0828ecd2..5964229e 100644 --- a/main.py +++ b/main.py @@ -153,7 +153,7 @@ DEFAULT_LIVE_POOL_LEGACY_PATH = STRATEGY_RUNTIME.default_local_artifact_path DEFAULT_TREND_POOL_FIRESTORE_COLLECTION = "strategy" -DEFAULT_TREND_POOL_FIRESTORE_DOCUMENT = "CRYPTO_LEADER_ROTATION_LIVE_POOL" +DEFAULT_TREND_POOL_FIRESTORE_DOCUMENT = "CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL" RETIRED_TREND_POSITIONS_KEY = "retired_trend_positions" TREND_POOL_LAST_GOOD_PAYLOAD_KEY = "trend_pool_last_good_payload" TREND_POOL_ACTION_HISTORY_KEY = "trend_action_history" @@ -532,8 +532,8 @@ def maybe_send_periodic_btc_status_report( notifier_fn=None, ): resolved_strategy_display_name = strategy_display_name or build_strategy_display_name(t)( - getattr(STRATEGY_RUNTIME, "profile", "crypto_leader_rotation"), - fallback_name="Crypto Leader Rotation", + getattr(STRATEGY_RUNTIME, "profile", "crypto_live_pool_rotation"), + fallback_name="Crypto Live Pool Rotation", ) return report_maybe_send_periodic_btc_status_report( state, diff --git a/notify_i18n_support.py b/notify_i18n_support.py index feca8bad..556423c0 100644 --- a/notify_i18n_support.py +++ b/notify_i18n_support.py @@ -129,7 +129,7 @@ "usdt_unavailable_for_trend_buy": "USDT unavailable for trend buy", "usdt_unavailable_for_btc_dca_buy": "USDT unavailable for BTC DCA buy", "btc_unavailable_for_dca_sell": "BTC unavailable for DCA sell", - "strategy_name_crypto_leader_rotation": "Crypto Leader Rotation", + "strategy_name_crypto_live_pool_rotation": "Crypto Live Pool Rotation", }, "zh": { "telegram_prefix": "🤖 加密量化助手", @@ -253,7 +253,7 @@ "usdt_unavailable_for_trend_buy": "USDT 不足,无法执行趋势买入", "usdt_unavailable_for_btc_dca_buy": "USDT 不足,无法执行 BTC 定投买入", "btc_unavailable_for_dca_sell": "BTC 不足,无法执行定投止盈卖出", - "strategy_name_crypto_leader_rotation": "加密领涨轮动", + "strategy_name_crypto_live_pool_rotation": "加密领涨轮动", }, } diff --git a/research/backtest.py b/research/backtest.py index c90f7151..8e892a4a 100644 --- a/research/backtest.py +++ b/research/backtest.py @@ -13,7 +13,7 @@ if str(path) not in sys.path: sys.path.insert(0, str(path)) -from crypto_strategies.strategies.crypto_leader_rotation.core import ( +from crypto_strategies.strategies.crypto_live_pool_rotation.core import ( allocate_trend_buy_budget, build_rotation_pool_ranking, compute_allocation_budgets, diff --git a/runtime_config_support.py b/runtime_config_support.py index a9761faa..49f0acc2 100644 --- a/runtime_config_support.py +++ b/runtime_config_support.py @@ -7,7 +7,6 @@ from notify_i18n_support import build_strategy_display_name, build_translator, get_notify_lang from runtime_support import ExecutionRuntime -from strategy_artifact_support import get_strategy_artifact_env from strategy_registry import ( BINANCE_PLATFORM, resolve_strategy_definition, @@ -29,13 +28,6 @@ def get_env_bool(name: str, default: bool = False) -> bool: return str(value).strip().lower() in {"1", "true", "yes", "y", "on"} -def get_env_bool_alias(name: str, legacy_name: str, default: bool = False) -> bool: - value = get_strategy_artifact_env(name, legacy_name) - if not value: - return bool(default) - return str(value).strip().lower() in {"1", "true", "yes", "y", "on"} - - def get_env_csv(name: str, default_values: list[str] | tuple[str, ...]) -> list[str]: raw = os.getenv(name) if raw is None or not str(raw).strip(): @@ -69,9 +61,8 @@ def load_cycle_execution_settings() -> CycleExecutionSettings: ) return CycleExecutionSettings( btc_status_report_interval_hours=max(1, min(24, get_env_int("BTC_STATUS_REPORT_INTERVAL_HOURS", 24))), - allow_new_trend_entries_on_degraded=get_env_bool_alias( + allow_new_trend_entries_on_degraded=get_env_bool( "STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED", - "TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED", False, ), strategy_profile=strategy_definition.profile, diff --git a/runtime_support.py b/runtime_support.py index d299c1f4..caaa2bd8 100644 --- a/runtime_support.py +++ b/runtime_support.py @@ -42,7 +42,7 @@ def build_execution_report(runtime): platform="binance", deploy_target=os.getenv("LOG_DEPLOY_TARGET", "vps"), service_name=os.getenv("SERVICE_NAME", "binance-platform"), - strategy_profile=str(runtime.strategy_profile or os.getenv("STRATEGY_PROFILE", "crypto_leader_rotation")), + strategy_profile=str(runtime.strategy_profile or os.getenv("STRATEGY_PROFILE", "crypto_live_pool_rotation")), strategy_domain=str(runtime.strategy_domain or os.getenv("STRATEGY_DOMAIN", "crypto")), run_id=str(runtime.run_id), run_source="github_actions" if os.getenv("GITHUB_RUN_ID") or os.getenv("GITHUB_ACTIONS") else "runtime", diff --git a/scripts/print_strategy_switch_env_plan.py b/scripts/print_strategy_switch_env_plan.py index ddd88c46..cc7f861e 100644 --- a/scripts/print_strategy_switch_env_plan.py +++ b/scripts/print_strategy_switch_env_plan.py @@ -50,16 +50,9 @@ def build_switch_plan(profile: str) -> dict[str, object]: "STRATEGY_ARTIFACT_ACCEPTABLE_MODES", "STRATEGY_ARTIFACT_EXPECTED_SIZE", "STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED", - "TREND_POOL_FILE", - "TREND_POOL_FIRESTORE_COLLECTION", - "TREND_POOL_FIRESTORE_DOCUMENT", - "TREND_POOL_MAX_AGE_DAYS", - "TREND_POOL_ACCEPTABLE_MODES", - "TREND_POOL_EXPECTED_SIZE", - "TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED", ] notes = [ - "Binance runtime resolves strategy artifacts through STRATEGY_ARTIFACT_* settings; TREND_POOL_* remains accepted as a compatibility alias for crypto_leader_rotation.", + "Binance runtime resolves strategy artifacts through STRATEGY_ARTIFACT_* settings.", "Switching is mainly STRATEGY_PROFILE plus the shared strategy artifact settings.", "Keep exchange credentials and Telegram settings stable across strategy switches.", ] @@ -78,7 +71,7 @@ def build_switch_plan(profile: str) -> dict[str, object]: "remove_if_present": [], "hints": { "strategy_artifact_default_firestore_collection": "strategy", - "strategy_artifact_default_firestore_document": "CRYPTO_LEADER_ROTATION_LIVE_POOL", + "strategy_artifact_default_firestore_document": "CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL", "default_local_artifact": str(ROOT / "artifacts" / "live_pool_legacy.json"), "default_local_artifact_manifest": str(ROOT / "artifacts" / "artifact_manifest.json"), }, diff --git a/strategy_registry.py b/strategy_registry.py index 46008af0..aaa51baa 100644 --- a/strategy_registry.py +++ b/strategy_registry.py @@ -19,7 +19,7 @@ BINANCE_PLATFORM = "binance" -DEFAULT_STRATEGY_PROFILE = "crypto_leader_rotation" +DEFAULT_STRATEGY_PROFILE = "crypto_live_pool_rotation" ROLLBACK_STRATEGY_PROFILE = DEFAULT_STRATEGY_PROFILE STRATEGY_CATALOG = get_strategy_catalog() diff --git a/tests/fixtures/cycle_replay/pool_input_snapshot.json b/tests/fixtures/cycle_replay/pool_input_snapshot.json index 29e666b8..c5555d4a 100644 --- a/tests/fixtures/cycle_replay/pool_input_snapshot.json +++ b/tests/fixtures/cycle_replay/pool_input_snapshot.json @@ -11,5 +11,5 @@ "LTCUSDT": {"base_asset": "LTC"}, "BCHUSDT": {"base_asset": "BCH"} }, - "source_project": "crypto-leader-rotation" + "source_project": "crypto-live-pool-pipelines" } diff --git a/tests/test_cycle_service.py b/tests/test_cycle_service.py index 638d662c..ebd7b1be 100644 --- a/tests/test_cycle_service.py +++ b/tests/test_cycle_service.py @@ -34,7 +34,7 @@ def fake_execute_cycle(runtime): with patch.dict( os.environ, { - "STRATEGY_PROFILE": "crypto_leader_rotation", + "STRATEGY_PROFILE": "crypto_live_pool_rotation", "SERVICE_NAME": "binance-quant", }, clear=False, @@ -65,7 +65,7 @@ def test_run_live_cycle_emits_structured_runtime_events(self): with patch.dict( os.environ, { - "STRATEGY_PROFILE": "crypto_leader_rotation", + "STRATEGY_PROFILE": "crypto_live_pool_rotation", "SERVICE_NAME": "binance-quant", "LOG_DEPLOY_TARGET": "vps", }, @@ -75,8 +75,8 @@ def test_run_live_cycle_emits_structured_runtime_events(self): runtime_builder=lambda: SimpleNamespace( run_id="run-001", dry_run=True, - strategy_profile="crypto_leader_rotation", - strategy_display_name="Crypto Leader Rotation", + strategy_profile="crypto_live_pool_rotation", + strategy_display_name="Crypto Live Pool Rotation", strategy_display_name_localized="加密领涨轮动", ), execute_cycle=lambda _runtime: { @@ -101,8 +101,8 @@ def test_run_live_cycle_emits_structured_runtime_events(self): start_log = json.loads(observed["printed"][0]) end_log = json.loads(observed["printed"][2]) self.assertEqual(start_log["event"], "strategy_cycle_started") - self.assertEqual(start_log["strategy_profile"], "crypto_leader_rotation") - self.assertEqual(start_log["strategy_display_name"], "Crypto Leader Rotation") + self.assertEqual(start_log["strategy_profile"], "crypto_live_pool_rotation") + self.assertEqual(start_log["strategy_display_name"], "Crypto Live Pool Rotation") self.assertEqual(start_log["strategy_display_name_localized"], "加密领涨轮动") self.assertEqual(start_log["run_id"], "run-001") self.assertEqual(end_log["event"], "strategy_cycle_completed") @@ -116,7 +116,7 @@ def test_run_live_cycle_uses_shared_runtime_report_archive(self): with patch.dict( os.environ, { - "STRATEGY_PROFILE": "crypto_leader_rotation", + "STRATEGY_PROFILE": "crypto_live_pool_rotation", "SERVICE_NAME": "binance-quant", "EXECUTION_REPORT_GCS_URI": "gs://demo-bucket/runtime-reports", "GCP_PROJECT_ID": "demo-project", @@ -133,7 +133,7 @@ def test_run_live_cycle_uses_shared_runtime_report_archive(self): ) or SimpleNamespace( local_path=kwargs.get("output_path"), - gcs_uri="gs://demo-bucket/runtime-reports/binance/crypto_leader_rotation/2026-04/run-001.json", + gcs_uri="gs://demo-bucket/runtime-reports/binance/crypto_live_pool_rotation/2026-04/run-001.json", ), ): report, persisted_path = run_live_cycle( diff --git a/tests/test_main_runtime_error_notification.py b/tests/test_main_runtime_error_notification.py index 1960cb8e..be038176 100644 --- a/tests/test_main_runtime_error_notification.py +++ b/tests/test_main_runtime_error_notification.py @@ -61,11 +61,11 @@ class BinanceAPIException(Exception): strategy_registry_module = types.ModuleType("strategy_registry") strategy_registry_module.BINANCE_PLATFORM = "binance" strategy_registry_module.resolve_strategy_definition = lambda *_args, **_kwargs: types.SimpleNamespace( - profile="crypto_leader_rotation", + profile="crypto_live_pool_rotation", domain="crypto", ) strategy_registry_module.resolve_strategy_metadata = lambda *_args, **_kwargs: types.SimpleNamespace( - display_name="Crypto Leader Rotation", + display_name="Crypto Live Pool Rotation", ) sys.modules["strategy_registry"] = strategy_registry_module @@ -112,7 +112,7 @@ def fake_send_tg_msg(token, chat_id, text): { "TG_TOKEN": "token-1", "GLOBAL_TELEGRAM_CHAT_ID": "chat-1", - "STRATEGY_PROFILE": "crypto_leader_rotation", + "STRATEGY_PROFILE": "crypto_live_pool_rotation", }, clear=False, ): diff --git a/tests/test_notify_i18n.py b/tests/test_notify_i18n.py index 737d899d..6bd1caa1 100644 --- a/tests/test_notify_i18n.py +++ b/tests/test_notify_i18n.py @@ -132,7 +132,7 @@ def test_trend_pool_source_logs_use_chinese_when_notify_lang_is_zh(self): "mode": "core_major", "version": "2026-03-10-core_major", "as_of_date": "2026-03-10", - "source_project": "crypto-leader-rotation", + "source_project": "crypto-live-pool-pipelines", "messages": ["payload stale", "using cached pool"], "degraded": True, }, diff --git a/tests/test_rotation_strategy.py b/tests/test_rotation_strategy.py index a7a99b00..fecd1775 100644 --- a/tests/test_rotation_strategy.py +++ b/tests/test_rotation_strategy.py @@ -11,12 +11,12 @@ if str(path) not in sys.path: sys.path.insert(0, str(path)) -from crypto_strategies.strategies.crypto_leader_rotation.rotation import ( # noqa: E402 +from crypto_strategies.strategies.crypto_live_pool_rotation.rotation import ( # noqa: E402 get_trend_sell_reason, plan_trend_buys, refresh_rotation_pool, ) -from crypto_strategies.strategies.crypto_leader_rotation.core import allocate_trend_buy_budget # noqa: E402 +from crypto_strategies.strategies.crypto_live_pool_rotation.core import allocate_trend_buy_budget # noqa: E402 class RotationStrategyTests(unittest.TestCase): diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 72d968e1..3613bef6 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -32,7 +32,7 @@ def test_load_cycle_execution_settings_clamps_interval_and_reads_degraded_flag(s os.environ, { "BTC_STATUS_REPORT_INTERVAL_HOURS": "48", - "TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1", + "STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1", }, clear=False, ): @@ -41,22 +41,21 @@ def test_load_cycle_execution_settings_clamps_interval_and_reads_degraded_flag(s self.assertEqual(settings.btc_status_report_interval_hours, 24) self.assertTrue(settings.allow_new_trend_entries_on_degraded) self.assertEqual(settings.strategy_profile, DEFAULT_STRATEGY_PROFILE) - self.assertEqual(settings.strategy_display_name, "Crypto Leader Rotation") - self.assertEqual(settings.strategy_display_name_localized, "Crypto Leader Rotation") + self.assertEqual(settings.strategy_display_name, "Crypto Live Pool Rotation") + self.assertEqual(settings.strategy_display_name_localized, "Crypto Live Pool Rotation") self.assertEqual(settings.strategy_domain, CRYPTO_DOMAIN) - def test_load_cycle_execution_settings_accepts_strategy_artifact_degraded_alias(self): + def test_load_cycle_execution_settings_ignores_legacy_trend_pool_degraded_alias(self): with patch.dict( os.environ, { - "STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1", - "TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED": "0", + "TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1", }, - clear=False, + clear=True, ): settings = load_cycle_execution_settings() - self.assertTrue(settings.allow_new_trend_entries_on_degraded) + self.assertFalse(settings.allow_new_trend_entries_on_degraded) def test_load_cycle_execution_settings_rejects_unknown_strategy_profile(self): with patch.dict(os.environ, {"STRATEGY_PROFILE": "global_etf_rotation"}, clear=False): @@ -77,7 +76,7 @@ def test_platform_profile_status_matrix_marks_default_profile_eligible_and_enabl { "platform": BINANCE_PLATFORM, "canonical_profile": DEFAULT_STRATEGY_PROFILE, - "display_name": "Crypto Leader Rotation", + "display_name": "Crypto Live Pool Rotation", "eligible": True, "enabled": True, "is_default": True, @@ -115,7 +114,7 @@ def test_build_live_runtime_reads_env_and_preserves_injected_hooks(self): self.assertEqual(runtime.tg_token, "tg-token") self.assertEqual(runtime.tg_chat_id, "chat-id") self.assertEqual(runtime.strategy_profile, DEFAULT_STRATEGY_PROFILE) - self.assertEqual(runtime.strategy_display_name, "Crypto Leader Rotation") + self.assertEqual(runtime.strategy_display_name, "Crypto Live Pool Rotation") self.assertIs(runtime.state_loader, state_loader) self.assertIs(runtime.state_writer, state_writer) self.assertIs(runtime.notifier, notifier) @@ -179,10 +178,10 @@ def test_switch_env_plan_script_json_matches_binance_runtime_shape(self): self.assertIn("TG_TOKEN", plan["keep_env"]) self.assertIn("STRATEGY_ARTIFACT_FILE", plan["optional_env"]) self.assertIn("STRATEGY_ARTIFACT_MANIFEST_FILE", plan["optional_env"]) - self.assertIn("TREND_POOL_FILE", plan["optional_env"]) + self.assertNotIn("TREND_POOL_FILE", plan["optional_env"]) self.assertEqual( plan["hints"]["strategy_artifact_default_firestore_document"], - "CRYPTO_LEADER_ROTATION_LIVE_POOL", + "CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL", ) self.assertEqual(plan["remove_if_present"], []) @@ -197,7 +196,7 @@ def test_switch_env_plan_script_table_contains_expected_sections(self): ) self.assertIn("platform: binance", result.stdout) - self.assertIn("profile: crypto_leader_rotation", result.stdout) + self.assertIn("profile: crypto_live_pool_rotation", result.stdout) self.assertIn("set_env:", result.stdout) self.assertIn("keep_env:", result.stdout) self.assertIn("optional_env:", result.stdout) diff --git a/tests/test_runtime_support.py b/tests/test_runtime_support.py index d484b9cd..248e8ac0 100644 --- a/tests/test_runtime_support.py +++ b/tests/test_runtime_support.py @@ -32,7 +32,7 @@ def test_report_preserves_existing_fields(self): with patch.dict( os.environ, { - "STRATEGY_PROFILE": "crypto_leader_rotation", + "STRATEGY_PROFILE": "crypto_live_pool_rotation", "SERVICE_NAME": "binance-runtime", "LOG_DEPLOY_TARGET": "vps", }, @@ -44,7 +44,7 @@ def test_report_preserves_existing_fields(self): self.assertFalse(report["dry_run"]) self.assertEqual(report["schema_version"], "runtime_report.v1") self.assertEqual(report["platform"], "binance") - self.assertEqual(report["strategy_profile"], "crypto_leader_rotation") + self.assertEqual(report["strategy_profile"], "crypto_live_pool_rotation") self.assertIn("buy_sell_intents", report) self.assertIn("log_lines", report) diff --git a/tests/test_runtime_workflow_shared_config.sh b/tests/test_runtime_workflow_shared_config.sh index 68884db5..21429159 100644 --- a/tests/test_runtime_workflow_shared_config.sh +++ b/tests/test_runtime_workflow_shared_config.sh @@ -7,7 +7,7 @@ workflow_file="$repo_dir/.github/workflows/main.yml" grep -Fq 'TG_TOKEN: ${{ secrets.TG_TOKEN }}' "$workflow_file" grep -Fq 'GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}' "$workflow_file" grep -Fq 'NOTIFY_LANG: ${{ vars.NOTIFY_LANG }}' "$workflow_file" -grep -Fq "STRATEGY_PROFILE: \${{ vars.STRATEGY_PROFILE || 'crypto_leader_rotation' }}" "$workflow_file" +grep -Fq "STRATEGY_PROFILE: \${{ vars.STRATEGY_PROFILE || 'crypto_live_pool_rotation' }}" "$workflow_file" grep -Fq 'id-token: write' "$workflow_file" grep -Fq 'workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}' "$workflow_file" grep -Fq 'service_account: ${{ env.GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT }}' "$workflow_file" diff --git a/tests/test_strategy_core.py b/tests/test_strategy_core.py index e527f553..059fbc12 100644 --- a/tests/test_strategy_core.py +++ b/tests/test_strategy_core.py @@ -11,7 +11,7 @@ sys.path.insert(0, str(path)) try: - from crypto_strategies.strategies.crypto_leader_rotation import core as strategy_core + from crypto_strategies.strategies.crypto_live_pool_rotation import core as strategy_core except ModuleNotFoundError as exc: if exc.name != "pandas": raise diff --git a/tests/test_strategy_loader.py b/tests/test_strategy_loader.py index b9ba43a6..7a955277 100644 --- a/tests/test_strategy_loader.py +++ b/tests/test_strategy_loader.py @@ -20,9 +20,9 @@ def test_load_strategy_entrypoint_for_profile_returns_unified_entrypoint(self): self.skipTest("pandas is not installed") raise - entrypoint = load_strategy_entrypoint_for_profile("crypto_leader_rotation") + entrypoint = load_strategy_entrypoint_for_profile("crypto_live_pool_rotation") - self.assertEqual(entrypoint.manifest.profile, "crypto_leader_rotation") + self.assertEqual(entrypoint.manifest.profile, "crypto_live_pool_rotation") self.assertEqual(entrypoint.manifest.domain, "crypto") self.assertIn("market_prices", entrypoint.manifest.required_inputs) self.assertIn("portfolio_snapshot", entrypoint.manifest.required_inputs) diff --git a/tests/test_strategy_runtime.py b/tests/test_strategy_runtime.py index f712193d..f8a30768 100644 --- a/tests/test_strategy_runtime.py +++ b/tests/test_strategy_runtime.py @@ -32,12 +32,12 @@ def test_load_strategy_runtime_exposes_explicit_artifact_contract(self): self.skipTest("pandas is not installed") raise - runtime = load_strategy_runtime("crypto_leader_rotation") + runtime = load_strategy_runtime("crypto_live_pool_rotation") - self.assertEqual(runtime.profile, "crypto_leader_rotation") + self.assertEqual(runtime.profile, "crypto_live_pool_rotation") self.assertEqual(runtime.runtime_adapter.portfolio_input_name, "portfolio_snapshot") self.assertTrue(str(runtime.default_local_artifact_path).endswith("BinancePlatform/artifacts/live_pool_legacy.json")) - self.assertEqual(runtime.artifact_contract["version"], "crypto_leader_rotation.live_pool.v1") + self.assertEqual(runtime.artifact_contract["version"], "crypto_live_pool_rotation.live_pool.v1") self.assertTrue(runtime.artifact_contract["requires_artifacts"]) self.assertTrue(runtime.artifact_contract["requires_manifest"]) self.assertEqual(runtime.artifact_contract["config_source_policy"], "none") @@ -52,7 +52,7 @@ def test_strategy_runtime_evaluate_returns_decision_with_buy_sell_diagnostics(se self.skipTest("pandas is not installed") raise - runtime = load_strategy_runtime("crypto_leader_rotation") + runtime = load_strategy_runtime("crypto_live_pool_rotation") account_metrics = { "total_equity": 10000.0, "cash_usdt": 2500.0, @@ -115,7 +115,7 @@ def test_strategy_runtime_evaluate_returns_decision_with_buy_sell_diagnostics(se raise diagnostics = evaluation.decision.diagnostics - self.assertEqual(evaluation.metadata["strategy_display_name"], "Crypto Leader Rotation") + self.assertEqual(evaluation.metadata["strategy_display_name"], "Crypto Live Pool Rotation") self.assertIn("planned_trend_buys", diagnostics) self.assertIn("eligible_buy_symbols", diagnostics) self.assertIn("sell_reasons", diagnostics) @@ -132,10 +132,10 @@ def test_load_strategy_runtime_uses_entrypoint_only(self): fake_entrypoint = types.SimpleNamespace( manifest=types.SimpleNamespace( - profile="crypto_leader_rotation", + profile="crypto_live_pool_rotation", default_config={ "trend_pool_size": 4, - "artifact_contract_version": "crypto_leader_rotation.live_pool.v1", + "artifact_contract_version": "crypto_live_pool_rotation.live_pool.v1", }, ) ) @@ -161,9 +161,9 @@ def test_load_strategy_runtime_uses_entrypoint_only(self): "tp_get_default_live_pool_candidates", side_effect=lambda default_path: [str(default_path), "/tmp/live_pool_fallback.json"], ): - runtime = strategy_runtime_module.load_strategy_runtime("crypto_leader_rotation") + runtime = strategy_runtime_module.load_strategy_runtime("crypto_live_pool_rotation") - mock_entrypoint_loader.assert_called_once_with("crypto_leader_rotation") + mock_entrypoint_loader.assert_called_once_with("crypto_live_pool_rotation") self.assertIs(runtime.entrypoint, fake_entrypoint) self.assertIs(runtime.runtime_adapter, fake_runtime_adapter) self.assertEqual(runtime.merged_runtime_config["trend_pool_size"], 4) @@ -184,9 +184,9 @@ def test_strategy_runtime_maps_binance_inputs_into_canonical_strategy_context(se class FakeEntrypoint: manifest = StrategyManifest( - profile="crypto_leader_rotation", + profile="crypto_live_pool_rotation", domain="crypto", - display_name="Crypto Leader Rotation", + display_name="Crypto Live Pool Rotation", description="test", required_inputs=frozenset( { @@ -197,7 +197,7 @@ class FakeEntrypoint: "universe_snapshot", } ), - default_config={"trend_pool_size": 4, "artifact_contract_version": "crypto_leader_rotation.live_pool.v1"}, + default_config={"trend_pool_size": 4, "artifact_contract_version": "crypto_live_pool_rotation.live_pool.v1"}, ) def evaluate(self, ctx): @@ -213,7 +213,7 @@ def evaluate(self, ctx): merged_runtime_config=FakeEntrypoint.manifest.default_config, ) - with patch.object(strategy_runtime_module, "resolve_strategy_metadata", return_value=SimpleNamespace(display_name="Crypto Leader Rotation")): + with patch.object(strategy_runtime_module, "resolve_strategy_metadata", return_value=SimpleNamespace(display_name="Crypto Live Pool Rotation")): evaluation = runtime.evaluate( prices={"BTCUSDT": 60000.0, "ETHUSDT": 3000.0}, trend_indicators={"ETHUSDT": {"close": 3000.0}}, @@ -232,7 +232,7 @@ def evaluate(self, ctx): self.assertEqual(ctx.market_data["market_prices"]["ETHUSDT"], 3000.0) self.assertEqual(ctx.market_data["universe_snapshot"], ("ETHUSDT",)) self.assertEqual(ctx.portfolio.metadata["account_metrics"]["cash_usdt"], 2000.0) - self.assertEqual(evaluation.metadata["strategy_display_name"], "Crypto Leader Rotation") + self.assertEqual(evaluation.metadata["strategy_display_name"], "Crypto Live Pool Rotation") if __name__ == "__main__": diff --git a/tests/test_trend_pool_loading.py b/tests/test_trend_pool_loading.py index 214d213f..97b7a2e3 100644 --- a/tests/test_trend_pool_loading.py +++ b/tests/test_trend_pool_loading.py @@ -91,8 +91,8 @@ def set(self, *args, **kwargs): import main from degraded_mode_support import format_trend_pool_source_logs import degraded_mode_support -from crypto_strategies.strategies.crypto_leader_rotation.core import allocate_trend_buy_budget -from crypto_strategies.strategies.crypto_leader_rotation.rotation import refresh_rotation_pool +from crypto_strategies.strategies.crypto_live_pool_rotation.core import allocate_trend_buy_budget +from crypto_strategies.strategies.crypto_live_pool_rotation.rotation import refresh_rotation_pool def build_payload(as_of_date="2026-03-10", *, mode="core_major"): @@ -110,7 +110,7 @@ def build_payload(as_of_date="2026-03-10", *, mode="core_major"): "pool_size": len(symbol_map), "symbols": list(symbol_map.keys()), "symbol_map": symbol_map, - "source_project": "crypto-leader-rotation", + "source_project": "crypto-live-pool-pipelines", } @@ -158,24 +158,21 @@ def test_default_live_pool_candidates_include_renamed_repo_checkout(self): self.assertEqual(candidates[0], default_path) self.assertTrue(any("CryptoLivePoolPipelines" in value for value in candidate_text)) - def test_strategy_artifact_env_aliases_override_legacy_trend_pool_settings(self): + def test_strategy_artifact_settings_ignore_legacy_trend_pool_env_names(self): with patch.dict( os.environ, { - "STRATEGY_ARTIFACT_MAX_AGE_DAYS": "12", - "STRATEGY_ARTIFACT_ACCEPTABLE_MODES": "core_major,shadow", - "STRATEGY_ARTIFACT_EXPECTED_SIZE": "3", "TREND_POOL_MAX_AGE_DAYS": "45", "TREND_POOL_ACCEPTABLE_MODES": "legacy", "TREND_POOL_EXPECTED_SIZE": "5", }, - clear=False, + clear=True, ): settings = main.get_trend_pool_contract_settings() - self.assertEqual(settings["max_age_days"], 12) - self.assertEqual(settings["acceptable_modes"], ["core_major", "shadow"]) - self.assertEqual(settings["expected_pool_size"], 3) + self.assertEqual(settings["max_age_days"], main.DEFAULT_TREND_POOL_MAX_AGE_DAYS) + self.assertEqual(settings["acceptable_modes"], list(main.DEFAULT_TREND_POOL_ACCEPTABLE_MODES)) + self.assertEqual(settings["expected_pool_size"], main.TREND_POOL_SIZE) def test_resolve_trend_pool_source_prefers_last_known_good_before_local_file(self): last_good_payload = build_payload(as_of_date="2026-02-15") @@ -209,7 +206,7 @@ def test_resolve_trend_pool_source_prefers_last_known_good_before_local_file(sel def test_update_trend_pool_state_persists_metadata_and_last_good_payload(self): validated = main.validate_trend_pool_payload( build_payload(), - source_label="firestore:strategy/CRYPTO_LEADER_ROTATION_LIVE_POOL", + source_label="firestore:strategy/CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL", now_utc=datetime(2026, 3, 14, tzinfo=timezone.utc), acceptable_modes=["core_major"], expected_pool_size=5, @@ -228,7 +225,7 @@ def test_update_trend_pool_state_persists_metadata_and_last_good_payload(self): self.assertEqual(state["trend_pool_source"], "fresh_upstream") self.assertEqual(state["trend_pool_version"], "2026-03-10-core_major") self.assertEqual(state["trend_pool_mode"], "core_major") - self.assertEqual(state["trend_pool_source_project"], "crypto-leader-rotation") + self.assertEqual(state["trend_pool_source_project"], "crypto-live-pool-pipelines") self.assertEqual( state[main.TREND_POOL_LAST_GOOD_PAYLOAD_KEY]["version"], "2026-03-10-core_major", @@ -364,7 +361,7 @@ def test_format_trend_pool_source_logs_highlights_degraded_buy_pause(self): "mode": "core_major", "version": "2026-03-10-core_major", "as_of_date": "2026-03-10", - "source_project": "crypto-leader-rotation", + "source_project": "crypto-live-pool-pipelines", "messages": ["payload stale", "using cached pool"], "degraded": True, }, diff --git a/trend_pool_support.py b/trend_pool_support.py index fb61b4e0..a17bac04 100644 --- a/trend_pool_support.py +++ b/trend_pool_support.py @@ -94,20 +94,20 @@ def get_trend_pool_contract_settings(*, max_age_days_default, acceptable_modes_d 0, get_strategy_artifact_int( "STRATEGY_ARTIFACT_MAX_AGE_DAYS", - "TREND_POOL_MAX_AGE_DAYS", + None, max_age_days_default, ), ), "acceptable_modes": get_strategy_artifact_csv( "STRATEGY_ARTIFACT_ACCEPTABLE_MODES", - "TREND_POOL_ACCEPTABLE_MODES", + None, acceptable_modes_default, ), "expected_pool_size": max( 1, get_strategy_artifact_int( "STRATEGY_ARTIFACT_EXPECTED_SIZE", - "TREND_POOL_EXPECTED_SIZE", + None, expected_pool_size_default, ), ), @@ -250,7 +250,7 @@ def get_default_live_pool_candidates(default_live_pool_legacy_path): "CryptoLivePoolPipelines", "crypto-live-pool-pipelines", "CryptoLeaderRotation", - "crypto-leader-rotation", + "crypto-live-pool-pipelines", ) for root in search_roots: @@ -273,12 +273,12 @@ def load_trend_pool_from_firestore( ): collection = get_strategy_artifact_env( "STRATEGY_ARTIFACT_FIRESTORE_COLLECTION", - "TREND_POOL_FIRESTORE_COLLECTION", + None, default_collection, ) document = get_strategy_artifact_env( "STRATEGY_ARTIFACT_FIRESTORE_DOCUMENT", - "TREND_POOL_FIRESTORE_DOCUMENT", + None, default_document, ) settings = settings or {}