跳转至

常见坑位速查(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 → CLIcode="US.MSFT" trd_market=2--market US --code MSFT
  • CLI → MCP--market US --code MSFTsymbol="US.MSFT"
  • MCP → RESTsymbol="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(推荐):

echo -n "your-trade-password" | md5sum
# 或 macOS:
echo -n "your-trade-password" | md5

Python

import hashlib
pwd_md5 = hashlib.md5(b"your-trade-password").hexdigest()

Rust

use md5::{Md5, Digest};
let pwd_md5 = format!("{:x}", Md5::digest(b"your-trade-password"));

常见错误

  • ❌ 传明文密码(如 "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 返回字段为准。

建议流程

  1. 先跑 futucli quote-rightsfutucli user-info 看账号实时权限。
  2. 没有实时权限时,优先用 snapshot 获取可用的延时快照。
  3. 如果需要持续推送、摆盘或实时 quote,请升级对应市场 / 品类的实时行情权限。

相关文档