交叉标记说明事件发生在哪里。如果还想知道从数据开头至今发生过多少次,程序就必须记住计数器的值。

为什么普通变量不够?

下面两行会在每根 K 线重新开始计数:

count = 0
count += 1

第一行在每次执行 main() 时都将变量设为零,第二行加一,所以每根 K 线的最终结果都是 1。

+= 是赋值的简写:count += 1 与 count = count + 1 含义相同。先读取旧值,加一,再把结果放回变量。

累积事件需要不同的起点:我们只想在计数器首次创建时归零。

Persistent 变量

count: Persistent[int] = 0

Persistent 标记要求在 K 线之间保留变量值。初始零值只设置一次。到下一根 K 线,继续使用上一根已收盘 K 线留下的值。

这里 int 表示计数器使用整数类型。Persistent 与先前的 Series 一样,从 pynecore 模块导入。

这个标记不会把数据保存到硬盘。新的运行会重新从数据开头、零计数开始。这里讲的是同一次程序运行中 K 线之间的保留。

统计上穿次数

创建 event_counter.py:

"""
@pyne edge
"""
from pynecore import Persistent
from pynecore.lib import close, color, plot, script, ta


@script.indicator("Crossing Counter", overlay=False)
def main():
    fast_average = ta.sma(close, 5)
    slow_average = ta.sma(close, 20)
    crosses_up = ta.crossover(fast_average, slow_average)
    count: Persistent[int] = 0
    if crosses_up:
        count += 1
    plot(count, "Crossing count", color=color.blue, linewidth=2)

在演示数据上运行。单独面板的计数从零开始,每次上穿增加一,其余 K 线保持不变。

event_counter.py 旁的 PyneIDE 图表显示累计交叉次数的曲线。
每次上穿,计数加一;其他 K 线上保持原值。

例如,图表最后一个值是 12,表示全部已处理数据中共有十二次上穿。屏幕所见的一小段时间内,标记可能更少。缩放图表不会重置计数器。

状态与历史

两种标记负责不同任务:

标记对执行器的要求
Series[float]可查询该值在之前各根 K 线上的记录
Persistent[int]下一根 K 线从已保存值继续

Series 变量的赋值在每根 K 线上执行,这些结果形成历史。Persistent 变量的初始赋值只执行一次,之后由代码中的修改持续更新状态。

如果还要直接查询计数器之前的值,可以把当前计数放到单独的 Series 变量里。保留值和索引历史值是两件事。

保留变量应放在哪里

我们在条件之前、每根 K 线都能执行到的位置声明计数器。这样,即使条件不满足,它也有值,plot 才能在每根 K 线上绘制。

不要在 main() 上方创建普通的可变全局计数器。应在程序使用状态的地方,用标记说明需要跨 K 线保留。

这里的历史运行每根 K 线计算一次。在同根 K 线会被多次求值的运行模式中,Persistent 会重新从上一根已收盘 K 线的状态开始。因此,它并不统计所有底层执行次数。本书的计数器用于已收盘 K 线上的事件。

条件也可以重置数值

若要统计连续上涨 K 线,可以在下跌或开收盘相同的 K 线上清零:

if close > open:
    streak += 1
else:
    streak = 0

需要提前用 Persistent[int] 创建 streak。这里清零是一项明确的条件操作,并非每根 K 线开头自动发生。

自己试一试

添加第二个保留计数器,统计下穿次数,并用橙线显示。两项计算都要在每根 K 线上执行。

参考答案

在上穿计算旁添加:

crosses_down = ta.crossunder(fast_average, slow_average)
down_count: Persistent[int] = 0
if crosses_down:
    down_count += 1
plot(down_count, "Downward count", color=color.orange, linewidth=2)

保留原有 count 及其绘图。两个计数器相互独立,重新运行会让两者都从零开始。

在一个信号处检查

在同一数据上,比较交叉章节的标记与计数器变化。发生上穿的 K 线上,计数应恰好增加一。下一根只有发生新的上穿时才能再次增加。