自分でデータを集める:配列
サイズを制限した配列に最近の価格を保存し、要素の順序を確認します。
Seriesは、ローソク足に対応した履歴を保存します。一方、何をどの順序で集め、いつ削除するかを自分で決めたい場合もあります。そのために配列を使えます。
配列に保存するもの
配列は、順に並んだ要素からなります。要素にはインデックスがあり、最初は0です。
1つの配列には同じ型の要素を入れます。数値の配列や文字列の配列を作れますが、数値の配列に文字列は入れません。同じ型とは、同じ値という意味ではありません。100、103、101は、1つの配列に入る異なる数です。
この順で100、103、101を入れれば、インデックス0は100、1は103、2は101です。ここでのインデックスは格納位置であり、自動的に何本前の値を表すものではありません。
close[1]は前の足の終値です。array.get(prices, 1)はpricesの2番目の要素です。その値がいつのものかは、配列への入れ方で決まります。
array名前空間
array.new_float(0)は、空の数値配列を作ります。0は最初の要素数であり、初期価格ではありません。
| 操作 | 役割 |
|---|---|
array.push(prices, close) | 末尾に要素を追加する |
array.size(prices) | 要素数を返す |
array.shift(prices) | 先頭の要素を取り除いて返す |
array.get(prices, 0) | 最初の要素を読む |
array.avg(prices) | 保存した数値の平均を求める |
array.getには、存在する要素のインデックスを指定します。3要素なら0、1、2が有効で、3は範囲外です。
最近の5つの価格を保存する
recent_prices.pyを作ります。
"""
@pyne edge
"""
from pynecore import Persistent
from pynecore.lib import array, close, color, plot, script, ta
@script.indicator("Recent Prices", overlay=True)
def main():
prices: Persistent[list[float]] = array.new_float(0)
array.push(prices, close)
if array.size(prices) > 5:
array.shift(prices)
stored_average = array.avg(prices)
full_average = ta.sma(close, 5)
plot(stored_average, "Stored average", color=color.blue, linewidth=2)
plot(full_average, "SMA 5", color=color.orange)list[float]は数値を保存する配列の型です。Persistentは、次のローソク足でも配列を残す指定です。毎回新しく作るわけではありません。
操作はarray名前空間で行います。汎用のPythonライブラリを読み込んだり、リストリテラルを書いたりする必要はありません。
Pythonを知っている方へ: PyneCoreのPython実行環境では、配列の実体はPythonのリストです。型にlist[float]と書くのはそのためです。要素は指定した要素型にそろえます。一般的なPythonリストと違い、ここでは数値と文字列を混在させません。
Pythonの機能も使える@pyneプログラムでは、リスト自身の操作も使えます。例えばprices.append(close)で追加し、prices.pop(0)で先頭を削除できます。この本の@pyne edgeでは、array.pushとarray.shiftで行います。Edgeでは、直接prices[0]で読む代わりにarray.get(prices, 0)を使い、作成にも[]リテラルではなく、例えばarray.new_float(0)を使います。
デモデータで実行してください。青い線は、それまでに集めた価格の平均です。1本目は1つ、2本目は2つ、その後3つ、4つが入ります。5本目以降は5要素を保ちます。

追加と削除の順序
6本目では、まず6つ目の終値を加えます。一時的に6要素となり、ifが真になるので、最初の最も古い値を取り除きます。平均を求める時点では、2本目から6本目までの5つの終値が残ります。
このプログラムでは、先頭が残っているうちの最古、末尾が最新です。5要素そろってからのarray.get(prices, 0)は、4本前の価格です。現在の価格ではありません。
最新値を読むには、最後のインデックスarray.size(prices) - 1を使います。0から数えるので、要素数と最後のインデックスは1違います。
途中のウィンドウと完全なウィンドウ
最初は2つの平均の動きが違います。配列の平均は、集まっている値が4つ以下でも求められます。ta.sma(close, 5)は、5つそろってから値を返します。
5本目からは、同じ5つの終値を平均します。以後の線は、数値表現の精度の範囲で一致するはずです。
そのため、配列の平均が組み込み移動平均をそのまま置き換えられるとは限りません。初期ウィンドウの扱いは、自分で決める必要があります。
サイズを制限する理由
追加するだけなら、データの長さとともに配列も大きくなります。最後には、処理した終値すべてを保存することになります。
ここで必要なのは直近5つなので、古いものを取り除きます。計算する時点で、サイズは最大5です。実際の収集処理でも、どのデータをいつまで残すかを決めてください。
自分で試す
残す価格を3つにし、SMAの長さも3にします。さらに、配列内の最も古い価格を3本目の線として描画してください。
解答
削除条件です。
if array.size(prices) > 3:
array.shift(prices)組み込み平均と新しい線です。
full_average = ta.sma(close, 3)
oldest_price = array.get(prices, 0)
plot(oldest_price, "Oldest stored price", color=color.gray)毎回、読み出す前にarray.pushを行うので、要素は少なくとも1つあります。3要素そろってからは、最古の価格は2本前のものです。
配列を書き出す
残す数を5から3にし、価格が100、103、101、105の順なら、各ローソク足の後の配列を書き出してください。4本目では、103、101、105がこの順で入っているはずです。