← 返回量化板块
QUANT NOTE · 期货量化

从 0 到 1 搭一套期货量化系统:广发仿真 + vnpy + CTP 接入踩坑全记录

原创 · 果壳量化笔记

期货量化 vnpy CTP 看穿式认证 广发期货仿真 双均线策略 4040/4097

写在前面

我一直想有一套属于自己的、能自动买卖的期货量化系统——不是买别人的软件,而是从框架、策略、接入到界面全部自己掌握。

这篇文章记录我用一个周末把"玉米双均线趋势跟踪策略"跑通在广发期货 CTP 仿真环境上的全过程。重点不是炫技,而是把过程中最折磨人的 CTP 握手失败(错误码 4040 / 4097) 从现象到根因彻底讲清楚——这部分在网上的资料非常零散,踩过的坑希望你能直接跳过。

所有代码、配置、界面都已实际跑通,截图里的黑窗口日志都是真实输出。

一、整体方案与技术选型

选型说明
量化框架VeighNa(vnpy)4.4.0开源、社区大、CTA 模块成熟
CTP 接口vnpy_ctp 6.7.11.4(自带 6.7.11 API)接上期所 CTP 协议
券商环境广发期货仿真 CTP2358仿真账号免门槛,先验证再上实盘
策略玉米双均线趋势跟踪 v1.0(增强版)金叉多 / 死叉空 + ATR 止损 + 移动止盈
运行虚拟环境 + 一键 .bat 启动器双击即跑,不用敲命令
品种玉米 / 橡胶 / 白糖 / PTA(多实例)一个品种一份策略实例

为什么选 vnpy:CTA 模块原生支持策略的"初始化 / 启动 / 停止",有图形界面可以看盘,也有无界面模式适合常驻运行。对新手最友好。

为什么先跑仿真:仿真环境和实盘用的是同一套 CTP 协议、同一套看穿式认证流程,但不需要真金白银。先把连接和策略在仿真里验证稳了,再填实盘配置(BrokerID 换成 9000、换生产授权码)即可。

二、策略:玉米双均线趋势跟踪 v1.0

策略本身不难,核心是趋势跟踪 + 风控,而不是预测涨跌:

双均线是"右侧"策略,震荡市会反复被打脸,所以加了均线排列过滤 + ATR 止损来控回撤。v1.0 阶段目标就是"能稳定自动跑、不爆仓",不是追求高收益。

三、界面与运行效果

启动图形界面(python run_vnpy.py --gui)后,窗口标题显示为"果壳 1.0 玉米交易系统":左侧有交易/委托/持仓面板,右侧和底部是行情、活动、成交、日志区。底部的"日志"区域会实时打印 CTP 连接状态,不用依赖被列宽截断的界面提示。

🖥️果壳 1.0 玉米交易系统 · GUI 界面(配图略)

如果想完全自动、不需要看盘,直接双击 启动玉米自动策略.bat(无界面模式),会在黑窗口里看到每个品种的连接、订阅和交易信号日志。

四、最硬的一关:CTP 仿真接入的 4040 / 4097

这是整篇最值得看的部分。

3.1 现象

双击启动器,黑窗口卡在:

行情接口:4040  CTP:API Front shake hand err: decode err
交易服务器:4097 连接断开

连锁反应:收不到合约列表 → 订阅 c2609 失败 → 策略初始化失败。

4040 / 4097 的本质是"握手认证失败"——CTP 前置在建立连接时校验你的身份信息(账号、密码、AppID、授权码、运行模式)没通过,直接断开。它发生在"下单"之前,所以和策略逻辑毫无关系。

3.2 三个根因(逐个排除)

我前后折腾了很久,最后定位到三个叠加因素,缺一不可:

根因 1:vnpy 根本不读你写的 production_mode 字段 ⭐(最大的坑)

我们最初在配置里写:

GF_SIM = {
    "production_mode": True,   # 以为这个控制实盘/仿真
    ...
}

翻 vnpy_ctp 源码才发现,网关 connect() 实际只读一个叫 柜台环境 的键:

# vnpy_ctp/gateway/ctp_gateway.py
envrionment = setting.get("柜台环境", "实盘")
production_mode = envrionment == "实盘"   # 只有"实盘"才是 True

我们的配置里压根没有 柜台环境 这个键,于是 vnpy 默认当成 "实盘" —— 也就是一直用生产模式去连仿真前置。之前我反复改 production_mode 那几次,对 vnpy 来说完全是无效操作,这也是"怎么改都不行"的真正原因。

根因 2:仿真环境必须 production_mode = false(测评模式)

广发下载的 CTP API 包里有一份官方《接入仿真环境须知》,白纸黑字:

交易 TraderAPI 和行情 MdAPI,有一个 bool 类型的默认参数 blsProductionMode,表示 api 是否使用生产模式,true 为生产模式(默认值),false 为测评模式。即,接入评测(仿真)环境时,需留意配置 blsProductionModefalse

也就是说,连仿真必须显式传 false。我们之前一直按"实盘 / true"连,握手必然失败。

✅ 正确写法:

GF_SIM = {
    "柜台环境": "评测",   # 关键!让 vnpy 传 production_mode=false
    ...
}

