复盘时想数一件事:过去一年里,某个条件成立过多少次。写出来的通达信公式统计次数跑完给了一个数,拿手数一遍却对不上——多出来的那部分,未必是公式算错,而是「数什么」这件事一开始没定义清楚。数周期、数事件、数信号段,是三种不同的东西,同一句条件写出来,三种口径的结果可以差出一大截。

这篇把口径拆开讲:先定数什么,再定从哪一根开始数,然后处理连续与重复,最后落到可以写进复盘表的形式。文中函数与写法按通达信 PC 版的常见形态描述,不同版本对边界值的处理有差异,涉及具体数值时请在公式管理器的测试输出里核一次。

一、先定义数的是什么

「次数」这个词在公式里对应三种不同的量,写之前必须先选一种。

口径 数的是什么 用的写法 什么时候选它
周期数 条件成立的 K 线根数 COUNT(X,N) 想知道条件在多少个交易日里成立过
事件数 条件由不成立转为成立的次数 COUNT(CROSS(X,0.5),N) 想知道「触发了几回」而不是「占了多少根」
信号段数 连续成立的区间被算作一段 COUNT(X AND NOT(REF(X,1)),N) 想知道「出现了几次」,一次持续的成立只算一次

三行里的差别可以用一段具体的行情说明。某个条件连续成立 12 根 K 线,中途没有中断:按周期数算,结果是 12;按事件数算,结果是 1;按信号段数算,结果也是 1。同一段走势,两个答案差 12 倍。所以问「这个条件一年成立多少次」的时候,得先说清是要 12 还是 1。

这三种口径在通达信里都不需要发明新函数,靠 COUNT 加一个条件表达式就能拿到。REF(X,1) 取上一根的值,把「本根成立、上一根不成立」作为条件,就得到事件与段的起点。

{ 同一句条件,三种数法 }
X:=CLOSE>MA(CLOSE,20);
CNT1:=COUNT(X,250);                        { 周期数:250 根里成立多少根 }
CNT2:=COUNT(CROSS(X,0.5),250);             { 事件数:由不成立转成立的次数 }
CNT3:=COUNT(X AND NOT(REF(X,1)),250);      { 信号段数:连续成立只算一次 }
{ 三者的值在公式管理器的测试输出区逐行读,不必画到图上 }

三个中间量的值在公式管理器点「测试公式」后逐行读出。想看图上对照,可以把其中一条挂成输出;不挂也不影响计算,:= 声明的中间量本来就不起笔。

二、COUNT 的窗口从哪一根算起

写通达信公式统计次数时,COUNT(X,N) 的第二个参数就是窗口长度,数的是「包含当前这根在内的最近 N 根」。这一点决定了结果对 N 极为敏感:写 20 与写 250,不是同一个量的放大与缩小,而是两个不同的统计区间。

{ 同一条件,三个窗口 }
C20:=COUNT(CLOSE>MA(CLOSE,20),20);
C60:=COUNT(CLOSE>MA(CLOSE,20),60);
CALL:=COUNT(CLOSE>MA(CLOSE,20),0);

第三个写法把 N 写成 0,含义是按当前加载到的全部有效数据统计。这个写法在复盘里好用,省去猜窗口长度;代价是它随你缩放、翻页而变化——你看到的历史长度不同,结果就不同。要拿这个数写进记录,先固定加载的 K 线根数,再读值。

窗口还有一个隐含的起点问题:数据不足 N 根的时候怎么算。刚上市的新标的、刚下载的分钟数据,前 N 根都没有完整的前置窗口,此时的 COUNT 只数到实际有的那几根。这种情况下的结果偏小,不是公式错了,是样本不够。判断方法很简单:把窗口改小到 20,再读一次,如果两次结果接近,说明前面的数据本来就少。

写复盘记录时把窗口长度一起记下来。同一句条件筛出来的数,窗口 20 与窗口 250 不是同一件事,隔几天回看时没有窗口值,这两个数就没法互相解释。

三、连续成立:EVERY 与 COUNT 不是一回事

「连续 N 根都成立」这个需求,用 COUNT(X,N)=N 能算出结果,但每个周期都要重算一次窗口,句式也长。EVERY(X,N) 直接表达这个含义:最近 N 根里每一根都成立。

{ 连续成立:两种等价写法 }
Y:=VOL>MA(VOL,5);
A1:=COUNT(Y,3)=3;      { 三个窗口全成立 }
A2:=EVERY(Y,3);        { 同上,写法更短 }
{ 连续成立的第一根,也就是这一段刚起头的位置 }
START:=A2 AND NOT(REF(A2,1));
DRAWICON(START,LOW*0.98,1);

