Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ For IBKR, keep `paper` as a single account-group entry. If you later add live ac
| `CRISIS_ALERT_TELEGRAM_CHAT_IDS` | No | Dedicated crisis-alert Telegram chat IDs. Separate from the strategy-cycle Telegram chat. |
| `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. |

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

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

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`.

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.

**Recommended shared-config mode**

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

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.
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.
Expand Down Expand Up @@ -444,8 +449,9 @@ IBKR 账户
| `CRISIS_ALERT_TELEGRAM_CHAT_IDS` | 否 | 危机告警专用 Telegram chat ID,和常规策略周期 Telegram 分开。 |
| `CRISIS_ALERT_TELEGRAM_BOT_TOKEN` | 否 | 危机告警专用 Telegram bot token。Cloud Run env sync 建议配置 `CRISIS_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME`。 |

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

- `execution_backend`(可选;默认 `gateway`)
- `ib_gateway_instance_name`
- `ib_gateway_mode`
- `ib_client_id`
Expand All @@ -458,6 +464,8 @@ IBKR 账户

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

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

**推荐的共享配置模式**

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

如果 `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` 配置额外发送独立危机通知。
告警结果会写入 runtime report。重复发送抑制使用稳定的插件告警 key;如配置了 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` 则写入该前缀,否则复用 `EXECUTION_REPORT_GCS_URI`,并有本地 `/tmp` marker fallback。
Expand Down
1 change: 1 addition & 0 deletions docs/examples/ibkr-account-groups.paper.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"groups": {
"paper": {
"execution_backend": "gateway",
"ib_gateway_instance_name": "interactive-brokers-quant-instance",
"ib_gateway_zone": "us-central1-c",
"ib_gateway_mode": "paper",
Expand Down
31 changes: 29 additions & 2 deletions docs/ibkr_runtime_rollout.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@

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

至少放这些字段
Gateway 后端至少放这些字段

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

当前代码里,`ib_gateway_instance_name`、`ib_gateway_mode`、`ib_client_id` 已经不再从 Cloud Run env 读了,所以不要继续把它们放在 GitHub Repository Variables 里当主配置源。
当前代码里,`execution_backend`、`ib_gateway_instance_name`、`ib_gateway_mode`、`ib_client_id` 已经不再从 Cloud Run env 读了,所以不要继续把它们放在 GitHub Repository Variables 里当主配置源。

如果账号组未来交给 QuantConnect Cloud 管理,可以把 `execution_backend` 设为 `quantconnect`。这时该账号组不再要求 Gateway 主机、端口和 client id,但要在私有配置里提供 QuantConnect 项目、节点、compile id 和 Secret 名。当前平台只识别并保护这个后端,不会把实盘账号配置写入仓库;真正下单仍需 QuantConnect 算法项目消费同一套策略/目标配置。

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

Expand All @@ -67,6 +70,7 @@ cp docs/examples/ibkr-account-groups.paper.json /tmp/ibkr-account-groups.json
{
"groups": {
"paper": {
"execution_backend": "gateway",
"ib_gateway_instance_name": "interactive-brokers-quant-instance",
"ib_gateway_zone": "us-central1-c",
"ib_gateway_mode": "paper",
Expand All @@ -83,6 +87,7 @@ cp docs/examples/ibkr-account-groups.paper.json /tmp/ibkr-account-groups.json
说明:

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

QuantConnect 后端占位结构示例:

```json
{
"groups": {
"qc-slot": {
"execution_backend": "quantconnect",
"service_name": "interactive-brokers-qc-slot-service",
"account_ids": ["U00000000"],
"quantconnect_project_id": 12345678,
"quantconnect_node_id": "LN-placeholder",
"quantconnect_compile_id": "compile-placeholder",
"quantconnect_version_id": "-1",
"quantconnect_credentials_secret_name": "qc-api-credentials",
"quantconnect_brokerage_secret_name": "qc-ibkr-slot"
}
}
}
```

上面只展示字段形状;真实账号、用户名、密码、API token 和 TOTP 不应进入仓库。

把 secret 建起来:

```bash
Expand Down
29 changes: 28 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@
)
from application.paper_liquidation_service import execute_paper_liquidation
from runtime_logging import build_run_id, emit_runtime_log, extract_cloud_trace
from runtime_config_support import load_platform_runtime_settings, resolve_ib_gateway_ip_mode
from runtime_config_support import (
EXECUTION_BACKEND_GATEWAY,
load_platform_runtime_settings,
resolve_ib_gateway_ip_mode,
)
from strategy_runtime import load_strategy_runtime

app = Flask(__name__)
Expand Down Expand Up @@ -197,6 +201,9 @@ def _env_flag(name: str) -> bool:
SERVICE_NAME = RUNTIME_SETTINGS.service_name
ACCOUNT_IDS = RUNTIME_SETTINGS.account_ids
PROJECT_ID = RUNTIME_SETTINGS.project_id
EXECUTION_BACKEND = RUNTIME_SETTINGS.execution_backend
QUANTCONNECT_PROJECT_ID = getattr(RUNTIME_SETTINGS, "quantconnect_project_id", None)
QUANTCONNECT_NODE_ID = getattr(RUNTIME_SETTINGS, "quantconnect_node_id", None)

STRATEGY_RUNTIME = load_strategy_runtime(
STRATEGY_PROFILE,
Expand Down Expand Up @@ -275,8 +282,11 @@ def t(key, **kwargs):
"strategy_artifact_dir": RUNTIME_SETTINGS.strategy_artifact_dir,
"strategy_display_name": STRATEGY_DISPLAY_NAME,
"strategy_display_name_localized": strategy_display_name,
"execution_backend": EXECUTION_BACKEND,
"ib_connect_attempts": IB_CONNECT_ATTEMPTS,
"ib_client_id_retry_offset": IB_CLIENT_ID_RETRY_OFFSET,
"quantconnect_project_id": QUANTCONNECT_PROJECT_ID,
"quantconnect_node_id": QUANTCONNECT_NODE_ID,
},
).build_log_context(run_id="")

Expand Down Expand Up @@ -392,6 +402,11 @@ def build_composer(*, dry_run_only_override: bool | None = None):
env_reader=os.getenv,
printer=print,
runtime_target=RUNTIME_SETTINGS.runtime_target,
extra_reporting_fields={
"execution_backend": EXECUTION_BACKEND,
"quantconnect_project_id": QUANTCONNECT_PROJECT_ID,
"quantconnect_node_id": QUANTCONNECT_NODE_ID,
},
)


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


def require_gateway_execution_backend():
if EXECUTION_BACKEND == EXECUTION_BACKEND_GATEWAY:
return
raise RuntimeError(
f"IBKR execution_backend={EXECUTION_BACKEND!r} is configured; "
"Gateway connection and direct order execution are disabled for this service. "
"Run the QuantConnect deployment/algorithm path for this account group."
)


def connect_ib():
require_gateway_execution_backend()
return build_broker_adapters().connect_ib()


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


def run_paper_liquidation_cycle():
require_gateway_execution_backend()
if RUNTIME_SETTINGS.ib_gateway_mode != "paper":
raise RuntimeError("IBKR_PAPER_LIQUIDATE_ONLY is only allowed when ib_gateway_mode=paper")
return build_broker_adapters().run_paper_liquidation_cycle(
Expand Down
Loading