常见坑位速查(UX-06/07/10/QOT)¶
本页回答跨 surface 参数格式差异、错误码 sim vs real 差异、 unlock pwd_md5 字段歧义、行情权限 / 延时快照边界四类常踩但无专门文档的问题。 v1.4.84+ 新加。
1. 跨 surface 参数格式速查(UX-07)¶
同一个字段在不同 surface 有不同命名 / 不同格式,一不小心就混用失败。
code / symbol 字段¶
| Surface | 字段名 | 格式 | 例子 |
|---|---|---|---|
REST /api/* |
code |
带 market prefix | "US.MSFT" / "HK.00700" |
gRPC futu.Trade |
code |
带 market prefix | 同上 |
CLI futucli |
--code |
不带 prefix(配 --market 用) |
--market US --code MSFT |
MCP futu_* tools |
symbol |
带 market prefix | "US.MSFT" |
market / trd_market 字段¶
| Surface | 字段 | 类型 | 数值对照 |
|---|---|---|---|
| REST / gRPC | trd_market |
数字 int32 |
1=HK, 2=US, 3=CN, 4=HKCC, 5=Futures, 6=SG, 15=JP |
| CLI | --market |
字符串 | HK / US / CN / HKCC / Futures / SG / JP |
| MCP | market |
字符串 | 同 CLI |
跨 surface 换 surface checklist¶
- REST → CLI:
code="US.MSFT" trd_market=2→--market US --code MSFT - CLI → MCP:
--market US --code MSFT→symbol="US.MSFT" - MCP → REST:
symbol="US.MSFT"→code="US.MSFT" trd_market=2
v1.4.84 起,daemon 对所有 surface 统一 strip prefix¶
入口层 derive_security_type / derive_sec_market / is_futures_code /
is_option_code / parse_option_dte / cache key 构造都会自动剥 HK. / US.
等前缀,所以带不带前缀都能 work(REST 带前缀 + CLI 不带前缀的文化差异
不再致命)。
但建议遵循 surface 惯例(上表),避免日后 daemon 升级时出奇怪 regression。
2. 错误码 sim vs real 差异(UX-06)¶
重要:sim 账户和 real 账户对同一个 invalid 请求可能返不同错误行为, 用 sim 测出来的结果不一定代表 real 会 work。
典型差异¶
| 场景 | sim 行为 | real 行为 | 教训 |
|---|---|---|---|
下单 symbol 格式错 (US.MSFT v1.4.56 之前) |
两种格式都返 -1 generic | 带 prefix 失败 / 不带 prefix 成功 | sim 看不出 prefix bug |
| trd_env 不匹配(real acc + trd_env=0) | v1.4.50 之前不校验 | backend 拒 | sim 误通 |
不存在的 acc_id (999999) |
返空列表 | backend "Nonexisting acc_id" | sim 误导"账户无资产" |
| 期货 prefix 识别错 (v1.4.56-57 之前) | 两种可能都返 -1 | 精确 110005 合约不一致 | sim 覆盖不到 |
规则¶
- 开发用 sim(安全、不真花钱)
- 关键交易验证必须 real 至少过一次(对齐 C++ OpenD 行为)
- 回归验证建议同时测 sim + real 双验证
v1.4.51 起 daemon 加强 sim vs real 一致性¶
- trd_env 必校验(v1.4.51 起)
- acc_id 存在性必校验(v1.4.51 起)
- camelCase / snake_case normalize(v1.4.45 serde drop 防御)
但不能 100% 消除差异 —— backend 层 sim 和 real 是不同服务路径。
3. unlock_trade.pwd_md5 字段歧义(UX-10)¶
UnlockTradeReq.pwd_md5 字段名暗示要 MD5 hash,但不清楚:
- 是否真要 hash?还是明文?
- 如果 hash,hash 什么?小写 hex 还是 upper?
真相¶
必须是 pwd 的 MD5 hash,小写 hex,32 字符。
Why MD5(对齐 C++ OpenD)¶
- backend 历史设计:cipher 协议用 pwd_md5 不是明文 pwd
v1.4.6+Rust daemon 对齐- 如果传明文密码,backend 签名校验失败返 generic error(不会告诉你字段格式错)
如何生成¶
CLI helper(推荐):
Python:
Rust:
常见错误¶
- ❌ 传明文密码(如
"fuTUnn!ryA88") → daemon 返 generic error,不 block 但没 成功 unlock - ❌ MD5 大写 hex(如
"0FE6...")→ backend 校验失败(不同 byte repr) - ❌ SHA1 / SHA256 hash 代替 MD5 → 同样失败
- ❌ 带 salt 的 hash → 同样失败(backend 不加 salt 算 pwd_md5)
- ✅ 纯 MD5, 32 char 小写 hex(如
"a1b2c3d4e5f6789012345678901234ab")
v1.4.84+ 强化 error 提示(未来 v1.4.84+ 可能加)¶
目前 daemon 对非 32-char hex 输入只是透传 backend,backend 返 generic error。 未来可能在 daemon 入口 check:
if pwd_md5.len() != 32 || !pwd_md5.chars().all(|c| c.is_ascii_hexdigit()) {
return error("unlock_trade.pwd_md5 must be 32-char lowercase hex (MD5 hash)");
}
4. 行情权限与延时快照边界¶
quote-rights / user-info 显示的是当前账号各市场的行情权限概览。它能帮助你判断
当前账号是否有实时行情权限,但不同接口对“没有实时权限”的处理并不完全一样。
路径差异¶
| 路径 | 行为 |
|---|---|
snapshot / /api/snapshot / futu_get_snapshot |
单次快照;当后端提供延时快照时,可在无实时权限时返回可用的延时数据 |
quote / subscribe / orderbook |
实时行情路径;需要对应市场 / 品类的实时权限,不自动 fallback 到延时行情 |
quote-rights / user-info |
权限展示路径;用于查看当前账号有哪些行情权限,不代表所有品类都有同样的延时数据形状 |
常见误解¶
- “snapshot 能拿到数据,所以 subscribe 也应该能拿到”:不一定。snapshot 是单次请求, subscribe / quote / orderbook 是实时行情链路,权限 gate 更严格。
- “有 crypto 权限字段,所以所有
CC.*symbol 都能返回”:不一定。symbol 仍需要存在于 静态证券库;不存在的 symbol 会报错,而不是返回空成功。 - “不同品类的延时数据字段应该一样”:不一定。股票、期权、期货、加密货币等品类由后端 返回的延时字段可能不同;以实际 snapshot 返回字段为准。
建议流程¶
- 先跑
futucli quote-rights或futucli user-info看账号实时权限。 - 没有实时权限时,优先用
snapshot获取可用的延时快照。 - 如果需要持续推送、摆盘或实时 quote,请升级对应市场 / 品类的实时行情权限。
相关文档¶
guide/cli.md— CLI 命令完整列表guide/rest.md— REST API 端点guide/mcp.md— MCP tools 列表changelog.md— 版本更新历史