两种写法在窗口完整时结果一致,差别出在边界上。COUNT(Y,3)=3 要求窗口里确实有三根可数,数据不足三根时 COUNT 返回的是实际成立的根数,等于 3 的可能性下降;EVERY 在数据不足时同样按实际根数判断。想避开这种边界差,就在公式开头用 BARSCOUNT(CLOSE)>N 这类条件把前几根屏蔽掉。

连续成立还有一种变体:允许中断。比如「近 10 根里有 8 根成立」,这既不是 EVERY,也不是简单的 COUNT,写法是 COUNT(X,10)>=8。这类写法在形态识别里用得多,但它的口径要写清楚,否则回看记录时会分不清是「连续 8 根」还是「10 根里 8 根」。

四、去重与分段:FILTER 改变的是统计口径

FILTER(X,N) 常在画图时用来降低标注密度,它对统计同样有影响:条件成立后 N 根之内不再触发,于是同样一段数据,加不加 FILTER,数出来的次数不同。

{ 加不加 FILTER,数出来的东西不一样 }
S:=CROSS(CLOSE,MA(CLOSE,20));
RAW:=COUNT(S,250);              { 原始触发次数 }
FLT:=COUNT(FILTER(S,10),250);   { 10 根内的重复触发被压掉 }
{ 两个数并排读,差额即重复触发的量 }

两个数之差,就是「短时间内重复触发」的量。这个差本身是有信息量的:差得大,说明该条件在震荡段里反复翻转;差得小,说明触发之间间隔较开。把两个数一起记,比只记一个更能说明条件的脾气。

FILTER 的 N 取值没有通用答案。取值偏小,去重不彻底;取值偏大,会把真正独立的两次触发合成一次。判断办法是把 N 从 5 改到 20,看结果在哪个区间开始明显塌下去——塌下去的位置,就是两次触发的最小间隔被吃掉的点。

去重只改变统计,不改变图上那条原始信号。公式里同时保留 RAW 与 FLT 两个中间量,回看时能分清「今天没触发」与「触发了但被去重压掉」,这两件事在复盘记录里含义完全不同。

五、跨周期计数与写进复盘表

跨周期时,通达信公式统计次数数的是被引用周期的根数,不是当前图的根数。日线图里引用周线数据做统计,窗口写 20 数的是 20 周,图上占的宽度是 100 根日线。这一点不写清楚,结果会被误读成「近 20 天」。

{ 跨周期计数:数的是周线的根数 }
KW:="KDJ.K#WEEK"(9,3,3);
WK:=COUNT(KW>50,20);       { 最近 20 周里 K 值在 50 以上的周数 }
CNT_W:WK,NODRAW;

写法上有一条硬限制:只能从较短周期引用较长周期,反方向不允许。日线引用周线可以,周线引用日线不行。这条限制决定了跨周期统计的方向只能是「在细周期图上看粗周期的计数」。

把结果落进复盘表,建议固定三列,缺一列以后就解释不清:

  • 窗口:N 取了多少,以及单位是日线还是周线。
  • 口径:周期数、事件数、信号段数,三选一写明白。
  • 是否去重:用了 FILTER 就写 N 值,没用就写「原始」。

这三列加上日期与条件名,一条记录就完整了。这样的表攒上几十行,再回看时能做的事就多了:同一条件在不同窗口下的次数变化、去重前后的差额走势、跨周期计数与单周期计数的偏离。站内 复盘方法与数据工具 与 指标原理与参数 两个栏目里另有几篇讲复盘表的字段怎么排,本篇只讲次数这一列的算法口径。

需要留意的边界在于:通达信公式统计次数只描述历史数据里发生过什么,描述不了以后会怎样。数出来一年触发 30 次,说明这个条件在过去一年被满足过 30 次;它是否值得关注、在什么环境下失效,是另一个层面的判断,公式本身给不出答案。公式里引用的字段与窗口如果变了,比如换复权方式、换数据源,同一个条件的历史次数也会跟着变,这时候要比的是同一口径下的前后两段,不是两个不同口径的数。

一句话:先定口径,再写 COUNT,最后把窗口与去重方式一起记下来。数字本身不难取,难的是让这个数字在三个月后还能被读懂。

本文只作技术研究口径,内容为统计口径的定义与写法,不涉及对任何标的的判断,也不提供操作层面的建议。函数名与菜单路径以本机版本的实际显示为准,不同版本在边界值处理上可能存在差异。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。