From f480fa1f08d868d728c4fd2caf12d196a8dcf47a Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:27:36 +0800 Subject: [PATCH] Preserve upstream trend pool order --- docs/operator_runbook.md | 8 +++++--- tests/test_trend_pool_loading.py | 30 ++++++++++++++++++++++++++++++ trend_pool_support.py | 10 ++++++++-- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/docs/operator_runbook.md b/docs/operator_runbook.md index cd8ca765..77d1c47d 100644 --- a/docs/operator_runbook.md +++ b/docs/operator_runbook.md @@ -34,18 +34,20 @@ It is responsible for: - consuming upstream live-pool artifacts and Firestore summary payloads - validating freshness, contract shape, and fallback eligibility +- preserving the accepted upstream `symbols` order when passing the pool into strategy code - executing orders, persisting runtime state, and emitting minimal operator alerts It is not responsible for: - monthly research reporting +- monthly live-pool selection, ranking, or local reranking - upstream release summaries or review packages - maintaining a second copy of the upstream publish narrative ## Normal Live Flow 1. Load runtime credentials and Firestore state. -2. Resolve the upstream strategy artifact in this order: +2. Resolve the upstream ordered strategy artifact in this order: - fresh upstream Firestore payload - last known good upstream payload from state - validated local upstream file fallback @@ -58,13 +60,13 @@ It is not responsible for: Runtime output should stay operational: - current upstream source and degraded status -- upstream official pool and current local execution pool logged as separate concepts +- upstream official pool order and current local execution pool logged as separate concepts - current execution targets and intents - explicit gating / no-trade reasons and side-effect suppression counts - zero-trade diagnostics grouped by BTC core / trend sleeve and gate - exceptions, circuit breakers, and alert-worthy failures -The monthly execution pool is locked to the accepted upstream `version` / `as_of_date`. It is rebuilt when upstream release metadata changes and otherwise reused across cycles. +The monthly execution pool is locked to the accepted upstream `version` / `as_of_date`. It refreshes when upstream release metadata changes and otherwise reuses the accepted ordered artifact pool; BinancePlatform should not rebuild the monthly pool with local ranking logic. ## Runtime Trigger Model diff --git a/tests/test_trend_pool_loading.py b/tests/test_trend_pool_loading.py index 2c17c7e2..4272a26b 100644 --- a/tests/test_trend_pool_loading.py +++ b/tests/test_trend_pool_loading.py @@ -8,6 +8,18 @@ def install_test_stubs(): + if "numpy" not in sys.modules: + numpy_module = types.ModuleType("numpy") + numpy_module.arange = lambda *args, **kwargs: [] + numpy_module.exp = lambda value: value + numpy_module.log = lambda value: value + sys.modules["numpy"] = numpy_module + + if "pandas" not in sys.modules: + pandas_module = types.ModuleType("pandas") + pandas_module.isna = lambda value: value is None + sys.modules["pandas"] = pandas_module + if "binance" not in sys.modules: binance_module = types.ModuleType("binance") client_module = types.ModuleType("binance.client") @@ -119,6 +131,24 @@ def test_validate_trend_pool_payload_rejects_stale_payload(self): self.assertFalse(result["ok"]) self.assertIn("stale", " ".join(result["errors"])) + def test_validate_trend_pool_payload_preserves_ordered_symbols_in_symbol_map(self): + payload = build_payload() + payload["symbols"] = ["BCHUSDT", "ETHUSDT", "LTCUSDT", "SOLUSDT", "XRPUSDT"] + + result = main.validate_trend_pool_payload( + payload, + source_label="test", + now_utc=datetime(2026, 3, 14, tzinfo=timezone.utc), + max_age_days=30, + acceptable_modes=["core_major"], + expected_pool_size=5, + enforce_freshness=True, + ) + + self.assertTrue(result["ok"]) + self.assertEqual(list(result["symbol_map"]), payload["symbols"]) + self.assertEqual(list(result["payload"]["symbol_map"]), payload["symbols"]) + def test_strategy_artifact_env_aliases_override_legacy_trend_pool_settings(self): with patch.dict( os.environ, diff --git a/trend_pool_support.py b/trend_pool_support.py index 44fa6046..bc4360b2 100644 --- a/trend_pool_support.py +++ b/trend_pool_support.py @@ -204,13 +204,19 @@ def validate_trend_pool_payload( source_project = "unknown" warnings.append(t("source_project_missing_unknown")) + ordered_symbol_map = { + symbol: symbol_map[symbol] + for symbol in symbols + if symbol in symbol_map + } + normalized_payload = { "as_of_date": as_of_date.isoformat() if as_of_date is not None else "", "version": version, "mode": mode, "pool_size": len(symbols), "symbols": symbols, - "symbol_map": symbol_map, + "symbol_map": ordered_symbol_map, "source_project": source_project, } @@ -220,7 +226,7 @@ def validate_trend_pool_payload( "warnings": warnings, "source_label": str(source_label), "payload": normalized_payload, - "symbol_map": symbol_map, + "symbol_map": ordered_symbol_map, "symbols": symbols, "pool_size": len(symbols), "as_of_date": normalized_payload["as_of_date"],