
简介这份面向股票技术分析初学者与通达信公式爱好者的文档围绕一套声称准确率高达98%的自定义指标公式展开帮助读者理解通达信平台中公式源码的编写逻辑与买卖信号设计思路。文档内含1个docx文件压缩包约125KB篇幅集中、便于快速浏览适合对照软件逐段阅读与调试。源码部分呈现了较完整的变量与条件判断链Q_1用收盘价与其前一交易日收盘价之差刻画短期动能X2统计连续两日下跌的天数X4比较当前趋势与前一日强弱买进由多组条件复合而成TJ0、TJ1则依据Q_A与Q_B的变化给出卖出提示。持股、买一绝、堪称卖绝借助STICKLINE以不同颜色与线宽在图上标注持仓及买卖节点形成直观的图形信号。目前已有595人学习。需提醒的是高准确率说法应经历史回测与实盘验证指标仅作辅助工具宜结合基本面与市场情绪综合判断。1. 98% 准确率的通达信指标公式先弄清这个数字在说什么一个 docx 文件几十行公式标题写着准确率 98%。把它复制进通达信的公式管理器编译通过副图上按要求画出了红绿柱和箭头但照着信号买进去两个月账户是负的。这种落差我见过太多次。问题多半不在通达信也不在公式能不能算而在于 98% 这个数字从来没有定义过分母是 500 次信号里 490 次次日收红还是只在某段单边上涨行情里统计的是收盘价确认信号还是盘中触碰就算命中持有几天止损怎么算这篇要处理的就是这件事把一份来源不明的通达信指标公式源码从「看着很准」变成「口径清楚、可以自己验证」。我会沿着公式语法、导入排错、未来函数自查、Python 复现统计这条线讲下来适合两类人手上已经攒了一堆 docx、tn6、txt 指标文件想弄明白到底怎么回事的以及想自己写副图指标和选股公式、不甘心只当公式搬运工的。中间会给出可直接粘贴的源码、逐条对照的报错表和能跑起来的统计脚本。2. 通达信指标公式源码的语法骨架主力追踪与量能饱和度共用的结构网上流传的主力追踪、量能饱和度圆圈 1.00、三步点金这类东西看着名字五花八门拆开源码之后骨架高度相似一条平滑后的方向线一条量能归一化曲线再加几个阈值判断和画图函数。把骨架搞清楚比收藏一百个 docx 有用。2.1 公式管理器里的类型划分与各自的输出约束通达信公式管理器默认快捷键 CtrlF把公式分成技术指标、条件选股、交易系统、五彩 K 线、K 线模式、专家系统几类。同一段源码换个类型粘进去行为完全不同这是最先要立住的边界。技术指标公式画在图上可以输出多条线、柱、图标允许DRAWICON、DRAWTEXT、STICKLINE条件选股公式只能有一个逻辑结果最后一行必须是布尔表达式写了画图函数直接编译失败交易系统公式需要买入条件和卖出条件各一组用来做系统测试。所谓的主力追踪副图指标本质是技术指标类公式想拿去选股就得把画图部分删掉重写一遍。公式类型输出要求能否画图典型用途技术指标至少一条:输出可以副图指标、主图叠加条件选股最后一行布尔值不可以盘中预警、批量选股交易系统买入/卖出条件各一组可以系统测试、胜率统计五彩 K 线条件加颜色只能改 K 线颜色形态标记专家系统多组条件组合不可以复杂选股过滤提示公式名支持中文但不要带空格和短横线导出成 tn6 之后跨版本导入容易出现识别问题。命名用「前缀_对象_周期」这种结构后面批量处理会省很多事。2.2 赋值、输出与周期源码里最容易写错的几处通达信源码里:和:是两个完全不同的东西。A:MA(C,5)定义中间变量不画线、不参与输出A:MA(C,5)定义输出线会在图上出现一句话。很多从网上抄来的公式第一次编译就报「变量未定义」八成是把某个中间变量写成了:或者反过来。高频坑还有这么几个docx 里复制出来的逗号、括号、分号经常是全角的编译报错但肉眼看不出来通达信内部函数不区分大小写自定义变量名却区分ma5和MA5是两个变量REF(X,N)的 N 在多数版本上要求常量用变量做周期会失败CROSS(A,B)只判断当前这一根 K 线连续上涨行情里信号会断需要配合FILTER或BARSLAST去重除零问题也常见VOL/MA(V,20)在上市首日或长期停牌复牌后分母可能为 0。函数含义注意点REF(X,N)N 周期前的 XN 通常要求常量CROSS(A,B)A 上穿 B只对当前 K 线成立FILTER(X,N)X 成立后 N 周期内屏蔽信号去重用BARSLAST(X)上次 X 成立至今的周期数从未成立时返回极大值HHV/LLV(X,N)N 周期最高/最低含当前 K 线SMA(X,N,M)通达信加权移动平均与MA不等价ZIG(K,N)之字转向未来函数慎用2.3 主力追踪副图与量能饱和度的可复用写法下面这段是主力追踪副图的常见结构把量能饱和度、短期方向线和饱和标记放在一起每一行都加了注释可以直接贴进公式管理器测试。{主力追踪副图均量线 量能饱和度 短期方向线} VOLUME:VOL,VOLSTICK; {第1行成交量柱副图基础} VMA5:MA(VOL,5),COLORWHITE; {5日均量线} VMA20:MA(VOL,20),COLORYELLOW; {20日均量线饱和度的分母} DEN:MAX(VMA20,1); {兜底防止停牌复牌后分母为0} SAT:VOL/DEN,NODRAW; {量能饱和度原始值NODRAW 只算不画} SAT100:SAT*100,COLORCYAN; {放大100倍显示1.00 对应 100 的饱和区} RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9)0.0001)*100; K:SMA(RSV,3,1); D:SMA(K,3,1); J:3*K-2*D; 方向线:EMA(J,5),COLORRED,LINETHICK2; 饱和标记:IF(SAT1.0,方向线,DRAWNULL),COLORMAGENTA,LINETHICK3;逻辑上分三段第一段用VOL/MA(VOL,20)得到量能饱和度NODRAW让原始值只参与计算不出图方便后面复用成选股条件第二段是经典的随机指标变形0.0001是为了避免一字板时高低价相等导致除零第三段IF(SAT1.0,方向线,DRAWNULL)只在饱和区画粗线DRAWNULL表示该位置不画比用颜色隐藏干净。参数改动的影响可以直接对照下表不用一个个试。参数默认调大调小均量周期20饱和信号变少更偏中线信号变多噪声上升RSV 周期9方向线迟钝方向线频繁翻转方向线平滑5更稳滞后加大更贴价假信号多同一个逻辑改成条件选股只保留最后判断那一行中间变量全部用:{条件选股量能饱和度≥1 且方向线上穿 20} DEN:MAX(MA(VOL,20),1); SAT:VOL/DEN; RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9)0.0001)*100; K:SMA(RSV,3,1); D:SMA(K,3,1); J:3*K-2*D; 方向线:EMA(J,5); XG:SAT1 AND CROSS(方向线,20);选股公式里不能出现画图函数最后一行必须是一个返回真假的表达式XG这个名字本身不重要重要的是它带:输出。把这两段对照着看就明白为什么同一个指标在副图上能用、拿去选股却报错了。3. 从 docx 到可编译公式txt 清理、tn6 导入与报错对照拿到一份 docx第一件事不是粘贴而是清理。全角符号是导致首次编译失败最主要的原因比逻辑错误还常见因为逻辑错误至少报错行为是可预期的全角符号报出来的行号和实际问题位置常常对不上。3.1 docx 复制出来的源码先做四步文本清理清理动作固定为四步全角标点转半角去掉 Word 智能引号逐行去首尾空格并删空行确认没有把:写成全角冒号加等号。手改容易漏用脚本更快。import re from pathlib import Path SRC Path(formula_raw.txt) DST Path(formula_clean.txt) # 全角 - 半角映射公式里能用到的符号基本覆盖全了 FULL2HALF { ord(): ,, ord(): ;, ord(): :, ord(): (, ord(): ), ord(): %, ord( ): , ord(): , ord(): , ord(): , ord(): -, ord(): *, ord(): /, } text SRC.read_text(encodingutf-8, errorsignore) text text.translate(FULL2HALF) # 第一步全角转半角 text text.replace(\u201c, ).replace(\u201d, ) # 第二步智能引号 lines [ln.strip() for ln in text.splitlines()] # 第三步去首尾空白 lines [ln for ln in lines if ln] # 第四步删空行 DST.write_text(\n.join(lines), encodingutf-8) print(f清理完成共 {len(lines)} 行)脚本里errorsignore是关键docx 导出文本偶尔会混入不可见控制字符直接解码会抛异常。清理之后不要急着粘贴先在文本编辑器里搜一遍、、这三个出现的概率最高。注意清理只解决符号问题不解决逻辑问题。一份写着 98% 准确率的公式符号全部改对之后能不能用仍然要看第 4 章那套验证。3.2 tn6、txt 与公式管理器的三种流转路径实际工作中会在三种格式之间来回倒路径大致是固定的。路径 Adocx 或 txt → 手动粘贴进公式管理器 → 测试公式 → 确定 → 另存为 tn6 备份。这条最可控源码全程可见。路径 B别人给的 tn6 → 公式管理器里的导入公式 → 选择分组 → 确认覆盖。这条的风险在于看不到源码。路径 C直接把文件复制进通达信安装目录下的公式目录 → 重启客户端生效。不同版本目录结构有差异以本地版本为准。格式能否读源码常见来源建议做法txt / docx能论坛、群文件先跑清理脚本再粘贴tn6视加密情况他人打包导入前确认能否展开源码tne / tni视版本公式导出扩展名跨版本有差异以本地为准公网上流传的那类指标加密处理工具直接用名字去搜能搜到一堆我的建议是别用。原因不是别的是工程问题加密或混淆过的 tn6 你没法审源码里面藏一个ZIG或者跨周期引用回测曲线立刻变得非常好看实盘就是另一回事。来路不明的公式文件导入之后先看公式管理器里能不能展开源码展开不了就直接删掉。3.3 编译报错逐条对照报错现象常见原因改法未定义的变量 xxx中间变量写成了:或大小写不一致统一改成:全篇变量名统一大小写参数个数不对REF(X,5,1)这类多传了参数查函数原型对照 2.2 的表格语法错误行号指向空白行全角符号、括号不配对、注释{}没闭合跑 3.1 的脚本再逐行数括号该行不是布尔表达式选股公式里写了画图函数删掉DRAWICON/DRAWTEXT最后一行写成XG:...计算结果全是 0 或异常大除零、分母用了MA(VOL,20)套一层MAX(...,1)排查顺序建议从最小公式开始先确认环境没问题再逐步加复杂度{自检四行能全编译过说明输入法和环境都正常} T1:MA(CLOSE,5); T2:REF(CLOSE,1); T3:CROSS(MA(CLOSE,5),MA(CLOSE,10)); T4:HHV(HIGH,20);这四行覆盖了序列引用、均线、交叉判断、区间极值四类基本操作。如果连它们都编译不过问题在环境或输入法不在你抄来的那份公式上别在源码里瞎改。4. 98% 从哪来未来函数自查、Python 复现回测与参数过拟合准确率虚高通常来自三个来源用了未来函数、统计口径放松、参数过拟合。这三件事可以分别验证不需要信任任何人的口头声明。4.1 未来函数自查抓住 ZIG、BACKSET 和跨周期引用未来函数指的是在某一根 K 线位置上用到了当时还不可能知道的信息。通达信里最典型的有三类。第一类是ZIG(K,N)及其配套的PEAK、TROUGH、PEAKBARS、TROUGHBARS。之字转向只有在后续高低点确认之后才能画出拐点所以最后一个拐点的位置会随着新 K 线不断移动。用它标注的顶底历史回看极其精准实盘那个点位根本还没确认。第二类是BACKSET(X,N)把当前条件回填到前 N 个周期直接制造出提前 N 天就知道的假象。第三类是跨周期和大周期引用比如日线公式里引用周线收盘价或者调用DYNAINFO系列取当日动态数据这些值在盘中会变。自查方法有两个。慢的那个把公式挂到副图记下今天所有信号位置隔几天用同一份数据重新加载对比历史信号有没有漂移。快的那个用两段不同结束日期的数据跑同一份公式取重叠区间比对信号位置全部一致才说明没有未来函数。提示REF(X,1)、MA、EMA、HHV都不是未来函数它们的共同点是只使用当前和过去的数据。看到公式里出现ZIG或者BACKSET先把那个准确率数字打个对折再说。4.2 用 Python 复现指标口径并统计信号胜率通达信导出的日线数据可以直接读出成 CSV用 pandas 复现公式好处是口径完全掌握在自己手里。先定义通达信EMA的等价实现它的平滑系数是2/(N1)和 pandas 默认的span一致但要显式关掉adjust。import pandas as pd import numpy as np df pd.read_csv(sz000001.csv, parse_dates[date]).sort_values(date).reset_index(dropTrue) def tdx_ema(s, n): 通达信 EMA(X,N) 等价实现alpha 2/(N1) return s.ewm(alpha2 / (n 1), adjustFalse).mean() df[dif] tdx_ema(df[close], 12) - tdx_ema(df[close], 26) df[dea] tdx_ema(df[dif], 9) df[macd] (df[dif] - df[dea]) * 2 # 通达信 CROSS(A,B) 等价于当日 AB 且 前一日 AB prev_dif, prev_dea df[dif].shift(1), df[dea].shift(1) df[signal] (df[dif] df[dea]) (prev_dif prev_dea)信号定义好之后统计部分要同时给出两种口径只报一种都容易误导。HOLD, TARGET 5, 0.03 # 持有 5 个交易日目标涨幅 3% def evaluate(df, holdHOLD, targetTARGET): rows [] for i in df.index[df[signal]]: if i hold len(df): continue entry df.loc[i 1, open] # 次日开盘成交避免用信号当日收盘价的偷价 window df.loc[i 1:i hold] rows.append({ date: df.loc[i, date], ret: df.loc[i hold, close] / entry - 1, # 持有到期收益 max_up: window[high].max() / entry - 1, # 期间最大浮盈 max_dn: window[low].min() / entry - 1, # 期间最大浮亏 }) r pd.DataFrame(rows) loose (r[max_up] target).mean() # 口径一期间曾达标 strict (r[ret] 0).mean() # 口径二持有到期为正 return len(r), round(loose, 4), round(strict, 4), round(r[max_dn].mean(), 4) print(evaluate(df))参数说明HOLD是持有周期改大改小会明显改变结论短线信号建议 3 到 5TARGET是宽松口径的达标线一般取 0.02 到 0.05entry用次日开盘价而不是信号日收盘价是为了避开偷价这一点对准确率的影响往往比指标本身还大。输出里最后一个值是平均最大浮亏账户扛不住这个数字的话前面两项再好看也没意义。统计口径计算方式数值通常偏高还是偏低宽松期间曾达标max_up TARGET明显偏高容易到 80% 以上严格持有到期为正ret 0接近真实体感平均最大浮亏max_dn的均值用于判断是否扛得住有效样本数信号条数少于 30 条基本没有统计意义把这份脚本套回 docx 里的公式如果严格口径掉到 50% 上下、平均最大浮亏超过 8%那么 98% 这个数字的来源基本就清楚了它统计的是宽松口径而且样本区间大概率是精心挑过的。4.3 参数扫描与稳健性判断别只看最优那一组参数能调就一定能调出一组历史数据上好看的。判断是否过拟合的办法是做网格扫描然后看最优值周围的情况而不是只看最优点本身。from itertools import product def win_rate(df, fast, slow, sig, hold5): dif tdx_ema(df[close], fast) - tdx_ema(df[close], slow) dea tdx_ema(dif, sig) cross (dif dea) (dif.shift(1) dea.shift(1)) rets [] for i in df.index[cross]: if i hold len(df): continue rets.append(df.loc[i hold, close] / df.loc[i 1, open] - 1) if len(rets) 30: # 样本太少直接丢弃不参与比较 return 0, np.nan return len(rets), float((pd.Series(rets) 0).mean()) # 沿 MACD 经典参数 (12,26,9) 的邻域扫描 rows [] for f, s, g in product(range(6, 20, 2), range(18, 34, 4), range(4, 14, 2)): if f s: continue n, wr win_rate(df, f, s, g) rows.append({fast: f, slow: s, signal: g, trades: n, win: wr}) grid pd.DataFrame(rows).dropna(subset[win]).sort_values(win, ascendingFalse) print(grid.head(8)) print(胜率分布, grid[win].describe()[[mean, std, min, max]].round(4).to_dict())逻辑上做了两件事一是限定fast slow排除无意义的组合二是把交易次数少于 30 的组合直接返回nan丢掉避免少量样本刷出极端值。判断结论看的是分布而不是榜首如果最优那一组 78%但邻域普遍在 45% 到 55%标准差 0.06那 78% 就是噪声。真正稳健的参数区间是整片区域的胜率都差不多而不是单独冒出一个尖峰。改到这一步再回头去看那份 docx基本就能判断它值不值得留在自己的公式库里。5. 自建通达信指标公式源码的长期维护命名规范、批量预检与信号对拍公式攒到几十份之后靠记忆管理就不行了。有一阵子我手上同时有七八个版本的「主力追踪」改到后来自己都分不清哪个是哪个最后是靠命名规范加脚本预检重新理清楚的。5.1 命名规范是批量处理的前提文件名和公式名保持一致格式用前缀_对象_周期_版本比如ZL_主力追踪_D_V2。前缀用两三个 ASCII 字母避免跨版本导入时的编码问题对象名用中文方便搜索周期用D、W、60这种短标记版本号必须加改一次进一位旧版本不要删留着做信号对拍。比命名更省事的是加一条黑名单预检。很多问题在导入通达信之前就能查出来没必要等到编译报错。import re from pathlib import Path # 未来函数与动态数据黑名单命中就人工复核 BAN [ZIG, PEAK, TROUGH, BACKSET, DYNAINFO] def lint(path: Path) - list[str]: errs, text [], path.read_text(encodingutf-8, errorsignore) for i, ln in enumerate(text.splitlines(), 1): if re.search(r[], ln): errs.append(f{path.name}:{i} 含全角符号) if ln.count(() ! ln.count()): errs.append(f{path.name}:{i} 括号不配对) if ln.count({) ! ln.count(}): errs.append(f{path.name}:{i} 注释未闭合) for kw in BAN: if kw in ln.upper(): errs.append(f{path.name}:{i} 命中黑名单 {kw}) return errs for p in sorted(Path(./formulas).glob(*.txt)): for e in lint(p): print(e)这个脚本检查四件事全角符号、圆括号配对、注释花括号配对、黑名单函数。前两项是语法层面的第三项能拦住那种注释写一半导致后面全被吞掉的诡异问题第四项是提醒你这不是语法错误而是逻辑风险需要人工确认。5.2 信号对拍用 Python 校准通达信的实际输出写完代码不等于算对。最可靠的做法是拿同一只股票、同一段区间让通达信和 Python 各出一份信号日期列表然后做差集比对。tdx pd.read_csv(tdx_signals.csv, parse_dates[date])[date] py_signals set(df.loc[df[signal], date]) only_tdx [d for d in tdx if d not in py_signals] only_py [d for d in py_signals if d not in set(tdx)] print(f通达信有、Python 没有{len(only_tdx)} 条前 5 条 {only_tdx[:5]}) print(fPython 有、通达信没有{len(only_py)} 条前 5 条 {only_py[:5]})对不上的原因通常就那么几个复权方式不同通达信默认前复权导出的原始数据可能是不复权均线数值会整体偏移停牌日处理不同通达信会跳过停牌 K 线Python 里如果数据是完整交易日历就会出现位置错位还有就是CROSS在边界值相等时的判定差异通达信把AB归入未交叉Python 里要写成严格大于当前、小于等于前值才对得上。先把差集数量降到零再去调参数和加过滤条件。顺序反了的话后面所有优化都建立在一份口径不一致的数据上等于白做。过滤条件我一般只加两条剔除信号当日一字涨停的标的因为买不进剔除信号前 60 日累计换手率过低的标的因为量能饱和度在这种标的上的参考价值会被严重稀释。本文还有配套的精品资源点击获取