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
117 changes: 112 additions & 5 deletions .github/workflows/sync-cloud-run-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@ on:
push:
branches: [ main ]
workflow_dispatch:
inputs:
target:
description: "Deployment target to run. Use hk-verify for an isolated HK dry-run Cloud Run service."
required: true
type: choice
default: configured
options:
- configured
- hk-verify
cloud_run_region:
description: "Cloud Run region for hk-verify. Leave blank to use repository/environment variables."
required: false
type: string
cloud_run_service:
description: "Cloud Run service for hk-verify."
required: false
type: string
default: interactive-brokers-hk-verify-service
account_group:
description: "IBKR account-group selector for hk-verify."
required: false
type: string
default: hk-verify
account_group_config_secret_name:
description: "Optional Secret Manager secret name for the IBKR account-group config."
required: false
type: string
deploy_image:
description: "Build and deploy the container image for hk-verify."
required: true
type: boolean
default: true
sync_env:
description: "Sync hk-verify Cloud Run environment variables."
required: true
type: boolean
default: true

env:
GCP_PROJECT_ID: interactivebrokersquant
Expand All @@ -23,6 +60,13 @@ jobs:
ENABLE_GITHUB_CLOUD_RUN_DEPLOY: ${{ vars.ENABLE_GITHUB_CLOUD_RUN_DEPLOY }}
ENABLE_GITHUB_ENV_SYNC: ${{ vars.ENABLE_GITHUB_ENV_SYNC }}
ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION: ${{ vars.ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION }}
WORKFLOW_TARGET: ${{ inputs.target || 'configured' }}
INPUT_CLOUD_RUN_REGION: ${{ inputs.cloud_run_region }}
INPUT_CLOUD_RUN_SERVICE: ${{ inputs.cloud_run_service }}
INPUT_ACCOUNT_GROUP: ${{ inputs.account_group }}
INPUT_ACCOUNT_GROUP_CONFIG_SECRET_NAME: ${{ inputs.account_group_config_secret_name }}
INPUT_DEPLOY_IMAGE: ${{ inputs.deploy_image }}
INPUT_SYNC_ENV: ${{ inputs.sync_env }}
GCP_ARTIFACT_REGISTRY_HOSTNAME: ${{ vars.GCP_ARTIFACT_REGISTRY_HOSTNAME }}
CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }}
CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}
Expand Down Expand Up @@ -108,12 +152,21 @@ jobs:
exit 0
fi

if [ "${ENABLE_GITHUB_CLOUD_RUN_DEPLOY:-}" = "true" ]; then
deploy_enabled=true
fi
if [ "${GITHUB_EVENT_NAME:-}" = "workflow_dispatch" ] && [ "${WORKFLOW_TARGET:-configured}" = "hk-verify" ]; then
if [ "${INPUT_DEPLOY_IMAGE:-true}" = "true" ]; then
deploy_enabled=true
fi
Comment on lines +155 to +158

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sync HK env before first deploy

When target=hk-verify is dispatched with the default deploy_image=true, this branch enables both deploy and env sync, but the workflow later runs Build, push, and deploy Cloud Run image before Sync Cloud Run environment. For a new isolated HK verify service, the container starts without the env values this new step only writes into the later sync plan; main.py loads runtime settings at import time and runtime_config_support.resolve_account_group raises if ACCOUNT_GROUP is absent, so the first gcloud run deploy can fail before the env-sync step ever applies these defaults. Consider passing the required HK env during deploy or performing the initial env update before deploying a new hk-verify service.

Useful? React with 👍 / 👎.

if [ "${INPUT_SYNC_ENV:-true}" = "true" ]; then
env_sync_enabled=true
fi
else
if [ "${ENABLE_GITHUB_CLOUD_RUN_DEPLOY:-}" = "true" ]; then
deploy_enabled=true
fi

if [ "${ENABLE_GITHUB_ENV_SYNC:-}" = "true" ]; then
env_sync_enabled=true
if [ "${ENABLE_GITHUB_ENV_SYNC:-}" = "true" ]; then
env_sync_enabled=true
fi
fi

echo "deploy_enabled=${deploy_enabled}" >> "$GITHUB_OUTPUT"
Expand All @@ -131,6 +184,60 @@ jobs:
if: steps.config.outputs.enabled == 'true'
uses: actions/checkout@v6

- name: Apply HK verify-only dispatch defaults
if: steps.config.outputs.enabled == 'true' && github.event_name == 'workflow_dispatch' && inputs.target == 'hk-verify'
run: |
set -euo pipefail

service="${INPUT_CLOUD_RUN_SERVICE:-interactive-brokers-hk-verify-service}"
account_group="${INPUT_ACCOUNT_GROUP:-hk-verify}"
region="${INPUT_CLOUD_RUN_REGION:-${CLOUD_RUN_REGION:-}}"
runtime_target="$(SERVICE="${service}" ACCOUNT_GROUP_VALUE="${account_group}" python3 - <<'PY'
import json
import os

