ARTICLE DETAIL

资讯详情

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

通达信公式迁移Python:量化选股与回测框架实战指南

通达信公式迁移Python:量化选股与回测框架实战指南 简介本资源是一个面向Python量化开发者的通达信策略迁移与实盘适配框架适用于具备基础金融知识和Python编程能力的中级量化爱好者及策略工程师解决通达信公式向Python环境迁移、本地化选股回测与交易信号生成等实际问题。压缩包共111个文件含73个核心Python模块如data_manager.py、api_config.json配置管理、多周期行情处理与策略引擎、13个编译后pyc文件、8个Linux部署脚本sh、4份Markdown项目文档含项目总结、更新说明与知识梳理整体仅211KB轻量易集成。已有61人学习下载资源结构清晰包含.env.example环境模板、.gitignore规范、StockMarket.iml工程配置及完整README指引便于快速搭建本地量化选股系统并扩展自定义因子与交易逻辑。 在通达信里写了好几年选股公式的朋友应该都有过这种感觉指标在软件里跑得挺欢盘后选股也能选出几只票但真要说这套逻辑历史上到底表现怎么样、能不能稳定赚钱心里其实没底。这个基于通达信策略的Python量化选股框架的量化股票交易策略系统项目就是把你在通达信里攒下的那些选股公式整体搬到 Python 环境里重新实现一遍。说白了就是把你熟悉的指标语言翻译成自动化脚本让程序每天自己拉数据、全市场扫描、输出股票池再把历史表现回测出来给你看。这能解决通达信最让人头疼的三个问题没法批量做历史回测、全市场选股效率低、策略结果没法量化评估。如果你已经熟悉通达信公式又想往系统化交易的方向走一步这个框架就是一个比较顺滑的过渡方案。我不是第一次干这种事。之前帮朋友迁移过一整套通达信自定义指标过程里踩了不少坑也总结出一套相对固定的套路。这篇文章就把整个框架的设计思路、通达信函数到 Python 的映射方法、全市场扫描选股的实操流程以及我在迁移过程中遇到过的高频问题一次性讲清楚。1. 项目背景与整体设计思路拆解1.1 为什么要把通达信策略搬到 Python先说一个扎心的事实通达信里写公式本质上是在一个封闭的沙盒里做逻辑验证。你写出来的指标软件能识别、能画线、能预警但如果你想回答这个策略过去三年每个月收益是多少最大回撤出现在什么时候这类问题通达信几乎给不了可靠答案。它的选股器是快照式的跑一次选股、看一眼结果中间没有历史回溯这一层。Python 解决的就是这个过程可回溯的问题。数据拉下来之后所有计算都是确定性、可重复的。同一套选股逻辑既可以拿今天的行情跑一遍也可以拿过去五年的历史行情跑一千遍结果完全可控。另一个优势是批量处理能力。通达信选股器在几千只票上跑复杂公式经常要等很久而且一旦涉及跨周期、跨品种的复合条件公式写起来非常别扭。在 Python 里这些操作无非是几个 DataFrame 的 join 和 groupby跑完整个市场的选股也就是几十秒的事。还有个很多人忽略的点通达信的公式语法和主流编程语言差距很大导致策略逻辑很难做单元测试。写错了往往只能在盘面上肉眼看到底对不对全靠直觉。而 Python 生态里断言、日志、可视化、回归测试这些工具都是现成的策略逻辑可以被严格校验。这个项目就是把通达信那套手工研报式的选股流程升级成自动化流水线式的量化系统。1.2 框架整体架构与模块划分这个框架的整体架构我习惯从数据流的角度来拆而不是从代码目录来拆。一条完整的链路大概是行情数据入库、因子计算、策略信号生成、选股筛选、回测验证、结果输出。数据服务层是地基。这个模块负责从数据源拉取日线行情、分钟线、财务数据、板块分类信息然后统一清洗成标准格式存到本地。清洗包括去重、复权处理、剔除上市不足 N 天的次新股、剔除 ST 股这些脏活累活全部在这层完成。策略指标层是整个框架的核心。这里存放的就是从通达信公式翻译过来的 Python 函数。比如你原来在通达信里写了一个量能饱和度选股公式到这里就需要拆解成几个原子操作计算量能饱和度指标、判断是否达到阈值、结合价格趋势做过滤。这层的设计原则是一个公式一个函数输入是标准行情 DataFrame输出是布尔序列或数值序列。选股引擎层负责全市场扫描。遍历所有股票代码依次调用指标层的函数把所有满足条件的股票汇总成股票池。这里要注意效率问题后面我会单独讲。回测验证层是框架和通达信拉开差距的关键。它负责把选股结果放到历史数据里跑模拟真实的买入卖出过程输出收益曲线、最大回撤、胜率、盈亏比这些核心指标。最后一层是输出与调度把选股结果和回测报告落盘通过定时任务每天自动执行。1.3 技术选型与方案取舍技术栈上我用了最稳妥的一套组合Python 3.10 以上版本pandas 和 numpy 做核心计算数据源用 AkShare 或 Tushare任务调度用系统自带的定时任务存储用 CSV 加 SQLite。没有引入分布式计算也没有上消息队列因为这个体量的项目不需要这些重型组件。有些朋友问我要不要用若依框架或者 Django 搭一个 Web 管理界面我的建议是现阶段完全没必要。量化选股系统的核心价值在策略逻辑和数据处理不在界面上。用若依这种重量级后端框架光是前后端联调就要耗掉大量精力而且对选股性能没有任何帮助。先做一个命令行工具跑通流程等真需要给别人看结果时再考虑加一个简单的看板页面也不迟。pandas 是整个框架的绝对主力。通达信公式里的大部分操作在 pandas 里都有对应实现比如 REF 对应 shiftHHV 对应 rolling max。把这些映射关系整理成一张对照表之后翻译公式的效率会高很多。下面用一整章来讲这部分这是把通达信策略搬进 Python 最核心的一步。2. 通达信指标函数到 Python 的核心映射实践2.1 必须掌握的函数映射关系清单通达信公式语言里最常用到的函数其实不超过二十个。我整理了一张对照表覆盖了 90% 以上的选股公式场景翻译公式的时候对照着查就行。通达信函数功能说明Python/pandas 实现REF(X, N)引用 N 周期前的 X 值series.shift(N)HHV(X, N)N 周期内 X 的最高值series.rolling(N).max()LLV(X, N)N 周期内 X 的最低值series.rolling(N).min()COUNT(条件, N)N 周期内满足条件的次数condition.rolling(N).sum()SUM(X, N)N 周期内 X 的总和series.rolling(N).sum()MA(X, N)N 周期简单移动平均series.rolling(N).mean()EMA(X, N)N 周期指数移动平均series.ewm(spanN, adjustFalse).mean()CROSS(A, B)A 上穿 B金叉(A B) (A.shift(1) B.shift(1))BARSLAST(条件)距离上次条件成立的天数自定义函数记录最近一次 True 的位置FILTER(条件, N)条件成立后 N 周期内不再触发自实现信号去重ABS(X)绝对值abs(series)MAX(A, B) / MIN(A, B)取较大 / 较小值np.maximum(a, b)/np.minimum(a, b)IF(条件, A, B)条件成立取 A否则取 Bnp.where(condition, a, b)BETWEEN(A, B, C)A 是否在 B 和 C 之间(A B) (A C)量比 / 换手率当日成交活跃度用成交量/流通股本自行计算这个映射逻辑很直观但有一个容易忽略的点通达信的序列默认是从最新数据往历史方向排列的下标 0 是今天。而 pandas 的 DataFrame/Series 默认是按时间升序排列最早的在前。如果不做排序统一shift 的方向就会反结果完全对不上。我的习惯是进入框架的数据统一按时间升序排列所有指标函数都基于这个前提来写这样逻辑更直观也不容易出错。2.2 实战案例把双线合一选股公式翻译成 Python拿一个比较有代表性的公式来演示。通达信里经典的双线合一选股思路大致是这样短期均线和中期均线粘合之后向上发散说明趋势即将启动。原生公式一般写成MA5:MA(CLOSE,5); MA10:MA(CLOSE,10); 粘合度:ABS(MA5-MA10)/MA10*100; 双线合一:CROSS(MA5,MA10) AND 粘合度2;这个公式的逻辑是5 日均线上穿 10 日均线同时两根均线的距离很近粘合度小于 2%过滤掉追高的情况。翻译到 Python 里是这样import pandas as pd def cross(short, long): 金叉short 上穿 long return (short long) (short.shift(1) long.shift(1)) def double_line_merge(df, short_n5, long_n10, threshold2.0): 通达信双线合一选股逻辑的 Python 实现 df 必须包含 close 列按时间升序排列 返回布尔 SeriesTrue 表示当天触发买入信号 close df[close] ma_short close.rolling(short_n).mean() ma_long close.rolling(long_n).mean() # 粘合度 均线差 / 长期均线 * 100 merge_ratio (ma_short - ma_long).abs() / ma_long * 100 # 金叉且粘合度低于阈值 signal cross(ma_short, ma_long) (merge_ratio threshold) return signal这段代码是不是很简单关键点就两个一是 rolling 窗口和通达信的周期参数一一对应二是粘合度的计算方式和原公式保持完全一致。很多人在这一步容易自作聪明觉得粘合度小于 2%太严格手动放宽到 5%结果选出来的股票池和原策略完全不是一回事。迁移公式的第一原则是先原样复刻再谈优化不要在一开始就夹带私货。我还见过有人把量能饱和度 100 选股公式拿来做迁移。这种公式的核心逻辑是量能指标达到某个饱和度阈值后结合价格位置进行筛选。翻译的难点不在技术而在理解原公式的编写者到底想表达什么交易思想。如果只是机械翻译迁移过来也只是一个不好用的筛子。所以我在做策略迁移前一定会先把原公式的每个线条都搞清楚再动手写代码。2.3 指标翻译正确性验证与常见错误写完翻译代码不能直接就拿去选股要做验证。我常用的方法是对比测试找一只股票最近 100 个交易日的数据在通达信里把指标值导出来再在 Python 里用同一段数据跑一遍逐行对比结果。这一步能暴露出大量问题。最常见的坑有三个。第一个是数据对齐问题。通达信的复权处理和 Python 里用的复权方式如果不一样均线、MACD 这类依赖历史价格的指标就会整体偏移。我的做法是统一使用前复权方式并且在数据拉取阶段一次性处理好计算指标的时候完全不再考虑复权问题。第二个是未来函数问题。通达信的某些公式里会悄悄用到未来数据比如引用了下一周期的值这在技术分析里是个隐藏的胜负手。Python 翻译时如果不小心shift(-1) 也会引入未来函数。验证方法很简单用回测结果对比 K 线图如果策略信号在图上看着极其精准每次买完就涨、卖完就跌大概率是哪里泄了未来信息。第三个是边界值处理问题。通达信公式在数据不足 N 个周期时通常会返回空值或 0而 pandas 的 rolling 默认从第 N 行才开始有值。这种差异在指标计算里可能被放大导致选股结果出现偏差。解决方法是统一约定数据不足的区间直接丢弃不参与后续计算。3. 实操过程从数据准备到全市场扫描选股3.1 数据层处理获取、清洗、复权我实战中先把数据层跑稳再碰策略。数据源我用 AkShare免费、接口多、不用注册拿日线数据和财务数据都方便。但免费接口有隐患频率限制、字段偶尔变更、数据偶发缺失。所以数据层必须要有容错机制。我的标准流程是这样拉取全部 A 股代码列表过滤掉 ST、退市整理股、上市不满 60 个交易日的次新股。对每只票拉取最近 500 个交易日的日线数据包含开高低收、成交量、成交额。统一按前复权方式处理价格同时保留原始价格用于涨跌停判断。剔除长期停牌连续停牌超过 20 个交易日的股票和上市不足历史数据长度的股票。全部数据合并成一个大的 DataFrame分列存储落盘到 SQLite 或按日期分片存 CSV。这里有个细节通达信选股公式通常以当前交易日收盘后的行情为基准所以数据拉取的时间点很关键。盘中跑选股和盘后跑选股结果会有差异甚至差别很大。我的惯例是每天收盘后 17 点再跑一次当日选股确保数据完整。3.2 全市场选股任务执行流程数据准备好之后全市场扫描就是一个标准流程。用双线合一策略举例完整代码大致是这样import pandas as pd from datetime import datetime def run_daily_screener(data_path, strategy_func, target_dateNone): 全市场扫描选股主流程 data_path: 数据文件目录 strategy_func: 策略函数输入 df输出布尔 Series target_date: 目标交易日默认取最近一个交易日 if target_date is None: target_date pd.Timestamp.today().strftime(%Y-%m-%d) stock_list load_stock_list(data_path) picked_codes [] for code in stock_list: df load_stock_data(data_path, code) if df is None or len(df) 120: continue df df[df[trade_date] target_date].tail(120) signal strategy_func(df) if signal.iloc[-1]: # 最后一天触发信号 picked_codes.append({ code: code, date: target_date, close: df[close].iloc[-1], }) result pd.DataFrame(picked_codes) result.to_csv(fresult_{target_date}.csv, indexFalse) return result这段代码的核心是循环遍历所有股票用 strategy_func 判断每只票在目标日期是否触发信号。逻辑不复杂但效率值得注意。如果你的股票池有 4000 只票每只票的 DataFrame 都要做一次 rolling 计算整个跑下来可能要几分钟。优化手段有两个一是用多进程并行按代码切片分配到不同进程二是把行情数据合并成大宽表一次性做向量化计算。向量化是更优雅的方案。把每只股票的 close 列按股票代码横向拼接变成一个 4000 列的 DataFrame然后对整个 DataFrame 统一做 rolling。这样原本几万次循环的操作变成了几千列上的一次向量化运算速度提升非常明显。代价是内存占用会上去但 4000 只票 500 天的数据几千万个浮点数现代电脑完全扛得住。3.3 回测验证与参数评估细节选股只是第一步选出股票后到底能不能赚钱必须靠回测来回答。这个框架里我实现了一个极简但完整的回测模块逻辑是信号触发当天收盘价计算买入价持有 N 天后按收盘价卖出统计每次交易的收益。def backtest_single_stock(df, signal, hold_days5): 极简回测每次信号触发后买入持有 hold_days 天卖出 返回交易记录列表 trades [] dates df.index[signal.fillna(False)] for entry_date in dates: idx df.index.get_loc(entry_date) if idx hold_days len(df): continue exit_date df.index[idx hold_days] entry_price df[close].iloc[idx] exit_price df[close].iloc[idx hold_days] ret exit_price / entry_price - 1 trades.append({ entry_date: entry_date, exit_date: exit_date, entry_price: entry_price, exit_price: exit_price, return: ret, }) return pd.DataFrame(trades)回测结果生成之后至少要统计几个核心指标总收益率、年化收益率、最大回撤、胜率、盈亏比、平均持仓周期、交易次数。交易次数太少的策略没有统计意义我一般要求至少 30 笔以上交易才认为结果可信。胜率不是唯一指标有些策略胜率只有 35%但盈亏比高长期也能赚钱。这两个指标要放在一起看。参数评估是回测里最容易过度优化的环节。双线合一里的粘合度阈值、均线周期每一个参数调整都会让回测结果发生变化。我的习惯是先把一组基准参数跑出来的结果存下来然后做小范围的参数扫描观察结果变化是否平滑。如果参数稍微动一点点结果就剧烈震荡说明策略存在过拟合风险这时候要果断降低参数敏感度而不是继续调参。还可以把历史数据切成样本内和样本外两段样本内调参、样本外验证防止自欺欺人。4. 常见问题与排查技巧实录4.1 指标结果对不上的排查方法这是被问得最多的问题同样的公式通达信里选出的股票和 Python 框架里算出来的结果对不上。遇到这种问题先别急着怀疑代码按下面的顺序排查。先看复权方式。通达信默认使用的复权方式和你的数据源是否一致几乎决定了均线、MACD 这类指标计算的百分之八十。如果复权基准不一致在除权除息日附近价格序列就会明显错位。再看除权日数据。有些数据源在除权日的价格处理上有 Bug涨跌幅对不上或者成交量异常。这会导致依赖于量价关系的公式产生偏差。我把量能类的通达信公式迁移过来时就遇到过类似的坑。解决方案很简单绘制某只票的价格曲线和成交量曲线和通达信里的图叠加对比一眼就能看出来。最后看公式的隐含细节。通达信公式里的某些函数有时间窗口默认值或者存在隐式类型转换这些在 Python 里不会自动发生。比如通达信的 CONST 函数引用某个固定值在 Python 里你可能需要显式把这个值算出来。翻译公式时一定要把原公式的每一处细节都看仔细不要想当然。4.2 回测漂亮实盘亏损的系统性原因回测结果非常漂亮年化收益 40%最大回撤不到 10%但实际跑起来却亏损这个问题我见过太多次。原因基本都是下面几个。第一个真是未来函数。回测代码里如果不小心用到了未来数据比如用了当天收盘后才知道的信息来做开盘买入决策回测结果必然虚高。检查方法就是我在前面提到的方法观察信号是否过于完美或者在回测框架里加入信号与决策时点的显式区分。第二个是没算交易成本。很多人在回测里只算了价格收益忽略了佣金、印花税、滑点。对于高频选股策略来说交易成本是致命的。A 股卖出有印花税加上佣金和滑点单次交易成本大约在 0.1% 到 0.2% 之间。如果一个策略平均持仓只有三天年化换手率很高这部分成本会吃掉大量利润。我的处理方式很简单回测里统一按买入价上浮 0.1%、卖出价下调 0.1% 来模拟冲击成本宁可保守一点也不要虚高。第三个是涨停板无法买入。回测模型默认信号出现后第二天开盘价能成交但实际上如果第二天直接一字涨停你根本买不进去只能干瞪眼。同理跌停时也卖不出去。这个过滤逻辑在回测里必须实现否则收益曲线会虚高得离谱。第四个是幸存者偏差。回测数据如果只用了当前还存活的股票把已经退市的股票都排除了那回测结果自然好看因为烂票都被剔除了。解决方法是使用历史快照数据保证每只股票在任意回测时点都被纳入。4.3 效率优化与自动化跑批经验框架要稳定跑起来效率优化和自动化部署必不可少。pandas 操作的效率大部分时候瓶颈不在数据量而在代码写法。最常见的问题是层层拷贝。我在循环里处理单只股票时总是习惯用 df.copy() 来防止原始数据被修改但循环 4000 次下来多出来的开销就很可观了。正确的做法是数据加载后统一转为需要的格式计算过程中尽量用视图操作遇到必须复制的情况再复制。另外频繁访问 df[close] 这种列名索引在 pandas 里其实是有开销的如果循环里反复调用可以先把列提取成 Series 变量。另一个优化手段是降低数据精度。日线数据里 close、open 这些价格数值用 float64 存储实际上 float32 完全够用可以把内存占用减半。如果是分钟级数据这个优化更重要。关于自动化我用的是最朴素的方式Windows 环境下用任务计划程序Linux 环境下用 cron每天收盘后固定时间点跑一遍全流程脚本。脚本里用 logging 模块记录完整运行日志任何一步出错都能查到。跑批的关键不是花哨的调度框架而是稳定性和可观测性。我见过有人用很复杂的任务编排系统结果配置文件出错数据没跑出来都不知道。反而是一段简单的脚本加上清晰日志更可靠。我还加了容错机制每只票的数据拉取都放在 try-except 里单只票失败不影响全市场扫描。失败的原因单独记录到一个 error 日志里跑批完成后检查一下。最后分享一点我的实际体会把通达信策略迁移到 Python 这件事真正难的地方从来不是技术。函数映射、数据清洗、回测框架这些都有现成的方法可以学花一两个星期就能搭起来。但这个策略为什么能赚钱这个问题不是代码能回答的。我在迁移过几十个通达信指标后最大的体会是很多在通达信里存在好多年的经典公式放到严格回测环境下看表现并没有想象中那么好。通达信给人的准很大程度来自复盘时的后视镜效应。所以我的建议是迁移策略就用这套框架老老实实跑回测不要迷信任何现成指标。如果回测结果不好不是你的代码写错了而是这个策略本来就那样。把精力花在理解策略逻辑的本质上比花在优化指标参数上有价值得多。这样从通达信到 Python 的这一趟搬家才算真正没白搬。本文还有配套的精品资源点击获取
返回列表