diff --git a/README.md b/README.md index 1546f19..2977c8b 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The mainline runtime now follows one path only: - `hk_blue_chip_leader_rotation` (architecture scaffold only; eligible but disabled) - `hk_index_mean_reversion` (market-history research candidate; eligible but disabled) - `hk_etf_regime_rotation` (market-history research candidate; eligible but disabled) -- `hk_listed_global_etf_rotation` (volatility-targeted market-history research candidate; eligible but disabled) +- `hk_listed_global_etf_rotation` (volatility-targeted market-history strategy; runtime-enabled, not deployed by default) **IBKR profile status** @@ -61,7 +61,7 @@ The mainline runtime now follows one path only: | `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: @@ -113,7 +113,7 @@ For IBKR, keep `paper` as a single account-group entry. If you later add live ac | `IBKR_CONNECT_ATTEMPTS` | No | Number of IBKR connection attempts before failing the cycle. Defaults to `3`. | | `IBKR_CONNECT_RETRY_DELAY_SECONDS` | No | Delay between failed IBKR connection attempts. Defaults to `5`. | | `IBKR_CLIENT_ID_RETRY_OFFSET` | No | Offset added to the configured `ib_client_id` on each retry, so a timed-out API handshake can retry with a fresh client id. Defaults to `100`. | -| `STRATEGY_PROFILE` | Yes | Strategy profile selector. Enabled values: `global_etf_rotation`, `russell_1000_multi_factor_defensive`, `tqqq_growth_income`, `soxl_soxx_trend_income`, `tech_communication_pullback_enhancement`, `mega_cap_leader_rotation_top50_balanced`, `nasdaq_sp500_smart_dca`. `hk_blue_chip_leader_rotation`, `hk_index_mean_reversion`, `hk_etf_regime_rotation`, and `hk_listed_global_etf_rotation` are present as eligible-but-disabled HK profiles and are not enabled. | +| `STRATEGY_PROFILE` | Yes | Strategy profile selector. Enabled values: `global_etf_rotation`, `russell_1000_multi_factor_defensive`, `tqqq_growth_income`, `soxl_soxx_trend_income`, `tech_communication_pullback_enhancement`, `mega_cap_leader_rotation_top50_balanced`, `nasdaq_sp500_smart_dca`, `hk_listed_global_etf_rotation`. `hk_blue_chip_leader_rotation`, `hk_index_mean_reversion`, and `hk_etf_regime_rotation` are eligible-but-disabled HK profiles; production Cloud Run keeps its configured profile until an explicit rollout changes it. | | `ACCOUNT_GROUP` | Yes | Account-group selector. Set explicitly for each deployment. | | `IBKR_MARKET` | No | Market scope. Defaults to `HK` when `ACCOUNT_GROUP` contains `hk`, otherwise `US`. | | `IBKR_MARKET_CALENDAR` | No | Market calendar. Defaults to `XHKG` for HK and `NYSE` for US. | @@ -453,7 +453,7 @@ IBKR 账户 | `IBKR_CONNECT_ATTEMPTS` | 否 | IBKR 连接失败前最多尝试次数。默认 `3`。 | | `IBKR_CONNECT_RETRY_DELAY_SECONDS` | 否 | IBKR 连接重试间隔,单位秒。默认 `5`。 | | `IBKR_CLIENT_ID_RETRY_OFFSET` | 否 | 每次重试时加到 `ib_client_id` 上的偏移量,用新的 client id 避开超时握手留下的卡住会话。默认 `100`。 | -| `STRATEGY_PROFILE` | 是 | 策略档位选择。当前已启用值:`global_etf_rotation`、`russell_1000_multi_factor_defensive`、`tqqq_growth_income`、`soxl_soxx_trend_income`、`tech_communication_pullback_enhancement`、`mega_cap_leader_rotation_top50_balanced`、`nasdaq_sp500_smart_dca`。`hk_blue_chip_leader_rotation`、`hk_index_mean_reversion`、`hk_etf_regime_rotation`、`hk_listed_global_etf_rotation` 是 eligible-but-disabled 港股档位,未启用 | +| `STRATEGY_PROFILE` | 是 | 策略档位选择。当前已启用值:`global_etf_rotation`、`russell_1000_multi_factor_defensive`、`tqqq_growth_income`、`soxl_soxx_trend_income`、`tech_communication_pullback_enhancement`、`mega_cap_leader_rotation_top50_balanced`、`nasdaq_sp500_smart_dca`、`hk_listed_global_etf_rotation`。`hk_blue_chip_leader_rotation`、`hk_index_mean_reversion`、`hk_etf_regime_rotation` 是 eligible-but-disabled 港股档位;生产 Cloud Run 保持原配置,除非显式 rollout | | `ACCOUNT_GROUP` | 是 | 账号组选择器,每个部署都要显式设置。 | | `IBKR_MARKET` | 否 | 市场范围。`ACCOUNT_GROUP` 包含 `hk` 时默认 `HK`,其他情况默认 `US`。 | | `IBKR_MARKET_CALENDAR` | 否 | 市场日历。港股默认 `XHKG`,美股默认 `NYSE`。 | diff --git a/docs/hk_equity_runtime.md b/docs/hk_equity_runtime.md index 7ea1d68..4d09c9b 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` 策略 profile、运行入口和 IBKR runtime adapter。 2. [`HkEquitySnapshotPipelines`](https://github.com/QuantStrategyLab/HkEquitySnapshotPipelines) 发布 snapshot-backed profile 的 `_feature_snapshot_latest.csv`、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: @@ -86,5 +86,5 @@ IBKR_MARKET_DATA_SYMBOL_SUFFIX=.HK - IBKR 港股实盘依赖账户权限、行情权限、Gateway 登录账户可见账号和交易许可;平台配置无法替代这些权限。 - 不同 IBKR 账户或区域对港股 symbol 格式可能有差异,首批上线前需要用 dry-run 和小范围 symbol 做实盘连接验证。 - `XHKG` 是否可用取决于部署环境里的 `pandas_market_calendars` 版本;如不可用,可用 `IBKR_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` 研究候选后续真正启用前,需要先用 IBKR 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 投入生产前,需要先用 IBKR HK 行情 feed 对 `02800`、`03033`、`02822`、`02840`、`03110`、`03188`、`02834`、`03175` 做 dry-run 校验,不提交真实订单。 diff --git a/notifications/telegram.py b/notifications/telegram.py index 75b80c7..a406f7e 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -139,6 +139,7 @@ "strategy_name_qqq_tech_enhancement": "科技通信回调增强", "strategy_name_mega_cap_leader_rotation_top50_balanced": "Mega Cap Top50 平衡龙头轮动", "strategy_name_nasdaq_sp500_smart_dca": "纳斯达克 / 标普智能定投", + "strategy_name_hk_listed_global_etf_rotation": "港股上市全球 ETF 轮动", "strategy_name_tqqq_growth_income": "TQQQ 增长收益", "strategy_name_soxl_soxx_trend_income": "SOXL/SOXX 半导体趋势收益", }, @@ -277,6 +278,7 @@ "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_nasdaq_sp500_smart_dca": "Nasdaq/S&P 500 Smart DCA", + "strategy_name_hk_listed_global_etf_rotation": "HK-listed Global ETF Rotation", "strategy_name_tqqq_growth_income": "TQQQ Growth Income", "strategy_name_soxl_soxx_trend_income": "SOXL/SOXX Semiconductor Trend Income", }, diff --git a/requirements.txt b/requirements.txt index 6c06539..3009974 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 numpy requests diff --git a/tests/test_notifications.py b/tests/test_notifications.py index 88c3a2c..b3f9992 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -84,6 +84,8 @@ def test_strategy_display_name_translates_new_live_profiles(): assert en_name("mega_cap_leader_rotation_top50_balanced") == "Mega Cap Leader Rotation Top50 Balanced" assert zh_name("nasdaq_sp500_smart_dca") == "纳斯达克 / 标普智能定投" assert en_name("nasdaq_sp500_smart_dca") == "Nasdaq/S&P 500 Smart DCA" + assert zh_name("hk_listed_global_etf_rotation") == "港股上市全球 ETF 轮动" + assert en_name("hk_listed_global_etf_rotation") == "HK-listed Global ETF Rotation" def test_supported_strategy_profiles_have_translated_names(): diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 691ad4c..51424f2 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -63,6 +63,7 @@ "soxl_soxx_trend_income", "tech_communication_pullback_enhancement", "tqqq_growth_income", + "hk_listed_global_etf_rotation", } ) HK_DISABLED_PROFILES = frozenset( @@ -70,7 +71,6 @@ "hk_blue_chip_leader_rotation", "hk_index_mean_reversion", "hk_etf_regime_rotation", - "hk_listed_global_etf_rotation", } ) EXPECTED_IBKR_PROFILES = EXPECTED_IBKR_ENABLED_PROFILES | HK_DISABLED_PROFILES @@ -588,7 +588,7 @@ def test_load_platform_runtime_settings_accepts_tech_communication_pullback_enha @pytest.mark.parametrize("profile", sorted(HK_DISABLED_PROFILES)) -def test_load_platform_runtime_settings_rejects_hk_profiles_until_runtime_enabled(monkeypatch, profile): +def test_load_platform_runtime_settings_rejects_disabled_hk_profiles(monkeypatch, profile): monkeypatch.setenv("RUNTIME_TARGET_JSON", runtime_target_json(profile)) monkeypatch.setenv("ACCOUNT_GROUP", "hk-live") monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_HK_GROUP_JSON) @@ -599,6 +599,25 @@ def test_load_platform_runtime_settings_rejects_hk_profiles_until_runtime_enable load_platform_runtime_settings(project_id_resolver=lambda: "project-1") +def test_load_platform_runtime_settings_accepts_runtime_enabled_hk_global_etf_rotation(monkeypatch): + monkeypatch.setenv("RUNTIME_TARGET_JSON", runtime_target_json("hk_listed_global_etf_rotation")) + monkeypatch.setenv("ACCOUNT_GROUP", "hk-live") + monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_HK_GROUP_JSON) + + settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1") + + assert settings.strategy_profile == "hk_listed_global_etf_rotation" + assert settings.strategy_display_name == "HK-listed Global ETF Rotation" + assert settings.strategy_domain == "hk_equity" + assert settings.strategy_target_mode == "weight" + assert settings.market == HK_MARKET + assert settings.market_calendar == HK_MARKET_CALENDAR + assert settings.market_currency == HK_MARKET_CURRENCY + assert settings.market_data_symbol_suffix == HK_MARKET_DATA_SYMBOL_SUFFIX + assert settings.market_exchange == HK_MARKET_EXCHANGE + assert settings.market_timezone == HK_MARKET_TIMEZONE + + @pytest.mark.parametrize( "archived_profile", ( @@ -711,7 +730,7 @@ def test_platform_profile_status_matrix_matches_current_ibkr_rollout(): "display_name": "HK-listed Global ETF Rotation", "domain": "hk_equity", "eligible": True, - "enabled": False, + "enabled": True, "platform": "ibkr", }