上一章根据单根 K 线计算。要计算收盘价的变化,就需要两根:当前与前一根。本章将把它们联系起来。

程序相同,K 线在变

在演示数据上启动程序时,PyneCore 按时间顺序遍历 K 线。main() 先处理第一根,再处理第二根,以此类推。每次的 close 都表示当前正在处理的 K 线收盘价。

因此,一次 plot(close, "Close") 调用就能绘制整条线。一次执行为线条添加一个点;一系列 K 线最终形成整条线。

上一章的 midpoint = (high + low) / 2 也会在每根 K 线上重新执行。赋值会重新计算该根 K 线的中间价。变量名不变,值可以变化。

处理顺序由执行器保证。你不需要为遍历整张图表编写循环。

使用方括号回看历史

close[1] 表示前一根 K 线的收盘价。方括号中的数字说明向前回看多少根。

表达式表示哪根 K 线的收盘价?
close当前处理的 K 线
close[0]同样是当前处理的 K 线
close[1]前一根 K 线
close[2]前两根 K 线

如果日线中连续三个收盘价是 100、103、101,那么在第三根 K 线上,close 为 101,close[1] 为 103,close[2] 为 100。

此时 close - close[1] 就是 101 - 103,结果为 −2。这是相邻收盘价的差。close - open 则比较同一根 K 线的开盘价与收盘价。两个计算回答的是不同问题。

回看的单位是 K 线,不是日历天数。在日线市场数据中,周末或休市可能让前一根 K 线的时间早于数天之前。

自己计算的值也能有历史

内置价格的历史值已经可用。如果想回看自己的计算结果,把变量标记为 Series:

midpoint: Series[float] = (high + low) / 2

冒号之后是类型标注。float 表示可以带小数部分的数字。Series[float] 还要求执行器保留该变量逐根 K 线的历史值。

赋值依然在每根 K 线上执行。midpoint 是当前计算值,midpoint[1] 是上一根 K 线保存的值。Series 需要从 pynecore 模块导入。

图表上的两种变化

在项目的 scripts 文件夹创建 history_change.py:

"""
@pyne edge
"""
from pynecore import Series
from pynecore.lib import close, color, high, low, plot, script


@script.indicator("Changes Between Candles", overlay=False)
def main():
    midpoint: Series[float] = (high + low) / 2
    close_change = close - close[1]
    midpoint_change = midpoint - midpoint[1]
    plot(close_change, "Close change", color=color.blue)
    plot(midpoint_change, "Midpoint change", color=color.orange)

保存后,选择 Run Pyne code 和 demo 数据。单独面板会出现两条线。蓝线表示收盘价变化,橙线表示中间价变化。某点为正,说明该值高于上一根 K 线。

history_change.py 程序旁的 PyneIDE 图表显示两条变化曲线。
收盘价和计算所得中间价在相邻 K 线间的变化,显示在单独面板中。

将鼠标移到某根 K 线上,图表值就会显示该时点的数据;不需要再到代码文件中单独选择时间。

第一根 K 线会发生什么?

数据文件的第一根 K 线没有更早的 K 线。此时 close[1] 是缺失值 na,名称来自 “not available”。

缺失价格不等于零。close - close[1] 的结果也缺失,所以第一根 K 线不会画出有效的变化值。这是正确结果:只有一根 K 线,无法计算两根之间的差。

使用 na(value) 可以判断一个值是否缺失。条件与标记会介绍条件处理。在此之前,不要试图把图表开头的缺失点“修正”为零价格。

这种写法不会让未来 K 线变得可用。close[-1] 也不表示下一个收盘价。决策只能使用在该次执行时已知的数据。

自己试一试

修改蓝线,将当前收盘价与五根 K 线之前的收盘价比较。在线条标题中也写明五根 K 线的间隔。

运行前判断:开头多少根 K 线会缺少结果?

参考答案
close_change = close - close[5]
plot(close_change, "Five-candle change", color=color.blue)

前五根 K 线没有五根之前的收盘价。到第六根时就有了:数据文件第一根 K 线的收盘价。

橙线不会因此改变。如果也要将中间价变化改成五根的间隔,还需要修改 midpoint[1]。

检查计算

选择连续三根 K 线,记下它们的收盘价,手算第二根和第三根相对前一根的变化。蓝线应显示相同的值。如有差异,先检查所选时点和回看的根数。

进阶练习:用保留值替代历史访问

单个历史值也可以通过保存到下一根 K 线来使用。这正是 Persistent 变量的用途,程序记住的内容一章会介绍。你也可以读完该章后再做这道题。

改写 history_change.py,使程序中既不出现 close[1],也不出现 midpoint[1]。用两个持久变量保留上一根 K 线的收盘价和中间价。两条线应与原程序一致,第一根 K 线仍然缺少变化值。

思考执行顺序:什么时候读取保留值,什么时候才能用当前值覆盖?

参考答案

将 Series 的导入替换成 Persistent:

from pynecore import Persistent

在 pynecore.lib 的导入中添加 na。保留文件开头的标记和指标设置,将 main() 改为:

def main():
    previous_close: Persistent[float] = na
    previous_midpoint: Persistent[float] = na
    midpoint = (high + low) / 2
    close_change = close - previous_close
    midpoint_change = midpoint - previous_midpoint
    plot(close_change, "Close change", color=color.blue)
    plot(midpoint_change, "Midpoint change", color=color.orange)
    previous_close = close
    previous_midpoint = midpoint

两个保留变量的初值是 na,因为第一根 K 线还没有前一个价格。因此,第一项差值也缺失。函数末尾保存当前值,下一根 K 线便用它们计算。

如果把 previous_close = close 放在减法前面,就会用当前收盘价减去自身,结果为零。使用持久变量时,需要自己保证更新顺序。

回看一根 K 线时,两种方法都可用。方括号版本更短,也能直接访问多根之前的数值。这两个持久变量只保留最近一次保存的价格。

在同一演示数据上运行两个版本,选几根 K 线比较两条曲线。