执行器负责遍历整组 K 线,但单根 K 线内部也可能需要重复操作,例如把最近五个收盘价相加。这时可以使用循环。

for 与 range

for offset in range(5):
    total += close[offset]

range(5) 依次将 0、1、2、3、4 赋给循环变量 offset,不包括 5。每个值都会让缩进行执行一次。

因此,close[offset] 先表示当前收盘价,再表示之前一、二、三、四根的收盘价,恰好五个值。

循环前需要将 total 清零。它是当前 K 线计算的中间结果,不是跨 K 线保存的计数器。

range(1, 5) 给出的序列是 1、2、3、4。包含起点,不包含终点。不写起点时从零开始。

手写移动平均

创建 manual_average.py:

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


@script.indicator("Manual Average Check", overlay=False)
def main(
    length: int = input.int(5, title="Length", minval=1, maxval=100),
):
    total = 0.0
    for offset in range(length):
        total += close[offset]
    manual_average = total / length
    builtin_average = ta.sma(close, length)
    difference = manual_average - builtin_average
    plot(difference, "Difference", color=color.blue)

在演示数据上运行。线条表示自写平均值与内置 SMA 的差。预热后应在零附近,或因数值表示精度而极接近零。

manual_average.py 旁的 PyneIDE 单独面板显示两种平均值之差。
预热后,自写循环与内置 SMA 的结果差值维持在零附近。

这里不画两条重叠线,因为微小差异不容易看出。直接显示差值更容易判断计算是否一致。

一次执行做了什么?

长度为五时,顺序如下:

  1. 将 total 设为零。
  2. 循环累加当前和前四个收盘价。
  3. 将总和除以五。
  4. 同时计算内置 SMA,并与自写结果求差。
  5. 为差值线绘制一个点。

随后执行器进入下一根 K 线。total 重新归零,循环相加的是新 K 线对应的五个值。

如果把 total 改成 Persistent,下一根就会从上一总和继续。每根都往旧结果再加五个数,这就不再是当前窗口的平均值。

缺少历史值时

最初四根 K 线中,五元素窗口至少缺一个收盘价。总和中的 na 会让自写平均值也缺失。内置五期 SMA 同样到第五根才有效。

本书演示数据的所有收盘价均有效。如果数据中段也有缺失,ta.sma 对缺失值的处理可能不同于简单按 K 线偏移求和。所以本例在指定数据上检查。

要重复多少次?

百根窗口意味着每根 K 线执行一百次加法。两千根数据就是二十万次循环步骤。

内置 ta.sma 的用途,就是通过执行器为常见任务设计的计算来完成这项工作。本例的自写循环用于理解和比较。如果现成函数满足需求,就使用它。

循环终点必须明确且有限。输入的最大值也限制了用户每根 K 线能请求多少计算。

break 与 continue

break 终止循环。continue 跳过当前步骤剩余部分,直接进入下一步。

在平均窗口中要谨慎使用。如果只相加三个值就停止,就不能像包含五个值那样仍除以五。跳过步骤会改变计算含义。

自己试一试

把长度设为三。在某根 K 线处,写出循环中的偏移量和对应三个价格。然后修改程序,以叠加模式绘制自写平均和内置平均,便于与差值结果比较。

参考答案

三个偏移是 0、1、2。装饰器使用 overlay=True,计算后添加:

plot(manual_average, "Manual average", color=color.blue, linewidth=2)
plot(builtin_average, "Built-in average", color=color.orange)

从这个视图移除差值绘图。零附近的线与价格水平的平均线共用刻度时不好阅读。可以另存原来的差值面板版本以供检查。

识别窗口错位

思考将循环改成 range(1, length + 1) 会怎样。元素数仍为五,但当前 K 线被排除,五根之前的值被纳入。这就变成上一根 K 线的平均窗口,而非当前窗口。