From 4f51c4669c2e40422b610ca19d244fb41c1ebf7a Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:23:26 +0800 Subject: [PATCH] Support generated US equity strategy onboarding --- README.md | 4 + docs/platform_runtime_inventory.md | 6 +- docs/platform_runtime_inventory.zh-CN.md | 6 +- docs/platform_strategy_matrix.md | 21 ++- docs/platform_strategy_matrix.zh-CN.md | 23 +-- .../us_equity_cross_platform_strategy_spec.md | 5 + ...uity_cross_platform_strategy_spec.zh-CN.md | 4 + docs/us_equity_live_switch_runbook.md | 58 +++++-- docs/us_equity_live_switch_runbook.zh-CN.md | 58 +++++-- docs/us_equity_strategy_onboarding.md | 149 ++++++++++++++++++ docs/us_equity_strategy_onboarding.zh-CN.md | 149 ++++++++++++++++++ pyproject.toml | 2 +- src/quant_platform_kit/common/strategies.py | 3 + tests/test_strategies.py | 10 ++ 14 files changed, 451 insertions(+), 47 deletions(-) create mode 100644 docs/us_equity_strategy_onboarding.md create mode 100644 docs/us_equity_strategy_onboarding.zh-CN.md diff --git a/README.md b/README.md index f35e8d0..73612c4 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Migration details and follow-up guidance live in [`docs/strategy_contract_migrat For the stricter end-state rules for new US equity strategies, see: +- [`docs/us_equity_strategy_onboarding.md`](./docs/us_equity_strategy_onboarding.md) +- [`docs/us_equity_strategy_onboarding.zh-CN.md`](./docs/us_equity_strategy_onboarding.zh-CN.md) - [`docs/us_equity_cross_platform_strategy_spec.md`](./docs/us_equity_cross_platform_strategy_spec.md) - [`docs/us_equity_cross_platform_strategy_spec.zh-CN.md`](./docs/us_equity_cross_platform_strategy_spec.zh-CN.md) - [`docs/us_equity_execution_translation_spec.md`](./docs/us_equity_execution_translation_spec.md) @@ -148,6 +150,8 @@ Cloud Run and self-hosted runner deployments should continue to deploy the strat 以后新增美股策略要遵守的更严格跨平台规范见: +- [`docs/us_equity_strategy_onboarding.md`](./docs/us_equity_strategy_onboarding.md) +- [`docs/us_equity_strategy_onboarding.zh-CN.md`](./docs/us_equity_strategy_onboarding.zh-CN.md) - [`docs/us_equity_cross_platform_strategy_spec.md`](./docs/us_equity_cross_platform_strategy_spec.md) - [`docs/us_equity_cross_platform_strategy_spec.zh-CN.md`](./docs/us_equity_cross_platform_strategy_spec.zh-CN.md) - [`docs/us_equity_live_switch_runbook.md`](./docs/us_equity_live_switch_runbook.md) diff --git a/docs/platform_runtime_inventory.md b/docs/platform_runtime_inventory.md index 9064fee..539cdab 100644 --- a/docs/platform_runtime_inventory.md +++ b/docs/platform_runtime_inventory.md @@ -19,7 +19,8 @@ For the current platform / strategy-domain / live-profile matrix, see [`platform - strategy selectors such as `STRATEGY_PROFILE` - secret selector variables such as `*_SECRET_NAME` - Secret Manager is the runtime source of truth for sensitive values that Cloud Run services actually consume. -- GitHub Secrets are still valid for CI/CD bootstrap values such as `GCP_SA_KEY`, and can remain as temporary fallbacks where runtime migration is not fully finished. +- The US equity Cloud Run env-sync workflows use GitHub OIDC + Workload Identity Federation. `GCP_SA_KEY` is not required for those workflows. +- GitHub Secrets can remain as temporary runtime fallbacks where migration is not fully finished. ## Current inventory @@ -160,8 +161,7 @@ For the current platform / strategy-domain / live-profile matrix, see [`platform ### Keep in GitHub Secrets -- `GCP_SA_KEY` -- temporary bootstrap fallbacks if a runtime migration is still in progress +- temporary fallback values if a runtime migration is still in progress ### Keep in Secret Manager diff --git a/docs/platform_runtime_inventory.zh-CN.md b/docs/platform_runtime_inventory.zh-CN.md index 0a991ea..4be3a15 100644 --- a/docs/platform_runtime_inventory.zh-CN.md +++ b/docs/platform_runtime_inventory.zh-CN.md @@ -19,9 +19,8 @@ _校验快照日期:2026-04-14_ - `STRATEGY_PROFILE` 这类策略选择器 - `*_SECRET_NAME` 这类 secret 选择器 - Secret Manager 负责 Cloud Run 运行时真正要吃的敏感值。 -- GitHub Secrets 依然保留给 CI/CD 启动凭据用,比如: - - `GCP_SA_KEY` - - 以及运行时迁移还没完成时的临时 fallback +- 美股 Cloud Run env-sync workflow 使用 GitHub OIDC + Workload Identity Federation,不再需要 `GCP_SA_KEY`。 +- GitHub Secrets 可以继续保留给运行时迁移还没完成时的临时 fallback。 ## 当前清单 @@ -162,7 +161,6 @@ _校验快照日期:2026-04-14_ ### 继续放在 GitHub Secrets -- `GCP_SA_KEY` - 迁移还没结束时的临时 fallback ### 应该放在 Secret Manager diff --git a/docs/platform_strategy_matrix.md b/docs/platform_strategy_matrix.md index 9508fea..e6cd19b 100644 --- a/docs/platform_strategy_matrix.md +++ b/docs/platform_strategy_matrix.md @@ -1,6 +1,6 @@ # Platform Strategy Matrix -_Verified snapshot: 2026-04-14_ +_Verified snapshot: 2026-04-15_ This page is the short answer to one question: @@ -16,7 +16,8 @@ For repository responsibility boundaries, see [`platform_repo_boundaries.md`](./ - `us_equity` - `crypto` - Runtime repositories already expose `STRATEGY_PROFILE`, but this is **not** a full multi-strategy marketplace yet. -- Today, each US equity platform can switch among its enabled live `us_equity` profiles through its rollout allowlist. +- Today, each US equity platform can switch among the live `runtime_enabled` `us_equity` profiles published by `UsEquityStrategies`. +- Platform runtime adapters are generated from strategy input/target-mode declarations plus platform capabilities, so new in-contract profiles should not need per-platform allowlist edits. - The shared contract is in `QuantPlatformKit`; real `us_equity` strategy implementations now live in `UsEquityStrategies`, while platform repositories own runtime adapters and broker execution. ## Current platform matrix @@ -40,12 +41,16 @@ Platforms currently in this domain: Important limitation: -- This does **not** mean every `us_equity` strategy can already run on every `us_equity` platform. -- It only means these platforms now share the same top-level domain and compatibility model. -- Each concrete strategy still needs its own platform-compatibility declaration and runtime fit. +- This does **not** mean any arbitrary future `us_equity` strategy can run by name alone. +- It means strategies that stay inside the shared input/target-mode contract can be admitted through `UsEquityStrategies` metadata and generated runtime adapters. +- If a strategy needs a new input type or broker capability, the shared contract and platform capability matrix must be extended first. -Current live profiles in `us_equity`: +Currently enabled live profiles in `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` @@ -77,8 +82,8 @@ These pieces are already real and shared: These are **not** true yet: -- selecting any future `us_equity` strategy on any `us_equity` platform by one env change -- every strategy having complete adapter coverage on every broker +- selecting a future `us_equity` strategy before it has a catalog entry, manifest, base runtime adapter spec, and supported input contract +- running strategies that require new platform capabilities before those capabilities are added to the shared matrix - independent strategy repositories outside `UsEquityStrategies` that are already used in production ## Recommended interpretation diff --git a/docs/platform_strategy_matrix.zh-CN.md b/docs/platform_strategy_matrix.zh-CN.md index 9102527..ece9584 100644 --- a/docs/platform_strategy_matrix.zh-CN.md +++ b/docs/platform_strategy_matrix.zh-CN.md @@ -1,6 +1,6 @@ # 平台策略矩阵 -_核对时间:2026-04-14_ +_核对时间:2026-04-15_ 这页只回答一个问题: @@ -16,7 +16,8 @@ _核对时间:2026-04-14_ - `us_equity` - `crypto` - 各个平台仓库现在都已经保留了 `STRATEGY_PROFILE` 入口,但这**还不是**真正的多策略平台。 -- 现在每个美股平台仓库都可以通过 rollout allowlist 在已启用的 live `us_equity` profile 之间切换。 +- 现在每个美股平台仓库都可以在 `UsEquityStrategies` 发布的 live `runtime_enabled` `us_equity` profile 之间切换。 +- 平台 runtime adapter 会根据策略输入、target mode 和平台 capability 自动生成;规范内的新 profile 不应该再需要三个平台分别手写 allowlist。 - 共享契约在 `QuantPlatformKit`;真实的 `us_equity` 策略实现现在放在 `UsEquityStrategies`,平台仓库负责运行时适配和券商执行。 ## 当前平台矩阵 @@ -40,14 +41,18 @@ _核对时间:2026-04-14_ 但要注意: -- 这**不等于**任何一个 `us_equity` 策略现在都能直接在这三个平台上切换运行。 -- 它只表示这些平台已经共享同一层“策略大类 + 兼容性”抽象。 -- 真正的具体策略,仍然要单独声明自己支持哪些平台、是否真的适合那个运行时。 +- 这**不等于**任意未来 `us_equity` 策略只靠名字就能跑。 +- 它表示只要策略遵守共享输入和 target-mode 契约,就可以通过 `UsEquityStrategies` 元数据和生成式 runtime adapter 接入。 +- 如果策略需要新的输入类型或券商能力,要先扩共享契约和平台 capability matrix。 -当前 `us_equity` 域里线上在跑的 profile 有: +当前 `us_equity` 域里已经启用的 live profile 有: -- `soxl_soxx_trend_income` +- `dynamic_mega_leveraged_pullback` +- `global_etf_rotation` +- `mega_cap_leader_rotation_dynamic_top20` +- `russell_1000_multi_factor_defensive` - `tqqq_growth_income` +- `soxl_soxx_trend_income` - `tech_communication_pullback_enhancement` ### `crypto` @@ -77,8 +82,8 @@ _核对时间:2026-04-14_ 下面这些现在都**还不是真的**: -- 只改一个 env 就能让任意未来 `us_equity` 策略在任意 `us_equity` 平台上跑起来 -- 每条策略都完整覆盖每个券商适配器 +- 未来策略没有 catalog、manifest、基础 runtime adapter spec 和标准输入契约时,不能只改一个 env 就上线 +- 需要新平台能力的策略,在 capability matrix 扩展前不能直接跑 - `UsEquityStrategies` 之外已经投入生产使用的独立策略仓库 ## 当前推荐理解方式 diff --git a/docs/us_equity_cross_platform_strategy_spec.md b/docs/us_equity_cross_platform_strategy_spec.md index a678796..0085c93 100644 --- a/docs/us_equity_cross_platform_strategy_spec.md +++ b/docs/us_equity_cross_platform_strategy_spec.md @@ -212,6 +212,11 @@ Current live profiles can migrate incrementally, but the end state should be: `feature_snapshot` artifact delivery - `tech_communication_pullback_enhancement`: portable through standardized `feature_snapshot` artifact delivery +- `mega_cap_leader_rotation_dynamic_top20`: portable through standardized + `feature_snapshot` artifact delivery +- `dynamic_mega_leveraged_pullback`: portable through standardized + `feature_snapshot` artifact delivery plus canonical market, benchmark, and + portfolio inputs New profiles should target the end state immediately instead of adding more one-off runtime contracts. diff --git a/docs/us_equity_cross_platform_strategy_spec.zh-CN.md b/docs/us_equity_cross_platform_strategy_spec.zh-CN.md index ae558c3..fdf617d 100644 --- a/docs/us_equity_cross_platform_strategy_spec.zh-CN.md +++ b/docs/us_equity_cross_platform_strategy_spec.zh-CN.md @@ -212,5 +212,9 @@ allowlist 只影响 `enabled`,不要再手写一堆“这个策略天生只能 - 通过标准化 `feature_snapshot` artifact contract 实现跨平台 - `tech_communication_pullback_enhancement` - 通过标准化 `feature_snapshot` artifact contract 实现跨平台 +- `mega_cap_leader_rotation_dynamic_top20` + - 通过标准化 `feature_snapshot` artifact contract 实现跨平台 +- `dynamic_mega_leveraged_pullback` + - 通过标准化 `feature_snapshot` artifact contract,加标准 market、benchmark 和 portfolio 输入实现跨平台 以后新策略应该直接朝这个目标写,不要再新增一堆一次性的运行时契约。 diff --git a/docs/us_equity_live_switch_runbook.md b/docs/us_equity_live_switch_runbook.md index 7f2902d..f9e57ab 100644 --- a/docs/us_equity_live_switch_runbook.md +++ b/docs/us_equity_live_switch_runbook.md @@ -10,10 +10,12 @@ Do **not** use it to justify switching a profile that is not already supported b Current live US equity profiles: +- `dynamic_mega_leveraged_pullback` - `global_etf_rotation` -- `tqqq_growth_income` -- `soxl_soxx_trend_income` +- `mega_cap_leader_rotation_dynamic_top20` - `russell_1000_multi_factor_defensive` +- `soxl_soxx_trend_income` +- `tqqq_growth_income` - `tech_communication_pullback_enhancement` Note: older deployments may still accept `qqq_tech_enhancement` as a legacy alias for `tech_communication_pullback_enhancement`, but runbooks should use the canonical profile name. @@ -24,7 +26,7 @@ Current runtime platforms: - `schwab` - `longbridge` -For the current five-profile scope, all three platforms now report the full matrix as `eligible=true` and `enabled=true`. That means live switching is now an operational change, not a strategy-contract migration. +For the current seven-profile scope, all three platforms now report the full matrix as `eligible=true` and `enabled=true`. That means live switching is now an operational change, not a strategy-contract migration. ## Operational profile groups @@ -35,6 +37,8 @@ Treat the live profiles as two operational groups: - `tqqq_growth_income` - `soxl_soxx_trend_income` - **Snapshot-backed profiles** + - `dynamic_mega_leveraged_pullback` + - `mega_cap_leader_rotation_dynamic_top20` - `russell_1000_multi_factor_defensive` - `tech_communication_pullback_enhancement` @@ -42,6 +46,7 @@ The platform scripts now expose this view directly: - `input_mode` - `requires_snapshot_artifacts` +- `requires_snapshot_manifest_path` - `requires_strategy_config_path` So the operator does not need to remember the distinction from profile names alone. @@ -107,15 +112,19 @@ If any of those checks fail, stop. That is a code or rollout problem, not a live | Profile | Extra runtime inputs beyond `STRATEGY_PROFILE` | | --- | --- | +| `dynamic_mega_leveraged_pullback` | feature snapshot path + snapshot manifest path | | `global_etf_rotation` | none | -| `tqqq_growth_income` | none | +| `mega_cap_leader_rotation_dynamic_top20` | feature snapshot path + snapshot manifest path | +| `russell_1000_multi_factor_defensive` | feature snapshot path | | `soxl_soxx_trend_income` | none | -| `russell_1000_multi_factor_defensive` | feature snapshot path + snapshot manifest path | +| `tqqq_growth_income` | none | | `tech_communication_pullback_enhancement` | feature snapshot path + snapshot manifest path + strategy config path | Notes: - `tech_communication_pullback_enhancement` on IBKR may also keep a reconciliation output path when the deployment wants that artifact. +- `dynamic_mega_leveraged_pullback` also uses market history, benchmark history, and portfolio snapshot, but the platform runtime supplies those from broker/runtime data, not extra artifact env. +- `russell_1000_multi_factor_defensive` currently requires the snapshot path but not a manifest path. - When switching away from a feature-snapshot profile, remove stale snapshot/config envs from the service instead of leaving them behind. ## Step 3: update GitHub-managed runtime variables @@ -140,8 +149,8 @@ Optional: Feature-snapshot profiles additionally need: - `IBKR_FEATURE_SNAPSHOT_PATH` -- `IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH` -- `IBKR_STRATEGY_CONFIG_PATH` for `tech_communication_pullback_enhancement` +- `IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH` when the selected profile requires a manifest +- `IBKR_STRATEGY_CONFIG_PATH` when the selected profile requires an external runtime config Remove when not needed: @@ -163,8 +172,8 @@ Optional: Feature-snapshot profiles additionally need: - `SCHWAB_FEATURE_SNAPSHOT_PATH` -- `SCHWAB_FEATURE_SNAPSHOT_MANIFEST_PATH` -- `SCHWAB_STRATEGY_CONFIG_PATH` when the chosen profile uses an external config file +- `SCHWAB_FEATURE_SNAPSHOT_MANIFEST_PATH` when the selected profile requires a manifest +- `SCHWAB_STRATEGY_CONFIG_PATH` when the selected profile requires an external runtime config Remove when not needed: @@ -190,8 +199,8 @@ Optional: Feature-snapshot profiles additionally need: - `LONGBRIDGE_FEATURE_SNAPSHOT_PATH` -- `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` -- `LONGBRIDGE_STRATEGY_CONFIG_PATH` +- `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` when the selected profile requires a manifest +- `LONGBRIDGE_STRATEGY_CONFIG_PATH` when the selected profile requires an external runtime config Remove when not needed: @@ -354,6 +363,33 @@ Why: - non-snapshot profiles do not need the feature-snapshot artifact chain - SG often carries dry-run as an operational choice, not as a profile requirement +### Example E: switch LongBridge SG dry-run to `dynamic_mega_leveraged_pullback` + +Keep: + +- `ACCOUNT_PREFIX=SG` +- `ACCOUNT_REGION=SG` +- `LONGPORT_SECRET_NAME` +- `LONGPORT_APP_KEY_SECRET_NAME` +- `LONGPORT_APP_SECRET_SECRET_NAME` + +Set: + +- `STRATEGY_PROFILE=dynamic_mega_leveraged_pullback` +- `LONGBRIDGE_FEATURE_SNAPSHOT_PATH` +- `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` +- `LONGBRIDGE_DRY_RUN_ONLY=true` + +Remove if present: + +- `LONGBRIDGE_STRATEGY_CONFIG_PATH` + +Why: + +- the strategy uses a feature snapshot for the dynamic mega-cap pool +- the daily market, benchmark, and portfolio inputs are supplied by the platform runtime +- dry-run is a deployment choice, not part of the strategy contract + ## Rollback rules Rollback is simple if you keep it operational: diff --git a/docs/us_equity_live_switch_runbook.zh-CN.md b/docs/us_equity_live_switch_runbook.zh-CN.md index 1b006ea..8fb5451 100644 --- a/docs/us_equity_live_switch_runbook.zh-CN.md +++ b/docs/us_equity_live_switch_runbook.zh-CN.md @@ -13,10 +13,12 @@ 当前美股 live profile: +- `dynamic_mega_leveraged_pullback` - `global_etf_rotation` -- `tqqq_growth_income` -- `soxl_soxx_trend_income` +- `mega_cap_leader_rotation_dynamic_top20` - `russell_1000_multi_factor_defensive` +- `soxl_soxx_trend_income` +- `tqqq_growth_income` - `tech_communication_pullback_enhancement` 说明:旧部署里 `qqq_tech_enhancement` 仍可能作为 `tech_communication_pullback_enhancement` 的 legacy alias 被接受,但运行手册统一使用 canonical profile 名。 @@ -27,7 +29,7 @@ - `schwab` - `longbridge` -对当前这 5 条策略来说,三个平台现在都已经是 `eligible=true` 且 `enabled=true`。也就是说,接下来换线上策略主要是运维切换,不再是契约迁移。 +对当前这 7 条策略来说,三个平台现在都已经是 `eligible=true` 且 `enabled=true`。也就是说,接下来换线上策略主要是运维切换,不再是契约迁移。 ## 运维分组 @@ -38,6 +40,8 @@ - `tqqq_growth_income` - `soxl_soxx_trend_income` - **snapshot 驱动策略** + - `dynamic_mega_leveraged_pullback` + - `mega_cap_leader_rotation_dynamic_top20` - `russell_1000_multi_factor_defensive` - `tech_communication_pullback_enhancement` @@ -45,6 +49,7 @@ - `input_mode` - `requires_snapshot_artifacts` +- `requires_snapshot_manifest_path` - `requires_strategy_config_path` 这样切换时不用再靠记忆判断“这条是不是 snapshot 策略”。 @@ -110,15 +115,19 @@ PYTHONPATH=/Users/lisiyi/Projects/QuantPlatformKit/src:/Users/lisiyi/Projects/Us | 策略 | 除了 `STRATEGY_PROFILE` 之外还需要的输入 | | --- | --- | +| `dynamic_mega_leveraged_pullback` | feature snapshot 路径 + manifest 路径 | | `global_etf_rotation` | 无 | -| `tqqq_growth_income` | 无 | +| `mega_cap_leader_rotation_dynamic_top20` | feature snapshot 路径 + manifest 路径 | +| `russell_1000_multi_factor_defensive` | feature snapshot 路径 | | `soxl_soxx_trend_income` | 无 | -| `russell_1000_multi_factor_defensive` | feature snapshot 路径 + manifest 路径 | +| `tqqq_growth_income` | 无 | | `tech_communication_pullback_enhancement` | feature snapshot 路径 + manifest 路径 + strategy config 路径 | 说明: - `tech_communication_pullback_enhancement` 在 IBKR 上如果还要留对账产物,可以继续配 reconciliation output path。 +- `dynamic_mega_leveraged_pullback` 还会用到 market history、benchmark history 和 portfolio snapshot,但这些由平台运行时从券商/行情侧供应,不是额外 artifact env。 +- `russell_1000_multi_factor_defensive` 目前只强制 snapshot 路径,不强制 manifest 路径。 - 如果从 feature-snapshot 策略切回普通策略,要把旧的 snapshot/config env 一起删掉,不要留脏状态。 ## 第三步:改 GitHub 管理的运行时变量 @@ -143,8 +152,8 @@ PYTHONPATH=/Users/lisiyi/Projects/QuantPlatformKit/src:/Users/lisiyi/Projects/Us 如果是 feature-snapshot 策略,还需要: - `IBKR_FEATURE_SNAPSHOT_PATH` -- `IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH` -- `IBKR_STRATEGY_CONFIG_PATH`(`tech_communication_pullback_enhancement` 需要) +- `IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH`(当目标 profile 要求 manifest 时) +- `IBKR_STRATEGY_CONFIG_PATH`(当目标 profile 要求外部 runtime config 时) 不再需要时要删掉: @@ -166,8 +175,8 @@ PYTHONPATH=/Users/lisiyi/Projects/QuantPlatformKit/src:/Users/lisiyi/Projects/Us 如果是 feature-snapshot 策略,还需要: - `SCHWAB_FEATURE_SNAPSHOT_PATH` -- `SCHWAB_FEATURE_SNAPSHOT_MANIFEST_PATH` -- `SCHWAB_STRATEGY_CONFIG_PATH`(当该策略走外部配置文件时) +- `SCHWAB_FEATURE_SNAPSHOT_MANIFEST_PATH`(当目标 profile 要求 manifest 时) +- `SCHWAB_STRATEGY_CONFIG_PATH`(当目标 profile 要求外部 runtime config 时) 不再需要时要删掉: @@ -193,8 +202,8 @@ PYTHONPATH=/Users/lisiyi/Projects/QuantPlatformKit/src:/Users/lisiyi/Projects/Us 如果是 feature-snapshot 策略,还需要: - `LONGBRIDGE_FEATURE_SNAPSHOT_PATH` -- `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` -- `LONGBRIDGE_STRATEGY_CONFIG_PATH` +- `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH`(当目标 profile 要求 manifest 时) +- `LONGBRIDGE_STRATEGY_CONFIG_PATH`(当目标 profile 要求外部 runtime config 时) 不再需要时要删掉: @@ -355,6 +364,33 @@ gcloud run services describe longbridge-quant-sg-service \ - 非 snapshot 策略不需要 feature-snapshot artifact 链 - SG 是否 dry-run 是运维选择,不是策略本身要求 +### 示例 E:把 LongBridge SG dry-run 切到 `dynamic_mega_leveraged_pullback` + +保留: + +- `ACCOUNT_PREFIX=SG` +- `ACCOUNT_REGION=SG` +- `LONGPORT_SECRET_NAME` +- `LONGPORT_APP_KEY_SECRET_NAME` +- `LONGPORT_APP_SECRET_SECRET_NAME` + +设置: + +- `STRATEGY_PROFILE=dynamic_mega_leveraged_pullback` +- `LONGBRIDGE_FEATURE_SNAPSHOT_PATH` +- `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` +- `LONGBRIDGE_DRY_RUN_ONLY=true` + +如果还留着,就删掉: + +- `LONGBRIDGE_STRATEGY_CONFIG_PATH` + +原因: + +- 这个策略用 feature snapshot 获取动态 mega-cap 池 +- 每日 market、benchmark 和 portfolio 输入由平台运行时供应 +- dry-run 是部署选择,不是策略契约的一部分 + ## 回滚原则 回滚时保持简单: diff --git a/docs/us_equity_strategy_onboarding.md b/docs/us_equity_strategy_onboarding.md new file mode 100644 index 0000000..e2f0f50 --- /dev/null +++ b/docs/us_equity_strategy_onboarding.md @@ -0,0 +1,149 @@ +# US Equity Strategy Onboarding Checklist + +_Verified snapshot: 2026-04-15_ + +This checklist describes how a new `us_equity` strategy becomes available on the three US equity broker platforms without adding a manual allowlist entry in each platform repository. + +It applies to: + +- `InteractiveBrokersPlatform` +- `CharlesSchwabPlatform` +- `LongBridgePlatform` +- `UsEquityStrategies` +- `QuantPlatformKit` + +## Target operating model + +`UsEquityStrategies` is the source of truth for live strategy profiles. The three platform repositories derive their runtime allowlists from `get_runtime_enabled_profiles()`. + +A new strategy should become selectable on a platform only after these are true: + +1. The strategy is registered in the `UsEquityStrategies` catalog. +2. The strategy declares `status="runtime_enabled"` only when it is ready for live runtime. +3. The strategy lists the exact platforms it supports in `supported_platforms`. +4. The strategy exposes a unified entrypoint returning `StrategyDecision`. +5. The strategy has one platform-neutral runtime adapter spec. +6. Platform runtime adapters are derived from that spec plus platform native target mode. +7. The platform capability matrix says the platform can supply the declared `required_inputs`. +8. The relevant platform status script reports `eligible=true` and `enabled=true`. + +Do not add a second per-platform rollout allowlist for live US equity profiles. + +## Strategy repository requirements + +In `UsEquityStrategies`, a new live profile needs: + +- a canonical profile id in `catalog.py` +- a `StrategyDefinition` with `domain="us_equity"` +- explicit `required_inputs` +- explicit `target_mode` +- `status="runtime_enabled"` only after platform coverage is complete +- `supported_platforms` set to the intended platforms +- a manifest and unified entrypoint +- focused strategy tests +- one platform-neutral runtime adapter spec + +The current standard input names are: + +- `market_history` +- `benchmark_history` +- `derived_indicators` +- `portfolio_snapshot` +- `feature_snapshot` + +Do not introduce a new input name unless the cross-platform contract docs are updated first. + +## Runtime adapter requirements + +Each strategy needs one base `StrategyRuntimeAdapter` spec in `UsEquityStrategies`. + +The base adapter should declare strategy-owned metadata: + +- required feature snapshot columns when the strategy uses `feature_snapshot` +- snapshot date/freshness rules when the artifact has date-sensitive content +- manifest requirements and contract version when a manifest is required +- runtime config loader only when the strategy genuinely needs an external config file + +Platform-specific adapters are generated from: + +- the strategy's `required_inputs` +- the strategy's `target_mode` +- the platform's native target mode +- the platform's declared capabilities + +When a weight-mode strategy runs on a value-native platform, the generated adapter adds `portfolio_snapshot` so the platform can translate weights into dollar orders. When the strategy already requires `portfolio_snapshot`, the generated adapter maps it as the portfolio input automatically. + +The adapter generator is the bridge between strategy contract and platform runtime. Strategy code should not branch on broker platform ids. + +## Platform repository requirements + +The three US equity platform repositories should stay thin: + +- resolve the selected `STRATEGY_PROFILE` +- ask the shared strategy registry whether the profile is supported +- assemble `StrategyContext` +- load the unified entrypoint +- map `StrategyDecision` to broker-specific orders and notifications + +The platform registry should derive live profiles from `UsEquityStrategies.get_runtime_enabled_profiles()`. + +Platform repositories should not hard-code strategy symbol pools, private strategy constants, or manual live profile allowlists. + +## Artifact-backed profiles + +If a strategy uses `feature_snapshot`, the platform env sync workflow must enforce the artifact env vars before Cloud Run is updated. The workflow should derive this from `scripts/print_strategy_profile_status.py --json`, not from a hard-coded profile-name list. + +Current env mapping: + +| Platform | Snapshot path env | Manifest path env | Strategy config env | +| --- | --- | --- | --- | +| IBKR | `IBKR_FEATURE_SNAPSHOT_PATH` | `IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH` | `IBKR_STRATEGY_CONFIG_PATH` | +| Schwab | `SCHWAB_FEATURE_SNAPSHOT_PATH` | `SCHWAB_FEATURE_SNAPSHOT_MANIFEST_PATH` | `SCHWAB_STRATEGY_CONFIG_PATH` | +| LongBridge | `LONGBRIDGE_FEATURE_SNAPSHOT_PATH` | `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` | `LONGBRIDGE_STRATEGY_CONFIG_PATH` | + +Only profiles whose adapter requires a manifest should require the manifest path. Only profiles with a runtime config loader should require the strategy config path. + +## Test gates + +Before marking a new profile `runtime_enabled`, run: + +- `QuantPlatformKit` strategy contract tests +- `UsEquityStrategies` contract governance and entrypoint tests +- IBKR runtime config, loader, runtime, and workflow tests +- LongBridge runtime config, loader, runtime, and workflow tests +- Schwab runtime config, loader, runtime, and workflow tests +- each platform's `scripts/print_strategy_profile_status.py --json` + +`UsEquityStrategies` governance tests should fail if a `runtime_enabled` strategy does not have the expected generated platform adapter coverage. + +## Release order + +Use this order for a live rollout: + +1. Merge and tag `QuantPlatformKit` if shared contracts changed. +2. Merge and tag `UsEquityStrategies`. +3. Update platform repository dependency pins to the released tags. +4. Merge the platform repositories. +5. Let Cloud Run source deployments build the platform repositories. +6. Enable or update GitHub-managed env sync values. +7. Verify Cloud Run env and the first runtime heartbeat. + +Current pending tags for this change set: + +- `QuantPlatformKit` -> `v0.7.16` +- `UsEquityStrategies` -> `v0.7.23` + +## Permission-dependent steps + +Local code, tests, and docs do not require GitHub or Google Cloud login. + +These steps require credentials: + +- pushing commits and tags with `gh` or `git` +- creating pull requests or releases +- updating GitHub repository variables, secrets, or environments +- creating or updating Workload Identity Federation bindings +- creating or updating Secret Manager secrets +- deploying or inspecting Cloud Run services +- creating or updating Cloud Scheduler jobs +- reading or writing GCS strategy artifacts diff --git a/docs/us_equity_strategy_onboarding.zh-CN.md b/docs/us_equity_strategy_onboarding.zh-CN.md new file mode 100644 index 0000000..df0f412 --- /dev/null +++ b/docs/us_equity_strategy_onboarding.zh-CN.md @@ -0,0 +1,149 @@ +# 美股策略接入清单 + +_校验快照日期:2026-04-15_ + +这份清单说明一条新的 `us_equity` 策略,如何在不手写三个平台 allowlist 的情况下,进入 IBKR / Schwab / LongBridge 三个美股运行平台。 + +适用仓库: + +- `InteractiveBrokersPlatform` +- `CharlesSchwabPlatform` +- `LongBridgePlatform` +- `UsEquityStrategies` +- `QuantPlatformKit` + +## 目标模型 + +`UsEquityStrategies` 是 live 策略 profile 的事实来源。三个平台仓库从 `get_runtime_enabled_profiles()` 派生自己的运行时 allowlist。 + +新增策略只有满足下面条件后,才应该被某个平台选中: + +1. 策略已经注册到 `UsEquityStrategies` 的 catalog。 +2. 只有准备好实盘运行后,才标记 `status="runtime_enabled"`。 +3. `supported_platforms` 明确写出支持的平台。 +4. 策略暴露统一 entrypoint,并返回 `StrategyDecision`。 +5. 策略提供一份平台无关的 runtime adapter spec。 +6. 平台 runtime adapter 由这份 spec 加平台原生 target mode 自动派生。 +7. 平台 capability matrix 能供应策略声明的 `required_inputs`。 +8. 对应平台的状态脚本输出 `eligible=true` 且 `enabled=true`。 + +不要再给美股 live profile 维护第二套平台手写 allowlist。 + +## 策略仓库要求 + +在 `UsEquityStrategies` 里,一条新的 live profile 需要: + +- 在 `catalog.py` 里有 canonical profile id +- 有 `domain="us_equity"` 的 `StrategyDefinition` +- 显式声明 `required_inputs` +- 显式声明 `target_mode` +- 只有平台覆盖完成后才设为 `status="runtime_enabled"` +- 在 `supported_platforms` 里声明目标平台 +- 有 manifest 和统一 entrypoint +- 有聚焦的策略测试 +- 一份平台无关的 runtime adapter spec + +当前标准输入名: + +- `market_history` +- `benchmark_history` +- `derived_indicators` +- `portfolio_snapshot` +- `feature_snapshot` + +不要随手新增临时 input 名。如果确实需要新输入,要先更新跨平台契约文档。 + +## Runtime adapter 要求 + +每条策略只需要在 `UsEquityStrategies` 里有一份基础 `StrategyRuntimeAdapter` spec。 + +基础 adapter 只声明策略自己拥有的元数据: + +- 使用 `feature_snapshot` 时需要的 snapshot columns +- artifact 对日期敏感时的日期列和 freshness 规则 +- 需要 manifest 时的 manifest 要求和 contract version +- 只有策略确实需要外部配置文件时,才加 runtime config loader + +平台 adapter 会根据下面这些信息自动生成: + +- 策略的 `required_inputs` +- 策略的 `target_mode` +- 平台原生 target mode +- 平台声明的 capability + +当 weight-mode 策略运行在 value-native 平台上时,生成器会自动加入 `portfolio_snapshot`,让平台能把权重翻译成金额订单。策略本身已经需要 `portfolio_snapshot` 时,生成器会自动把它映射为 portfolio input。 + +adapter 生成器是策略契约和平台运行时之间的桥。策略代码本身不要按券商平台分支。 + +## 平台仓库要求 + +三个美股平台仓库应该保持薄运行层: + +- 解析 `STRATEGY_PROFILE` +- 通过共享策略注册表确认 profile 是否支持 +- 组装 `StrategyContext` +- 加载统一 entrypoint +- 把 `StrategyDecision` 映射成券商订单和通知 + +平台 registry 应该从 `UsEquityStrategies.get_runtime_enabled_profiles()` 派生 live profile。 + +平台仓库不要硬编码策略股票池、策略私有常量或手写 live profile allowlist。 + +## Artifact 驱动策略 + +如果策略使用 `feature_snapshot`,平台 env sync workflow 必须在更新 Cloud Run 前校验 artifact env。workflow 应该从 `scripts/print_strategy_profile_status.py --json` 动态解析需求,不要维护硬编码 profile 名单。 + +当前 env 映射: + +| 平台 | Snapshot 路径 env | Manifest 路径 env | Strategy config env | +| --- | --- | --- | --- | +| IBKR | `IBKR_FEATURE_SNAPSHOT_PATH` | `IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH` | `IBKR_STRATEGY_CONFIG_PATH` | +| Schwab | `SCHWAB_FEATURE_SNAPSHOT_PATH` | `SCHWAB_FEATURE_SNAPSHOT_MANIFEST_PATH` | `SCHWAB_STRATEGY_CONFIG_PATH` | +| LongBridge | `LONGBRIDGE_FEATURE_SNAPSHOT_PATH` | `LONGBRIDGE_FEATURE_SNAPSHOT_MANIFEST_PATH` | `LONGBRIDGE_STRATEGY_CONFIG_PATH` | + +只有 adapter 要求 manifest 的 profile,才强制 manifest path。只有带 runtime config loader 的 profile,才强制 strategy config path。 + +## 测试门槛 + +新增 profile 标记 `runtime_enabled` 前,要跑: + +- `QuantPlatformKit` 策略契约测试 +- `UsEquityStrategies` contract governance 和 entrypoint 测试 +- IBKR runtime config、loader、runtime、workflow 测试 +- LongBridge runtime config、loader、runtime、workflow 测试 +- Schwab runtime config、loader、runtime、workflow 测试 +- 三个平台的 `scripts/print_strategy_profile_status.py --json` + +`UsEquityStrategies` 的治理测试应该在 `runtime_enabled` 策略缺少预期生成式平台 adapter 覆盖时直接失败。 + +## 发布顺序 + +线上发布按这个顺序走: + +1. 如果共享契约变了,先合并并打 tag `QuantPlatformKit`。 +2. 合并并打 tag `UsEquityStrategies`。 +3. 更新平台仓库依赖到已发布 tag。 +4. 合并平台仓库。 +5. 让 Cloud Run source deployment 构建平台仓库。 +6. 打开或更新 GitHub 管理的 env sync 变量。 +7. 核对 Cloud Run env 和第一条运行心跳。 + +当前这批改动等待发布的 tag: + +- `QuantPlatformKit` -> `v0.7.16` +- `UsEquityStrategies` -> `v0.7.23` + +## 需要权限的步骤 + +本地代码、测试和文档不需要 GitHub 或 Google Cloud 登录。 + +下面这些需要凭据: + +- push commits 和 tags +- 创建 PR 或 release +- 更新 GitHub repository variables、secrets、environments +- 创建或更新 Workload Identity Federation 绑定 +- 创建或更新 Secret Manager secrets +- 部署或查看 Cloud Run 服务 +- 创建或更新 Cloud Scheduler jobs +- 读取或写入 GCS 策略 artifact diff --git a/pyproject.toml b/pyproject.toml index 9a7639a..498e88b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "quant-platform-kit" -version = "0.7.15" +version = "0.7.16" description = "Shared broker adapters, domain models, execution ports, and notification utilities for QuantStrategyLab strategies." readme = "README.md" requires-python = ">=3.9" diff --git a/src/quant_platform_kit/common/strategies.py b/src/quant_platform_kit/common/strategies.py index e9a67ed..0cf45e9 100644 --- a/src/quant_platform_kit/common/strategies.py +++ b/src/quant_platform_kit/common/strategies.py @@ -342,6 +342,9 @@ def _matches_platform_capability_matrix( runtime_adapter: StrategyRuntimeAdapter, capability_matrix: PlatformCapabilityMatrix, ) -> bool: + if capability_matrix.platform_id not in frozenset(definition.supported_platforms): + return False + if definition.domain not in capability_matrix.supported_domains: return False diff --git a/tests/test_strategies.py b/tests/test_strategies.py index f1f6ed3..4d4ca0b 100644 --- a/tests/test_strategies.py +++ b/tests/test_strategies.py @@ -295,6 +295,13 @@ def test_platform_capability_matrix_derives_eligible_profiles(self) -> None: required_inputs=frozenset({"feature_snapshot"}), target_mode="weight", ), + "schwab_only_weight": StrategyDefinition( + profile="schwab_only_weight", + domain=US_EQUITY_DOMAIN, + supported_platforms=frozenset({"schwab"}), + required_inputs=frozenset({"feature_snapshot"}), + target_mode="weight", + ), } ) ibkr_matrix = PlatformCapabilityMatrix( @@ -315,6 +322,9 @@ def test_platform_capability_matrix_derives_eligible_profiles(self) -> None: "tech_pullback_cash_buffer": StrategyRuntimeAdapter( available_inputs=frozenset({"feature_snapshot"}), ), + "schwab_only_weight": StrategyRuntimeAdapter( + available_inputs=frozenset({"feature_snapshot"}), + ), } eligible = derive_eligible_profiles_for_platform(