Skip to content

Commit cf83ad0

Browse files
committed
Add IBKR execution backend routing config
1 parent 8e19cd2 commit cf83ad0

11 files changed

Lines changed: 240 additions & 28 deletions

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,9 @@ For IBKR, keep `paper` as a single account-group entry. If you later add live ac
122122
| `CRISIS_ALERT_TELEGRAM_CHAT_IDS` | No | Dedicated crisis-alert Telegram chat IDs. Separate from the strategy-cycle Telegram chat. |
123123
| `CRISIS_ALERT_TELEGRAM_BOT_TOKEN` | No | Dedicated crisis-alert Telegram bot token. For Cloud Run, prefer `CRISIS_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME` in env sync. |
124124

125-
The selected account-group entry must provide at least:
125+
For the default Gateway execution backend, the selected account-group entry must provide at least:
126126

127+
- `execution_backend` (optional; defaults to `gateway`)
127128
- `ib_gateway_instance_name`
128129
- `ib_gateway_mode`
129130
- `ib_client_id`
@@ -136,6 +137,8 @@ For the recommended Cloud Run deployment, also include:
136137

137138
If you use instance-name resolution with `ib_gateway_zone`, the Cloud Run runtime service account needs `roles/compute.viewer`. If you load the payload from Secret Manager, the same runtime service account also needs `roles/secretmanager.secretAccessor` on `ibkr-account-groups`.
138139

140+
The account-group entry may also set `execution_backend` to `quantconnect`. In that mode, Gateway host/port/client-id fields are no longer required and this Cloud Run service will fail fast instead of accidentally connecting to Gateway. The QuantConnect path is a deployment/algorithm backend: real account mapping, QuantConnect project/node/compile identifiers, API token, and IBKR brokerage credentials must stay in private runtime configuration, and a QuantConnect algorithm must consume the same strategy or target configuration before live orders can be placed.
141+
139142
**Recommended shared-config mode**
140143

