Skip to content

Commit 397e2a2

Browse files
authored
[codex] Require explicit strategy profile selection (#20)
* Require explicit strategy profile selection * Set explicit profile in shared chat fallback test
1 parent b4d0df6 commit 397e2a2

6 files changed

Lines changed: 75 additions & 53 deletions

README.md

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Quant system on LongPort OpenAPI and Google Cloud Run.
1111

1212
This repository uses `QuantPlatformKit` for LongPort token handling, context bootstrap, account snapshot access, market data, and order submission. Cloud Run deploys this repository directly.
13-
The LongBridge runtime can execute all seven live `us_equity` profiles from `UsEquityStrategies`; `LongBridgePlatform` keeps the LongPort runtime, token refresh, execution, and notification flow.
13+
The LongBridge runtime can execute all eight live `us_equity` profiles from `UsEquityStrategies`; `LongBridgePlatform` keeps the LongPort runtime, token refresh, execution, and notification flow.
1414

1515
Full strategy documentation now lives in [`UsEquityStrategies`](https://github.com/QuantStrategyLab/UsEquityStrategies). The sections below focus on LongBridge runtime behavior, profile enablement, deployment, and credentials.
1616
This runtime matrix is the authoritative enablement source for LongBridge. `UsEquityStrategies` carries strategy-layer logic, cadence, compatibility, and metadata.
@@ -29,15 +29,16 @@ Platform execution no longer depends on `strategy/allocation.py` or hard-coded s
2929

3030
**LongBridge profile status**
3131

32-
| Canonical profile | Display name | Eligible | Enabled | Default | Rollback | Domain | Runtime note |
33-
| --- | --- | --- | --- | --- | --- | --- | --- |
34-
| `global_etf_rotation` | Global ETF Rotation | Yes | Yes | No | No | `us_equity` | enabled weight-mode rotation line |
35-
| `russell_1000_multi_factor_defensive` | Russell 1000 Multi-Factor | Yes | Yes | No | No | `us_equity` | enabled feature-snapshot stock baseline |
36-
| `mega_cap_leader_rotation_dynamic_top20` | Mega Cap Leader Rotation Dynamic Top20 | Yes | Yes | No | No | `us_equity` | selectable monthly feature-snapshot leader rotation |
37-
| `dynamic_mega_leveraged_pullback` | Dynamic Mega Leveraged Pullback | Yes | Yes | No | No | `us_equity` | selectable 2x mega-cap pullback line |
38-
| `soxl_soxx_trend_income` | SOXL/SOXX Semiconductor Trend Income | Yes | Yes | Yes | Yes | `us_equity` | current LongBridge default |
39-
| `tqqq_growth_income` | TQQQ Growth Income | Yes | Yes | No | No | `us_equity` | current SG dry-run line |
40-
| `tech_communication_pullback_enhancement` | Tech/Communication Pullback Enhancement | Yes | Yes | No | No | `us_equity` | current HK feature-snapshot line |
32+
| Canonical profile | Display name | Eligible | Enabled | Domain | Runtime note |
33+
| --- | --- | --- | --- | --- | --- |
34+
| `global_etf_rotation` | Global ETF Rotation | Yes | Yes | `us_equity` | enabled weight-mode rotation line |
35+
| `russell_1000_multi_factor_defensive` | Russell 1000 Multi-Factor | Yes | Yes | `us_equity` | enabled feature-snapshot stock baseline |
36+
| `mega_cap_leader_rotation_aggressive` | Mega Cap Leader Rotation Aggressive | Yes | Yes | `us_equity` | selectable aggressive monthly feature-snapshot leader rotation |
37+
| `mega_cap_leader_rotation_dynamic_top20` | Mega Cap Leader Rotation Dynamic Top20 | Yes | Yes | `us_equity` | selectable monthly feature-snapshot leader rotation |
38+
| `dynamic_mega_leveraged_pullback` | Dynamic Mega Leveraged Pullback | Yes | Yes | `us_equity` | selectable 2x mega-cap pullback line |
39+
| `soxl_soxx_trend_income` | SOXL/SOXX Semiconductor Trend Income | Yes | Yes | `us_equity` | current SG deployment |
40+
| `tqqq_growth_income` | TQQQ Growth Income | Yes | Yes | `us_equity` | selectable growth line |
41+
| `tech_communication_pullback_enhancement` | Tech/Communication Pullback Enhancement | Yes | Yes | `us_equity` | current HK feature-snapshot line |
4142

4243
Check the current matrix locally:
4344

@@ -63,7 +64,7 @@ Telegram notifications include structured execution and heartbeat messages, with
6364
| `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-hk` / `longport-app-secret-sg` |
6465
| `LONGPORT_SECRET_NAME` | No | Secret Manager secret name for LongPort token (default: `longport_token_hk`) |
6566
| `ACCOUNT_PREFIX` | No | Alert/log prefix for account/environment (default: `DEFAULT`) |
66-
| `STRATEGY_PROFILE` | No | Strategy profile selector (default: `soxl_soxx_trend_income`; enabled values include `dynamic_mega_leveraged_pullback`, `global_etf_rotation`, `mega_cap_leader_rotation_dynamic_top20`, `russell_1000_multi_factor_defensive`, `soxl_soxx_trend_income`, `tech_communication_pullback_enhancement`, and `tqqq_growth_income`) |
67+
| `STRATEGY_PROFILE` | Yes | Strategy profile selector. Set explicitly per deployment; enabled values include `dynamic_mega_leveraged_pullback`, `global_etf_rotation`, `mega_cap_leader_rotation_aggressive`, `mega_cap_leader_rotation_dynamic_top20`, `russell_1000_multi_factor_defensive`, `soxl_soxx_trend_income`, `tech_communication_pullback_enhancement`, and `tqqq_growth_income` |
6768
| `ACCOUNT_REGION` | No | Account region marker for platform-style deployment (e.g. `HK`, `SG`; defaults to `ACCOUNT_PREFIX` / `DEFAULT`) |
6869
| `LONGBRIDGE_DRY_RUN_ONLY` | No | Set to `true` to keep the selected deployment in dry-run mode. |
6970
| `INCOME_THRESHOLD_USD` | No | Optional override for the `tqqq_growth_income` income-layer threshold. Leave unset to use the strategy package default. |
@@ -89,7 +90,7 @@ Deploy the same codebase as multiple Cloud Run services (e.g. `HK` and `SG`) by
8990

9091
- `LONGPORT_SECRET_NAME`: point to different secrets (e.g. `longport_token_hk`, `longport_token_sg`)
9192
- `ACCOUNT_PREFIX`: e.g. `HK`, `SG` (all Telegram/log alerts will include `[ACCOUNT_PREFIX]`)
92-
- `STRATEGY_PROFILE`: set per service; current live examples are `tech_communication_pullback_enhancement` on HK and `tqqq_growth_income` on SG
93+
- `STRATEGY_PROFILE`: set per service; current live examples are `tech_communication_pullback_enhancement` on HK and `soxl_soxx_trend_income` on SG
9394
- Current strategy domain is `us_equity`. `STRATEGY_PROFILE` now goes through a platform capability matrix plus a rollout allowlist derived from `runtime_enabled` strategy metadata: `eligible` means the platform can run it in theory, `enabled` means the current rollout really allows it.
9495
- `ACCOUNT_REGION`: explicitly mark the deployed account region (`HK` / `SG`); if unset, the app falls back to `ACCOUNT_PREFIX` or `DEFAULT`
9596
- `LONGBRIDGE_DRY_RUN_ONLY`: set per service when that deployment should stay dry-run
@@ -116,7 +117,7 @@ Recommended setup:
116117
- **GitHub Environment: `longbridge-sg`**
117118
- Variables: `CLOUD_RUN_REGION`, `CLOUD_RUN_SERVICE`, `ACCOUNT_PREFIX`, `ACCOUNT_REGION`, `STRATEGY_PROFILE`, `LONGPORT_SECRET_NAME`, `LONGPORT_APP_KEY_SECRET_NAME`, `LONGPORT_APP_SECRET_SECRET_NAME`
118119
- Optional variables: `LONGBRIDGE_FEATURE_SNAPSHOT_PATH`, `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH`, `LONGBRIDGE_STRATEGY_CONFIG_PATH`, `LONGBRIDGE_DRY_RUN_ONLY`, `INCOME_THRESHOLD_USD`, `QQQI_INCOME_RATIO`
119-
- Current live example: `STRATEGY_PROFILE=tqqq_growth_income`
120+
- Current live example: `STRATEGY_PROFILE=soxl_soxx_trend_income`
120121
- Recommended secret-name values: `longport-app-key-sg`, `longport-app-secret-sg`
121122

122123
On every push to `main`, the workflow updates both Cloud Run services with the shared and per-environment values above, and removes `TELEGRAM_CHAT_ID` from each Cloud Run service.
@@ -157,7 +158,7 @@ IAM: the Cloud Run service account needs **Secret Manager Admin** (or Secret Acc
157158
基于 LongPort OpenAPI 和 Google Cloud Run 的量化交易系统。
158159

159160
这个仓库通过 `QuantPlatformKit` 复用 LongPort token 处理、上下文初始化、账户快照、行情读取和下单逻辑。Cloud Run 直接部署这个仓库。
160-
`LongBridgePlatform` 现在可直接执行 `UsEquityStrategies` 里的全部 7 条 live `us_equity` 策略:`dynamic_mega_leveraged_pullback``global_etf_rotation``mega_cap_leader_rotation_dynamic_top20``russell_1000_multi_factor_defensive``soxl_soxx_trend_income``tqqq_growth_income``tech_communication_pullback_enhancement`;仓库本身继续保留 LongPort 运行时、token 刷新、执行和通知流程。
161+
`LongBridgePlatform` 现在可直接执行 `UsEquityStrategies` 里的全部 8 条 live `us_equity` 策略:`dynamic_mega_leveraged_pullback``global_etf_rotation``mega_cap_leader_rotation_aggressive``mega_cap_leader_rotation_dynamic_top20``russell_1000_multi_factor_defensive``soxl_soxx_trend_income``tqqq_growth_income``tech_communication_pullback_enhancement`;仓库本身继续保留 LongPort 运行时、token 刷新、执行和通知流程。
161162

162163
完整策略说明现在放在 [`UsEquityStrategies`](https://github.com/QuantStrategyLab/UsEquityStrategies)。下面这些章节只保留 LongBridge 运行时、profile 启用状态、部署和凭据说明。
163164

@@ -174,15 +175,16 @@ IAM: the Cloud Run service account needs **Secret Manager Admin** (or Secret Acc
174175

175176
**LongBridge profile status**
176177

177-
| Canonical profile | Display name | Eligible | Enabled | Default | Rollback | Domain | Runtime note |
178-
| --- | --- | --- | --- | --- | --- | --- | --- |
179-
| `global_etf_rotation` | Global ETF Rotation | Yes | Yes | No | No | `us_equity` | 已启用的 weight-mode 轮动线 |
180-
| `russell_1000_multi_factor_defensive` | Russell 1000 Multi-Factor | Yes | Yes | No | No | `us_equity` | 已启用的 feature-snapshot 个股基线 |
181-
| `mega_cap_leader_rotation_dynamic_top20` | Mega Cap Leader Rotation Dynamic Top20 | Yes | Yes | No | No | `us_equity` | 可选的月度 feature-snapshot 龙头轮动线 |
182-
| `dynamic_mega_leveraged_pullback` | Dynamic Mega Leveraged Pullback | Yes | Yes | No | No | `us_equity` | 可选的 2x 龙头回调线 |
183-
| `soxl_soxx_trend_income` | SOXL/SOXX 半导体趋势收益 | Yes | Yes | Yes | Yes | `us_equity` | 当前 LongBridge 默认回退线 |
184-
| `tqqq_growth_income` | TQQQ 增长收益 | Yes | Yes | No | No | `us_equity` | 当前 SG dry-run 线路 |
185-
| `tech_communication_pullback_enhancement` | 科技通信回调增强 | Yes | Yes | No | No | `us_equity` | 当前 HK feature-snapshot 线路 |
178+
| Canonical profile | Display name | Eligible | Enabled | Domain | Runtime note |
179+
| --- | --- | --- | --- | --- | --- |
180+
| `global_etf_rotation` | Global ETF Rotation | Yes | Yes | `us_equity` | 已启用的 weight-mode 轮动线 |
181+
| `russell_1000_multi_factor_defensive` | Russell 1000 Multi-Factor | Yes | Yes | `us_equity` | 已启用的 feature-snapshot 个股基线 |
182+
| `mega_cap_leader_rotation_aggressive` | Mega Cap Leader Rotation Aggressive | Yes | Yes | `us_equity` | 可选的激进月度 feature-snapshot 龙头轮动线 |
183+
| `mega_cap_leader_rotation_dynamic_top20` | Mega Cap Leader Rotation Dynamic Top20 | Yes | Yes | `us_equity` | 可选的月度 feature-snapshot 龙头轮动线 |
184+
| `dynamic_mega_leveraged_pullback` | Dynamic Mega Leveraged Pullback | Yes | Yes | `us_equity` | 可选的 2x 龙头回调线 |
185+
| `soxl_soxx_trend_income` | SOXL/SOXX 半导体趋势收益 | Yes | Yes | `us_equity` | 当前 SG 部署线路 |
186+
| `tqqq_growth_income` | TQQQ 增长收益 | Yes | Yes | `us_equity` | 可选增长线路 |
187+
| `tech_communication_pullback_enhancement` | 科技通信回调增强 | Yes | Yes | `us_equity` | 当前 HK feature-snapshot 线路 |
186188

187189
本地可直接查看当前矩阵:
188190

@@ -208,7 +210,7 @@ Telegram 通知包含结构化的调仓和心跳消息,支持中英文切换
208210
| `LONGPORT_APP_SECRET` || LongPort OpenAPI 应用密钥(用于刷新 Token);建议从当前部署对应区域的 Secret Manager 密钥注入,例如 `longport-app-secret-hk` / `longport-app-secret-sg` |
209211
| `LONGPORT_SECRET_NAME` || Secret Manager 中的密钥名称(默认: `longport_token_hk`|
210212
| `ACCOUNT_PREFIX` || 通知/日志前缀,区分账户环境(默认: `DEFAULT`|
211-
| `STRATEGY_PROFILE` | | 策略档位选择(默认: `soxl_soxx_trend_income`;已启用值还包括 `dynamic_mega_leveraged_pullback``global_etf_rotation``mega_cap_leader_rotation_dynamic_top20``russell_1000_multi_factor_defensive``soxl_soxx_trend_income``tech_communication_pullback_enhancement``tqqq_growth_income` |
213+
| `STRATEGY_PROFILE` | | 策略档位选择。每个部署都要显式设置;已启用值包括 `dynamic_mega_leveraged_pullback``global_etf_rotation``mega_cap_leader_rotation_aggressive``mega_cap_leader_rotation_dynamic_top20``russell_1000_multi_factor_defensive``soxl_soxx_trend_income``tech_communication_pullback_enhancement``tqqq_growth_income` |
212214
| `ACCOUNT_REGION` || 平台化部署时的账户区域标记(如 `HK``SG`;默认按 `ACCOUNT_PREFIX` / `DEFAULT` 推断) |
213215
| `LONGBRIDGE_DRY_RUN_ONLY` || 设为 `true` 时,该部署保持 dry-run。 |
214216
| `INCOME_THRESHOLD_USD` || 可选的 `tqqq_growth_income` 收入层启动阈值覆盖。不填时使用策略包默认值。 |
@@ -234,7 +236,7 @@ Secret Manager 中需存在 `LONGPORT_SECRET_NAME` 指定的密钥(默认: `lo
234236

235237
- `LONGPORT_SECRET_NAME`: 指向不同密钥(如 `longport_token_hk``longport_token_sg`
236238
- `ACCOUNT_PREFIX`: 如 `HK``SG`(所有通知/日志将包含 `[ACCOUNT_PREFIX]`
237-
- `STRATEGY_PROFILE`: 按服务分别设置;当前线上 HK 用 `tech_communication_pullback_enhancement`,SG 用 `tqqq_growth_income`
239+
- `STRATEGY_PROFILE`: 按服务分别设置;当前线上 HK 用 `tech_communication_pullback_enhancement`,SG 用 `soxl_soxx_trend_income`
238240
- 当前策略域是 `us_equity``STRATEGY_PROFILE` 现在会先经过平台能力矩阵,再经过从 `runtime_enabled` 策略元数据派生的 rollout allowlist:`eligible` 表示平台理论可跑,`enabled` 表示当前 rollout 真正放开。
239241
- `ACCOUNT_REGION`: 显式标记部署账户区域(`HK` / `SG`);未设置时会回退到 `ACCOUNT_PREFIX``DEFAULT`
240242
- `LONGBRIDGE_DRY_RUN_ONLY`: 需要保持模拟运行时按服务单独设置
@@ -261,7 +263,7 @@ Secret Manager 中需存在 `LONGPORT_SECRET_NAME` 指定的密钥(默认: `lo
261263
- **GitHub Environment: `longbridge-sg`**
262264
- Variables: `CLOUD_RUN_REGION``CLOUD_RUN_SERVICE``ACCOUNT_PREFIX``ACCOUNT_REGION``STRATEGY_PROFILE``LONGPORT_SECRET_NAME``LONGPORT_APP_KEY_SECRET_NAME``LONGPORT_APP_SECRET_SECRET_NAME`
263265
- 可选 Variables: `LONGBRIDGE_FEATURE_SNAPSHOT_PATH``LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH``LONGBRIDGE_STRATEGY_CONFIG_PATH``LONGBRIDGE_DRY_RUN_ONLY``INCOME_THRESHOLD_USD``QQQI_INCOME_RATIO`
264-
- 当前线上示例:`STRATEGY_PROFILE=tqqq_growth_income`
266+
- 当前线上示例:`STRATEGY_PROFILE=soxl_soxx_trend_income`
265267
- 建议的 secret-name 值:`longport-app-key-sg``longport-app-secret-sg`
266268

267269
每次 push 到 `main` 时,这个 workflow 会分别更新两个 Cloud Run 服务,把共享和各自隔离的变量同步进去,并删除旧的 `TELEGRAM_CHAT_ID`

runtime_config_support.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@
77

88
from quant_platform_kit.common.strategies import derive_strategy_artifact_paths
99
from strategy_registry import (
10-
DEFAULT_STRATEGY_PROFILE as PLATFORM_DEFAULT_STRATEGY_PROFILE,
1110
LONGBRIDGE_PLATFORM,
1211
resolve_strategy_definition,
1312
resolve_strategy_metadata,
1413
)
1514
from us_equity_strategies import get_strategy_catalog
1615

1716
DEFAULT_ACCOUNT_REGION = "DEFAULT"
18-
DEFAULT_STRATEGY_PROFILE = PLATFORM_DEFAULT_STRATEGY_PROFILE
1917
DEFAULT_LONGPORT_SECRET_NAME = "longport_token_hk"
2018

2119

scripts/print_strategy_profile_status.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ def _print_table(rows: list[dict[str, object]]) -> None:
4343
"requires_strategy_config_path",
4444
"eligible",
4545
"enabled",
46-
"is_default",
47-
"is_rollback",
4846
"domain",
4947
)
5048
widths = {

strategy_registry.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323

2424
LONGBRIDGE_PLATFORM = "longbridge"
2525

26-
DEFAULT_STRATEGY_PROFILE = "soxl_soxx_trend_income"
27-
ROLLBACK_STRATEGY_PROFILE = DEFAULT_STRATEGY_PROFILE
28-
2926
LONGBRIDGE_ROLLOUT_ALLOWLIST = get_runtime_enabled_profiles()
3027

3128
PLATFORM_SUPPORTED_DOMAINS: dict[str, frozenset[str]] = {
@@ -71,11 +68,17 @@
7168
platform_id=LONGBRIDGE_PLATFORM,
7269
supported_domains=PLATFORM_SUPPORTED_DOMAINS[LONGBRIDGE_PLATFORM],
7370
enabled_profiles=LONGBRIDGE_ENABLED_PROFILES,
74-
default_profile=DEFAULT_STRATEGY_PROFILE,
75-
rollback_profile=ROLLBACK_STRATEGY_PROFILE,
71+
default_profile="",
72+
rollback_profile="",
73+
require_explicit_profile=True,
7674
)
7775

7876
SUPPORTED_STRATEGY_PROFILES = LONGBRIDGE_ENABLED_PROFILES
77+
_SELECTION_ROLE_FIELDS = frozenset({"is_default", "is_rollback"})
78+
79+
80+
def _without_selection_role_fields(row: dict[str, object]) -> dict[str, object]:
81+
return {key: value for key, value in row.items() if key not in _SELECTION_ROLE_FIELDS}
7982

8083

8184
def get_eligible_profiles_for_platform(platform_id: str) -> frozenset[str]:
@@ -89,15 +92,21 @@ def get_supported_profiles_for_platform(platform_id: str) -> frozenset[str]:
8992

9093

9194
def get_platform_profile_matrix() -> list[dict[str, object]]:
92-
return build_platform_profile_matrix(STRATEGY_CATALOG, policy=PLATFORM_POLICY)
95+
return [
96+
_without_selection_role_fields(row)
97+
for row in build_platform_profile_matrix(STRATEGY_CATALOG, policy=PLATFORM_POLICY)
98+
]
9399

94100

95101
def get_platform_profile_status_matrix() -> list[dict[str, object]]:
96-
return build_platform_profile_status_matrix(
97-
STRATEGY_CATALOG,
98-
policy=PLATFORM_POLICY,
99-
eligible_profiles=ELIGIBLE_STRATEGY_PROFILES,
100-
)
102+
return [
103+
_without_selection_role_fields(row)
104+
for row in build_platform_profile_status_matrix(
105+
STRATEGY_CATALOG,
106+
policy=PLATFORM_POLICY,
107+
eligible_profiles=ELIGIBLE_STRATEGY_PROFILES,
108+
)
109+
]
101110

102111

103112
def resolve_strategy_definition(

0 commit comments

Comments
 (0)