程序记住的内容
用 Persistent 变量统计交叉次数,并区分状态与历史值。
交叉标记说明事件发生在哪里。如果还想知道从数据开头至今发生过多少次,程序就必须记住计数器的值。
为什么普通变量不够?
下面两行会在每根 K 线重新开始计数:
count = 0
count += 1第一行在每次执行 main() 时都将变量设为零,第二行加一,所以每根 K 线的最终结果都是 1。
+= 是赋值的简写:count += 1 与 count = count + 1 含义相同。先读取旧值,加一,再把结果放回变量。
累积事件需要不同的起点:我们只想在计数器首次创建时归零。
Persistent 变量
count: Persistent[int] = 0Persistent 标记要求在 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 线保持不变。

例如,图表最后一个值是 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 线上,计数应恰好增加一。下一根只有发生新的上穿时才能再次增加。