diff --git a/README.md b/README.md index a261ee8..9b3ee6c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Quant system on LongPort OpenAPI and Google Cloud Run. This repository uses `QuantPlatformKit` for LongPort token handling, context bootstrap, account snapshot access, market data, and order submission. Cloud Run deploys this repository directly. The runtime now carries a structured `RuntimeTarget` / `RUNTIME_TARGET_JSON` alongside the compatibility `STRATEGY_PROFILE` selector. Strategy-owned defaults come from `UsEquityStrategies` and `HkEquityStrategies`; platform variables are only explicit overrides. -The LongBridge runtime can execute the current `runtime_enabled` `us_equity` profiles from `UsEquityStrategies`. It also carries eligible-but-disabled HK profiles from `HkEquityStrategies`: `hk_blue_chip_leader_rotation`, `hk_index_mean_reversion`, `hk_etf_regime_rotation`, and `hk_listed_global_etf_rotation`; `LongBridgePlatform` keeps the LongPort runtime, token refresh, execution, and notification flow. +The LongBridge runtime can execute the current `runtime_enabled` `us_equity` profiles from `UsEquityStrategies`. It also carries HK profiles from `HkEquityStrategies`: eligible-but-disabled `hk_blue_chip_leader_rotation`, `hk_index_mean_reversion`, `hk_etf_regime_rotation`, plus runtime-enabled `hk_listed_global_etf_rotation`; `LongBridgePlatform` keeps the LongPort runtime, token refresh, execution, and notification flow. `STRATEGY_PROFILE` remains the compatibility selector for strategy routing, while `RuntimeTarget` describes the running service identity. Strategy documentation lives in [`UsEquityStrategies`](https://github.com/QuantStrategyLab/UsEquityStrategies) and [`HkEquityStrategies`](https://github.com/QuantStrategyLab/HkEquityStrategies). Snapshot artifact contracts for the HK profile are produced by [`HkEquitySnapshotPipelines`](https://github.com/QuantStrategyLab/HkEquitySnapshotPipelines). The sections below focus on LongBridge runtime behavior, profile enablement, deployment, and credentials. @@ -45,7 +45,7 @@ Platform execution no longer depends on `strategy/allocation.py` or hard-coded s | `hk_blue_chip_leader_rotation` | HK Blue Chip Leader Rotation | Yes | No | `hk_equity` | architecture scaffold only; not runtime-enabled | | `hk_index_mean_reversion` | HK Index Mean Reversion | Yes | No | `hk_equity` | market-history research candidate; not runtime-enabled | | `hk_etf_regime_rotation` | HK ETF Regime Rotation | Yes | No | `hk_equity` | market-history research candidate; not runtime-enabled | -| `hk_listed_global_etf_rotation` | HK-listed Global ETF Rotation | Yes | No | `hk_equity` | volatility-targeted market-history research candidate; not runtime-enabled | +| `hk_listed_global_etf_rotation` | HK-listed Global ETF Rotation | Yes | Yes | `hk_equity` | runtime-enabled; production env unchanged until explicit rollout | Check the current matrix locally: @@ -73,7 +73,7 @@ Telegram notifications include structured execution and heartbeat messages, with | `LONGPORT_APP_SECRET` | Yes | LongPort OpenAPI app secret (for token refresh); recommended to inject from the region-specific Secret Manager secret for this deployment, such as `longport-app-secret-paper` / `longport-app-secret-hk` / `longport-app-secret-sg` | | `LONGPORT_SECRET_NAME` | No | Secret Manager secret name for LongPort token (default: `longport_token_paper`) | | `ACCOUNT_PREFIX` | No | Alert/log prefix for account/environment (default: `DEFAULT`) | -| `STRATEGY_PROFILE` | Yes | Strategy profile selector for compatibility and strategy routing. Set explicitly per deployment; enabled values include `global_etf_confidence_vol_gate`, `global_etf_rotation`, `mega_cap_leader_rotation_top50_balanced`, `russell_1000_multi_factor_defensive`, `soxl_soxx_trend_income`, `tech_communication_pullback_enhancement`, and `tqqq_growth_income`. The structured runtime target is carried separately as `RUNTIME_TARGET_JSON`. | +| `STRATEGY_PROFILE` | Yes | Strategy profile selector for compatibility and strategy routing. Set explicitly per deployment; enabled values include `global_etf_confidence_vol_gate`, `global_etf_rotation`, `mega_cap_leader_rotation_top50_balanced`, `russell_1000_multi_factor_defensive`, `soxl_soxx_trend_income`, `tech_communication_pullback_enhancement`, `tqqq_growth_income`, and `hk_listed_global_etf_rotation`. The structured runtime target is carried separately as `RUNTIME_TARGET_JSON`; production Cloud Run remains on its configured profile until an explicit rollout changes it. | | `ACCOUNT_REGION` | No | Account region marker for platform-style deployment (e.g. `PAPER`, `HK`, `SG`; defaults to `ACCOUNT_PREFIX` / `DEFAULT`) | | `LONGBRIDGE_MARKET` | No | Market scope. Defaults to `HK` when `ACCOUNT_REGION=HK`, otherwise `US`. | | `LONGBRIDGE_MARKET_CALENDAR` | No | Market calendar for market-hours checks. Defaults to `XHKG` for HK and `NYSE` for US. | @@ -204,7 +204,7 @@ IAM: the Cloud Run service account needs **Secret Manager Admin** (or Secret Acc 这个仓库通过 `QuantPlatformKit` 复用 LongPort token 处理、上下文初始化、账户快照、行情读取和下单逻辑。Cloud Run 直接部署这个仓库。 LongBridge 的账户身份按 `paper`、`HK`、`SG` 三个维度建模。 -`LongBridgePlatform` 现在可直接执行 `UsEquityStrategies` 里的 `runtime_enabled` `us_equity` 策略,同时带有 `HkEquityStrategies` 里的港股 eligible-but-disabled profile:`hk_blue_chip_leader_rotation`、`hk_index_mean_reversion`、`hk_etf_regime_rotation` 和 `hk_listed_global_etf_rotation`。这些港股 profile 当前仅用于框架和 feed/dry-run 兼容性检查,未 enabled。仓库本身继续保留 LongPort 运行时、token 刷新、执行和通知流程。 +`LongBridgePlatform` 现在可直接执行 `UsEquityStrategies` 里的 `runtime_enabled` `us_equity` 策略,同时带有 `HkEquityStrategies` 里的港股 profile:`hk_blue_chip_leader_rotation`、`hk_index_mean_reversion`、`hk_etf_regime_rotation` 仍是 eligible-but-disabled,`hk_listed_global_etf_rotation` 已 runtime-enabled。生产环境仍保持原策略,除非单独变更 `RUNTIME_TARGET_JSON` / `STRATEGY_PROFILE`。仓库本身继续保留 LongPort 运行时、token 刷新、执行和通知流程。 策略说明放在 [`UsEquityStrategies`](https://github.com/QuantStrategyLab/UsEquityStrategies) 和 [`HkEquityStrategies`](https://github.com/QuantStrategyLab/HkEquityStrategies);港股 snapshot artifact 由 [`HkEquitySnapshotPipelines`](https://github.com/QuantStrategyLab/HkEquitySnapshotPipelines) 生成。下面这些章节只保留 LongBridge 运行时、profile 启用状态、部署和凭据说明。 @@ -233,7 +233,7 @@ LongBridge 的账户身份按 `paper`、`HK`、`SG` 三个维度建模。 | `hk_blue_chip_leader_rotation` | HK Blue Chip Leader Rotation | Yes | No | `hk_equity` | 仅架构占位,暂未 runtime-enabled | | `hk_index_mean_reversion` | HK Index Mean Reversion | Yes | No | `hk_equity` | market-history 研究候选,暂未 runtime-enabled | | `hk_etf_regime_rotation` | HK ETF Regime Rotation | Yes | No | `hk_equity` | market-history 研究候选,暂未 runtime-enabled | -| `hk_listed_global_etf_rotation` | HK-listed Global ETF Rotation | Yes | No | `hk_equity` | 波动率目标 market-history 研究候选,暂未 runtime-enabled | +| `hk_listed_global_etf_rotation` | HK-listed Global ETF Rotation | Yes | Yes | `hk_equity` | 已 runtime-enabled;生产环境保持原策略直到显式 rollout | 本地可直接查看当前矩阵: @@ -261,7 +261,7 @@ Telegram 通知包含结构化的调仓和心跳消息,支持中英文切换 | `LONGPORT_APP_SECRET` | 是 | LongPort OpenAPI 应用密钥(用于刷新 Token);建议从当前部署对应区域的 Secret Manager 密钥注入,例如 `longport-app-secret-paper` / `longport-app-secret-hk` / `longport-app-secret-sg` | | `LONGPORT_SECRET_NAME` | 否 | Secret Manager 中的密钥名称(默认: `longport_token_paper`) | | `ACCOUNT_PREFIX` | 否 | 通知/日志前缀,区分账户环境(默认: `DEFAULT`) | -| `STRATEGY_PROFILE` | 是 | 策略档位选择。每个部署都要显式设置;已启用值包括 `global_etf_confidence_vol_gate`、`global_etf_rotation`、`mega_cap_leader_rotation_top50_balanced`、`russell_1000_multi_factor_defensive`、`soxl_soxx_trend_income`、`tech_communication_pullback_enhancement` 和 `tqqq_growth_income` | +| `STRATEGY_PROFILE` | 是 | 策略档位选择。每个部署都要显式设置;已启用值包括 `global_etf_confidence_vol_gate`、`global_etf_rotation`、`mega_cap_leader_rotation_top50_balanced`、`russell_1000_multi_factor_defensive`、`soxl_soxx_trend_income`、`tech_communication_pullback_enhancement`、`tqqq_growth_income` 和 `hk_listed_global_etf_rotation`;生产 Cloud Run 保持原配置,除非显式 rollout | | `ACCOUNT_REGION` | 否 | 平台化部署时的账户区域标记(如 `PAPER`、`HK`、`SG`;默认按 `ACCOUNT_PREFIX` / `DEFAULT` 推断) | | `LONGBRIDGE_MARKET` | 否 | 市场范围。`ACCOUNT_REGION=HK` 时默认 `HK`,其他情况默认 `US`。 | | `LONGBRIDGE_MARKET_CALENDAR` | 否 | 市场开闭市检查用的日历。港股默认 `XHKG`,美股默认 `NYSE`。 | diff --git a/docs/hk_equity_runtime.md b/docs/hk_equity_runtime.md index 22b8d93..f565f18 100644 --- a/docs/hk_equity_runtime.md +++ b/docs/hk_equity_runtime.md @@ -14,7 +14,7 @@ QuantStrategyLab 现有平台仓库里,能做港股股票交易运行时接入 ## 运行时设计 -平台运行时已具备港股市场维度,并接入 `HkEquityStrategies` 的港股 profile 元数据:`hk_blue_chip_leader_rotation` 是架构占位,`hk_index_mean_reversion`、`hk_etf_regime_rotation` 和 `hk_listed_global_etf_rotation` 是 `market_history` 研究候选。这些 profile 都只用于框架 wiring、feed/dry-run 兼容性检查和尚未 runtime-enabled。整体仍沿用美股策略的分层方式: +平台运行时已具备港股市场维度,并接入 `HkEquityStrategies` 的港股 profile 元数据:`hk_blue_chip_leader_rotation` 是架构占位,`hk_index_mean_reversion`、`hk_etf_regime_rotation` 是 `market_history` 研究候选,`hk_listed_global_etf_rotation` 已 runtime-enabled。生产 Cloud Run 仍保持原策略,除非单独变更 `RUNTIME_TARGET_JSON` / `STRATEGY_PROFILE`。整体仍沿用美股策略的分层方式: 1. [`HkEquityStrategies`](https://github.com/QuantStrategyLab/HkEquityStrategies) 提供 `hk_equity` 策略定义、运行入口和 LongBridge runtime adapter。 2. [`HkEquitySnapshotPipelines`](https://github.com/QuantStrategyLab/HkEquitySnapshotPipelines) 产出 snapshot-backed profile 的特征快照、manifest、ranking 和 release summary。 @@ -31,7 +31,7 @@ QuantStrategyLab 现有平台仓库里,能做港股股票交易运行时接入 | `hk_blue_chip_leader_rotation` | `hk_equity` | `feature_snapshot` | `weight` | required | eligible but disabled | | `hk_index_mean_reversion` | `hk_equity` | `market_history` | `weight` | not required | eligible but disabled | | `hk_etf_regime_rotation` | `hk_equity` | `market_history` | `weight` | not required | eligible but disabled | -| `hk_listed_global_etf_rotation` | `hk_equity` | `market_history` | `weight` | not required | eligible but disabled | +| `hk_listed_global_etf_rotation` | `hk_equity` | `market_history` | `weight` | not required | runtime-enabled; not deployed by default | 未来启用 snapshot-backed profile 后的最小策略配置示例;当前不要写入 Cloud Run: @@ -74,6 +74,6 @@ LONGBRIDGE_TRADING_CURRENCY=HKD ## 风险和注意事项 - `XHKG` 是否可用取决于部署环境里的 `pandas_market_calendars` 版本;如不可用,可用 `LONGBRIDGE_MARKET_CALENDAR` 临时覆盖。 -- `hk_blue_chip_leader_rotation`、`hk_index_mean_reversion`、`hk_etf_regime_rotation`、`hk_listed_global_etf_rotation` 当前均未启用;不要把这些 profile 写入生产 Cloud Run。 -- `market_history` 研究候选后续真正启用前,需要先用 LongBridge HK 行情 feed 对 `02800`、`03033`、`02822`、`02840`、`03110`、`03188`、`02834`、`03175` 做 dry-run 校验,不提交真实订单。 +- `hk_listed_global_etf_rotation` 已在策略包 runtime-enabled,但生产 Cloud Run 仍保持原配置;`hk_blue_chip_leader_rotation`、`hk_index_mean_reversion`、`hk_etf_regime_rotation` 仍未启用,不要写入生产 Cloud Run。 +- 港股 `market_history` profile 投入生产前,需要先用 LongBridge HK 行情 feed 对 `02800`、`03033`、`02822`、`02840`、`03110`、`03188`、`02834`、`03175` 做 dry-run 校验,不提交真实订单。 - LongBridge 下单仍保持整数股规则;如果未来港股策略涉及碎股或特殊交易单位,需要在策略层明确 lot-size 约束后再扩展。 diff --git a/notifications/telegram.py b/notifications/telegram.py index 563c145..ae59483 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -101,6 +101,7 @@ "strategy_name_tech_communication_pullback_enhancement": "科技通信回调增强", "strategy_name_qqq_tech_enhancement": "科技通信回调增强", "strategy_name_mega_cap_leader_rotation_top50_balanced": "Mega Cap Top50 平衡龙头轮动", + "strategy_name_hk_listed_global_etf_rotation": "港股上市全球 ETF 轮动", "strategy_plugin_line": "🧩 插件:{plugin} | 状态:{route} | 提醒:{action}", "strategy_plugin_alert_subject": "🚨 策略插件告警:{plugin} | {route}", "strategy_plugin_alert_title": "🚨 【策略插件告警】", @@ -218,6 +219,7 @@ "strategy_name_tech_communication_pullback_enhancement": "Tech/Communication Pullback Enhancement", "strategy_name_qqq_tech_enhancement": "Tech/Communication Pullback Enhancement", "strategy_name_mega_cap_leader_rotation_top50_balanced": "Mega Cap Leader Rotation Top50 Balanced", + "strategy_name_hk_listed_global_etf_rotation": "HK-listed Global ETF Rotation", "strategy_plugin_line": "🧩 Plugin: {plugin} | status: {route} | notice: {action}", "strategy_plugin_alert_subject": "🚨 Strategy plugin alert: {plugin} | {route}", "strategy_plugin_alert_title": "🚨 【Strategy Plugin Alert】", diff --git a/requirements.txt b/requirements.txt index a2b7311..8173619 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ flask gunicorn quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@v0.7.35 us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@v0.7.49 -hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@400ef6145bff0b89b46ec00ebb235987ac499a61 +hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@71141ce9e8a343cec8e2140994071eea66422bc6 pandas requests pytz diff --git a/tests/test_notifications.py b/tests/test_notifications.py index ba2aa6d..5dd03dc 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -108,6 +108,8 @@ def test_supported_strategy_profiles_have_translated_names(self): en_name = build_strategy_display_name(build_translator("en")) self.assertEqual(zh_name("global_etf_confidence_vol_gate"), "全球 ETF 置信波动门控") self.assertEqual(en_name("global_etf_confidence_vol_gate"), "Global ETF Confidence Vol Gate") + self.assertEqual(zh_name("hk_listed_global_etf_rotation"), "港股上市全球 ETF 轮动") + self.assertEqual(en_name("hk_listed_global_etf_rotation"), "HK-listed Global ETF Rotation") for profile in SUPPORTED_STRATEGY_PROFILES: self.assertNotEqual(zh_name(profile), profile) diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 9576822..dd2323b 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -62,19 +62,19 @@ } ) OPTIONAL_LONGBRIDGE_PROFILES = frozenset({"global_etf_confidence_vol_gate"}) +HK_RUNTIME_ENABLED_PROFILES = frozenset({"hk_listed_global_etf_rotation"}) HK_DISABLED_PROFILES = frozenset( { "hk_blue_chip_leader_rotation", "hk_index_mean_reversion", "hk_etf_regime_rotation", - "hk_listed_global_etf_rotation", } ) def expected_longbridge_enabled_profiles(actual_profiles) -> frozenset[str]: actual = frozenset(actual_profiles) - return BASE_LONGBRIDGE_PROFILES | (OPTIONAL_LONGBRIDGE_PROFILES & actual) + return BASE_LONGBRIDGE_PROFILES | HK_RUNTIME_ENABLED_PROFILES | (OPTIONAL_LONGBRIDGE_PROFILES & actual) def expected_longbridge_profiles(actual_profiles) -> frozenset[str]: @@ -643,7 +643,7 @@ def test_platform_profile_status_matrix_matches_current_longbridge_rollout(self) "display_name": "HK-listed Global ETF Rotation", "domain": "hk_equity", "eligible": True, - "enabled": False, + "enabled": True, "platform": "longbridge", }, ) @@ -669,7 +669,7 @@ def test_loads_feature_snapshot_env_for_tech_profile(self): self.assertEqual(settings.strategy_config_path, "/workspace/configs/tech.json") self.assertEqual(settings.strategy_config_source, "env") - def test_rejects_hk_profiles_until_runtime_enabled(self): + def test_rejects_disabled_hk_profiles(self): for profile in sorted(HK_DISABLED_PROFILES): with self.subTest(profile=profile): with patch.dict( @@ -685,6 +685,26 @@ def test_rejects_hk_profiles_until_runtime_enabled(self): with self.assertRaisesRegex(ValueError, "Unsupported STRATEGY_PROFILE"): load_platform_runtime_settings(project_id_resolver=lambda: "project-1") + def test_accepts_runtime_enabled_hk_global_etf_rotation(self): + with patch.dict( + os.environ, + { + "RUNTIME_TARGET_JSON": runtime_target_json("hk_listed_global_etf_rotation"), + "ACCOUNT_REGION": "HK", + }, + clear=True, + ): + settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1") + + self.assertEqual(settings.strategy_profile, "hk_listed_global_etf_rotation") + self.assertEqual(settings.strategy_display_name, "HK-listed Global ETF Rotation") + self.assertEqual(settings.strategy_domain, "hk_equity") + self.assertEqual(settings.market, HK_MARKET) + self.assertEqual(settings.market_calendar, HK_MARKET_CALENDAR) + self.assertEqual(settings.market_timezone, HK_MARKET_TIMEZONE) + self.assertEqual(settings.symbol_suffix, HK_SYMBOL_SUFFIX) + self.assertEqual(settings.trading_currency, HK_TRADING_CURRENCY) + def test_derives_feature_snapshot_paths_from_artifact_root(self): with TemporaryDirectory() as tmp_dir: with patch.dict(