根因 3:CTP API 版本不匹配(6.7.11 vs 6.7.13)

vnpy_ctp 自带的底层 DLL 是 6.7.11,而广发仿真要求 V6.7.13。客户经理让从官网"软件下载"页下 CTP 仿真评测 API 包,里面 Win_Linux_v6.7.13_20260225_trader.zip 解压出 64 位 thosttraderapi_se.dll / thostmduserapi_se.dll,直接覆盖进:

vnpy_env/Lib/site-packages/vnpy_ctp/api/

覆盖前记得备份旧 DLL(建个 _backup_6.7.11/ 目录),万一要回滚。Python 是 64 位,所以一定要用包里的 64 位 se 版本。

4.3 解决后的样子

三个坑全填上之后,黑窗口出现如下日志:

行情服务器连接成功
交易服务器连接成功
行情服务器登录成功
交易服务器登录成功
交易服务器授权验证成功
CTP 连接成功日志(配图略,敏感账号已打码)

4040 / 4097 彻底消失。复盘一下:握手失败基本只有两条路——要么"模式/认证"配错,要么"版本"不对。按上面三点逐一核对,基本都能过。

五、另一个差点让我放弃的坑:改错文件

4040 修好之后又报了一轮"找不到合约"。排查一圈,发现我前面把配置改在了根目录 ctp_config.py,但 run_vnpy.py 实际 from config.ctp_config import ... 读的是 config/ctp_config.py

教训:Python 的导入路径和文件实际位置必须一致。改完配置一定用一行 python -c "from config.ctp_config import get_ctp_config; ..." 验证"真正被加载的那个文件"内容对不对,而不是只看自己改的那个。

六、从单品种到多品种

量化不可能只做玉米。加上橡胶、白糖、PTA 时,架构上做了个清晰的决定:

vnpy 的 CTA 策略,每个实例绑定一个合约(vt_symbol)。多品种 = 每个品种起一份策略实例。 它们各自管自己的仓位、止损、信号,互不干扰。

实现上只是在入口维护一张表:

STRATEGY_SYMBOLS = {
    "玉米": "c2609.DCE",    # 大商所,乘数 10
    "橡胶": "ru2609.SHF",   # 上期所,乘数 10
    "白糖": "SR2609.CZCE",  # 郑商所,乘数 10
    "PTA":  "TA2609.CZCE",  # 郑商所,乘数 5
}

然后遍历这张表,逐个 add_strategy → init → start。同时给策略加了个 multiplier 参数(玉米/橡胶/白糖 10 吨每手,PTA 5 吨每手),仓位计算才不会算错手数。

换月也很简单:把 c2609 改成 c2611 之类即可,仿真里哪个合约有行情就用哪个。

七、踩坑清单(Cheat Sheet)

给想自己搭的朋友一份速查:

  1. 握手 4040/4097:先查 柜台环境 是否为 "评测"(仿真),再查 DLL 版本是否和券商要求一致。
  2. vnpy 不读 production_mode:它读的是 柜台环境 这个键,"实盘"= 生产模式。
  3. DLL 必须 64 位且和 Python 位数一致import struct; print(struct.calcsize('P')*8) 看位数。
  4. 改完配置验证的是"被加载的那个文件",不是你手改的那个。
  5. 非交易时间(午休 11:30–13:30、夜盘外)仿真不推送合约/行情,连上了也可能 30 秒收不到合约——这不是 bug,等开盘。
  6. 授权码有 T+1 生效:报备后第二天才可用,当天连会一直握手失败。
  7. 仿真账号首次登录必须改一次密码,改完同步到配置。
  8. 启动 ≠ 立刻下单:策略只在出现信号且空仓时才交易;停止也不会自动平仓,持仓需手动处理。

八、写在最后

这套系统现在能:双击启动器 → 自动连广发仿真 → 4 个品种各自跑双均线策略 → 自动买卖 + 止损止盈,图形界面也能实时看盘。

下一步计划:

量化最有意思的地方,不是某次赚了多少,而是把"凭感觉"变成"可复现、可验证、可改进"的系统。这套骨架搭好之后,换策略、加品种、上实盘都是增量工作。

如果你也在用 vnpy 接券商仿真,希望这篇能帮你省下那几天和那几次"为什么就是不行"的崩溃。

附录:关键配置脱敏示例

# config/ctp_config.py(已脱敏)
GF_SIM = {
    "用户名": "6000****",              # 广发下发的仿真资金账号(已打码)
    "密码": "********",                 # 首次登录改过的密码
    "经纪商代码": "2358",               # 广发仿真 BrokerID
    "交易服务器": "183.62.252.251:42405",
    "行情服务器": "183.62.252.251:42413",
    "产品名称": "client_999071860_1.0",  # 报备的 AppID
    "授权编码": "********",             # 广发下发的看穿式授权码(勿外泄)
    "柜台环境": "评测",                  # 仿真必须为"评测"
}

⚠️ 授权码、密码、账号属于敏感信息,切勿发到公网。文中的图片和代码示例里的 **** 都是发布前必须打码的位置。AppID 和前置地址一般可公开,但建议也视券商要求处理。

本文为个人搭建经验记录,不构成任何投资建议。期货交易有杠杆风险,仿真验证充分后再考虑实盘。