
简介这是面向期货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之前就撤可能遇到“已报待撤”的中间状态这在测试中是完全正常的不要把它当成错误处理。这里额外说一个边界情况就是FOKFill or Kill和FAKFill and Kill类订单的撤单。这类订单存在生命周期极短的可能性你还没来得及撤就已经全部成交或自动撤销了。所以代码里对撤单的结果一定要做“订单已不存在”的处理否则会得到“撤单找不到原报单”的报错。4. 从零到一跑通测试的完整步骤4.1 环境准备与账号申请在开始任何代码工作之前先把账号和环境准备好这一步看似简单实际最容易出错。SIMNOW账号直接在官网注册即可注册后系统会分配一个模拟资金账号和对应的密码同时需要你在网页端申请AppID和AuthCode。这里有个细节SIMNOW的AppID申请不是即时生效的我实测等待了大概一个小时才通过所以建议提前一天把这一步做完。宏源环境则需要联系期货公司的技术对接人员申请交易前置地址、BrokerID、AppID和AuthCode。这个流程通常需要提供软件名称和版本号如果你是自己开发的程序如实填写即可。另外宏源环境的终端信息校验比SIMNOW严格得多如果你的程序需要伪造或者修改终端信息这里是通不过的老老实实上报真实信息更靠谱。4.2 编译、配置与运行全流程这份资源包里的源代码是Visual Studio工程我用VS2019顺利编译通过。打开解决方案后你需要在Config目录下找到配置文件把前面申请到的账号参数填进去[TradeFront] addresstcp://simnow.simnow.com.cn:41205 [Broker] broker_id9999 user_id你的模拟账号 password你的密码 app_id你申请的AppID auth_code你申请的AuthCode [Contract] instrument_idrb2410 exchange_idSHFE limit_price3660.0 volume1配置完成后直接编译运行。程序启动后的正常顺序是连接交易前置OnFrontConnected回调触发发起认证请求ReqAuthenticate收到认证成功OnRspAuthenticate发起登录请求ReqUserLogin收到登录成功OnRspUserLogin等待你按回车触发下单报单成功OnRtnOrder状态为已报等待你按回车触发撤单撤单成功程序退出如果你看到的日志顺序和上面一致那恭喜你整个穿透式监管的链路已经完整跑通了。4.3 验证结果与日志分析程序跑通之后不要急着关掉重点看一下日志里几个关键节点的ErrorID和ErrorMsg。正常情况下OnRspAuthenticate和OnRspUserLogin的ErrorID都应该是0如果有非零值多半是账号权限或者配置问题而且这个日志中的ErrorMsg往往能直接告诉你原因。另外你需要核对终端信息是否完整上报。在穿透式监管的要求下报单指令里会附带终端信息。如果期货公司要求严格你还需要在测试前把测试机的IP、MAC地址等报备给期货公司否则即使程序显示报单成功期货公司侧也可能校验不通过。我把一些关键回报字段整理在了下面方便你核实自己程序的处理逻辑回调函数关键字段成功标志OnRspAuthenticateErrorID0为成功OnRspUserLoginErrorID、SessionID0为成功OnRtnOrderOrderStatus、StatusMsg订单状态为已报/成交/已撤OnRtnTradePrice、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的测试跑通把日志里每个字段都看明白再考虑适配宏源环境。穿透式监管这套逻辑本身不复杂但细节决定成败只要耐心一点多半都能顺利通过测试。本文还有配套的精品资源点击获取