一段公式在历史图上回看时,标记每次落在低点附近;把本地数据往后补一段再回看同一根 K 线,标记的位置挪了。这不是源码写错了符号,是它在计算那根 K 线时读了当时还不存在的数据。通达信未来函数检测要处理的,就是这类引用位置的问题。
下面按判断标准、常见写法、三步检测法、替换思路四层展开,每一层都给可动手核对的动作。全文只讨论公式语言与数据引用的口径,不评价任何公式的效果,也不给出操作结论。文中出现的函数名以通达信金融终端个人版自带的函数说明为准,版本号可以在「帮助 → 关于」里核对。
一、判断依据是引用位置,不是函数名
以正在计算的那根 K 线为界,公式能引用的数据分成两段:界之前已经落地的,和界之后还没有产生的。一段源码只要引用了界之后的数据,它就含未来函数。判断依据在这里,与函数名听起来是否可疑无关。
按这个依据分,情形有两类。一类是函数在设计上就向后读,比如直接取当前之后第若干根的写法。另一类是算法要确认一个区域内的极值,而区域的边界要等区域走完才能确定。第二类更隐蔽,因为源码里可能只出现了看起来很普通的统计函数。
由此还能推出一个现象:同一段源码在只看历史的那段区间上表现平稳,问题只在处理最后一根 K 线时暴露。这也是为什么只在历史图上检验,很难发现问题。
二、可疑写法集中在三族
按用途归类,通达信公式里需要重点核对的写法集中在三族。函数名的拼写以你所用版本自带的函数列表为准,不同版本收词并不完全一致。
| 族别 | 典型写法 | 为什么会读到界之后的数据 |
|---|---|---|
| 峰谷定位类 | ZIG、ZIGZAG、PEAK、TROUGH 及同名加 BARS 后缀的写法 | 要确认一个高点,需要等后面出现足够低的点来把它固定下来 |
| 回填类 | BACKSET | 按条件反向修改前面若干根 K 线上的取值 |
| 向后引用类 | REFX | 直接取当前之后第 N 根的值 |
跨周期引用是另一处需要单独看的地方,它不属于上面三族,但可能落到同一类问题上。日线公式里引用本周的周线数据,在本周走完之前,这个周线值会随着本周行情推进而变化。判断办法在后文第三步。
三、重绘会让回看结果与使用结果错位
峰谷定位类函数在历史回看时的表现,和它在处理最新一根 K 线时不一样,原因在数据是否齐全。
回看历史时,一根低点后面的数据已经全部存在,函数能在整段数据上一次性找出拐点,标记落在低点上。处理最新一根时,它后面的数据还没有,函数无法判断当前这根是不是拐点,只能等到后面的点走出来、条件成立之后,再在那个确认位置上给出标记。两个位置之间的差,就是这个函数带来的错位。
这个错位对回测的影响很直接。回测使用的历史数据是齐全的,所以含未来函数的公式在回测里给出的标记,落在条件确认之前的位置上。回测结果因此显得比使用时的表现更好。这是机制层面的直接结果,与回测软件无关。
四、通达信未来函数检测的三步做法
三步做完,通达信未来函数检测的结论才落到具体的一行上:是某一个函数读到了界之后的数据,还是某个窗口的边界要等后续才能确定。第一步是函数名对照。把源码里用到的函数逐个抄出来,对着公式编辑器里的函数说明或帮助菜单里的函数列表,逐条看它引用了哪一段数据。这一步能把上面三族的写法直接筛出来。
第二步是时间切片对照。选一根 K 线,把与它相关的输出在收盘后记一次,在盘中再记一次。两次一致,说明这一段没有读取未完成的数据;两次不一致,公式里就含未来函数。这一步不需要函数表知识,是动手成本最低的一步,建议放在最先做。
第三步是数据裁断法。把本地日线数据裁到某个日期为止,重跑公式,比较裁断点前后那几根 K 线上的输出,看是否因为后面数据被去掉而变化。
{ 第一步:只输出中间量,便于逐根比对 }
Z1:=ZIG(3,5);
D1:PEAKBARS(3,5,1);
{ 第二步:同一根K线的两次记录 }
{ 收盘后记一次 X 的最后一根值 }
{ 盘中再记一次,两次不一致即含未来数据 }
{ 第三步:把本地数据裁到某日为止后重跑 }
{ 比较裁断点附近输出的变化 }
五、检测出来之后的替换思路
替换的方向是统一的:把「需要等后续数据确认」的判断,改成「只用界之前的数据就能算出」的判断。改写之后信号会有滞后,但滞后可以用数据说明,重绘没法用数据说明。
峰谷定位类的替换,思路是把依赖后续确认的拐点,改成固定窗口内的极值。窗口长度写成常数,边界就不依赖后续数据。
{ 原写法:拐点位置需要后续数据确认 }
G1:ZIG(3,5);
{ 替换写法:固定窗口内的最低值,边界不依赖未来 }
G2:LLV(LOW,20);
{ 两者都会在图上给出位置,差别在于 }
{ G2 在当前K线上就能确定,G1 要等后续确认 }
回填类的替换,是把「回头修改前面几根」改成「对当前及之后生效」的写法。若原本想表达的是「距上一次条件成立过了多少根」,用只向后看的计数写法即可,不必回头改写历史取值。
替换过程中要留意一处副作用:固定窗口的说法会引入窗口长度这个新参数,而窗口长度的选择本身又会回到参数调整的问题上。窗口长度选定后应当固定下来,不要为了让历史曲线好看而反复改动。
六、这些写法不属于未来函数
核对时也容易把正常写法误判成未来函数,下面几类不属于。
- 向前取值的引用。取上一根的写法只用到已经落地的数据,不在未来函数的范围内。
- 固定窗口的统计。窗口长度是常数的极值、均值、计数类写法,边界不依赖后续数据。
- 已落地字段。成交量、成交额、开高低收这些字段在计算时都已确定。
- 写在公式里的常数。源码里写死 20、60、250 这类数字,含义是设定的窗口长度,与未来数据无关。
需要单独判断的是跨周期引用。它是否算未来函数,取决于引用的那个周期有没有走完,而不是取决于写法本身。
七、替换之后的复核与记录
替换写完之后,用三项复核确认它真的解决了问题,而不是把问题挪到了别处。
第一项是时间切片复核。对新写法再做一次收盘后与盘中的对照,两次一致才算通过。第二项是位置差复核。把新旧两种写法在同一段历史上并排输出,数出两者信号位置相差多少根 K 线。这个差值是可以用数字写下来的,也正是替换必须付出的代价。第三项是数据起点复核。确认替换后的写法在本地数据的最左端返回空值还是 0,返回 0 的写法会在图上拉出一条水平线。
三项复核做完,建议留一份改动记录,字段写清四处:原来的函数名、替换后的写法、位置相差的根数、复核的日期。记录的作用是在后面的使用中遇到信号位置与预期不符时,能快速判断这是替换带来的滞后,还是又引入了新的引用问题。
文中函数名、目录名与菜单路径以通达信金融终端个人版为参照;不同版本与券商定制版的函数列表可能存在差异,请以你所用版本的「帮助 → 关于」与公式编辑器内的函数说明为准。一次完整的通达信未来函数检测包含函数名对照、时间切片对照与数据裁断三步。站内 公式编写与语法函数 一栏讲函数本身的用法,回测验证与风险控制 一栏讲回测口径与验证顺序,公式报错与卡顿排查 一栏讲编译与显示的提示原文,可对照查看。
