TIlibについて

トップ > TIlibについて

添付されているライブラリTIlibは、利用するだけならそれほど難しくないのですが、仕組みを理解したり、同じようなマクロを書いたりするのは難しいです。ここでは、このTIlibについて解説します。

利用方法

TIlibの概要

TIlibはオブジェクト指向風に記述されています。テクニカル指標の名前に_newの付いた関数を実行すると配列が返ります。テクニカル指標の名前をクラス名、この関数をコンストラクタ、返る配列をインスタンスと考えてください。ほとんどのコンストラクタは引数を持ち、日数などのパラメータを指定することができます。

この配列の最初の要素が前日の値を、次の要素が当日の値を保持しています。指標が複数ある場合には偶数番目の要素が前日の値を、奇数番目の要素が当日の値を保持します。過去のデータが足りずに計算できない場合には指標の値はnullになります。

それ以外の要素は翌日以降の値を計算するために必要な値を保持します。コンストラクタで生成した配列を、同じクラス名に_nextの付いた関数(nextメソッドと呼びます)に渡すと、格納された値を利用して翌日の値を計算して配列の値を更新します。

単純移動平均の描画

以下は単純移動平均を描画するプログラムです。Protraは最初にチャートの左端の日付でこのプログラムを実行します。最初は MA_new(5) が実行されて、その日の時点の5日間単純移動平均を格納した配列が返ります。その日も含めて過去に5日以上データがないと移動平均の値はnullになります。

if !$MA5
    $MA5 = MA_new(5)
else
    MA_next($MA5)
    MA_draw($MA5, $Colors[0])
end

次にProtraは翌営業日の日付でこのプログラムを実行します。今度は MA_next($MA5) が実行されて、その日の終値と前回までのデータをもとに、その日の移動平均が計算されます。MA_drawは前日の移動平均から当日の移動平均へ指定された色で線を引きます。以降はこの繰り返しでチャートが描画されます。

コンストラクタでint型@作用素を使用すると、それはnextメソッドに引き継がれます。以下の例ではMA_newにint型@要素で-10を指定しているので、描画される移動平均が10営業日前にずれます。この場合MA_nextにint型@作用素を指定する必要がないことに注意してください。

if !$MA5
    $MA5 = {-10}MA_new(5)
else
    MA_next($MA5)
    MA_draw($MA5, $Colors[0])
end

Protraはチャート描画プログラムを日付を進めながら繰り返し実行します。MA_nextはProtraが日付を進めていても進めていなくても、実行されるたびに翌営業日の値を計算します。以下の例では10営業日後の値が求まります。

i = 0
while i < 10
    MA_next($MA5)
    i = i + 1
end

以下の例のようにMA_nextにint型@作用素を指定して、10営業日後の値を求めることはできません。nextメソッドには絶対にint型@作用素を使用しないでください。

{10}MA_next($MA5)

2つの指標の交差

オブジェクト指向風に実装されているのはテクニカル指標だけではありません。以下は単純移動平均の交差で売買するMA Crossシステムの一部です。Crossoverクラスは2つの指標が交差したかを調べます。

if !$__INIT__
    $MA_fast = MA_new(10)
    $MA_slow = MA_new(40)
    $Cross = Crossover_new(0, 0.0)
    $__INIT__ = 1
else
    MA_next($MA_fast)
    MA_next($MA_slow)
end

ma_fast = MA_value($MA_fast)
ma_slow = MA_value($MA_slow)
...
if ma_fast == null || ma_slow == null || !price
    return
end
cross = Crossover_next($Cross, ma_fast, ma_slow)

コンストラクタは2つの引数を取り、交差にどれくらい敏感に反応するかを指定します。両方に0を指定すると、交差したときにすぐに反応します。最初の引数は日数で、交差してから指定した日数の間再び交差しない場合に反応します。2番目の引数は閾値で、交差してから指定した値以上差が開くと反応します。

