ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于THS终端的Python量化交易全链路实战指南

基于THS终端的Python量化交易全链路实战指南

量化交易圈子里,THS是一个绕不开的名字。很多人是从手工看盘开始接触它的,后来慢慢发现:与其每天盯盘手动下单,不如让程序替我们完成数据读取、策略计算、信号生成和交易执行这一整套闭环。这篇文章我不打算讲那些悬在空中的理论,直接把我这几年用THS终端做量化交易的完整链路拆开——从数据怎么拿、怎么清洗,到策略怎么写、回测怎么做,再到模拟盘怎么验证、实盘怎么接,最后附上我踩过的坑和排查思路。不管你是刚接触量化不久,还是已经写了几百行策略代码,这篇文章都值得花十分钟看完。

先说清楚这套方案能解决什么问题:日常看盘的效率问题、数据获取的标准化问题、策略验证的可靠性问题,以及实盘执行中容易忽视的风控细节。全文以THS终端作为数据源和交易执行中枢,技术栈以Python为主,适合那些已经在用THS看盘、想往程序化方向走一步的投资者,也适合已经写了策略但还没想清楚数据管线怎么搭的人。

1. 整体架构与方案选型思考

1.1 为什么把THS终端作为数据与执行中枢

很多人在量化入门时第一个问题就是:行情数据从哪来?市面上有免费接口、有付费数据商、有爬虫方案,但我最终还是选择围绕THS终端来搭。原因很朴实:它本身就覆盖了行情、财务、公告、板块、资金流这些量化常用的数据维度,而且我日常盯盘本来就在这个软件上,数据的一致性能直接对齐。

终端方案相比纯爬虫的优势在于稳定性。行情网站的反爬策略经常变,今天能抓的字段明天可能就换了字段名,维护成本极高。THS终端至少在数据完整度和更新频率上有保障,分时、日线、财务数据都能按固定节奏获取。再加上它自带的模拟交易功能,可以在不投入真金白银的前提下验证策略执行链路。

不过这里也有一个要提前想清楚的点:THS终端本质上是给“人”看盘用的工具,程序化读取数据时要在终端和后端脚本之间做一层桥接。这层桥接做得好不好,直接决定整个策略系统的稳定性。后面我会专门讲这部分的实现细节。

1.2 量化系统的四层架构

我习惯把这套量化系统拆成四个层次,每一层职责单一,互不纠缠:

  • 数据层:负责从THS终端读取行情、财务、板块等原始数据,做清洗、复权、对齐,最终落成可供策略直接调用的标准数据表。
  • 策略层:接收标准数据,运行交易信号逻辑,输出目标持仓或买卖指令。这是整个系统的大脑,也是迭代最频繁的一层。
  • 执行层:把策略信号翻译成真实或模拟的交易动作,包括下单、撤单、成交回报处理。
  • 风控层:把止损、仓位上限、单票持仓比例、黑名单等规则硬编码在系统中,任何交易动作必须过风控才能放行。

这样分层之后,每一层的替换成本都很低。数据源坏了不影响策略回测,策略迭代了不影响执行链路,风控规则加严了也不用去动策略代码。很多人一开始写着写着就变成一锅粥,所有逻辑揉在一个脚本里,后面想加一个风控条件都小心翼翼,这就是没做好分层的代价。

1.3 技术栈的选择与理由

我的技术栈很简单:Python写策略和脚本,SQLite存历史数据,CSV做轻量数据交换,计划任务做定时轮询。

选Python没有悬念,量化生态里它最成熟,pandas处理表格数据、numpy算指标、回测框架随手就能找到。存储用SQLite是够用原则——普通个人级别的量化场景,每天更新几千只股票的日线数据,SQLite完全扛得住,没必要上重型数据库。当然数据量如果到了上亿行级别,我会考虑换Parquet列式存储,但那不是大多数人的现状。

还有一点容易被忽略:系统和脚本的编码格式。THS导出的数据在Windows上通常是GBK编码,而Python 3默认UTF-8,读进来直接乱码。这个我在初学时踩过坑,后面会提到。技术选型时就要先确认好自己的运行环境,尽量把脚本和终端的字符集问题在源头解掉。

2. 数据管线的搭建:从行情读取到标准数据表

2.1 行情数据获取的三种方式对比

我从THS终端取数据主要试过三种方式,各有优劣。

第一种是手动导出。在终端里选好股票列表和时间范围,导出Excel或CSV,再写脚本读入。这种方式的优点是零开发成本,缺点是没法自动化,只适合一次性研究,比如我刚开始研究某个策略时,用这种方式拿几天数据练手。

