同一只标的在两处显示,日涨跌幅对不上:行情列表里读到的比值是 -0.0342,用算式算出来是 -0.0351。两个数都不是软件算错,差别落在通达信涨跌幅公式把哪个价当成了基准。列表那一列、交易所公布的数值、你自己写的算式,三处对基准的取法不一样,同一根 K 线就能给出三个数。
下面按基准、分母、复权、分档数字、停牌与除权、记录表六步展开。函数名与参数取值以本机公式编辑器里的函数列表为准。
一、基准取谁:前收、开盘与前结算
日线涨跌幅的骨架是 (C - REF(C,1)) / REF(C,1) * 100,REF(C,1) 取上一根 K 线的收盘价。式子里基准只有一处,换成谁就换一个含义。基准取 REF(C,1),算的是相对前一根收盘的变化;基准取 O,也就是当日开盘价,算的是相对当日开盘的变化。跳空开盘的根上,这两个结果分叉得最开。
{ 基准换一下,同一根的涨跌幅就换一个数 }
BAS1 := REF(C, 1); { 基准:前收盘价 }
BAS2 := O; { 基准:当日开盘价 }
ZF_A : (C - BAS1) / BAS1 * 100;
ZF_B : (C - BAS2) / BAS2 * 100;
{ 跳空开盘的根上,两条线分开 }
前结算价是另一个字段。股票日线里它与前收取到的是同一个数,期货与期权的行情把结算价单独给出,与前收不是一个值。把股票的写法搬到期货品种上,基准取错,整条线的数值都不对。
分母跟着基准一起换,两个输出不是同一个量,比较前要先对齐基准,给一个算式准备的阈值不能套到另一个算式上。
分钟周期上还有一处:基准取上一根收盘价还是当日开盘价,得到的是两条不同的线,周期越短分得越明显。核对前先把基准写进公式注释。
二、涨跌额与振幅的分母落在别处
写通达信涨跌幅公式时容易把三个数混成一个:涨跌幅、涨跌额、振幅,它们的分母各不相同。
涨跌额是价格差,写成分数时分母按 1 计,量纲是价格;涨跌幅的分母是基准价,量纲是比例,乘 100 之后才写成百分数;振幅的分子取当根的最高与最低,分母落在基准一侧,取法另有规定。这里只指出三者分母不同这一点,振幅本身的算法不展开。
{ 三个数,三种分母 }
BAS := REF(C, 1); { 基准:前收盘价 }
CHA : C - BAS; { 涨跌额,量纲是价格 }
ZF : (C - BAS) / BAS * 100; { 涨跌幅,量纲是比例 }
{ 分母不同,两个数不能互相代入 }
分母不同,两个数就不能互相代入。把一个 0.35 的价格差当成 0.0035 的比例来读,或者把 3.5 的比值当成 0.35 的价格差,都是分母看错了。两个数摆进一张表,量纲要分开标注,单位栏不能省。
涨跌额的数值随价格水平走,按涨跌额排序与按涨跌幅排序得到的次序不一样,横向对比之前先决定按哪一个排。
三、复权设置改了 REF(C,1) 的取值
复权处理改的是历史价格序列本身。C 是被改过的值,REF(C,1) 从这条序列上取数,前收跟着变,涨跌幅跟着变。通达信在 K 线图的右键菜单里给出「复权处理」,前复权把除权之前的价格按比例调整到与最新价衔接,后复权保持上市初期的价格不动、把之后的价格往上调整。
{ 复权方式改变的是 C 序列,公式一个字不用改 }
BAS : REF(C, 1); { 前收随复权设置变化 }
ZF : (C - BAS) / BAS * 100;
{ 同一条公式挂在不同复权方式的图上,读数不同 }
同一段行情,两种复权方式下的收盘价序列不同,前收不同,算出的涨跌幅也不同。这一点在除权日附近显出来:不复权口径下,除权日那根的 REF(C,1) 是除权前一根的收盘价,当日按除权参考价开盘,两个价之间夹着分红与送股,算式把这段缺口记成一根很大的跌幅;前复权把除权前后的价格接顺,这根上的涨跌幅回到与相邻根同一个量级。两条线上的数都对,回答的问题不同。
复权方式属于图表设置,不写在公式里。同一条公式挂在不同复权方式的图上读数不同,复制到另一台机器也会不同,那台机器上的设置说了算。
四、分档数字是设定值,不是统计结论
通达信涨跌幅公式里写的分档数字,例如按比值口径填的 0.1 与 0.05,是使用者填进去的设定值,不是从行情数据里算出来的统计结论。
把 0.1 写成常量,是给输出加一个分档标记。以 -9.8 作下档边界、把小于等于这个数的根记成一档,这个 -9.8 是你选的数,换一个数,同一段行情会被切成不同的段。统计口径的结论来自样本计算:先把一段时间里所有根的涨跌幅算出来,再看某个数值落在分布里的哪个位置。设定值先定,统计值后算,两者不能互相充当证据。
{ 常量是设定值,写进注释 }
UP := 9.8; { 设定的上档边界,不是统计结果 }
DN := -9.8; { 设定的下档边界 }
MARK : IF(ZF >= UP, 1, IF(ZF <= DN, -1, 0));
边界数值与分档标记分开写:边界放在公式开头的注释或者参数位置,标记写成独立的一支输出。改边界的时候只动一个地方,其余部分不用跟着改,脚本也便于复算。
本篇不写什么:不写任何价格限制幅度的判定条件与容差给法,不写振幅与波动率的算法,不写选股条件怎么组合。这三块各自要单独立题,塞进这一篇会把基准与复权这条主线冲散。
五、停牌与除权日的跳空缺口
停牌期间不生成 K 线,复牌首根的 REF(C,1) 接的是停牌前那一根的收盘价,中间隔过的交易日不计入分母,复牌跳空的部分被整根写进这一根的涨跌幅。首根的数值看着比相邻根大,原因在数据这一侧,不在行情当天的波动。
{ 缺口核算:基准与当根一起输出 }
BAS : REF(C, 1);
GAP : O - BAS; { 开盘缺口,量纲是价格 }
ZF : (C - BAS) / BAS * 100;
{ 停牌段的基准跨了若干交易日,与相邻根不等价 }
除权日的缺口按复权口径处理:不复权留在图上,前复权被接顺。同一段行情,两种口径下这一根的数不同,记录里必须写明当时用的是哪一种,否则两次统计的涨跌幅不可比。
停牌段的影响不止一根:复牌之后的一段时间里,前收可能取到停牌前的价格,首根的涨跌幅同时含停牌段的变化与当日的变化,这两部分在数据里拆不开,只能靠标记与交易日历去识别。
核对停牌与除权按这个顺序走:先看本地数据里这根 K 线之前有没有缺失的交易日;再看 REF(C,1) 取到的数值与停牌前那一根的收盘价是否相同;最后看这一根的除权标记有没有打开。
六、把口径写进复盘记录表
复盘统计做的是同一口径下的横比与纵比,口径不写进表里,两次统计的数就对不上。记录表要能回答三个问题:这一根的周期是什么、序列是哪一种复权、基准取到的是哪个价。
| 字段 | 取值示例 | 为什么要留这一列 |
|---|---|---|
| 证券代码 | 六位代码加市场后缀 | 对齐标的,避免同名混淆 |
| 交易日期 | 年月日 | 对齐交易日历,识别停牌缺口 |
| K 线周期 | 日线 / 60 分钟线 | 同一根在不同周期上的基准不同 |
| 复权方式 | 不复权 / 前复权 / 后复权 | 决定 C 序列,也就决定了 REF(C,1) |
| 基准价 | REF(C,1) 的数值 | 让涨跌幅能被复算,分母可核 |
| 收盘价 | 当日收盘的数值 | 与基准价配对才能还原算式 |
| 涨跌幅 | 百分数 | 统计对象本身 |
| 停牌 / 除权标记 | 是 / 否 | 标出缺口根,剔除或单列 |
| 数据下载日期 | 年月日 | 数据版本变化后能追溯 |
表里省掉两列会直接影响复算。「复权方式」省掉,两次统计跑在不同复权方式的图上,差的落在设置上;「基准价」省掉,涨跌幅只剩一个百分数,复算时分不出分母取的是哪一个价。
{ 导出这一行时一起写出的三个值 }
BAS : REF(C, 1); { 基准价 }
ZF : (C - BAS) / BAS * 100; { 涨跌幅 }
{ 随行记录:K 线周期、复权方式、基准价 }
记录表落成一份本地表格文件就够用。自编公式与备份在安装目录的 T0002 下,公式文件是 PriGS.dat,备份目录是 gs_bak,记录表在同层另建一个文件夹存放,恢复公式备份时不会把它一起覆盖。公式本身在「公式管理器」里维护,路径在功能菜单的公式系统下面。
同一根 K 线给出两个涨跌幅,源头在基准取谁与复权口径这两处设置;把口径写进复盘记录,通达信涨跌幅公式算出的数才可复算,也可横比。
站内还有 复盘方法与数据工具 的条目,指标原理与参数 讲取数与分母的口径,选股公式与预警 讲把条件写成公式时的字段对应,可对照查看。
本文只作技术研究口径,不涉及也不构成对任何标的的判断。