print(
json.dumps(
{
"platform_id": "ibkr",
"strategy_profile": "hk_listed_global_etf_rotation",
"deployment_selector": os.environ["ACCOUNT_GROUP_VALUE"],
"account_scope": os.environ["ACCOUNT_GROUP_VALUE"],
"execution_mode": "paper",
"dry_run_only": True,
"service_name": os.environ["SERVICE"],
},
separators=(",", ":"),
)
)
PY
)"

{
echo "CLOUD_RUN_SERVICE=${service}"
echo "CLOUD_RUN_SERVICES="
echo "CLOUD_RUN_SERVICE_TARGETS_JSON="
echo "ACCOUNT_GROUP=${account_group}"
echo "RUNTIME_TARGET_JSON=${runtime_target}"
echo "IBKR_DRY_RUN_ONLY=true"
echo "IBKR_MARKET=HK"
echo "IBKR_MARKET_CALENDAR=XHKG"
echo "IBKR_MARKET_CURRENCY=HKD"
echo "IBKR_MARKET_DATA_SYMBOL_SUFFIX=.HK"
echo "IBKR_MARKET_EXCHANGE=SEHK"
echo "IBKR_MARKET_TIMEZONE=Asia/Hong_Kong"
} >> "$GITHUB_ENV"

if [ -n "${region}" ]; then
echo "CLOUD_RUN_REGION=${region}" >> "$GITHUB_ENV"
fi
if [ -n "${INPUT_ACCOUNT_GROUP_CONFIG_SECRET_NAME:-}" ]; then
echo "IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=${INPUT_ACCOUNT_GROUP_CONFIG_SECRET_NAME}" >> "$GITHUB_ENV"
fi
if [ "${INPUT_DEPLOY_IMAGE:-true}" != "true" ]; then
echo "CLOUD_RUN_ENV_SYNC_WAIT_FOR_COMMIT=false" >> "$GITHUB_ENV"
fi

- name: Set up Python for strategy requirement resolution
if: steps.config.outputs.env_sync_enabled == 'true'
uses: actions/setup-python@v6
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ Snapshot-backed profiles use upstream artifacts from `UsEquitySnapshotPipelines`

For the HK-equity runtime scope, platform matrix, and env defaults, see [`docs/hk_equity_runtime.md`](docs/hk_equity_runtime.md).

For HK verify-only rollout planning, print the switch plan first instead of changing Cloud Run directly:
For HK verify-only rollout planning, print the switch plan first. To deploy an isolated dry-run service, manually trigger the `Deploy Cloud Run` workflow with `target=hk-verify`:

```bash
python scripts/print_strategy_switch_env_plan.py --profile hk_listed_global_etf_rotation --dry-run-only --deployment-selector hk-verify --account-scope hk-verify --account-group hk-verify --service-name interactive-brokers-hk-verify-service --json
gh workflow run sync-cloud-run-env.yml --repo QuantStrategyLab/InteractiveBrokersPlatform -f target=hk-verify -f cloud_run_region=<gcp-region> -f cloud_run_service=interactive-brokers-hk-verify-service -f account_group=hk-verify -f account_group_config_secret_name=ibkr-account-groups -f deploy_image=true -f sync_env=true
```