第二种是本地数据文件读取。THS终端会在本地目录缓存一些行情数据,找到对应路径后可以用脚本直接解析文件。这种方式速度很快,但问题在于文件格式不是公开文档化的,一旦终端升级,字段布局可能变化,脚本就得跟着改。

第三种是模拟键盘鼠标操作终端界面,自动点击菜单完成数据查询和导出。这个思路虽然在自动化程度上最高,但对界面变动非常敏感,稳定性一般。我现在的方案是结合第二种和第三种:常规日线数据走本地文件解析,特殊场景用界面自动操作补充。

无论哪种方式,有一个原则是共通的:获取数据只是第一步,数据的校验和清洗才是真正花时间的部分。

2.2 数据清洗的必做流程:复权、停牌与ST

行情数据拿到手之后,麻烦事才开始。你要是直接用原始价算收益和信号,结果会偏得离谱。这里说三个必做的清洗步骤。

复权处理是重中之重。股票分红送股之后,价格会出现跳空,但这是“假跳空”,并不是真实的市场波动。如果你拿不复权的数据算均线,信号可能在除权日当天被严重误导。我的习惯是统一用前复权数据做策略计算,也就是以当前价格为基准,把历史价格做相应调整。这样信号计算用的是同一价格基准,不会出现除权那天的“幽灵信号”。

停牌过滤也很容易漏。长时间停牌的股票在K线上表现为连续相同价格或者没有数据,如果不加处理,策略可能在里面“空转”甚至产生虚假成交。我的处理方式是:在股票池筛选阶段就把停牌超过一定天数的票剔除,在实盘信号生成阶段再次确认标的当天有正常交易状态。

ST股票过滤则是风险偏好的问题。很多策略并不希望买入ST股,因为涨跌幅限制不同,流动性也差。我在生成股票池的时候会拉取ST标记列表,统一做一个黑名单过滤。虽然这会在某些行情下损失一点收益,但换来的确定性和合规性值这个代价。

2.3 数据存储模型与每日更新节奏

清洗完的数据需要一个清晰的存储模型。我用的表结构大致如下:

  • stock_basic:证券代码、名称、行业、上市日期、是否ST等静态信息。
  • daily_price:代码、日期、开高低收、成交量、成交额、复权因子、前复权价。
  • daily_indicator:由价格数据算出的技术指标,比如均线值、RSI、MACD等。
  • trade_status:每日停复牌状态、涨跌停状态。

每日更新的节奏是:收盘后定时任务触发,从THS拉取当天数据,追加写入daily_price,增量更新trade_status,再对最新数据全量计算技术指标。整个过程大概十几分钟跑完,留到晚上复盘用足够了。

一个实用的技巧:入库之前先做一次去重和主键检查,以“代码+日期”作为唯一键,重复的要么覆盖要么跳过。不要小看这一步,终端偶尔会重复导出同一段区间,不查重的话指标计算会因为重复行悄悄出错。

3. 策略研究与回测:把想法变成可验证的信号

3.1 以双均线策略为例,讲清楚回测的完整流程

很多人写策略上来就追求复杂模型,其实经典的简单策略更能验证你的系统是否可靠。这里我用双均线策略来演示:当短期均线上穿长期均线时买入,下穿时卖出。

回测流程大体是:

  1. 从本地数据库读取股票池过去三年的前复权日线数据。
  2. 计算短均线(比如5日)和长均线(比如20日)。
  3. 定义信号:金叉记1,死叉记-1,中间保持原来持仓状态。
  4. 按信号模拟每日调仓,同时扣除手续费。
  5. 统计最终收益、年化收益率、最大回撤、夏普比率、胜率等指标。

这里最需要解释清楚的是第三步的做法。信号的持仓状态是连续的,而不是只有当天瞬间的买卖动作。金叉当天买入后,在没有出现死叉之前,你是持续持有的。这个状态转换逻辑写不对,回测结果会和实盘完全两样。我见过不少初学代码回测收益很好、实盘一塌糊涂的案例,细查之下都是状态保持逻辑写错了。

3.2 回测指标的镜像解释:别只看收益率

评估一个策略靠不靠谱,不能只看最终收益率。我每次回测必看的四个指标是:年化收益率、最大回撤、夏普比率和胜率。

年化收益代表赚钱能力,但必须配合最大回撤一起看。一个策略一年赚了80%,中间却回撤了40%,说实话这种策略一般人拿不住,心态早在回撤途中就崩了。我更关心的是收益回撤比,低回撤的稳健策略比高波动高收益的策略更适合个人投资者。

