拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python量化交易系统搭建:从CTP到天勤SDK的完整实战指南

简介本资源是一套面向本科毕业设计与量化交易初学者的Python量化交易系统实现聚焦于天勤SDK接口集成与实盘策略落地解决从数据获取、策略编写到自动下单的全流程开发问题。压缩包共58个文件含34个核心Python源码覆盖行情订阅、信号生成、订单管理、风控逻辑等模块、15个编译后pyc文件、5个XML配置及日志模板、1个conf配置文件整体仅44KB轻量易读适合快速理解框架结构与关键交互逻辑。目前已有133人学习下载反映出高校学生对金融工程实践项目的持续关注。读者可直接复用其模块化设计如基于tqsdk封装的标准化交易执行器、支持多周期K线合成的数据采集类、内置滑点与仓位控制的风险管理组件以及适配股票/期货的策略模板目录结构为毕设开发或策略原型验证提供即插即用的工程基础。 我最早搞期货量化的时候在行情接入这一层卡了整整两周。当时用的是某期货公司提供的CTP接口一个行情回调一个交易回调地处理还需要自己管理会话、重连、流控光是把行情打通就写了两千多行代码还没开始写策略就已经快崩溃了。后来换了天勤SDK同样的功能只用一个下午就跑通了后面整个项目的推进速度一下子提了上来。这篇文章我就把基于天勤SDK的Python量化交易系统的完整搭建过程拆开讲一遍从环境准备到行情处理、策略编写、回测、模拟盘、实盘部署再到那些文档里不会写的坑全部捋清楚。无论你是刚接触量化交易、还在纠结“Python到底能不能做实盘”的新手还是已经写了一些策略、想找一个省事又稳定的交易接口的老手这篇内容都值得你花十分钟读完。我用这套方案跑过的实盘账户不多但跑过的回测和模拟盘足够说明问题——天勤SDK在期货领域确实是一条非常值得走的路。1. 天勤SDK到底解决了什么问题先说一个很多新手容易误解的地方天勤SDK不是一个“策略库”它是一套集行情、交易、回测、模拟盘于一体的Python开发框架。它解决的是量化交易里最繁琐、最底层、最不产生策略价值的那部分工作——连接交易所、订阅行情、管理订单状态、处理断线重连。1.1 从CTP原生接口到天勤SDK我的选型过程CTP是期货行业最常见的交易接口但它的设计目标是给柜台系统用的不是一个给个人开发者友好使用的接口。它的原生接口是C风格的Python需要借助ctypes或者第三方封装才能调用。即便封装好了你依然要处理大量的事务性工作行情订阅需要自己维护合约列表和回调分发订单状态需要自己维护状态机断线后需要自己处理重连和数据补拉回测和实盘是完全两套逻辑切换成本高我当时做了一个简单的对比对比项CTP原生接口天勤SDK最快跑通行情2-3周起步1小时订单状态管理自己实现状态机内置订单状态跟踪回测支持需自建回测引擎内置回测框架断线重连自行处理框架自动处理上手门槛需要理解CTP协议只需要会Python实盘支持支持支持这个对比不是说要贬低CTP而是说CTP的定位是基础设施你要在其上做二次开发天勤SDK已经帮你把二次开发的成果封装好了一大部分你只需要关注策略本身。对我这种以策略盈利为核心的人来说选天勤SDK是效率最高的路径。1.2 TqSdk的核心架构天勤SDK的Python包叫tqsdk核心是一个异步事件循环后端用C实现高性能行情和交易处理对外暴露Python API。它的架构可以用一句话概括Pytthon负责策略逻辑C负责底层通信事件循环负责两者之间的消息调度。具体到代码层面核心概念有四个TqApi全局唯一的API对象负责建立连接、启动事件循环、分发数据TqAccount / TqKq / TqBacktest三种运行模式分别对应实盘账户、模拟盘和回测TqQuote行情快照包含最新价、买卖盘口、成交量等TqKLineK线序列支持多周期自动增量更新理解这几个概念之后你就能明白天勤SDK的设计哲学把你需要的数据以Python对象的形式直接给你你不需要关心数据是怎么进来的只需要写“数据到了之后应该干什么”。1.3 天勤SDK的能力边界搞清楚能做什么、不能做什么比搞清楚怎么用更重要。天勤SDK适合的场景是中低频期货策略包括日线、小时线、分钟线级别的策略需要稳定接入国内期货市场的个人和中小机构从回测到模拟盘到实盘的一条龙开发流程它不适合的场景超高频交易HFT因为Python本身和系统架构的限制亚毫秒级延迟的极限操作不是它的设计目标非期货品种比如美股、加密货币它主要覆盖国内期货市场股票和期权支持有限极其复杂的多市场跨品种套利虽然理论上可行但实践中需要更多定制开发知道了这些边界你才不会被一些不切实际的需求带偏。2. 环境准备与最小系统搭建跑通第一个行情程序很多人在这里就开始踩坑。天勤SDK的安装本身不难难的是环境里各种Python版本、依赖包之间的纠缠。我建议在开始之前先把环境理顺不然策略做了一半发现装不上包心态会很崩。2.1 Python环境的完整配置流程网上关于Python安装的教程太多了但真正到了量化交易场景有几个额外的要求需要注意建议使用Python 3.7到3.11之间的稳定版本太老的版本对tqsdk的依赖支持可能有问题太新的版本偶尔会有兼容性延迟强烈建议使用虚拟环境不要把tqsdk装到全局Python里一个项目一个环境是基本操作如果你的机器上有多个Python版本务必确认pip指向的是哪个Python我常用的环境创建流程# 安装虚拟环境工具如果没有 pip install virtualenv # 创建项目目录并进入 mkdir tqsdk_quant_project cd tqsdk_quant_project # 创建虚拟环境 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 安装tqsdk pip install tqsdk再确认一下版本和依赖是否正常python -c import tqsdk; print(tqsdk.__version__)如果能正常打印出版本号说明环境已经准备好了。如果报错缺少numpy或者pandas直接用pip补装就行pip install numpy pandas2.2 行情订阅的完整代码拆解天勤SDK跑通行情的代码异常简洁核心逻辑不超过十行from tqsdk import TqApi, TqAuth # 创建API连接这里使用免费行情账号 api TqApi(authTqAuth(你的用户名, 你的密码)) # 获取一个合约的行情引用 quote api.get_quote(SHFE.rb2310) # 打印行情快照 print(合约:, quote[instrument_id]) print(最新价:, quote[last_price]) print(买一价:, quote[bid_price1]) print(卖一价:, quote[ask_price1]) # 关闭连接 api.close()这段代码做了什么事呢它创建了一个TqApi对象通过这个对象拿到螺纹钢期货的实时行情快照然后打印出来。就这么简单。但要注意几点TqApi建立连接时会验证账号权限所以你需要先去天勤官网注册一个账号免费版就可以看行情get_quote返回的不是一个静态字典而是一个动态更新的对象行情每次变化它都会自动更新运行这段代码后程序会在几秒内拿到行情数据并输出然后立即结束因为api.close()后事件循环就退出了如果你想要持续获取行情就需要进入事件循环模式from tqsdk import TqApi, TqAuth api TqApi(authTqAuth(你的用户名, 你的密码)) quote api.get_quote(SHFE.rb2310) while True: # 等待行情更新 api.wait_update() if quote[last_price] ! quote[last_price]: # 这里可以写你的行情处理逻辑 pass这里api.wait_update()是天勤SDK最核心的等待机制。它会阻塞程序直到有新的数据推送过来然后返回。你的策略逻辑就是在这个循环里执行的。2.3 需要提前注册和认证的事项跑上面的代码之前有几个前置步骤必须完成注册天勤量化账号打开官网、注册账号、使用免费策略版即可获取账号认证信息TqAuth类需要你填入用户名和密码确认期货账户是否已有交易权限如果你只想看行情免费账号就够了如果要模拟盘天勤提供模拟盘账号如果要实盘需要你已经有期货公司的账户并确认开通了CTP交易权限我最开始用的就是免费账号跑行情、做回测、做模拟盘都没问题直到决定上实盘时才需要绑定期货账户。这算是比较友好的方式可以在不投入资金的情况下把整套系统跑通。3. 策略引擎的设计把交易逻辑和行情解耦跑通行情只是第一步真正的核心工作是写策略。很多人一上来就想写复杂策略我建议从最朴素的双均线策略开始先把“行情到信号到下单”这条链路走通再逐步迭代优化。3.1 双均线策略最小可行策略双均线策略的逻辑非常简单当短期均线上穿长期均线时买入开仓当短期均线下穿长期均线时卖出平仓但在量化系统里“买入开仓”和“卖出平仓”不是直接调接口就完了需要处理信号产生、仓位检查、下单确认、风控校验等多个环节。所以我的策略代码会分几层写from tqsdk import TqApi, TqAuth, TqKq from tqsdk.tafunc import ma api TqApi(authTqAuth(你的用户名, 你的密码), accountTqKq()) # 获取K线序列使用5分钟线 klines api.get_kline_serial(SHFE.rb2310, duration_seconds300, data_length100) # 获取行情引用 quote api.get_quote(SHFE.rb2310) # 持仓管理 position api.get_position(SHFE.rb2310) while True: api.wait_update() # 等K线闭合后计算均线 if api.is_changing(klines.iloc[-1], datetime): ma_short ma(klines[close], 5) # 短期均线 ma_long ma(klines[close], 20) # 长期均线 if ma_short.iloc[-1] ma_long.iloc[-1] and ma_short.iloc[-2] ma_long.iloc[-2]: # 金叉开仓 if position[pos_long] 0 and position[pos_short] 0: api.insert_order(symbolSHFE.rb2310, directionBUY, offsetOPEN, volume1, limit_pricequote[last_price]) print(金叉买入开仓) elif ma_short.iloc[-1] ma_long.iloc[-1] and ma_short.iloc[-2] ma_long.iloc[-2]: # 死叉平仓 if position[pos_long] 0: api.insert_order(symbolSHFE.rb2310, directionSELL, offsetCLOSE, volumeposition[pos_long], limit_pricequote[last_price]) print(死叉卖出平仓)这段代码有几点设计值得说明为什么需要api.is_changing(klines.iloc[-1], datetime)因为get_kline_serial返回的是K线序列最后一根K线还没有闭合是变化的。如果每一根新报价都去算均线那最后一根K线会在整个5分钟周期内不断变化导致信号可能反复触发。通过is_changing判断这一根K线的时间戳是否有更新可以确保我们只在新的K线闭合后计算一次信号。为什么使用TqKq()作为账户它是模拟盘账户可以帮助我们在不投入真金白银的情况下测试策略逻辑。后续要上实盘时只需将账户换成TqAccount即可。3.2 事件驱动模型K线闭合、Tick推送与回调双均线策略的代码看起来是“顺序执行”但底层其实是事件驱动的。天勤SDK有两种事件通知机制轮询模式api.wait_update()返回后检查所有你关心的数据对象是否发生变化回调模式通过api.register_handler注册事件回调函数对于不同频率的策略选择不同的事件模型日线级别策略用轮询模式就够了反正一天只处理一次信号分钟级别策略建议用轮询 时间戳判断保持代码的可读性高阶一点可以写一个策略基类把不同的信号判断逻辑封装到不同的handler中便于后续扩展和组合我自己写过一套简单的策略基类核心逻辑是class StrategyBase: def __init__(self, api, symbol): self.api api self.symbol symbol self.quote api.get_quote(symbol) self.klines api.get_kline_serial(symbol, duration_seconds300, data_length100) self.position api.get_position(symbol) def on_kline_closed(self): # 子类实现K线闭合后的信号判断 raise NotImplementedError def run(self): while True: self.api.wait_update() if self.api.is_changing(self.klines.iloc[-1], datetime): self.on_kline_closed()这个基类把“行情等待”和“策略逻辑”解耦了后续写新策略只需要继承这个基类并实现on_kline_closed方法。这在维护多策略系统时非常有用。3.3 持仓管理避免重复开仓和平仓新手最常见的bug是信号出现后因为行情继续变化信号反复触发导致重复下单。解决这个问题的方法是在下单前检查当前的持仓方向和数量。天勤SDK的get_position返回的对象包含以下关键字段pos_long多头持仓量正数pos_short空头持仓量正数volume总持仓量正数代表净多负数代表净空open_price_long多头开仓均价open_price_short空头开仓均价判断是否可以开多if position[pos_long] 0 and position[pos_short] 0: # 空仓可以开仓判断是否可以平多if position[pos_long] 0: # 有多头持仓可以平多这套持仓状态机的设计是策略系统的基础不光双均线策略要用所有策略都绕不开它。4. 回测与模拟盘验证策略能不能活下来策略写完之后最关键的一步是验证。新手常常犯的错误是直接把策略上实盘结果亏得怀疑人生。正确的路径是历史回测 - 模拟盘验证 - 小资金实盘 - 逐步放大。天勤SDK把这几个步骤都封装得非常简洁。4.1 历史回测的完整配置天勤SDK自带回测引擎只需要把TqApi的创建参数从实盘模式换成回测模式即可。下面是双均线策略接入回测的完整示例from tqsdk import TqApi, TqAuth, TqBacktest from datetime import datetime api TqApi( authTqAuth(你的用户名, 你的密码), backtestTqBacktest( start_dtdatetime(2022, 1, 1), end_dtdatetime(2022, 12, 31) ) )就这么简单。回测引擎会按照你在start_dt和end_dt之间指定时间段的历史数据进行回放你的策略代码完全不需要修改。但这里有几个细节要注意回测期间的数据是从天勤服务器下载的首次运行会有一段下载时间回测速度取决于数据量和你的策略复杂度通常几秒钟到几分钟不等回测结果会输出到控制台包括最终权益、最大回撤、总交易次数等指标4.2 回测报告里哪些指标最重要回测完成后天勤SDK会输出一张表格包含以下关键指标指标含义参考标准最终权益期末资产需要高于初始资金总收益率收益/初始资金年化至少5%以上才有意义最大回撤净值从峰值到谷底的最大跌幅越低越好夏普比率风险调整后的收益大于1为合理大于2为优秀胜率盈利交易占比40%-60%都正常盈亏比平均盈利/平均亏损大于1.5更稳健最容易被忽略的是最大回撤。很多新手只看收益率却忽略了回撤。如果回测最大回撤达到30%那么实盘时心理压力会非常大很可能在策略回撤期的中段就手动干预甚至砍仓退出。所以我的建议是最大回撤超过20%的策略最好直接放弃或大幅优化。4.3 模拟盘和回测结果对不上的原因回测表现不错的策略到了模拟盘常常出现偏差。这是正常的原因主要有三类撮合机制不同回测使用的是历史K线数据撮合逻辑基于K线级别的价格区间模拟盘则是实时撮合会考虑盘口深度、流动性等因素手续费和滑点回测中可以设置手续费和滑点但实际模拟盘的成交价格往往是买一卖一的价格和理论值有差异随机性回测结果是确定的模拟盘则包含实时市场的随机波动解决方法是在回测时主动设置更严格的滑点和手续费参数。比如设置每手手续费5元、滑点1个tick这样回测结果会更接近实盘表现。from tqsdk import TqApi, TqAuth, TqBacktest api TqApi( authTqAuth(你的用户名, 你的密码), backtestTqBacktest( start_dtdatetime(2022, 1, 1), end_dtdatetime(2022, 12, 31), quote_speed_scale1.0 ) )quote_speed_scale是行情回放速度默认是1.0表示实时速度可以加大到10.0或更高来加速回测。5. 实盘部署的关键细节从Demo到能扛事的系统回测和模拟盘都跑通之后才轮到真正的重头戏——实盘部署。这一步的挑战不是策略逻辑而是稳定性、安全性和异常处理。很多看起来能赚钱的策略实盘一上线就因为各种小问题亏损比如断线没重连、下单没确认、仓位对不上等。5.1 实盘账户的接入与切换从模拟盘切换到实盘代码改动非常小from tqsdk import TqApi, TqAuth, TqAccount api TqApi( authTqAuth(你的用户名, 你的密码), accountTqAccount( 期货公司代码, # 比如华泰期货 资金账号, 密码 ) )但要注意TqAccount的期货公司代码必须是天勤SDK支持的公司列表中的名称。如果你的期货公司不在列表里需要联系客服确认是否支持。切换实盘前我建议开展一轮“纸上交易”演练在模拟盘上连续运行两周以上确保策略在真实行情下表现和回测基本一致再切换实盘。5.2 断线重连与订单状态同步实盘最怕的就是断线。网络波动、服务器维护、交易时段切换都可能导致连接中断。天勤SDK本身有自动重连机制但策略层面的状态同步需要自己处理。我的做法是加入一套心跳监控和状态恢复逻辑import time from tqsdk import TqApi, TqAuth, TqAccount class TradingSystem: def __init__(self): self.api None self.connected False self.last_heartbeat time.time() def connect(self): self.api TqApi( authTqAuth(你的用户名, 你的密码), accountTqAccount(期货公司代码, 资金账号, 密码) ) self.connected True self.last_heartbeat time.time() def check_connection(self): if time.time() - self.last_heartbeat 10: # 心跳超时尝试重新连接 self.api.close() self.connect() else: self.last_heartbeat time.time()这只是一个简单的示例。实际生产级系统里还需要处理订单状态同步。比如断线前你下了一个单但没收到回报重连后需要查询该订单的状态确认是已成交、已撤销还是仍在排队。天勤SDK的get_order方法可以查询单个订单的状态api.get_trade_serial()可以获取成交记录。5.3 风控模块仓位限制、撤单与熔断我见过太多策略因为缺少风控模块而爆仓的案例。比如信号连续触发导致仓位越加越大比如行情反向剧烈波动但策略来不及平仓。实盘系统必须内置三层风控第一层单笔下单位限制。单笔开仓手数不超过总资金的2%对应的手数def get_max_volume(price, total_capital, risk_ratio0.02): max_amount total_capital * risk_ratio volume int(max_amount / price) return volume第二层单日亏损熔断。当日亏损超过策略总资金的3%停止开新仓并清仓class RiskControl: def __init__(self, init_equity, daily_loss_limit_ratio0.03): self.init_equity init_equity self.daily_loss_limit init_equity * daily_loss_limit_ratio def check_daily_loss(self, current_equity): loss self.init_equity - current_equity return loss self.daily_loss_limit第三层异常行情保护。当价格波动超过阈值、或者策略报错次数超过上限时自动停止交易并报警。这三层风控缺一不可。实盘拼的不是谁赚得多而是谁活得久。我见过很多高收益策略最后都因为一次极端行情或者一次系统bug直接清零风控的价值不是让你多赚钱而是让你不亏大钱。6. 避坑实录我在开发过程中踩过的几个坑最后这部分我把自己踩过的坑集中列一下每一行都是真金白银换来的教训。6.1 K线闭合判断的坑很多新手在用is_changing判断K线闭合时会写成这样if api.is_changing(klines.iloc[-1]):这在K线更新时就会触发但问题在于K线序列的最后一根K线是不断变化的这种写法会导致同一根K线周期内策略被重复触发多次。正确做法是if api.is_changing(klines.iloc[-1], datetime):也就是明确检查“最新K线的时间戳是否发生了变化”。时间戳变化意味着新的一根K线开始了旧的那根已经闭合了这时候才应该计算信号。这是一个非常小的细节但直接影响策略的信号准确性。6.2 合约代码格式的坑天勤SDK的合约代码格式是“交易所代码.合约代码”比如SHFE.rb2310。很多新手会把“上海期货交易所”写成SHFE.或shfe.rb2310结果就是拿不到行情。还要注意不同交易所的代码前缀交易所代码前缀示例上海期货交易所SHFESHFE.rb2310大连商品交易所DCEDCE.m2401郑州商品交易所CZCECZCE.SR309中国金融期货交易所CFFEXCFFEX.IF2308上海国际能源交易中心INEINE.sc2309其中郑州商品交易所的合约代码通常是大写字母加数字需要注意大小写和具体格式。如果拿不准可以用天勤提供的api.get_instrument查询合约信息。6.3 多策略并发时的数据共享问题如果你同时跑多个策略多个策略共同使用同一个TqApi实例需要注意数据对象的共享问题。比如两个策略都订阅了同一个合约的K线但它们需要的K线长度不一样可能导致数据覆盖或者信号错乱。我的解决方法是每个策略维护自己的K线序列实例即使合约相同也各自独立get_kline_serial。天勤SDK底层会做数据缓存和管理你不需要担心重复订阅浪费资源但策略逻辑上必须隔离。6.4 交易时段切换的坑国内期货市场有日盘和夜盘之分不同品种夜盘时间不同。策略在非交易时段订阅行情时wait_update会一直阻塞直到开盘。如果不做处理你可能会以为程序卡死了。我的做法是指在策略启动前判断当前时间是否在交易时段如果不在给出提示并定时重试。这样可以避免在夜盘时段错误启动日盘策略、或者反向操作的情况。6.5 Python环境变量和路径的坑在部署到服务器时容易遇到环境变量不对的问题。比如用cron定时运行策略时cron环境里可能找不到Python路径或者找不到tqsdk模块。解决方法是把策略包成shell脚本在脚本里显式指定虚拟环境的Python路径#!/bin/bash cd /path/to/your/project /path/to/venv/bin/python main.py这样可以确保定时任务使用的是正确的Python环境和依赖。7. 自定义策略扩展从单品种到多品种多策略当你把单个品种的双均线策略跑通之后下一步自然是想扩展到多品种、多策略。天勤SDK在这里也有灵活的支持。7.1 多品种轮询最简单的多品种策略扩展是把需要交易的品种都订阅一遍然后在事件循环里判断每个品种是否到了信号触发点symbols [SHFE.rb2310, DCE.i2309, CZCE.SR309] klines_map {} for symbol in symbols: klines_map[symbol] api.get_kline_serial(symbol, duration_seconds300, data_length100) while True: api.wait_update() for symbol, klines in klines_map.items(): if api.is_changing(klines.iloc[-1], datetime): # 对每个品种执行策略逻辑 pass这样写的好处是代码结构简单缺点是所有品种共用一套策略参数。如果你希望不同品种用不同的参数可以把参数改成字典结构每个品种单独配置。7.2 策略组合与资金分配多策略组合的核心思想是资金分配。比如你有两个策略一个趋势跟随、一个均值回归它们的收益相关性可能很低组合起来可以降低整体回撤。具体做法每个策略独立维护自己的持仓和资金主程序负责资金分配按比例把资金划给每个策略每个策略独立运行互不干扰风控模块监管整个账户的总风险这个架构比单策略系统复杂很多我建议在单策略稳定运行三个月以上、积累了足够经验之后再升级。否则多策略只会带来多倍的麻烦。最后再分享一个我在实盘部署中总结的小经验任何策略在正式上实盘之前都先跑至少一周的模拟盘同时观察程序有没有异常日志、有没有内存泄漏、有没有订单状态对不上的情况。这一周看起来很“耽误时间”但它能帮你避开绝大多数实盘上线后才发现的问题。我自己就是从这一步走过来才敢把资金真正放进去的。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门