Example runtime pointer:
Expand Down Expand Up @@ -433,10 +434,11 @@ feature-snapshot 类策略使用 `UsEquitySnapshotPipelines` 或 `HkEquitySnapsh

港股运行时范围、平台矩阵和环境变量默认值见 [`docs/hk_equity_runtime.md`](docs/hk_equity_runtime.md)。

港股 verify-only 接入先打印切换计划,不直接改 Cloud Run:
港股 verify-only 接入先打印切换计划;如需部署独立 dry-run 服务,再手动触发 `Deploy Cloud Run` workflow 的 `target=hk-verify`

```bash
python scripts/print_strategy_switch_env_plan.py --profile hk_listed_global_etf_rotation --dry-run-only --deployment-selector hk-verify --account-scope hk-verify --account-group hk-verify --service-name interactive-brokers-hk-verify-service --json
gh workflow run sync-cloud-run-env.yml --repo QuantStrategyLab/InteractiveBrokersPlatform -f target=hk-verify -f cloud_run_region=<gcp-region> -f cloud_run_service=interactive-brokers-hk-verify-service -f account_group=hk-verify -f account_group_config_secret_name=ibkr-account-groups -f deploy_image=true -f sync_env=true
```

### 架构
Expand Down
35 changes: 35 additions & 0 deletions docs/hk_equity_runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,41 @@ python scripts/print_strategy_switch_env_plan.py \

合并代码或打印计划不会触发生产部署;只有单独执行 Cloud Run env 更新/部署命令才会改变服务配置。

## 显式部署 HK verify-only Cloud Run

仓库的 `Deploy Cloud Run` workflow 支持手动 `workflow_dispatch` 目标 `hk-verify`。这个目标会覆盖为独立服务和港股 dry-run 环境:

- `CLOUD_RUN_SERVICE=interactive-brokers-hk-verify-service`(可通过输入改名)
- `STRATEGY_PROFILE=hk_listed_global_etf_rotation`
- `ACCOUNT_GROUP=hk-verify`
- `RUNTIME_TARGET_JSON.execution_mode=paper`、`dry_run_only=true`
- `IBKR_DRY_RUN_ONLY=true`
- `IBKR_MARKET=HK`、`IBKR_MARKET_EXCHANGE=SEHK`、`IBKR_MARKET_CURRENCY=HKD`、`IBKR_MARKET_DATA_SYMBOL_SUFFIX=.HK`

手动部署示例:

```bash
gh workflow run sync-cloud-run-env.yml \
--repo QuantStrategyLab/InteractiveBrokersPlatform \
-f target=hk-verify \
-f cloud_run_region=<gcp-region> \
-f cloud_run_service=interactive-brokers-hk-verify-service \
-f account_group=hk-verify \
-f account_group_config_secret_name=ibkr-account-groups \
-f deploy_image=true \
-f sync_env=true
```

如果只想同步环境变量、不重新部署镜像,可以设置 `-f deploy_image=false -f sync_env=true`;workflow 会跳过 commit wait,避免等待一个并未部署的新 revision。

执行前确认:

- 目标 Cloud Run service 是独立 HK verify service,不是当前生产服务。
- GitHub 变量或输入里有 `CLOUD_RUN_REGION`、`GLOBAL_TELEGRAM_CHAT_ID`、`NOTIFY_LANG`、`IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME`。
- `TELEGRAM_TOKEN_SECRET_NAME` 或 `TELEGRAM_TOKEN` 可用。
- `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME` 指向的账号组里存在 `hk-verify`,且只绑定预期的 HK paper/verify 账号。
- GCP deploy service account 仍只负责部署;IBKR 登录、账号、Gateway 地址等私密配置继续放在 Secret Manager 的 account-group payload。

## 订单、组合和行情口径

- 股票订单通过 `Stock(symbol, IBKR_MARKET_EXCHANGE, IBKR_MARKET_CURRENCY)` 构造;港股默认是 `SEHK/HKD`。
Expand Down
8 changes: 8 additions & 0 deletions scripts/print_strategy_switch_env_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ def build_switch_plan(
"checks": HK_DRY_RUN_CHECKS,
"blocked_actions": HK_BLOCKED_DRY_RUN_ACTIONS if dry_run_only else [],
}
if dry_run_only:
dry_run_plan["workflow_dispatch"] = {
"workflow": "sync-cloud-run-env.yml",
"target": "hk-verify",
"cloud_run_service": service_name or "interactive-brokers-hk-verify-service",
"deploy_image": True,
"sync_env": True,
}
notes.append(
"HK-equity switch plans are environment plans only; merge alone must not change production Cloud Run."
)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,13 @@ def test_print_strategy_switch_env_plan_for_hk_global_etf_dry_run():
assert "IBKR_FEATURE_SNAPSHOT_PATH" in plan["remove_if_present"]
assert plan["dry_run_plan"]["dry_run_only"] is True
assert plan["dry_run_plan"]["verify_only"] is True
assert plan["dry_run_plan"]["workflow_dispatch"] == {
"workflow": "sync-cloud-run-env.yml",
"target": "hk-verify",
"cloud_run_service": "interactive-brokers-hk-verify-service",
"deploy_image": True,
"sync_env": True,
}
assert any("lot-size" in check for check in plan["dry_run_plan"]["checks"])
assert any("production Cloud Run" in action for action in plan["dry_run_plan"]["blocked_actions"])

Expand Down
9 changes: 9 additions & 0 deletions tests/test_sync_cloud_run_env_workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ grep -Fq 'uses: actions/setup-python@v6' "$workflow_file"
grep -Fq 'python -m pip install -r requirements.txt' "$workflow_file"

grep -Fq 'ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION: ${{ vars.ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION }}' "$workflow_file"
grep -Fq 'target:' "$workflow_file"
grep -Fq 'default: hk-verify' "$workflow_file"
grep -Fq 'INPUT_DEPLOY_IMAGE: ${{ inputs.deploy_image }}' "$workflow_file"
grep -Fq 'Apply HK verify-only dispatch defaults' "$workflow_file"
grep -Fq '"strategy_profile": "hk_listed_global_etf_rotation"' "$workflow_file"
grep -Fq 'echo "IBKR_DRY_RUN_ONLY=true"' "$workflow_file"
grep -Fq 'echo "IBKR_MARKET=HK"' "$workflow_file"
grep -Fq 'echo "IBKR_MARKET_EXCHANGE=SEHK"' "$workflow_file"
grep -Fq 'echo "CLOUD_RUN_ENV_SYNC_WAIT_FOR_COMMIT=false" >> "$GITHUB_ENV"' "$workflow_file"
grep -Fq 'CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}' "$workflow_file"
grep -Fq 'CLOUD_RUN_SERVICES: ${{ vars.CLOUD_RUN_SERVICES }}' "$workflow_file"
grep -Fq 'CLOUD_RUN_SERVICE_TARGETS_JSON: ${{ vars.CLOUD_RUN_SERVICE_TARGETS_JSON }}' "$workflow_file"
Expand Down