简介:这是面向期货CTP量化交易者的穿透式监管下单测试程序包,针对2019年6月上期所CTP接口升级后申请交易权限需完成自动化测试的场景,提供自动开仓、撤单和平仓的完整实现。压缩包内共89个文件、15.64MB,包含C++源码(cpp/h)、已编译exe可执行程序、thosttraderapi/se等动态库、setting.ini与cfg配置文件、bat批处理脚本及说明文档,既可直接运行,也便于二次开发。程序需在setting.ini中配置账户与合约(如将合约字段改为当前主力合约),运行后自动对螺纹钢开1手、平1手完成穿透式监管测试,后续可凭结果申请宏源期货正式账户授权码。源码覆盖交易回调TraderSpi、行情订阅MdSpi等模块,可帮助开发者理解穿透式监管下CTP接口的报单与回报流程。目前已吸引757人学习下载,适合期货CTP接口开发者、量化交易研究者在仿真环境(如SIMNOW)或实盘权限申请前使用。 从拿到这份“宏源和SIMNOW CTP穿透式监管下单撤单过测试程序和源代码.zip”资源包开始,到真正在仿真环境里把第一条报单发出去、又成功撤回来,我前后折腾了差不多一周。这里面的坑说多不多,说少不少,但每一条都足够让人原地抓狂。这篇文章就是把我整个过测试流程、源码改造思路以及排查问题的心得完整记录下来,给后面要接CTP穿透式监管、要过期货公司测试的朋友一个参考。
这份资源包解决的核心问题很明确:在期货公司正式切换穿透式监管后,所有客户端交易指令必须附带完整的终端信息(操作系统、IP、MAC、硬盘序列号等),并且需要在SIMNOW或其他仿真环境完成认证、下单、撤单全流程测试后,期货公司才会给你开放生产环境的交易权限。所以不管是做量化交易的个人投资者,还是做交易软件的开发团队,这份资料都能帮你少走很多弯路——尤其是那些“看起来跑通了,但就是过不了测试”的玄学问题,我这次基本都替你踩了一遍。
1. 穿透式监管到底在监管什么
1.1 从“看得到”到“穿透看”,CTP接口背后的监管升级
先给刚接触这块的朋友补个基础。CTP(综合交易平台)是期货公司普遍使用的交易系统接口,过去我们写程序连CTP,只需要填个BrokerID、UserID、Password,登录后就能正常下单。但在穿透式监管要求落地之后,光有这几样已经不满足了。
穿透式监管的核心逻辑,是把“投资者->期货公司->交易所”这条链路里的每一层都看清楚。期货公司不仅要看到你交易账户是谁,还要看到你用什么终端软件、什么设备、什么网络环境发出的指令。所以CTP接口在原有功能之外,强制增加了一个认证环节,要求客户端在登录之前先完成AppID和AuthCode的认证握手,同时把终端信息(TerminalInfo)随报单指令一起提交。
这份资源包里的测试程序和源代码,说白了就是一套完整的“认证+登录+下单+撤单”流程示例。它不是只给你一个跑通的程序,而是把每个环节的函数调用、参数拼装、回报处理都写清楚了,能让你完整理解穿透式监管在代码层面是怎么落地的。
1.2 为什么SIMNOW成了首选测试环境
SIMNOW是上期技术提供的仿真交易环境,最大的价值在于它和真实CTP柜台在接口层面几乎一致,但是不需要真实资金,也不需要期货公司提前给你开生产权限。你可以用模拟账号在上面反复测试认证流程、报单撤单逻辑、异常情况处理,等完全跑通了再去申请生产环境。
我前期踩过的坑里,至少有三个是“在SIMNOW上正常,一上宏源真实环境就出问题”的类型。根源在于不同期货公司部署的CTP版本、安全代理配置、甚至前置机地址格式都有差异。所以建议你用这套代码做测试时,从第一天就把SIMNOW和宏源当作两个独立环境来适配,不要把“能在SIMNOW跑通”当成大功告成。
2. 宏源与SIMNOW的差异与适配逻辑
2.1 两个环境的配置差异对照
很多第一次接触的人以为CTP的接口是统一标准,配置不就改个地址的事嘛。这个想法大方向没错,但细节上能坑死你。我把两个环境的关键差异整理成了表格,照着这个去检查配置会省很多时间。
| 配置项 | SIMNOW仿真环境 | 宏源期货CTP环境 |
|---|---|---|
| 交易前置地址 | tcp://simnow.sit.com.cn:41205(旧)/ 新地址以官方公告为准 | 由宏源期货提供,通常为tcp://x.x.x.x:端口 |
| 行情前置地址 | tcp://simnow.sit.com.cn:42213 | 由宏源期货提供 |
| BrokerID | 通常为9999 | 宏源分配的ID |
| 资金账号 | 官网注册的模拟账号 | 真实资金账号 |
| AppID认证 | 需要提前在SIMNOW官网申请 | 需要向宏源期货申请并录入白名单 |
| 终端信息校验 | 相对宽松,但不校验也会失败 | 严格,终端信息缺失直接拒绝 |
这里特别提醒一下,宏源环境对AuthCode的校验非常严格。有朋友遇到过代码在SIMNOW上怎么跑都正常,换到宏源后登录阶段直接报“客户端认证失败”,日志里连具体原因都没有。后来才发现是宏源白名单里的AppID和自己程序里写的不一致,光是这个大小写和特殊字符的问题就排查了大半天。
2.2 程序适配的两种思路
针对不同环境,程序里通常有两种做法。第一种是直接用配置文件区分环境,启动时读取不同的前置地址、BrokerID和认证信息。这是我认为最推荐的方式,因为后续你自己要接其他期货公司时,只需要增加一个配置节就行,完全不用改代码。
第二种做法是代码里写死一套参数,每次切换环境时重新编译。这种方式在紧急测试时看着省事,但实际体验非常痛苦,改一个地址要重新编译一次,频繁切换环境时容易改错。我的源代码里采用的是第一种方式,所有环境差异都收敛在一个配置类里,后续接其他期货公司时替换配置即可。
3. 测试程序核心模块拆解
3.1 认证前置:AppID与AuthCode的握手流程
穿透式监管新增的认证环节是整个流程的第一道关卡。CTP的接口变化在于,原来登录前只需要ReqUserLogin,现在必须在登录前先走一次ReqAuthenticate。这个函数需要传入BrokerID、UserID、AppID、AuthCode这四个关键参数,其中AppID和AuthCode由期货公司分配,且必须在他们的白名单里。
我贴一段核心认证代码结构,供参考:
// 认证请求结构体 CThostFtdcReqAuthenticateField authReq; memset(&authReq, 0, sizeof(authReq)); strcpy(authReq.BrokerID, m_brokerId.c_str()); strcpy(authReq.UserID, m_userId.c_str()); strcpy(authReq.AppID, m_appId.c_str()); strcpy(authReq.AuthCode, m_authCode.c_str()); // 发送认证请求 m_pTradeApi->ReqAuthenticate(&authReq, nRequestId++);认证完成的标志是收到OnRspAuthenticate回调,并且ErrorID为0。只有等这个回调成功之后,才能继续调用ReqUserLogin。这个顺序千万不能乱,我见过有人把认证和登录一起发送,结果登录永远失败,报错信息还非常模糊。
3.2 下单流程的实现与核心参数
认证和登录都成功之后,就可以进入真正的交易流程了。下单的核心函数是ReqOrderInsert,需要构造CThostFtdcInputOrderField结构体。这里有一个原则性的注意点:当下穿透式监管环境下,这个字段里必须额外填写InstrumentID、ExchangeID、Direction、CombOffsetFlag等基础信息,同时必须正确设置OrderPriceType、LimitPrice、VolumeTotalOriginal等交易参数。
下面是我在测试程序里使用的下单请求结构,字段尽可能完整,避免因为缺少字段而被柜台拒绝:
CThostFtdcInputOrderField order; memset(&order, 0, sizeof(order)); strcpy(order.BrokerID, m_brokerId.c_str()); strcpy(order.InvestorID, m_userId.c_str()); strcpy(order.InstrumentID, "rb2410"); // 合约代码 strcpy(order.ExchangeID, "SHFE"); strcpy(order.OrderRef, GetNewOrderRef().c_str()); order.Direction = THOST_FTDC_D_Buy; // 买方向 order.CombOffsetFlag[0] = THOST_FTDC_OF_Open; // 开仓 order.CombHedgeFlag[0] = THOST_FTDC_HF_Speculation; order.OrderPriceType = THOST_FTDC_OPT_LimitPrice; order.LimitPrice = 3660.0; order.VolumeTotalOriginal = 1; order.TimeCondition = THOST_FTDC_TC_GFD; order.VolumeCondition = THOST_FTDC_VC_AV; order.MinVolume = 1; order.ContingentCondition = THOST_FTDC_CC_Immediately; order.ForceCloseReason = THOST_FTDC_FCC_NotForceClose; order.IsAutoSuspend = 0; order.UserForceClose = 0; m_pTradeApi->ReqOrderInsert(&order, nRequestId++);下单后的回报链路是:OnRtnOrder(报单状态变化)-> OnRtnTrade(成交回报)。测试程序里对这两个回调都做了日志输出,方便你对照着验证。注意,如果报单被拒,会先收到OnRspOrderInsert,它的ErrorID非零,后面不会再收到OnRtnOrder。
3.3 撤单流程与边界情况处理
撤单的核心函数是ReqOrderAction,关键点是必须用“交易所生成的OrderSysID”来撤单,而不是用你自己的OrderRef。因为同一时间可能有多个报单在途,OrderRef只在本地会话内有效,柜台真正识别单子靠的是OrderSysID。
撤单请求的构造如下:
CThostFtdcInputOrderActionField action; memset(&action, 0, sizeof(action)); strcpy(action.BrokerID, m_brokerId.c_str()); strcpy(action.InvestorID, m_userId.c_str()); strcpy(action.InstrumentID, order.InstrumentID); strcpy(action.ExchangeID, order.ExchangeID); strcpy(action.OrderSysID, m_orderSysId.c_str()); action.ActionFlag = THOST_FTDC_AF_Delete; m_pTradeApi->ReqOrderAction(&action, nRequestId++);撤单成功的标志是OnRtnOrder里订单状态变成THOST_FTDC_OST_Canceled。如果你的程序是在收到OnRtnTrade之前就撤,可能遇到“已报待撤”的中间状态,这在测试中是完全正常的,不要把它当成错误处理。
这里额外说一个边界情况,就是FOK(Fill or Kill)和FAK(Fill and Kill)类订单的撤单。这类订单存在生命周期极短的可能性,你还没来得及撤就已经全部成交或自动撤销了。所以代码里对撤单的结果一定要做“订单已不存在”的处理,否则会得到“撤单找不到原报单”的报错。
4. 从零到一跑通测试的完整步骤
4.1 环境准备与账号申请
在开始任何代码工作之前,先把账号和环境准备好,这一步看似简单,实际最容易出错。
SIMNOW账号直接在官网注册即可,注册后系统会分配一个模拟资金账号和对应的密码,同时需要你在网页端申请AppID和AuthCode。这里有个细节,SIMNOW的AppID申请不是即时生效的,我实测等待了大概一个小时才通过,所以建议提前一天把这一步做完。
宏源环境则需要联系期货公司的技术对接人员,申请交易前置地址、BrokerID、AppID和AuthCode。这个流程通常需要提供软件名称和版本号,如果你是自己开发的程序,如实填写即可。另外宏源环境的终端信息校验比SIMNOW严格得多,如果你的程序需要伪造或者修改终端信息,这里是通不过的,老老实实上报真实信息更靠谱。
4.2 编译、配置与运行全流程
这份资源包里的源代码是Visual Studio工程,我用VS2019顺利编译通过。打开解决方案后,你需要在Config目录下找到配置文件,把前面申请到的账号参数填进去:
[TradeFront] address=tcp://simnow.simnow.com.cn:41205 [Broker] broker_id=9999 user_id=你的模拟账号 password=你的密码 app_id=你申请的AppID auth_code=你申请的AuthCode [Contract] instrument_id=rb2410 exchange_id=SHFE limit_price=3660.0 volume=1配置完成后,直接编译运行。程序启动后的正常顺序是:
- 连接交易前置(OnFrontConnected回调触发)
- 发起认证请求(ReqAuthenticate)
- 收到认证成功(OnRspAuthenticate),发起登录请求(ReqUserLogin)
- 收到登录成功(OnRspUserLogin),等待你按回车触发下单
- 报单成功(OnRtnOrder状态为已报),等待你按回车触发撤单
- 撤单成功,程序退出
如果你看到的日志顺序和上面一致,那恭喜你,整个穿透式监管的链路已经完整跑通了。
4.3 验证结果与日志分析
程序跑通之后,不要急着关掉,重点看一下日志里几个关键节点的ErrorID和ErrorMsg。正常情况下OnRspAuthenticate和OnRspUserLogin的ErrorID都应该是0,如果有非零值,多半是账号权限或者配置问题,而且这个日志中的ErrorMsg往往能直接告诉你原因。
另外,你需要核对终端信息是否完整上报。在穿透式监管的要求下,报单指令里会附带终端信息。如果期货公司要求严格,你还需要在测试前把测试机的IP、MAC地址等报备给期货公司,否则即使程序显示报单成功,期货公司侧也可能校验不通过。
我把一些关键回报字段整理在了下面,方便你核实自己程序的处理逻辑:
| 回调函数 | 关键字段 | 成功标志 |
|---|---|---|
| OnRspAuthenticate | ErrorID | 0为成功 |
| OnRspUserLogin | ErrorID、SessionID | 0为成功 |
| OnRtnOrder | OrderStatus、StatusMsg | 订单状态为已报/成交/已撤 |
| OnRtnTrade | Price、Volume、TradeID | 有成交记录产生 |
5. 实操中遇到的坑与排查技巧
5.1 高频踩坑点:认证失败、终端信息不全
把这次测试过程中遇到的最典型问题整理成速查表,方便各位对照排查。
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 认证请求无任何回报 | 前置地址连错、前置未启动 | 确认OnFrontConnected已触发 |
| 认证回报“客户端认证失败” | AppID或AuthCode不在白名单 | 联系期货公司核对白名单 |
| 认证成功但登录失败 | 密码错误或账号未激活 | 确认密码和账号状态 |
| 下单后秒级收到拒单 | 终端信息不完整或合约代码错误 | 检查报单字段、TerminalInfo |
| 撤单失败提示未知订单 | OrderSysID为空或已成交 | 检查OnRtnOrder中OrderSysID赋值时机 |
| 程序崩溃退出 | 回调函数中处理了耗时操作 | 在回调里只做日志和状态更新 |
这里特别说明一下认证失败的问题。很多人会遇到OnFrontConnected已经触发,但发出ReqAuthenticate后迟迟没有回报的情况。这个大概率不是代码问题,而是SIMNOW的环境偶尔会限流。我试过晚上十点高峰期发认证请求,等了快一分钟才返回,而白天测试的时候基本秒回。建议测试避开交易高峰时段,实测下午两点到三点之间SIMNOW的响应最稳定。
5.2 日志定位与DEBUG技巧
整个测试流程里,日志是你最重要的排查工具。我在这份代码里特意把每个回调函数都加了详细日志,包括时间戳、函数名、关键字段值。实测下来,90%的问题都能通过日志直接定位。
这里分享一个技巧:当认证或登录失败时,优先看ErrorID,然后看ErrorMsg,不要凭感觉猜。CTP的错误信息虽然简洁,但基本都是准确的。比如“CTP:不合法的登录”往往就是密码错误或账号被锁定,而“CTP:还没有初始化”则说明前置未连接成功。
还有一个小坑,CTP的会话是单实例的,如果你在跑测试程序的同时打开了快期或其他交易软件连同一个账号,后登录的会把前面的会话踢掉。测试期间建议只保留自己程序一个连接,否则会出现诡异的断线重连问题。
6. 源代码怎么改造成自己的工具
6.1 模块划分与改造切入点
这份资源包的代码结构很规整,我把它拆解一下,方便你针对自己需求改造。整个程序按功能可以分成四个模块:连接管理模块(负责前置连接和断线重连)、认证登录模块(负责认证和登录流程)、交易业务模块(负责报单撤单)、日志模块(负责输出和记录)。
如果你是想把它改造成自己的交易工具,建议从交易业务模块入手。我的做法是把下单和撤单的请求封装成独立函数,然后对外提供简洁的调用接口。这样后续你不管是要做定时下单、条件单还是算法单,都只需要在这个模块上扩展,而不需要动认证和登录的逻辑。
6.2 从测试程序到实用工具的扩展建议
测试程序一般只做一次下单一次撤单,但实用工具需要考虑更多场景。我改造时重点做了这几件事:
- 增加报单引用(OrderRef)自增管理,避免每次用固定值导致报单混乱
- 增加订单状态管理器,用Map维护所有在途单子的状态
- 增加断开重连后的自动登录逻辑,确保网络抖动后还能继续工作
- 增加盘中定时任务,用独立线程触发交易逻辑,而不是等回车键
如果你接的是宏源的生产环境,建议把日志从控制台输出改成文件输出,同时加上滚动日志策略。尤其在生产环境里,控制台输出中断了,日志就是你唯一的排查依据。
改造完成后,建议先在SIMNOW上反复验证一周,确认各种异常路径都处理稳妥了,再切到宏源环境小仓位实跑几天。这个过程虽然慢,但能帮你避开很多生产事故。
7. 写在最后的一些经验
这次从拿到资源包到完全跑通整个穿透式监管流程,我最深的体会是:接口文档写得再清楚,都不如亲手跑一遍来得实在。很多细节问题只有在真实环境里才会暴露,比如不同期货公司的前置地址格式差异、AppID白名单生效延时、撤单时报单状态尚未更新导致的操作失败,这些都不是看文档能看出来的。
如果你手头也拿到了类似的测试包,我的建议是不要急着改代码,先把SIMNOW的测试跑通,把日志里每个字段都看明白,再考虑适配宏源环境。穿透式监管这套逻辑本身不复杂,但细节决定成败,只要耐心一点,多半都能顺利通过测试。
本文还有配套的精品资源,点击获取