141144
For the current first rollout, keep GitHub / Cloud Run focused on service-level values:
@@ -194,6 +197,7 @@ Recommended account-group config payload:
194197
{
195198
"groups": {
196199
"paper": {
200+
"execution_backend": "gateway",
197201
"ib_gateway_instance_name": "interactive-brokers-quant-instance",
198202
"ib_gateway_zone": "us-central1-c",
199203
"ib_gateway_mode": "paper",
@@ -220,7 +224,8 @@ Current behavior is fail-fast:
220224
- missing `STRATEGY_PROFILE` → startup error
221225
- missing `ACCOUNT_GROUP` → startup error
222226
- missing account-group config source → startup error
223-
- missing key fields in the selected group (`ib_gateway_instance_name`, `ib_gateway_mode`, `ib_client_id`) → startup error
227+
- missing Gateway key fields in a `gateway` group (`ib_gateway_instance_name`, `ib_gateway_mode`, `ib_client_id`) → startup error
228+
- direct Gateway execution requested while `execution_backend=quantconnect` → startup error before any Gateway connection attempt
224229

225230
When `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` includes the `crisis_response_shadow` plugin, the normal strategy-cycle Telegram message still includes the compact plugin line. If the plugin signal escalates beyond `no_action` (for example `canonical_route=true_crisis`, `suggested_action=defend`/`blocked`, or `would_trade_if_enabled=true`), the service also sends independent crisis alerts through configured `CRISIS_ALERT_CHANNELS` channels.
226231
Alert results are written into the runtime report. Duplicate suppression uses stable plugin alert keys and stores markers under `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` when set, otherwise `EXECUTION_REPORT_GCS_URI`, with a local `/tmp` marker fallback.
@@ -444,8 +449,9 @@ IBKR 账户
444449
| `CRISIS_ALERT_TELEGRAM_CHAT_IDS` || 危机告警专用 Telegram chat ID,和常规策略周期 Telegram 分开。 |
445450
| `CRISIS_ALERT_TELEGRAM_BOT_TOKEN` || 危机告警专用 Telegram bot token。Cloud Run env sync 建议配置 `CRISIS_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME`|
446451

447-
选中的账号组配置里,至少要有
452+
默认 Gateway 执行后端下,选中的账号组配置里至少要有
448453

454+
- `execution_backend`(可选;默认 `gateway`
449455
- `ib_gateway_instance_name`
450456
- `ib_gateway_mode`
451457
- `ib_client_id`
@@ -458,6 +464,8 @@ IBKR 账户
458464

459465
如果你配置了 `ib_gateway_zone` 让程序通过实例名解析内网 IP,Cloud Run runtime service account 需要 `roles/compute.viewer`。如果账号组配置来源是 Secret Manager,同一个 runtime service account 还需要对 `ibkr-account-groups` 具备 `roles/secretmanager.secretAccessor`
460466

467+
账号组也可以把 `execution_backend` 设为 `quantconnect`。这个模式不再要求 Gateway 主机、端口和 client id,当前 Cloud Run 服务也会 fail-fast,避免误连 Gateway。QuantConnect 路径是部署/算法后端:真实账号映射、QuantConnect project/node/compile 标识、API token 和 IBKR 券商凭证都必须留在私有运行配置里,并且需要 QuantConnect 算法项目消费同一套策略或目标配置后才能实盘下单。
468+
461469
**推荐的共享配置模式**
462470

463471
当前第一步,建议让 GitHub / Cloud Run 只维护服务级变量:
@@ -482,6 +490,7 @@ IB_GATEWAY_IP_MODE=internal
482490
{
483491
"groups": {
484492
"paper": {
493+
"execution_backend": "gateway",
485494
"ib_gateway_instance_name": "interactive-brokers-quant-instance",
486495
"ib_gateway_zone": "us-central1-c",
487496
"ib_gateway_mode": "paper",
@@ -508,7 +517,8 @@ IB_GATEWAY_IP_MODE=internal
508517
- 没有 `STRATEGY_PROFILE` → 启动直接报错
509518
- 没有 `ACCOUNT_GROUP` → 启动直接报错
510519
- 没有账号组配置来源 → 启动直接报错
511-
- 选中的账号组缺少关键字段(`ib_gateway_instance_name``ib_gateway_mode``ib_client_id`)→ 启动直接报错
520+
- `gateway` 账号组缺少关键字段(`ib_gateway_instance_name``ib_gateway_mode``ib_client_id`)→ 启动直接报错
521+
- `execution_backend=quantconnect` 时直接请求 Gateway 执行 → 启动/执行前直接报错,不会尝试连接 Gateway
512522

513523
如果 `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` 挂载了 `crisis_response_shadow` 插件,常规策略周期 Telegram 仍会包含插件摘要行。当插件信号升级到非 `no_action`(例如 `canonical_route=true_crisis``suggested_action=defend`/`blocked`,或 `would_trade_if_enabled=true`)时,服务还会按 `CRISIS_ALERT_CHANNELS` 配置额外发送独立危机通知。
514524
告警结果会写入 runtime report。重复发送抑制使用稳定的插件告警 key;如配置了 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` 则写入该前缀,否则复用 `EXECUTION_REPORT_GCS_URI`,并有本地 `/tmp` marker fallback。

docs/examples/ibkr-account-groups.paper.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"groups": {
33
"paper": {
4+
"execution_backend": "gateway",
45
"ib_gateway_instance_name": "interactive-brokers-quant-instance",
56
"ib_gateway_zone": "us-central1-c",
67
"ib_gateway_mode": "paper",

docs/ibkr_runtime_rollout.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434

3535
### Secret Manager 里的 `ibkr-account-groups` 管 broker 运行身份
3636

37-
至少放这些字段
37+
Gateway 后端至少放这些字段
3838

39+
- `execution_backend`:默认是 `gateway`,建议显式写出来。
3940
- `ib_gateway_instance_name`
4041
- `ib_gateway_mode`
4142
- `ib_client_id`
@@ -46,7 +47,9 @@
4647
- `ib_gateway_port`:同一台 VM 上跑多个 Gateway 时填写;不填则 live 默认 `4001`,paper 默认 `4002`
4748
- `ib_gateway_ip_mode`
4849

49-
当前代码里,`ib_gateway_instance_name``ib_gateway_mode``ib_client_id` 已经不再从 Cloud Run env 读了,所以不要继续把它们放在 GitHub Repository Variables 里当主配置源。
50+
当前代码里,`execution_backend``ib_gateway_instance_name``ib_gateway_mode``ib_client_id` 已经不再从 Cloud Run env 读了,所以不要继续把它们放在 GitHub Repository Variables 里当主配置源。
51+
52+
如果账号组未来交给 QuantConnect Cloud 管理,可以把 `execution_backend` 设为 `quantconnect`。这时该账号组不再要求 Gateway 主机、端口和 client id,但要在私有配置里提供 QuantConnect 项目、节点、compile id 和 Secret 名。当前平台只识别并保护这个后端,不会把实盘账号配置写入仓库;真正下单仍需 QuantConnect 算法项目消费同一套策略/目标配置。
5053

5154
## 2. `ibkr-account-groups` 怎么配
5255

@@ -67,6 +70,7 @@ cp docs/examples/ibkr-account-groups.paper.json /tmp/ibkr-account-groups.json
6770
{
6871
"groups": {
6972
"paper": {
73+
"execution_backend": "gateway",
7074
"ib_gateway_instance_name": "interactive-brokers-quant-instance",
7175
"ib_gateway_zone": "us-central1-c",
7276
"ib_gateway_mode": "paper",
@@ -83,6 +87,7 @@ cp docs/examples/ibkr-account-groups.paper.json /tmp/ibkr-account-groups.json
8387
说明:
8488

8589
- `paper`:就是这次先跑通的账号组名,要和 Cloud Run env 里的 `ACCOUNT_GROUP=paper` 一致。
90+
- `execution_backend``gateway` 表示继续通过自托管 IB Gateway 连接和下单;`quantconnect` 表示该账号组交给 QuantConnect 路径,当前服务不会误连 Gateway。
8691
- `ib_gateway_instance_name`:GCE 上 IB Gateway 实例名。
8792
- `ib_gateway_zone`:建议现在就配上。当前推荐是按实例名解析内网 IP,这样 Cloud Run 不用手填固定私网 IP。
8893
- `ib_gateway_mode``paper``live`
@@ -92,6 +97,28 @@ cp docs/examples/ibkr-account-groups.paper.json /tmp/ibkr-account-groups.json
9297
- `service_name`:当前只是预留元数据,建议先填成现有 Cloud Run 服务名,后面多账号拆服务时更顺。
9398
- `account_ids`:实盘账号组建议只放一个 UID。运行时会用它过滤持仓、pending/fill 检查,并写入 IBKR 订单的 `order.account`;如果一个服务配置多个 UID,实盘下单会因为账户路由不明确而失败。
9499

100+
QuantConnect 后端占位结构示例:
101+
102+
```json
103+
{
104+
"groups": {
105+
"qc-slot": {
106+
"execution_backend": "quantconnect",
107+
"service_name": "interactive-brokers-qc-slot-service",
108+
"account_ids": ["U00000000"],
109+
"quantconnect_project_id": 12345678,
110+
"quantconnect_node_id": "LN-placeholder",
111+
"quantconnect_compile_id": "compile-placeholder",
112+
"quantconnect_version_id": "-1",
113+
"quantconnect_credentials_secret_name": "qc-api-credentials",
114+
"quantconnect_brokerage_secret_name": "qc-ibkr-slot"
115+
}
116+
}
117+
}
118+
```
119+
120+
上面只展示字段形状;真实账号、用户名、密码、API token 和 TOTP 不应进入仓库。
121+
95122
把 secret 建起来:
96123

97124
```bash

main.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@
5858
)
5959
from application.paper_liquidation_service import execute_paper_liquidation
6060
from runtime_logging import build_run_id, emit_runtime_log, extract_cloud_trace
61-
from runtime_config_support import load_platform_runtime_settings, resolve_ib_gateway_ip_mode
61+
from runtime_config_support import (
62+
EXECUTION_BACKEND_GATEWAY,
63+
load_platform_runtime_settings,
64+
resolve_ib_gateway_ip_mode,
65+
)
6266
from strategy_runtime import load_strategy_runtime
6367

6468
app = Flask(__name__)
@@ -197,6 +201,9 @@ def _env_flag(name: str) -> bool:
197201
SERVICE_NAME = RUNTIME_SETTINGS.service_name
198202
ACCOUNT_IDS = RUNTIME_SETTINGS.account_ids
199203
PROJECT_ID = RUNTIME_SETTINGS.project_id
204+
EXECUTION_BACKEND = RUNTIME_SETTINGS.execution_backend
205+
QUANTCONNECT_PROJECT_ID = getattr(RUNTIME_SETTINGS, "quantconnect_project_id", None)
206+
QUANTCONNECT_NODE_ID = getattr(RUNTIME_SETTINGS, "quantconnect_node_id", None)
200207

201208
STRATEGY_RUNTIME = load_strategy_runtime(
202209
STRATEGY_PROFILE,
@@ -275,8 +282,11 @@ def t(key, **kwargs):
275282
"strategy_artifact_dir": RUNTIME_SETTINGS.strategy_artifact_dir,
276283
"strategy_display_name": STRATEGY_DISPLAY_NAME,
277284
"strategy_display_name_localized": strategy_display_name,
285+
"execution_backend": EXECUTION_BACKEND,
278286
"ib_connect_attempts": IB_CONNECT_ATTEMPTS,
279287
"ib_client_id_retry_offset": IB_CLIENT_ID_RETRY_OFFSET,
288+
"quantconnect_project_id": QUANTCONNECT_PROJECT_ID,
289+
"quantconnect_node_id": QUANTCONNECT_NODE_ID,
280290
},
281291
).build_log_context(run_id="")
282292

@@ -392,6 +402,11 @@ def build_composer(*, dry_run_only_override: bool | None = None):
392402
env_reader=os.getenv,
393403
printer=print,
394404
runtime_target=RUNTIME_SETTINGS.runtime_target,
405+
extra_reporting_fields={
406+
"execution_backend": EXECUTION_BACKEND,
407+
"quantconnect_project_id": QUANTCONNECT_PROJECT_ID,
408+
"quantconnect_node_id": QUANTCONNECT_NODE_ID,
409+
},
395410
)
396411

397412

@@ -411,7 +426,18 @@ def publish_notification(*, detailed_text, compact_text):
411426
)
412427

413428

429+
def require_gateway_execution_backend():
430+
if EXECUTION_BACKEND == EXECUTION_BACKEND_GATEWAY:
431+
return
432+
raise RuntimeError(
433+
f"IBKR execution_backend={EXECUTION_BACKEND!r} is configured; "
434+
"Gateway connection and direct order execution are disabled for this service. "
435+
"Run the QuantConnect deployment/algorithm path for this account group."
436+
)
437+
438+
414439
def connect_ib():
440+
require_gateway_execution_backend()
415441
return build_broker_adapters().connect_ib()
416442

417443

@@ -565,6 +591,7 @@ def _format_liquidation_orders(orders) -> str:
565591

566592

567593
def run_paper_liquidation_cycle():
594+
require_gateway_execution_backend()
568595
if RUNTIME_SETTINGS.ib_gateway_mode != "paper":
569596
raise RuntimeError("IBKR_PAPER_LIQUIDATE_ONLY is only allowed when ib_gateway_mode=paper")
570597
return build_broker_adapters().run_paper_liquidation_cycle(

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
flask
22
gunicorn
3-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@f176f5d1f208724381278c253941cbc6d0a1c964
3+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@3193c145326920d63cf60d234fb63f8a4b6fc012
44
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@f206ae7a5f2772873c8e3907daa8d753f616348c
55
pandas
66
numpy

0 commit comments

Comments
 (0)