nextメソッドには、コンストラクタの生成したインスタンスと2つの指数を指定します。インスタンスには2つの指数の差が保持されます。次回以降にnextメソッドが呼ばれたときに、指数の差が逆転していてコンストラクタで指定した条件を満たしていると、0以外の値が返ります。0より大きいときは最初の指数が2番目の指数の上に交差しており、0より小さいときは下に交差しています。

実装や使い方が似ているクラスにReversalがあります。このクラスは指標が反転したかを調べます。

実装の詳細

コンストラクタはインスタンスとなる配列の要素を確保したあと、当日の値を計算します。MA_newの実装を以下に示します。指標を計算するクラスのインスタンスは、当日と前日の値のほかにint型@作用素の値とIndexの値を保持します。これはコンストラクタで指定されたint型@作用素をnextメソッドに引き継ぐためと、nextメソッドの実行前にProtraかPtSimがIndexを進めたかどうかを判断するためです。

# コンストラクタ
def MA_new(days)
    obj = [6]
    obj[0] = null # 前日の値
    obj[1] = null # 当日の値
    obj[2] = MAutil_new(days) # 単純移動平均を計算するためのクラス
    obj[3] = {-days}LatestClose # n日以前のもっとも最近に売買が成立したときの終値
    obj[4] = -days # MA_nextで用いるint型@作用素の値
    obj[5] = Index # Indexを保存
    # days日前に@作用素を指定して、days日分進めることで当日の値を計算する。
    while obj[4] < 0
        MA_next(obj)
    end
    obj[4] = at # コンストラクタに渡されたint型@作用素の値
    obj[5] = {-at}Index # int型@作用素を打ち消した本来のIndexを保存
    return obj
end

nextメソッドは最初に計算で用いるint型@作用素の値(obj[4])を計算します。保存してあるIndex(obj[5])とIndexを比較して、もしProtraかPtSimがIndexを進めていないなら、int型@作用素を一つ進めて翌日の値を計算する準備をします。

# nextメソッド
def MA_next(obj)
    obj[4] = obj[4] + 1 + obj[5] - Index # 計算で用いるint型@作用素の値を計算
    obj[5] = Index # Indexの値を保存
    obj[0] = obj[1] # 前日の値を保存
    if {obj[4]}Close # 売買が成立しているか?
        obj[3] = {obj[4]}Close # 成立していたら終値を更新
    end
    if !obj[3] # まったく売買が成立していなければ計算しない
        return
    end
    MAutil_next(obj[2], obj[3]) # 終値の単純移動平均を計算
    obj[1] = obj[2][0] # MAutil_nextの計算した値を当日の値に代入
end

MAutilクラスはさまざまな値の単純移動平均を計算します。MAutilクラスは計算に使った値を保存しているので、過去の値を記録するためにも使えます。たとえば Smoothed ROC(13-21) では、13日間の指数移動平均を21日分保存するためにMAutilを用いています。

# コンストラクタ
def MAutil_new(days)
    obj = [6]
    obj[0] = null # データの平均
    obj[1] = 0 # データの合計
    obj[2] = days # 日数
    obj[3] = 0 # バッファの一番古い要素の位置を計算するためのカウンタ
    obj[4] = [days] # データを保存するバッファ
    obj[5] = null # バッファからこぼれた一番古い要素
    return obj
end

nextメソッドの最初の引数はコンストラクタの返したインスタンスで、2番目の引数は単純移動平均を計算する値です。

# nextメソッド
def MAutil_next(obj, val)
    if val == null
        return
    end
    oldest = obj[3] % obj[2] # バッファの一番古い要素の位置
    if obj[4][oldest] != null # 古い値が存在する?
        obj[5] = obj[4][oldest] # 古い値の退避
        obj[1] = obj[1] - obj[5] # 合計から古い値を引く
    end
    obj[1] = obj[1] + val # 合計に新しい値を加える
    obj[4][oldest] = val # 古い値を新しい値で置き換える
    obj[3] = obj[3] + 1 # カウンタをインクリメント
    if obj[3] >= obj[2] # 日数分のデータがそろったか?
        obj[0] = (float)obj[1] / obj[2] # 平均を計算
    end
end