API Key 授权配置¶
从"本地开发无鉴权"升级到"生产可用的 scope + 限额 + 审计"。
概念速览¶
一把 key 一条记录,SHA-256 存在 keys.json(明文只在生成时打印,一次性)。
每把 key 有多类约束:
- scope —— 能调什么接口。
qot:read/acc:read/trade:simulate/trade:real/trade:unlock/admin - allowed_markets / allowed_symbols / allowed_trd_sides —— 市场 / 品种 / 方向白名单
- allowed_acc_ids —— 账户白名单(v1.4.84+,per-key 限定只能动哪些 acc_id;见本文末尾)
- max_order_value / max_daily_value —— 单笔和日累计金额上限
- max_orders_per_minute —— 速率(滑动窗口)
- hours_window —— 时间窗口(本地时区,支持跨午夜
22:00-04:00) - allowed_machines —— 软机器绑定(防 keys.json 整体拷到别的机器)
REST / gRPC / 核心 WS / MCP 四条入口共享同一套 keys.json,你不用在四个地方各配一遍。
1. 安装 futucli¶
和 futu-opend 同一个 tarball / Cargo workspace 产出。
2. 生成一把只读 key¶
输出:
✅ Generated key "research"
plaintext: fc_8a3f2b9c1e5d7a4b8c2f9e1d3a5b7c9f ← 保存这串,别关终端
stored in: /Users/you/.config/futu/keys.json
明文只打一次
keys.json 里只存 SHA-256。明文丢了就丢了,只能 revoke 重生成。
3. 生成一把带额度的交易 key¶
./futucli gen-key \
--id sim-bot \
--scopes qot:read,acc:read,trade:simulate \
--allowed-markets HK,US \
--allowed-trd-sides SELL \
--max-order-value 100000 \
--max-daily-value 500000 \
--max-orders-per-minute 5 \
--hours-window 09:30-16:00 \
--expires 30d
这把 key 只能:
- 模拟环境下下单(trade:simulate,不是 trade:real)
- 只卖不买(--allowed-trd-sides SELL,allowed_trd_sides 白名单)
- 只做港美股
- 单笔 ≤ 10 万,日累计 ≤ 50 万
- 每分钟 ≤ 5 单
- 只在 09:30-16:00 之间能下
- 30 天后自动失效
4. 启动网关指向 keys.json¶
./futu-opend \
--login-account 12345678 --login-pwd 'your_pwd' \
--rest-port 22222 --rest-keys-file ~/.config/futu/keys.json \
--grpc-port 33333 --grpc-keys-file ~/.config/futu/keys.json \
--ws-keys-file ~/.config/futu/keys.json \
--audit-log /var/log/futu-audit.jsonl
启动日志会变:
INFO keys_loaded=2 REST keys file loaded (Bearer auth enabled)
INFO keys_loaded=2 gRPC keys file loaded (Bearer auth enabled)
INFO keys_loaded=2 WS keys file loaded (Bearer/?token auth enabled)
INFO audit JSONL logger enabled (target=futu_audit → file)
5. 用 key 访问各个接口¶
6. 列 / 改 / 吊销 key¶
# 看现在有哪些
./futucli list-keys
# 吊销一把
./futucli revoke-key sim-bot
# 改机器绑定(就地编辑,不换 plaintext)
./futucli bind-key sim-bot --this-machine
./futucli bind-key sim-bot --replace --machines fp_abc123,fp_def456
./futucli bind-key sim-bot --freeze # 临时禁用
./futucli bind-key sim-bot --clear # 解除绑定
热重载
改完 keys.json 不用重启 —— kill -HUP <pid> 就行。REST / gRPC / 核心 WS / MCP 四条入口都支持 SIGHUP 热重载,新 key 秒级生效,吊销的 key 立刻失效。
7. 查审计日志¶
# 最近被拒的请求
jq 'select(.outcome=="reject")' /var/log/futu-audit.jsonl | tail -20
# 某把 key 今天下了多少单
jq 'select(.key_id=="sim-bot" and .endpoint|test("order"))' /var/log/futu-audit.jsonl | wc -l
# 按 reason 统计(DuckDB 也行)
jq -r 'select(.outcome=="reject") | .reason' /var/log/futu-audit.jsonl \
| sort | uniq -c | sort -rn
8. Prometheus 抓 metrics¶
/metrics 端点在 bearer_auth middleware 之外,不需要 token:
curl http://localhost:22222/metrics
# futu_auth_events_total{iface="rest",outcome="allow",key_id="research"} 1234
# futu_auth_limit_rejects_total{iface="grpc",key_id="sim-bot",reason="rate"} 7
# futu_ws_filtered_pushes_total{required_scope="trade",key_id="research"} 42
生产用防火墙 / bind 127.0.0.1 限制外部访问 metrics。
9. 多 agent 账户隔离(v1.4.35+)¶
如果你跑多 agent / 多策略(比如用 LLM 把 N 个 bot 接 MCP),默认情况下每把带
trade:real scope 的 key 都能动所有已 unlock 的 acc_id。v1.4.35 加了
--allowed-acc-ids per-key 白名单:
# 生成 bot-A 的 key:只允许操作 10001 / 10002
./futucli gen-key --id bot-A \
--scopes trade:real,acc:read \
--allowed-acc-ids 10001,10002 \
--max-order-value 5000 --max-daily-value 20000
# 生成 bot-B 的 key:只允许操作 10003
./futucli gen-key --id bot-B \
--scopes trade:real,acc:read \
--allowed-acc-ids 10003
bot-A 的 key 试图对 10003 下单 → daemon 立即返
acc_id 10003 not in allowed list {10001, 10002},不碰到后端。
4 层隔离强度对照¶
--allowed-acc-ids 是 operational safety,不是 financial isolation。
两者的区别对选方案很关键:
| 层 | 方案 | 操作隔离 | 财务隔离 | 成本 | 场景 |
|---|---|---|---|---|---|
| L1 | 单 daemon 全权限 | ❌ | ❌ | 0 | 单人自用 |
| L2 | 单 daemon + 多 key + --allowed-acc-ids |
✅ | 纯现金下实质 ✅ | 低 | LLM 多 agent / 多策略 |
| L3 | 多 daemon + 同一 union card | ✅(等同 L2) | 同 L2 | 中 | ❌ 无额外隔离价值 |
| L4 | 多 daemon + 多个 union card(多 login_id) | ✅ | ✅(后端 customer 级) | 高(×N KYC) | 大额融资 / 期权 / 企业 |
关键认知:L2 能做到什么 + 不能做到什么¶
后端风控是 customer 级。同 union card 下 Futu 后端(总权益 / BP / margin / 强平触发)按 customer(union card) 做,不是按 session —— 多 daemon 登同一个 login_id 在后端看来等同于"同一个客户开了几个会话"。
但"财务传导"的严重程度看账户类型:
| 账户 / 策略类型 | 一个子账户亏光会怎样 | 是否传导 |
|---|---|---|
| 纯现金(不融资不融券) | 该子账户余额归零、仓位清零;B 账户一分不动 | ❌ 不传导 |
| 融资账户但未借款 | 行为同纯现金 | ❌ 不传导 |
| 融资 + 实际借款且亏穿本金 | A 净权益为负,broker 按统一账户可能跨账户强平 | ⚠️ 有传导 |
| 期权组合 / 跨品种保证金 | 风控模型跨子账户聚合 | ⚠️ 有传导 |
对主流 LLM 多 agent 场景(纯现金 / 小额),L2 --allowed-acc-ids 实质上已经
等同财务隔离——没借钱就没传导路径。
L2 主要防: - Agent 代码 bug / LLM 幻觉 / prompt injection 让它误动不该动的账户 - Key 泄露后被拿去动白名单外账户 - 多 agent / 多策略共享 daemon 时按 key 做审计归因
L2 不防(这些情况要上 L4): - 主动开融资并借款后跨账户 margin call 传导 - 期权组合 / 期货保证金跨子账户聚合 - 极端行情(跳空 / 熔断)下风控强平失灵赤字