夏普比率衡量的是“每承担一单位风险能换来多少超额收益”,通常大于1算合格,大于2就算不错。这个指标的计算公式是(策略年化收益率-无风险利率)/策略收益波动率。无风险利率我一般取一年期国债收益率或者定期存款利率,国内个人量化场景下通常用2%左右的近似值。

胜率这个东西要辩证看。有的策略胜率只有40%,但盈亏比很高,整体照样赚钱。回测报告里胜率和盈亏比要放在一起看,单独看任何一项都很容易误判。

3.3 参数优化的陷阱:如何防止过拟合

做回测不可避免会遇到参数优化。双均线的短周期、长周期参数,都可以通过遍历找出一组历史表现最好的数值。比如短周期从3到15,长周期从15到60,两两组合都跑一遍,选出夏普最高的那组参数。

这个思路本身没问题,但大多数人在这里栽跟头是因为过拟合:你找出的参数可能只是把历史噪声都拟合进去了,换到未来行情立刻失效。我有几个应对手段:

  • 数据分段验证:不要把整段历史数据都用来选参数,留下一段最近的数据做样本外验证。比如用前80%的数据选参数,后20%的数据验证效果。
  • 参数平原原则:不要选那种“唯一最优”的参数,而是看参数周围是否有一片区域表现都还可以。如果最优参数旁边都是断崖式下跌,说明策略对参数极其敏感,实盘中必死。
  • 减少参数个数:策略参数越多,过拟合空间越大。能用两个参数表达清楚的就别上五个。

我还会特意给回测脚本加入随机扰动测试,比如在价格序列上加一点随机噪声再跑一遍。如果策略绩效因为微小扰动就大幅变差,说明它捕捉的是噪声而非规律。

4. 模拟盘与实盘执行:从回测到真金白银的最后一公里

4.1 模拟盘的真正价值:不是验证收益,而是检验流程

模拟盘最容易被误解的地方在于:很多人用它来验证策略的收益能力,但模拟盘的撮合逻辑和实盘有本质区别。模拟盘往往按当前价直接成交,不产生滑点,也不考虑盘口深度。策略在模拟盘上跑出来的收益曲线,大概率比实盘漂亮。

我认为模拟盘的真正价值在流程验证。策略模块能不能按时产出信号?执行模块能不能准确下单?风控模块能不能在触发条件时果断拦截?有没有漏单、重复下单、订单状态不同步之类的致命bug?这些问题在模拟盘阶段暴露出来的成本,比实盘低太多。

我当时在模拟盘上刻意做了一件事:人为制造异常场景。比如临时拔掉网线再恢复,看看系统会怎么处理未确认的订单;比如在策略里故意放一个超过风控限额的指令,看风控模块到底拦不拦得住。这种故障注入测试,常规跑流程根本碰不到,但对实盘的稳健性至关重要。

4.2 实盘下单链路的落地要点

当模拟盘连续稳定运行一段时间后,可以开始考虑实盘。我接的下单链路大致是这样:

  1. 策略模块在收盘后或盘中按固定频率生成目标持仓。
  2. 执行模块对比当前持仓和目标持仓,计算差异。
  3. 风控模块对每一笔差异交易逐一检查:是否在股票池内、是否超仓位限制、是否触发止损。
  4. 通过风控的订单按优先级排序,逐笔下达到THS终端的交易接口。
  5. 成交回报返回后,更新实时持仓记录。

这里有一个容易被忽略的细节:下单频率要与行情更新频率匹配。如果你用的是日线级别信号,盘中每五分钟就跑一次策略就没意义,反而徒增计算量和重复下单风险。正确的做法是,在日线数据更新后计算一次仓位目标,然后在当日交易时段内按固定节奏执行调仓。

另一个要点是订单的幂等性。系统重试下单时,要保证同一指令不会下两次。我的做法是给每一笔订单生成唯一ID,终端返回订单号后立即记录,重复执行时先查这个订单号是否已存在。

4.3 风控规则硬编码:宁可错过,不可出错

风控是实盘和模拟盘最大的区别。模拟盘下单错了顶多心态难受,实盘一次风控失效可能就是大额亏损。我的风控规则全部硬编码进系统,不依赖任何人工判断:

  • 单票仓位上限:任何一只票不超过总资金的20%。
  • 行业集中度控制:同一行业不超过总资金的40%,防止单一行业系统性风险。
  • 止损硬线:单票浮亏达到8%无条件止损,不回滚、不商量。
  • 黑名单机制:ST、停牌、重大利空公告的标的直接从股票池剔除。
  • 异动熔断:策略单日调仓次数超过预设阈值,自动停用策略并人工介入。

