拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TqSdk期货量化实战笔记:从环境搭建到实盘交易

TqSdk期货量化实战笔记:从环境搭建到实盘交易 做期货量化这事最磨人的不是策略本身而是行情和交易通道不统一。我最初尝试自己拼 CTP 接口光是权限申请、行情解码、会话维护就折腾了一个多月后来换用 TqSdk才算把主要精力从“怎么连”转移到“怎么写策略”上。TqSdk 是天勤量化提供的开源 Python 接口它把国内期货市场的行情订阅、K线管理、委托下单、持仓查询、资金账户全部统一进同一个 API内部将不同交易所的合约直接映射成统一的数据结构。下面这份笔记完整记录我从零接触 TqSdk 的过程从环境搭建、账号认证、行情接口、交易接口到模拟与实盘的衔接以及我踩过的坑。整个经历适合已经会一点 Python、想直接把策略跑在期货市场上的开发者参考。1. 为什么最终选择了 TqSdk而不是自己拼 CTP1.1 一套 API 同时拿下行情、交易、数据和账户国内期货量化有一个绕不开的现状交易所提供的底层接口是 CTP它本身是 C 接口需要自己处理会话状态、报单回报、风控推送还要自己拼接行情协议。CTP 当然功能全但学习成本和维护成本都不低。自己写的话要解决三件事一是行情订阅和推送二是交易委托和回报三是账户和持仓的对账。这三件事只要有一件没处理好策略就跑不踏实。TqSdk 的定位就是把这三大块全部收回库内部。它对上屏蔽掉交易所、柜台、网关之间的差异对下统一暴露 Python 对象。行情是 quote 和 kline交易是 insert_order、cancel_order、get_position账户是 get_account。调用方不需要关心底层走的是 SimNow、快期还是期货公司柜台也不需要理解 CTP 的会话机制。我实际用下来的体感是在本地起一个连接行情自动推成交回报自动回调代码量比直接用 CTP 少一半以上。而且它跟市面上常见的解决方案不太一样。有些开源框架把行情、策略、交易强制绑定在自研的事件引擎里学起来很重TqSdk 更接近“接口库”而非“框架”它不强求你用某种策略模板你可以只在里面取数据、下单也可以自己搭策略循环。这种轻巧的定位对有自己想法的开发者来说非常友好。我现在生产环境里策略主体完全是自己写的循环逻辑TqSdk 只负责数据通道和交易通道分工明确。1.2 TqSdk 背后的生态和对新手的价值一开始我还担心开源库的稳定性和持久性后来发现 TqSdk 背后有完整的商业产品在支撑包括快期终端、天勤量化、信易云端认证、模拟盘环境等。这意味着我用的不是某个爱好者写的小工具而是一套在市场里被大量用户检验过的方案。模拟盘可以直接通过信易账号接入不用先开证券户就能跑通整套行情和交易逻辑这对策略验证来说起步门槛极低。面向初学者TqSdk 最大的价值是“所见即所得”。一个合约代码写对了立刻能拉回 K 线数据下单参数传对了模拟账户立刻能返回成交回报。整个链路短、反馈快、可调试。我甚至见过有人依赖 TqSdk 自带的历史数据回放去做简单回测虽然它定位不是专门的回测框架但胜在数据真实、调用简单。选型这件事没有绝对正确但如果你和我一样不想困在 CTP 的原生复杂度里又想保留自己对策略的控制权TqSdk 是一个值得认真投入的方向。我用了大概两三天就完成了从“不会”到“能下单”的转变这个吞吐量是我之前没有预料到的。2. 环境准备与账号体系先把门打开2.1 安装 Python 环境和 TqSdk 依赖TqSdk 是纯 Python 库安装路径非常简单。我的环境是 Python 3.10、Windows 11直接在虚拟环境里执行 pip 安装pip install tqsdk安装过程中会自动拉取 pandas、numpy、websockets 等依赖。如果你机器上已经装有旧版 pandas建议先升级到 TqSdk 要求的版本否则在数据序列操作时容易遇到版本兼容问题。装完验证一下导入是否正常python -c from tqsdk import TqApi, TqAuth; print(TqSdk OK)能正常打印说明环境就绪了。有一个细节需要注意TqSdk 对 Python 版本的兼容性整体不错官方推荐的版本区间我实测下来 3.8 到 3.11 都能跑但如果你用的是 3.12 以上个别依赖可能会有编译问题。建议新项目直接用 3.10 或 3.11省心很多。2.2 信易账号认证与三种连接模式新版本 TqSdk 强制要求使用 TqAuth 认证这是天勤云端的账号体系。注册成功后拿到用户名和密码就可以通过 TqAuth 在代码里完成鉴权。这个设计把“数据权限”和“交易权限”解耦同一个信易账号可以同时用于模拟盘和部分实盘渠道的接入。TqSdk 的连接模式要根据场景选择我整理成了一张对照表连接模式代码示例适用场景纯模拟TqApi(authTqAuth(用户, 密码))策略调试、数据观察内置模拟交易TqApi(TqSim(), authTqAuth(用户, 密码))跑模拟下单流程实盘对接TqApi(TqAccount(快期, 账号, 密码), authTqAuth(用户, 密码))真实交易关键点在于默认不传 account 参数时TqApi 会以纯模拟身份连上行情服务器能看行情但没法做真实委托。如果需要走完整的下单、持仓、回报链路必须显式传入 TqSim 或 TqAccount。TqSim 等于在本地模拟一个期货账户所有委托都在内存中撮合不会发出任何真实请求非常适合第一次写下单代码时使用。实盘连接时 TqAccount 的三个参数分别是柜台接入名称、期货账号、密码。实际项目中不同期货公司的接入名称可能不同如果你通过快期或其他行情终端接入在对应开通后台里能看到准确名称。第一次连实盘时我建议先在收盘后测试确认连接不报错再考虑运行策略。3. 行情接口核心实战K线与实时报价3.1 合约代码规则大小写、交易所和月份TqSdk 的合约代码格式是“交易所.品种代码合约月份”比如上海期货交易所黄金 2412 合约是SHFE.au2412大连商品交易所豆粕 2501 合约是DCE.m2501。交易所缩写保持大写品种代码用交易所官方的小写字母这一点最容易踩坑。很多人一开始写成SHFE.AU2412结果接口直接报“合约不存在”。各交易所对应关系如下表供快速查询交易所缩写合约示例品种说明上海期货交易所SHFESHFE.au2412黄金大连商品交易所DCEDCE.m2501豆粕郑州商品交易所CZCECZCE.SR605白糖中国金融期货交易所CFFEXCFFEX.IF2506沪深300股指上海国际能源交易中心INEINE.sc2503原油广州期货交易所GFEXGFEX.si2505工业硅合约月份通常是“交割年份的最后两位交割月份”比如2501表示 2025 年 1 月交割。写代码时建议把合约代码抽成配置文件或常量不要硬编码在策略各处。你还可以用 TqSdk 自带的合约查询接口动态拉取列表但更快的做法是从交易所官网手动确定自己要交易的合约然后在代码里校验是否存在。3.2 K线序列的获取与更新模式获取 K 线是 TqSdk 使用频率最高的功能。我一般用下面这段代码取 1 分钟 K 线from tqsdk import TqApi, TqAuth api TqApi(authTqAuth(手机号, 密码)) klines api.get_kline_serial(SHFE.au2412, duration_seconds60, data_length200) while True: api.wait_update() if api.is_changing(klines.iloc[-1]): bar klines.iloc[-1] print(bar.datetime, bar.open, bar.high, bar.low, bar.close, bar.volume)这里有个非常重要的习惯get_kline_serial只需要调用一次返回的是一个类似 pandas DataFrame 的对象里面的数据会自动通过 wait_update 更新。不要在 while 循环里反复调用 get_kline_serial那样会重新创建序列、浪费性能甚至导致数据错位。duration_seconds可填任意秒数常见值有 601分钟、3005分钟、90015分钟、36001小时、86400日线。data_length表示保留多少根 K 线在内存里同时决定了最早能取到的历史长度。实际策略中均线需要多少根历史 K 线就把 data_length 设置成对应的窗口大小再加一点余量。K 线的列包括datetime、open、high、low、close、volume、open_oi、close_oi。open_oi是开盘时的持仓量close_oi是收盘时的持仓量做持仓量分析时有用。用iloc[-1]取到的就是最新一根正在形成的 K 线它在每次 tick 变化时都会更新直到该周期结束生成定形 K 线。3.3 实时报价订阅和关键字段除了 K 线实时报价也是策略必需的。TqSdk 取报价非常直接quote api.get_quote(DCE.m2501) while True: api.wait_update() if api.is_changing(quote): print(最新价, quote.last_price) print(买一价, quote.bid_price1, 买一量, quote.bid_volume1) print(卖一价, quote.ask_price1, 卖一量, quote.ask_volume1) print(今开, quote.open, 最高, quote.high, 最低, quote.low) print(成交量, quote.volume, 持仓量, quote.open_interest)quote 对象每次行情变化后通过 wait_update 刷新。从币价、盘口到涨跌停、持仓量、成交量字段非常全。我最常用的字段是last_price、ask_price1、bid_price1、volume、open_interest、datetime。需要注意datetime字段是交易所时间戳不是本地时间做日志和回测时要注意时区。事件驱动是 TqSdk 行情机制的核心。wait_update()会阻塞直到有数据更新is_changing(对象)用来判断某个数据对象是否在这轮更新中发生变化。这两个函数配合可以把轮询式代码写成事件式代码每次行情变化只处理变化的部分不用自己去比较新旧数据。刚开始写的时候容易把 wait_update 放在不重要的位置导致程序一直卡住后来我总结的经验是所有行情和交易的动作都要放在 wait_update 之后的代码段里执行不要在循环开头直接读取数据。3.4 实时行情监控的小脚本把上面的模块串起来可以写一个简单的行情监控脚本用来观察特定合约的盘口变化。下面是我当时测试用的版本from tqsdk import TqApi, TqAuth api TqApi(authTqAuth(手机号, 密码)) quote api.get_quote(CZCE.SR605) max_price 0 while True: api.wait_update() if api.is_changing(quote): if quote.last_price max_price: max_price quote.last_price print(f最新价 {quote.last_price} 最高价 {max_price} 盘口差 {quote.ask_price1 - quote.bid_price1})运行这个脚本可以直观感受到行情推送的频率以及 wait_update 的触发节奏。这也是我建议新手做的第一个实验先跑通行情再跑交易不要一上来就同时碰两边。4. 交易接口全流程记录下单、持仓、撤单4.1 下单接口的参数和下单方式TqSdk 的下单接口设计得很简洁核心就一个函数api.insert_order(symbolDCE.m2501, directionBUY, offsetOPEN, volume2, limit_pricequote.ask_price1)参数含义如下symbol合约代码必须和行情订阅的一致。direction交易方向BUY买入SELL卖出。注意这里的“买入”和“卖出”是持仓方向而不是开平仓方向。offset开平标志OPEN开仓CLOSE平今仓CLOSEYESTERDAY平昨仓。volume手数。limit_price限价单价格填quote.ask_price1就是对手价填quote.bid_price1就是挂单买入价。对新手来说最容易绕晕的就是 direction 和 offset 的组合。比如你想“做空”先 SELL OPEN之后再 BUY CLOSE 平仓。想“做多”先 BUY OPEN之后 SELL CLOSE。不要用“买”和“卖”的日常语义去理解要用“开仓方向”和“平仓方向”去理解。在模拟盘里用错了方向你会发现持仓盈亏和自己想的方向完全相反这是很典型的入门错误。下单函数会返回一个订单对象你可以轮询它的状态或者直接依赖 wait_update 自动刷新。订单生命周期常见的有两种状态ALIVE表示委托在交易所存活可能挂在那边排队FINISHED表示完成全部成交、部分成交或已撤单。实际判断是否成交更可靠的是看volume_left它表示剩余未成交手数。4.2 订单状态跟踪与成交确认我常用的下单后跟踪模式是order api.insert_order(symbolDCE.m2501, directionBUY, offsetOPEN, volume2, limit_pricequote.ask_price1) while order.status ALIVE: api.wait_update() if order.trade_price and order.volume_origin - order.volume_left 0: print(成交价, order.trade_price, 成交手数, order.volume_origin - order.volume_left) else: print(未成交或已撤单)这里有个细节值得注意order.trade_price在部分成交时表示平均成交价如果订单只成交了一部分volume_left会大于 0。做策略时要考虑部分成交的情况不能看到 FINISHED 就默认全部成交了。在行情剧烈波动时限价单很容易只有部分成交。模拟盘撮合和真实交易所的撮合存在差异模拟盘的流动性由 TqSim 自己模拟大多数情况下你报对手价会立刻成交但真实市场上存在排队、滑点、部分成交等复杂情况。因此用模拟盘验证逻辑可以但不要用模拟盘的成交速度来评估真实策略的执行效果。4.3 持仓、资金和撤单查询持仓查询是用来确认当前仓位的基础操作pos api.get_position(DCE.m2501) print(多头今仓, pos.pos_long_today) print(多头昨仓, pos.pos_long_hd) print(空头今仓, pos.pos_short_today) print(空头昨仓, pos.pos_short_hd)这个接口会区分“今仓”和“昨仓”因为国内期货有“平今”和“平昨”的不同手续费标准。做高频交易时如果频繁开平最好根据持仓状态选择 CLOSE 还是 CLOSEYESTERDAY否则可能多交手续费或者报“平仓手数不足”的错。资金查询同样简单account api.get_account() print(动态权益, account.balance) print(可用资金, account.available)撤单接口则是对订单对象直接操作order api.insert_order(symbolDCE.m2501, directionBUY, offsetOPEN, volume1, limit_pricequote.bid_price1) api.wait_update() # 如果还没有成交就撤掉 if order.status ALIVE: api.cancel_order(order) print(已提交撤单)实用建议是高频策略中撤单必须和一个状态检查配对使用撤单后要继续 wait_update 并确认订单最终状态。我见过有人刚提交撤单就立刻开新仓结果旧单没撤掉导致仓位叠加超出自己的控制范围。撤单是异步动作必须等订单状态变成 FINISHED 且成交数量不再变化再继续后续操作。4.4 一个双均线策略的完整例子下面的代码演示了一个最简单的双均线策略5 分钟 K 线上当 5 周期均线上穿 20 周期均线时开多下穿时平多。我刻意简化了风控和报错处理但整条链路是完整的from tqsdk import TqApi, TqAuth, TqSim import time api TqApi(TqSim(), authTqAuth(手机号, 密码)) kl api.get_kline_serial(DCE.m2501, duration_seconds300, data_length50) while True: api.wait_update() if api.is_changing(kl.iloc[-1]): close kl[close] ma5 close.iloc[-5:].mean() ma20 close.iloc[-20:].mean() ma5_prev close.iloc[-6:-1].mean() ma20_prev close.iloc[-21:-1].mean() pos api.get_position(DCE.m2501) # 上穿开多 if ma5_prev ma20_prev and ma5 ma20 and pos.pos_long_today 0: api.insert_order(symbolDCE.m2501, directionBUY, offsetOPEN, volume1, limit_priceapi.get_quote(DCE.m2501).ask_price1) print(开多) # 下穿平多 if ma5_prev ma20_prev and ma5 ma20 and pos.pos_long_today 0: api.insert_order(symbolDCE.m2501, directionSELL, offsetCLOSE, volumepos.pos_long_today, limit_priceapi.get_quote(DCE.m2501).bid_price1) print(平多)示例里有两个我特别在意的细节一是均线比较时我取了前一根 K 线的均值用来判断“穿越”而不是单点交叉二是平仓手数直接用持仓数量而不是写死 1 手。如果你把平仓手数写死遇到部分成交就永远平不完。真实策略里我会把开平逻辑封装成独立函数并加上订单状态跟踪但作为演示这个结构足够帮你理解循环的下单节奏。5. 常见问题排查实录我踩过的那些坑5.1 连接与认证异常第一批遇到的错误基本都集中在连接阶段。最常见的是auth认证失败通常表现为连接时报权限错误或直接连接超时。排查方向有三个确认信易账号密码是否输入正确确认网络能正常访问外网确认没有在短时间内频繁重连触发服务端限流。另一个容易遇到的问题是把模拟盘和实盘的账户参数搞混。有些朋友第一次连实盘时误把期货柜台账号填成了信易账号导致认证失败。这里要澄清TqAuth 是信易云的账号TqAccount 才是期货账户信息两者不是一回事。写代码时建议分成两个配置变量避免混淆。还有一种是本地数据中心相关的问题。TqSdk 在启动时会向云端请求数据服务地址如果本地防火墙或代理限制了连接会在日志里看到网络层报错而不是业务层报错。排查这类问题最简单的方法是先关闭代理再用纯直连模式测试。5.2 数据异常与操作习惯问题数据类问题主要集中在 K 线更新和合约代码错误上。合约代码错误是最隐蔽的因为错误提示有时候很直接有时候则是你等了大半天数据都没有变化。解决方法是先打印出api.get_quote(合约代码)确认返回的不是空数据再进入正式逻辑。在循环内重复调用 K 线接口也是新手重灾区。一旦每次循环都调用 get_kline_serial不仅内存持续增长还会因为序列被重置而得到不一致的历史数据。正确方式是我在前文强调的初始化时调用一次循环里只依赖 wait_update 自动更新。还有一个细节是is_changing的判断粒度。它支持传入整个 quote 对象也支持传入具体字段比如api.is_changing(quote.last_price)。如果只关心最新价变化用字段粒度可以减少无效计算。我在做盘口监控时就只监听 ask_price1 和 bid_price1 的变化而不是每次行情变动都全量处理。5.3 交易操作中的典型问题交易操作最常见的报错是“仓位不足”和“平仓方向错误”。当代码报平仓手数不足时优先检查持仓查询结果看是今仓还是昨仓再决定用 CLOSE 还是 CLOSEYESTERDAY。模拟和实盘对今昨仓的区分是同样严格的提前在模拟盘里把这些边界情况测试完实盘才不至于手足无措。第二个典型的坑是部分成交后的仓位残留。如果你的订单最终只成交了 1 手而策略逻辑假设它成交了 2 手就会造成仓位统计偏差。我的习惯是每次平仓后都重新查一次持仓并把未成交的单子单独记入日志。宁可等一个循环确认状态也不要盲目撮合下一单。此外限价单的风险要重视。模拟盘对价格限制的宽容度很高你在模拟盘里用极端价格下单大概率也能成交但在实盘里会用涨停价或跌停价成交产生巨大亏损。真实交易中建议始终使用ask_price1或bid_price1作为参考并设置最大偏离范围。I created a table summarizing common issues for quick reference:问题现象可能原因处理建议连接认证失败信易账号或密码错误、网络受限核对账号、关闭代理、检查重连频率数据一直不更新合约代码错误或非交易时段用 get_quote 验证合约是否存在K 线序列错乱循环内重复调用 K 线接口初始化时一次性获取后续只 wait_update平仓报手数不足今仓昨仓没有区分查询持仓后再决定 CLOSE 还是 CLOSEYESTERDAY策略仓位统计偏差部分成交未处理下单后跟踪 volume_left成交后重新查持仓5.4 实盘中的风控侧问题实盘和模拟最大的差别不是接口调用而是“订单不会按你设想的方式成交”。我刚开始跑实盘时有一次策略认为已经全部平仓实际上还有一单挂在跌停价附近等成交结果下一轮信号又开了一倍仓位。后来我把风控规则前置到下单函数里单笔开仓仓位上限、总持仓上限、单日亏损上限三个硬限制任何一个触发都不再发新单。风控是整个系统里优先级最高的模块。我的做法是把风控的判断放在策略信号之前每一轮循环先执行风控检查再判断是否需要下单。宁可行情来了没跟上也不要在风控状态异常时强行交易。另外日志系统必须有至少记录每一项委托的发出时间、参数、状态变化和成交回报。6. 从模拟盘到实盘部署心得与建议6.1 模拟盘与实盘的差异梳理从模拟切到实盘之前建议先在统一代码框架下分别跑几天。模拟盘对极端行情的反应远没有实盘激烈滑点、盘口厚度、涨跌停排队这些机制都没有真实市场那么有效。实盘连上后你会发现同一个策略模拟盘里稳定盈利实盘里却是另一回事这不是代码问题而是对接市场的执行成本变了。我的体会是模拟盘适合验证两件事业务逻辑是否正确能否按预期开仓平仓、行情判断是否灵敏。实盘则要额外评估第三件事市场的流动性和摩擦成本占据多少利润。尤其做短线策略手续费和滑点很可能直接把策略利润吃光。这部分很难在模拟盘里准确评估只能在实盘小仓位试运行中获取第一手数据。6.2 运行架构和我的实操建议实盘运行环境我建议和开发环境彻底分离至少做到独立机器或云服务器、独立日志目录、独立账号密码管理。不要在你的日常开发电脑上直接跑实盘策略因为系统升级、休眠、网络切换都可能中断程序而程序中断意味着仓位暴露在市场风险里。我在实盘环境中加了几个基础保障组件进程守护程序挂掉自动重启、每日定时校验持仓、策略外层的全局风控盾。进程守护用系统自带的任务计划就能实现关键是重启策略时不能重复下单建议在启动时做一次仓位核对如果有历史残留仓位就进入暂停状态等待人工处理。日志是实盘交易中最容易被忽视的基础设施。我刚上实盘时日志只记录关键节点后来排错发现信息不够。现在每条委托、每次撤单、每次持仓变化都会写入独立的日志文件消息里带上完整的时间戳和订单号。一次盘中故障排查这些日志能帮你快速定位是策略层、接口层还是网络层出了问题。最后分享一个我自己坚持的习惯实盘策略尽量采用小仓位分段验证。先跑一手合约跑通一个完整交易日确认从行情到报单到成交回报的链路没有任何遗漏再逐步加大仓位。这个节奏看似保守但比起一开始就用大资金试错节省的学费远超想象。期货交易的使用记录说到底不是“接口调用记录”而是一份对账户安全和策略持续优化的长期积累。
返回列表