Skip to content

Commit aee01b4

Browse files
committed
Add execution windows to runtime target
1 parent a68a53b commit aee01b4

6 files changed

Lines changed: 64 additions & 12 deletions

File tree

docs/platform_runtime_inventory.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Platform Runtime Inventory
22

3-
_Verified snapshot: 2026-04-18_
3+
_Verified snapshot: 2026-05-14_
44

55
This document records the public runtime wiring inventory across platform repositories and deployment projects. It is meant to answer one question quickly:
66

@@ -45,7 +45,12 @@ For the platform / strategy-domain / configurable-profile matrix, see [`platform
4545
- **Runtime service account**
4646
- `ibkr-platform-runtime@interactivebrokersquant.iam.gserviceaccount.com`
4747
- **Scheduler**
48-
- `interactive-brokers-quant-service-scheduler`
48+
- `interactive-brokers-live-slot-a-precheck-scheduler`
49+
- `interactive-brokers-live-slot-a-probe-scheduler`
50+
- `interactive-brokers-live-slot-a-scheduler`
51+
- `interactive-brokers-live-slot-b-precheck-scheduler`
52+
- `interactive-brokers-live-slot-b-probe-scheduler`
53+
- `interactive-brokers-live-slot-b-scheduler`
4954
- region: `us-central1`
5055
- **Core runtime selectors**
5156
- `STRATEGY_PROFILE=<runtime_enabled us_equity profile>`
@@ -69,6 +74,8 @@ For the platform / strategy-domain / configurable-profile matrix, see [`platform
6974
- **Runtime service account**
7075
- `schwab-platform-runtime@charlesschwabquant.iam.gserviceaccount.com`
7176
- **Scheduler**
77+
- `charles-schwab-quant-service-precheck-scheduler`
78+
- `charles-schwab-quant-service-probe-scheduler`
7279
- `charles-schwab-quant-service-scheduler`
7380
- region: `us-central1`
7481
- **Core runtime selectors**
@@ -97,19 +104,27 @@ For the platform / strategy-domain / configurable-profile matrix, see [`platform
97104
- **Runtime service account**
98105
- `longbridge-platform-runtime@longbridgequant.iam.gserviceaccount.com`
99106
- **Schedulers**
107+
- `longbridge-quant-paper-service-precheck-scheduler` in `asia-east2`
108+
- `longbridge-quant-paper-service-probe-scheduler` in `asia-east2`
100109
- `longbridge-quant-paper-service-scheduler` in `asia-east2`
101-
- HK: reserved / not yet wired
110+
- `longbridge-quant-hk-service-precheck-scheduler` in `asia-east2`
111+
- `longbridge-quant-hk-service-probe-scheduler` in `asia-east2`
112+
- `longbridge-quant-hk-service-scheduler` in `asia-east2`
113+
- `longbridge-quant-sg-service-precheck-scheduler` in `asia-southeast1`
114+
- `longbridge-quant-sg-service-probe-scheduler` in `asia-southeast1`
102115
- `longbridge-quant-sg-service-scheduler` in `asia-southeast1`
103116
- **Core runtime selectors**
104-
- `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` per account service
117+
- `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` per account service
105118
- `ACCOUNT_REGION=PAPER|HK|SG`
106119
- `LONGPORT_SECRET_NAME=<account token secret>`
107120
- **Runtime secrets**
108121
- Secret Manager refs for LongPort app key / app secret
109122
- account token secrets selected by `LONGPORT_SECRET_NAME`
110123
- runtime Telegram token secret
111124
- **Runtime notes**
112-
- PAPER and SG are live today; HK keeps the same deployment pattern when it is added. Each account identity gets its own Cloud Run service, trigger, and GitHub Environment.
125+
- Each account identity gets its own Cloud Run service, trigger, and GitHub Environment. HK can keep the same deployment pattern when it is promoted; rollout state is controlled outside the public docs.
126+
- Each live account identity now has three Cloud Scheduler triggers: `precheck` after the open window, `probe` at open+30 minutes, and the root execution trigger near the close window.
127+
- For Cloud Scheduler HTTP targets, keep the OIDC audience on the Cloud Run service root URL; do not append `precheck` or `probe` to the audience.
113128
- Snapshot-backed profiles require feature snapshot path / manifest envs; direct-runtime profiles do not.
114129
- App key / secret are account-specific Secret Manager refs; Telegram token is shared inside the LongBridge project.
115130
- `SERVICE_NAME` should use the full runtime-facing service names above, not older short prefixes.

docs/platform_runtime_inventory.zh-CN.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 平台运行接线清单
22

3-
_校验快照日期:2026-04-18_
3+
_校验快照日期:2026-05-14_
44

55
这份文档记录公开仓库里可以保留的 runtime 接线信息,用来快速回答一个问题:
66

@@ -44,7 +44,12 @@ _校验快照日期:2026-04-18_
4444
- **runtime service account**
4545
- `ibkr-platform-runtime@interactivebrokersquant.iam.gserviceaccount.com`
4646
- **Scheduler**
47-
- `interactive-brokers-quant-service-scheduler`
47+
- `interactive-brokers-live-slot-a-precheck-scheduler`
48+
- `interactive-brokers-live-slot-a-probe-scheduler`
49+
- `interactive-brokers-live-slot-a-scheduler`
50+
- `interactive-brokers-live-slot-b-precheck-scheduler`
51+
- `interactive-brokers-live-slot-b-probe-scheduler`
52+
- `interactive-brokers-live-slot-b-scheduler`
4853
- region:`us-central1`
4954
- **核心运行选择器**
5055
- `STRATEGY_PROFILE=<runtime_enabled us_equity profile>`
@@ -68,6 +73,8 @@ _校验快照日期:2026-04-18_
6873
- **runtime service account**
6974
- `schwab-platform-runtime@charlesschwabquant.iam.gserviceaccount.com`
7075
- **Scheduler**
76+
- `charles-schwab-quant-service-precheck-scheduler`
77+
- `charles-schwab-quant-service-probe-scheduler`
7178
- `charles-schwab-quant-service-scheduler`
7279
- region:`us-central1`
7380
- **核心运行选择器**
@@ -95,7 +102,14 @@ _校验快照日期:2026-04-18_
95102
- **runtime service account**
96103
- `longbridge-platform-runtime@longbridgequant.iam.gserviceaccount.com`
97104
- **Scheduler**
105+
- `longbridge-quant-paper-service-precheck-scheduler``asia-east2`
106+
- `longbridge-quant-paper-service-probe-scheduler``asia-east2`
107+
- `longbridge-quant-paper-service-scheduler``asia-east2`
108+
- `longbridge-quant-hk-service-precheck-scheduler``asia-east2`
109+
- `longbridge-quant-hk-service-probe-scheduler``asia-east2`
98110
- `longbridge-quant-hk-service-scheduler``asia-east2`
111+
- `longbridge-quant-sg-service-precheck-scheduler``asia-southeast1`
112+
- `longbridge-quant-sg-service-probe-scheduler``asia-southeast1`
99113
- `longbridge-quant-sg-service-scheduler``asia-southeast1`
100114
- **核心运行选择器**
101115
- 每个区域服务设置 `STRATEGY_PROFILE=<runtime_enabled us_equity profile>`
@@ -106,7 +120,7 @@ _校验快照日期:2026-04-18_
106120
-`LONGPORT_SECRET_NAME` 选择的区域 token secret
107121
- runtime Telegram token secret
108122
- **运行说明**
109-
- HK / SG 继续保持两个 Cloud Run 服务、两个 trigger、两个 GitHub Environment。
123+
- 每个账户身份都对应自己的 Cloud Run 服务、triggerGitHub Environment;公开文档不记录当前哪几个账户处于实盘。每个账户都有三条 Cloud Scheduler 触发:开盘后 `precheck`,开盘后 `probe`(+30 分钟),临近收盘执行。Scheduler 的 OIDC audience 也要指向 Cloud Run 服务根 URL,不要拼到 `precheck``probe` 路径
110124
- snapshot 驱动策略需要 feature snapshot path / manifest env;直接运行输入策略不需要。
111125
- App key / secret 使用分区域的 Secret Manager 引用;Telegram token 在 LongBridge 项目内共享。
112126
- `SERVICE_NAME` 应使用上面的完整运行时服务名,不再使用旧短前缀。

docs/platform_strategy_matrix.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ For strategy behavior, research status, and archived backtest evidence, see
2929
|---|---|---|---|---|---|---|
3030
| IBKR | `QuantStrategyLab/InteractiveBrokersPlatform` | `us_equity` | `RUNTIME_TARGET_JSON` + `ACCOUNT_GROUP` | `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` | Cloud Run | Yes - controlled by platform rollout config |
3131
| Charles Schwab | `QuantStrategyLab/CharlesSchwabPlatform` | `us_equity` | `RUNTIME_TARGET_JSON` | `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` | Cloud Run | Yes - controlled by platform rollout config |
32-
| LongBridge | `QuantStrategyLab/LongBridgePlatform` | `us_equity` | `RUNTIME_TARGET_JSON` + `ACCOUNT_REGION` | `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` per account service | Cloud Run | Yes - paper and SG today; HK later |
32+
| LongBridge | `QuantStrategyLab/LongBridgePlatform` | `us_equity` | `RUNTIME_TARGET_JSON` + `ACCOUNT_REGION` | `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` per account service | Cloud Run | Yes - controlled by per-account rollout config |
3333
| Binance | `QuantStrategyLab/BinancePlatform` | `crypto` | `RUNTIME_TARGET_JSON` (workflow-local) | `crypto_leader_rotation` | Oracle Cloud + self-hosted runner | No - only this profile is supported today |
3434

3535
## What this means right now
@@ -42,7 +42,7 @@ Platforms currently in this domain:
4242
- `CharlesSchwabPlatform`
4343
- `LongBridgePlatform`
4444

45-
LongBridge account identities are modeled as `paper`, `HK`, and `SG`; today the live services use `paper` and `SG`, and `HK` follows the same contract when it is introduced.
45+
LongBridge account identities are modeled as `paper`, `HK`, and `SG`; each account service follows the same contract, with deployment-specific rollout control deciding which ones are active.
4646

4747
Important limitation:
4848

docs/platform_strategy_matrix.zh-CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ _核对时间:2026-04-18_
2929
|---|---|---|---|---|---|
3030
| IBKR | `QuantStrategyLab/InteractiveBrokersPlatform` | `us_equity` | `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` | Cloud Run | 可以,按平台 rollout 配置启用 |
3131
| Charles Schwab | `QuantStrategyLab/CharlesSchwabPlatform` | `us_equity` | `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` | Cloud Run | 可以,按平台 rollout 配置启用 |
32-
| LongBridge | `QuantStrategyLab/LongBridgePlatform` | `us_equity` | 每个区域服务配置 `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` | Cloud Run | 可以,按平台 rollout 配置启用 |
32+
| LongBridge | `QuantStrategyLab/LongBridgePlatform` | `us_equity` | 每个区域服务配置 `STRATEGY_PROFILE=<runtime_enabled us_equity profile>` | Cloud Run | 可以,按账户服务 rollout 配置启用 |
3333
| Binance | `QuantStrategyLab/BinancePlatform` | `crypto` | `crypto_leader_rotation` | Oracle Cloud + self-hosted runner | 当前只支持这个 crypto profile |
3434

3535
## 这张表现在该怎么理解

src/quant_platform_kit/common/runtime_target.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class RuntimeTarget:
1515
account_selector: tuple[str, ...] = ()
1616
account_scope: str | None = None
1717
service_name: str | None = None
18+
execution_windows: dict[str, Any] | None = None
1819

1920
@property
2021
def execution_mode(self) -> str:
@@ -67,6 +68,7 @@ def build_runtime_target(
6768
account_selector: Iterable[str] | str | None = None,
6869
account_scope: str | None = None,
6970
service_name: str | None = None,
71+
execution_windows: Mapping[str, Any] | None = None,
7072
) -> RuntimeTarget:
7173
return RuntimeTarget(
7274
platform_id=str(platform_id).strip(),
@@ -76,6 +78,7 @@ def build_runtime_target(
7678
account_selector=_normalize_account_selector(account_selector),
7779
account_scope=_normalize_optional_string(account_scope),
7880
service_name=_normalize_optional_string(service_name),
81+
execution_windows=dict(execution_windows) if execution_windows is not None else None,
7982
)
8083

8184

@@ -137,6 +140,10 @@ def resolve_runtime_target_from_env(
137140
"RUNTIME_TARGET_JSON.execution_mode does not match dry_run_only"
138141
)
139142

143+
execution_windows = payload.get("execution_windows")
144+
if execution_windows is not None and not isinstance(execution_windows, dict):
145+
raise ValueError("RUNTIME_TARGET_JSON.execution_windows must be an object when present")
146+
140147
return build_runtime_target(
141148
platform_id=resolved_platform_id,
142149
strategy_profile=resolved_strategy_profile,
@@ -145,4 +152,5 @@ def resolve_runtime_target_from_env(
145152
account_selector=payload.get("account_selector"),
146153
account_scope=payload.get("account_scope"),
147154
service_name=payload.get("service_name"),
155+
execution_windows=execution_windows,
148156
)

tests/test_runtime_target.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ def test_build_runtime_target_normalizes_selectors_and_mode(self) -> None:
1919
account_selector=(" U123 ", "", None),
2020
account_scope=" hk ",
2121
service_name=" longbridge-quant-hk-service ",
22+
execution_windows={
23+
"precheck": {"enabled": True, "offset_minutes": 15, "mode": "notify_only"},
24+
"execution": {"enabled": True, "offset_minutes": 15, "mode": "paper"},
25+
},
2226
)
2327

2428
self.assertEqual(target.platform_id, "longbridge")
@@ -28,6 +32,8 @@ def test_build_runtime_target_normalizes_selectors_and_mode(self) -> None:
2832
self.assertEqual(target.account_selector, ("U123",))
2933
self.assertEqual(target.account_scope, "hk")
3034
self.assertEqual(target.service_name, "longbridge-quant-hk-service")
35+
self.assertEqual(target.execution_windows["precheck"]["offset_minutes"], 15)
36+
self.assertEqual(target.execution_windows["execution"]["mode"], "paper")
3137

3238
def test_build_runtime_target_supports_live_mode_without_account_selector(self) -> None:
3339
target = build_runtime_target(
@@ -50,7 +56,9 @@ def test_resolve_runtime_target_from_env_prefers_structured_json(self) -> None:
5056
'{"platform_id":"longbridge","strategy_profile":"global_etf_rotation",'
5157
'"dry_run_only":true,"deployment_selector":"HK","account_selector":["HK"],'
5258
'"account_scope":"HK","service_name":"longbridge-quant-hk-service",'
53-
'"execution_mode":"paper"}'
59+
'"execution_mode":"paper","execution_windows":{"precheck":{"enabled":true,'
60+
'"offset_minutes":15,"mode":"notify_only"},"execution":{"enabled":true,'
61+
'"offset_minutes":15,"mode":"paper"}}}'
5462
)
5563
},
5664
)
@@ -63,6 +71,8 @@ def test_resolve_runtime_target_from_env_prefers_structured_json(self) -> None:
6371
self.assertEqual(target.account_selector, ("HK",))
6472
self.assertEqual(target.account_scope, "HK")
6573
self.assertEqual(target.service_name, "longbridge-quant-hk-service")
74+
self.assertTrue(target.execution_windows["precheck"]["enabled"])
75+
self.assertEqual(target.execution_windows["execution"]["offset_minutes"], 15)
6676

6777
def test_resolve_runtime_target_from_env_rejects_mismatched_execution_mode(self) -> None:
6878
with self.assertRaisesRegex(ValueError, "execution_mode does not match dry_run_only"):
@@ -101,6 +111,10 @@ def test_build_runtime_context_fields_merges_runtime_target_without_overwriting_
101111
strategy_profile="soxl_soxx_trend_income",
102112
dry_run_only=True,
103113
service_name="longbridge-platform",
114+
execution_windows={
115+
"precheck": {"enabled": True, "offset_minutes": 15, "mode": "notify_only"},
116+
"execution": {"enabled": True, "offset_minutes": 15, "mode": "paper"},
117+
},
104118
)
105119

106120
fields = build_runtime_context_fields(
@@ -115,3 +129,4 @@ def test_build_runtime_context_fields_merges_runtime_target_without_overwriting_
115129
self.assertEqual(fields["account_scope"], "HK")
116130
self.assertEqual(fields["runtime_target"]["platform_id"], "longbridge")
117131
self.assertEqual(fields["runtime_target"]["execution_mode"], "paper")
132+
self.assertEqual(fields["runtime_target"]["execution_windows"]["precheck"]["enabled"], True)

0 commit comments

Comments
 (0)