这些规则写起来都不复杂,难的是坚决执行。我见过太多人在盘中止损线到了却手动操作“再等等”,等来的往往是更深的亏损。把规则交给代码执行,反而是对自己情绪的最大保护。

5. 常见问题与排查技巧实录

5.1 数据对不上:先查数据源,再查自己的代码

跑策略时经常遇到一种诡异情况:某个标的的行情明显波动很大,但策略完全没反应。第一步基本是怀疑策略代码有bug,但排查到最后,往往发现是数据层的问题。

我遇到的一个典型场景是:THS终端里看到的日线数据和本地库里的数据对不上。后来发现是导出时日期没有按从早到晚排序,而我的策略脚本默认数据是有序的,导致计算出来的均线值错位。从那之后我在数据入库时强制按“代码+日期”排序,并在读取时加了一层校验,发现乱序直接报警。

另一个常见问题是复权状态不统一。不同时间导出的数据,可能一次是前复权、一次是不复权,混在一起用的话策略信号会出现莫名其妙的跳变。我的对策很简单:在数据表里加一个字段记录复权状态,入库时校验,坚决禁止混合状态。

5.2 信号延迟:先确认计算时点,再怪网络

盘中跑策略出现信号延迟时,很多人的第一反应是网络慢、接口慢。但我的排查经验是:大概率是策略脚本的计算时点错了。

盘中实时行情每秒钟都在更新,如果你的策略用了当日未收盘的最新价去算日线指标,结果必然处于漂移状态。正确的做法是:盘中信号用盘中快照计算,收盘信号等当日数据完全落地后再计算,两套信号分别处理。我一开始偷懒,一套代码两个场景通吃,结果盘中信号和收盘信号经常互相矛盾。

另外,本地脚本从THS读数据时,要留意终端是否正处于数据刷新中。我碰到过终端正在下载历史数据时,本地脚本读到的文件是半截状态,解析直接报错。现在的处理方式是读文件前先检查文件大小是否稳定,两次读取间隔一秒大小一致才认为写完了。

5.3 常见问题速查表

问题现象可能原因排查方法
中文乱码编码格式不匹配指定GBK读取导出文件
回测收益虚高未扣除手续费和滑点回测中增加交易成本模型
实盘信号与回测不符复权状态不一致统一使用前复权数据
重复下单订单状态未持久化为订单生成唯一ID并入库
某日数据缺失停牌或数据未刷新校准trade_status表
策略触发频率异常信号状态保持逻辑错误检查金叉死叉状态转换
实盘持仓与目标偏离部分成交未处理成交回报逐笔对齐

这张表是我平时排查问题的粗线条框架。真遇到问题的时候,我会从数据层开始逐层向上排查,先确认输入数据没问题,再看信号计算,接着看风控拦截,最后才怀疑下单环节。这个顺序基本能覆盖90%的异常场景。

5.4 日志记录:复盘和排障的底牌

最后必须强调日志的重要性。我的系统从一开始就在每个关键环节打日志:数据入库时间、信号生成时间、风控检查结果、下单指令、成交回报,全部记录。一开始觉得麻烦,但几次排查线上问题全靠日志快速定位。

日志记录有几个注意点:

  • 日志要带时间戳,精确到秒级甚至毫秒级。
  • 策略信号要记录当时的计算输入快照,方便事后复现。
  • 风控拦截要明确记录拦截原因,否则你根本不知道哪个规则在起作用。
  • 日志文件要按日期切割,方便回看某一天的全部轨迹。

我在实盘运营中发现,很多看似奇怪的问题,翻出当天日志一看,原因立刻清晰。比如某个标的没买入,不是策略没信号,而是风控因为当天有重大公告把它拉黑了。这种事情在盘面上完全看不出来,只有日志能还原完整因果链。

我个人在实际操作中最深的体会是:量化交易的成败多半不在策略代码本身,而在于你愿不愿意把数据、回测、执行、风控这些“细碎环节”一项项做扎实。很多人把精力全放在策略模型上,最后却因为一个复权处理不当导致实盘亏损,这太可惜了。如果你想尝试把THS终端变成自己的量化基础设施,建议从数据管线开始搭建,然后跑熟回测流程,再逐步接上模拟盘和实盘执行,一步一步来,别急着全链路打通。最后再分享一个小技巧:系统的每个关键脚本,都不要只写一个默认版本,留一个带详细调试输出的开关,平时关掉,排查问题时打开,你会省下大量猜来猜去的时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进