简介:通达信 99.75% 成功率指标公式源码以 doc 文档收录,面向借助通达信复盘与自编指标的股票交易者、量化入门学习者,用于解决指标逻辑难复现、买卖信号条件不清晰的问题。文档给出完整公式源码,可看到 TYP 典型价与 AVEDEV 构建的 CC1 通道、由 SMA 平滑衍生出的 C3 与 CB1 快慢线,以及基于前收盘价计算的 RSI1 强弱值、RSV 随机指标和 EMA 平滑后的 RR、BB1 判断线;买入与卖出信号均以多条件组合判断,并配合 DRAWBAND、DRAWICON 绘图语句标出通道带宽与信号位置。资源包仅含 1 个 doc 文件,约 197KB,体量轻便,打开与检索都很快。已有 1816 人学习下载,适合对照源码拆解多指标共振思路、核验信号触发频率,再按需调整参数用于自选股跟踪。
1. 「99.75% 成功率」标签下,这套通达信副图公式到底在算什么
副图公式在股票论坛上的命名向来夸张,「99.75% 成功率」属于典型的标题营销,但把源码粘进公式管理器里跑起来会发现,它不是随机拼函数的产物。整段代码四十来行,真正干活的只有三条摆动线:CCI 变体 CC1、放大五倍的 RSI 支路 RR1、放大五倍的 KDJ 支路 BB1,参数只有 N=9、M=14、N1=6 三个。三条线全部沉到 -100 以下、其中一条刚刚掉头向上,就在副图底部打一个买入箭头。它解决的是「从几千只票里筛出当天符合深度超卖 + 短周期动能反转形态的那几十只」,适合已经装好通达信、能打开公式管理器、想拿现成模板改参数继续用的人。至于那个百分比,把条件拆开、把样本数出来,自然就知道是怎么来的了。
2. TYP、AVEDEV 与 SMA 递归:公式语法底座拆开看
2.1 赋值符、参数区与 POINTDOT 基线
通达信公式里:=和:是两个完全不同的东西。:=只做赋值,计算完不画线;:会把结果输出成一条可显示的曲线。参数区的三行用的是前者,所以 N、M、N1 在副图上什么都看不到,它们只在后续表达式里被引用。与之相对的100,POINTDOT;是一个匿名输出:数值 100,线型点状,作用是给副图的 ±100 打两条刻度参考线,买点阈值恰好就贴着这两条线。源码里这对基线反复出现了四次,属于复制粘贴留下的冗余,删掉不影响计算结果。
N:=9;M:=14;N1:=6; { 中间变量,:= 不输出 } 100,POINTDOT; { 副图 +100 参考线 } -100,POINTDOT; { 副图 -100 参考线 } TYP := (HIGH + LOW + CLOSE)/3; { 典型价,CCI 的输入序列 }复制公式的时候如果把:=误写成:,副图上会凭空多出三条几乎不动的直线,很多人第一次导入公式看到「多了三根线」就是这个原因。
2.2 TYP 与 AVEDEV:CC1 的分母是怎么来的
CC1 这一行是整段代码里最标准的部分,它就是教科书上的 CCI,只是换了变量名:
CC1:(TYP-MA(TYP,M))/(0.015*AVEDEV(TYP,M)),COLORFF00FF;分子是典型价对 14 日均值的偏离,分母是 0.015 乘以平均绝对偏差。AVEDEV 算的是「每根 K 线的 TYP 与这 14 根均值之差的绝对值,再取平均」,它衡量的是波动幅度而不是方向,所以 CC1 的量纲被消掉了,天然落在 ±100 附近,正负 100 就是常说的超买超卖线。0.015 这个系数是 CCI 定义里带的,不是作者调出来的,改它等于把阈值线整体平移。
| 写法 | 含义 | 在本公式中的角色 |
|---|---|---|
TYP := (H+L+C)/3 | 典型价 | 代替收盘价作为输入 |
MA(TYP,M) | M 日简单均值 | 乖离的基准点 |
AVEDEV(TYP,M) | 平均绝对偏差 | 归一化分母,消量纲 |
0.015*AVEDEV | CCI 标准缩放 | 把结果压进 ±100 |
DRAWBAND | 双线之间填色 | 正负分区着色 |
2.3 SMA(X,N,M) 不是 MA,也不是 EMA
源码里C1:=SMA(CC1,3,1)这种写法出现得很频繁,但通达信的 SMA 和常见的简单移动平均是两码事。它的递推式是Y = (M*X + (N-M)*Y_prev) / N,当 M=1 时退化成一个指数衰减平均,平滑系数是 1/N。而通达信的EMA(X,N)用的是另一套递推Y = (2*X + (N-1)*Y_prev)/(N+1),X 的权重是 2/(N+1)。
| 写法 | 递推关系 | 新值权重(N=3) | 记忆长度 |
|---|---|---|---|
MA(X,3) | 等权平均 3 根 | 1/3 | 固定 3 根 |
SMA(X,3,1) | Y=(X+2Y')/3 | 1/3 | 无限,衰减慢 |
EMA(X,3) | Y=(2X+2Y')/4 | 1/2 | 无限,衰减快 |
这个差别直接决定了信号的手感:SMA(X,3,1)比EMA(X,3)钝得多,源码里CB1:=EMA(C3,5)之所以敢用 5 周期这么短的参数,是因为前面已经叠了两层 SMA 做粗平滑,再叠一层 EMA 只是让拐头更干脆。反过来,如果把SMA(CC1,3,1)随手换成EMA(CC1,3),买入信号会明显变多,形态完全不同。
2.4 拿 14 根 K 线把 CC1 手算一遍
想确认自己真的读懂了分母,用一段 Python 复现最快:
import numpy as np # 14 根 K 线的典型价,最后一根视为当天 typ = np.array([10.20, 10.35, 10.18, 9.95, 9.80, 9.62, 9.75, 9.90, 10.05, 10.22, 10.40, 10.15, 9.88, 9.60]) m = 14 avg = typ.mean() # MA(TYP,14) mad = np.abs(typ - avg).mean() # AVEDEV(TYP,14) cc1 = (typ[-1] - avg) / (0.015 * mad) # 当天 CC1 print(round(avg, 4), round(mad, 4), round(cc1, 2))跑出来avg≈10.0036、mad≈0.2179、cc1≈-123.5,这个负值已经踩在 -100 以下了。注意这里算的是 CC1 本身,而买入条件里比较的是经过 J 值放大和平滑之后的 CB1,两者量级相近但数值不等,不能混用。
3. 三条摆动线的构造:CCI 通道、5 倍 RSI 与 5 倍 KDJ
3.1 CC1 支路:从 CCI 到 CB1 的 J 值放大
CC1 之后跟着三行:
C1:=SMA(CC1,3,1); C2:=SMA(C1,3,1); C3:=3*C1-2*C2; CB1:=EMA(C3,5); CB2:=REF(CB1,1);3*C1-2*C2是 KDJ 里 J 值的算法,作用是把两条平滑线的差值放大三倍。所以 CB1 不是 CCI 本身,而是「CCI 的加速度」再取 5 日 EMA。上升趋势里 CB1 会明显跑到 CC1 上方,下跌趋势里会快速扎到 CC1 下方,这就是为什么买入条件敢用CB1<-100这种偏深的阈值:它天然比 CCI 波动更大。顺带一提,CB2定义了但整段公式里从来没被引用过,属于死代码。
3.2 RSI 支路:把 0~100 的指标拉到 -250~250
LC := REF(CLOSE,1); RSI1:=SMA(MAX(CLOSE-LC,0),N1,1)/SMA(ABS(CLOSE-LC),N1,1)*100; RSI:(RSI1-50)*5,LINETHICK2,COLORFFFFFF; R1:=SMA(RSI,3,1); D1:=SMA(R1,3,1); J1:=3*R1-2*D1; RR1:=EMA(J1,5); RR2:=REF(RR1,1); RR:=RR1>RR2;RSI1 是标准的 6 日相对强弱指标,取值 0~100。(RSI1-50)*5做了一次线性映射,把中轴 50 挪到 0,同时放大五倍,于是 RSI1=30 对应 -100,RSI1=10 对应 -200。这一步的意图很明确:让 RSI 的数值和 CC1 落在同一个坐标系里,才能用同一套 ±100 阈值做「三线共振」判断。后面 R1、D1、J1、RR1 的链条和 CC1 支路一模一样,最后RR是个布尔量,表示这条 5 日 EMA 是否在向上走。
3.3 KDJ 支路:RSV 的窗口是 9 天
RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100; K:=SMA(RSV,3,1); K1:(K-50)*5,COLOR00FF00; K2:=SMA(K1,3,1); D2:=SMA(K2,3,1); J2:=3*K2-2*D2; BB1:=EMA(J2,5); BB2:=REF(BB1,1);RSV 用的是 N=9,也就是 9 日内收盘价在最高最低区间里的相对位置,K、D 用SMA(...,3,1)平滑两轮,再做 J 值放大和映射。这一支路和 RSI 支路的结构完全对称,只是输入的原始指标不同:一个看涨跌幅累积,一个看区间位置。两条线同时深度超卖,意味着「短期跌幅大」和「价格贴着区间下沿」这两件事同时成立,这是源码作者想要的双重确认。
3.4 三条线在 -100 处各自意味着什么
| 支路 | 关键变量 | 缩放方式 | -100大致对应的原始状态 |
|---|---|---|---|
| CCI | CB1 = EMA(3*C1-2*C2,5) | J 值放大后取 EMA | CCI 已跌破经典超卖线 |
| RSI | RR1 = EMA(J1,5) | (RSI1-50)*5再放大 | RSI1 已在 30 以下甚至更低 |
| KDJ | BB1 = EMA(J2,5) | (K-50)*5再放大 | K 值贴近 30 以下区间 |
表格里的「大致」不是含糊,而是因为 J 值放大让偏离幅度随 K、D 差值变化,固定阈值 -100 只能保证「落在深超卖区」,不能精确等价于原始指标等于某个数。这一点在做参数迁移时必须记住:换周期之后阈值不会自动等比缩放。
4. 买卖信号条件拆解、参数调优与通达信选股落地
4.1 买入条件:四个与项缺一不可
买入:RR AND NOT(REF(RR,1)) AND (RR1<-100 AND BB1<-100) AND CB1<-100,LINETHICK0; DRAWICON(买入,-200,1);拆成四项看:RR表示 RSI 支路的 5 日 EMA 当天向上;NOT(REF(RR,1))表示前一天还在向下,也就是金叉发生在今天;RR1<-100 AND BB1<-100要求 RSI 支路和 KDJ 支路的 EMA 都在深超卖区;CB1<-100要求 CCI 支路同样在深超卖区。四条同时成立才触发,LINETHICK0让这条线不画出来,只留给后面画图标用。DRAWICON(买入,-200,1)是在价格坐标 -200 处画 1 号图标,由于副图纵轴大致是 -250 到 250,-200 落在底部,视觉上不会压住曲线。
| 条件项 | 含义 | 去掉后的后果 |
|---|---|---|
RR | 动能方向向上 | 变成无条件抄底,信号量翻几倍 |
NOT(REF(RR,1)) | 当天刚翻转 | 趋势中段也会触发,买点被稀释 |
RR1<-100 AND BB1<-100 | 双指标深超卖 | 高位回调也进池子 |
CB1<-100 | CCI 支路同步 | 信号更稀但精度未必要更高 |
4.2 卖出条件与图标渲染
卖出:REF(RR,1)=1 AND NOT(RR) AND CB1>100,LINETHICK0; DRAWICON(卖出,200,2);卖出只有两个与项:RSI 支路由升转跌,且 CCI 支路在 +100 以上。不对称是这套公式最值得注意的设计——买入要四条全中,卖出只要两条,所以卖出信号必然比买入密集得多。DRAWICON的第三个参数是图标编号,1 通常给向上箭头、2 给向下箭头,编号写错会出现方向箭头反着的尴尬画面。另外DRAWBAND(CC1,RGB(0,244,244),0,RGB(255,200,90))画的是 CC1 与 0 轴之间的色带,正负异色,属于视觉辅助,和信号无关。
4.3 N、M、N1 三个参数的调整方向
| 参数 | 默认值 | 控制对象 | 调大 | 调小 |
|---|---|---|---|---|
M | 14 | CCI 窗口 | 曲线变钝,信号变少 | 抖动加剧,假信号增多 |
N | 9 | KDJ 的 RSV 窗口 | 更偏中期超卖 | 更偏短线急跌 |
N1 | 6 | RSI 窗口 | 反应慢,容忍度提高 | 对单日暴跌极敏感 |
改参数时最容易犯的错是只动一个。M 从 14 调到 20,CCI 的波动幅度会缩小,CB1<-100的触发率断崖式下降,看起来「信号更准了」,实际是样本被砍到个位数,统计上已经没有意义。
4.4 转成条件选股公式的改法
副图公式不能直接拿去选股,DRAWBAND、DRAWICON、POINTDOT这些画图函数在条件选股里会报错。改造原则是:保留所有:=赋值行,把带:的输出行统一改成:=,最后只留一行输出信号。
{ 参数区与赋值区原样保留,删掉所有 DRAWBAND/DRAWICON/POINTDOT 行 } XG: RR AND NOT(REF(RR,1)) AND (RR1<-100 AND BB1<-100) AND CB1<-100;在公式管理器里新建一个「条件选股公式」,把这行贴进去,公式名取个能认出来的名字,然后在选股平台的「条件选股」里挂上它,选股周期选日线。这一步有个坑:选股公式的输出只能有一个,如果前面还留着RSI:(RSI1-50)*5这种带冒号的行,会提示输出变量过多。
4.5 用 Python 复现条件并统计信号后的表现
论坛上的成功率基本都没交代统计口径。想自己核一遍,用 pandas 把核心条件重写出来,再按信号后的 N 日收益统计:
import numpy as np import pandas as pd def tdx_sma(s, n, m=1): """通达信 SMA(X,N,M): Y=(M*X+(N-M)*Y_prev)/N""" y = np.empty(len(s), dtype=float) prev = float(s.iloc[0]) for i, x in enumerate(s.to_numpy(dtype=float)): prev = (m * x + (n - m) * prev) / n y[i] = prev return pd.Series(y, index=s.index) def tdx_ema(s, n): """通达信 EMA(X,N): alpha = 2/(N+1)""" return s.ewm(alpha=2.0 / (n + 1), adjust=False).mean() N, M, N1 = 9, 14, 6 typ = (df["high"] + df["low"] + df["close"]) / 3 avg = typ.rolling(M).mean() mad = typ.rolling(M).apply(lambda w: np.abs(w - w.mean()).mean(), raw=True) cc1 = (typ - avg) / (0.015 * mad) c1 = tdx_sma(cc1, 3, 1) cb1 = tdx_ema(3 * c1 - 2 * tdx_sma(c1, 3, 1), 5) lc = df["close"].shift(1) rsi1 = tdx_sma((df["close"] - lc).clip(lower=0), N1, 1) \ / tdx_sma((df["close"] - lc).abs(), N1, 1) * 100 r1 = tdx_sma((rsi1 - 50) * 5, 3, 1) d1 = tdx_sma(r1, 3, 1) rr1 = tdx_ema(3 * r1 - 2 * d1, 5) rr = rr1 > rr1.shift(1) # KDJ 支路 K1、BB1 同理,此处略 df["buy"] = rr & ~rr.shift(1).fillna(False) & (rr1 < -100) & (bb1 < -100) & (cb1 < -100)信号算出来以后,用未来 N 日收益做一次朴素统计,注意shift(-HOLD)只能用在评估环节,绝不能出现在信号计算里:
HOLD = 5 df["fwd"] = df["close"].shift(-HOLD) / df["close"] - 1 # 信号日收盘买入,N 日后收盘卖出 sig = df[df["buy"]] print(f"信号数 {len(sig)},5 日胜率 {(sig['fwd'] > 0).mean():.2%},均值 {sig['fwd'].mean():.4%}")这段代码里几个关键点:tdx_sma用第一根有效值作为递推初值,与通达信行为对齐;mad必须用raw=True走 numpy,否则滚动窗口里反复构造 Series 会慢得离谱;胜率统计没有扣手续费和滑点,也没有处理停牌和涨停无法成交的情况,真实口径要再打折。跑完通常会看到信号数远少于想象,几十个样本上算出来的百分比,置信区间宽得可以放下任何结论。
5. 从副图到选股:公式改造、除零保护与重绘验证
5.1 三个必须加的保护
第一是除零。AVEDEV(TYP,M)在连续一字板、TYP 全相等时会等于 0,CC1 直接算出无效值,后续链条整条崩掉。稳妥的写法是在 CC1 外面套一层判断:
DEV:=AVEDEV(TYP,M); CC1:IF(DEV=0,0,(TYP-MA(TYP,M))/(0.015*DEV)),COLORFF00FF;第二是数据长度。SMA 和 EMA 都是无限记忆的递推,前面几十根的值取决于更早的历史,选股时如果股票上市不足 3*M+N 根 K 线,信号基本没有意义,可以在公式末尾加一句AND BARSCOUNT(CLOSE)>60过滤掉。
第三是复权。除权当天的跳空会被 RSI 和 RSV 当成暴跌,触发一批假信号,做日线选股一律切到前复权,否则不同数据源之间根本对不上。
5.2 确认信号不会重绘
这套公式没有 ZIG、PEAK、BACKSET 这类引用未来的函数,REF只是往回取值,理论上历史信号是钉死的。验证方法很直接:在副图里临时加一行REF(买入,1);,看它和「昨天那根 K 线上显示的买入图标」是否一一对应。如果某天收盘后信号出现、第二天又消失,说明用到了未来函数,这种情况要么是公式被改过,要么是数据本身在盘中不断刷新导致的临时显示,需要用当日收盘数据复核。
5.3 把硬阈值换成可调参数
-100和+100在源码里是硬编码的,换周期或换品种就得逐个改数字。更省事的做法是提到参数区:
N:=9;M:=14;N1:=6;TH:=100; 买入:RR AND NOT(REF(RR,1)) AND (RR1<-TH AND BB1<-TH) AND CB1<-TH,LINETHICK0;调 TH 的时候建议一次只动 10,改完在副图上肉眼比对信号落点,同时用 4.5 节那段脚本统计信号数量和样本外区间的表现。任何固定阈值都是在历史数据上拟合出来的,换个市场阶段衰减几乎是必然的,把阈值和窗口一起做小范围网格扫描,比反复改公式参数去凑一个好看的历史胜率要靠谱得多。
本文还有配套的精品资源,点击获取