## 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`は前日の移動平均から当日の移動平均へ指定された色で線を引きます。以降はこの繰り返しでチャートが描画されます。

コンストラクタで@作用素を使用すると、それはnextメソッドに引き継がれます。10営業日前の5日間単純移動平均を求めるために`$MA5 = {-10}MA_new(5)`としてコンストラクタを実行したら、nextメソッドを`MA_next($MA5)`として実行するだけで9営業日前の値が計算されます。

上の例ではProtraが日付を進めたあとにnextメソッドを実行していますが、以下のように日付を進めない状態で連続して実行しても、次々と次の営業日の値が求まります。

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

### 2つの指標の交差

オブジェクト指向風に実装されているのはテクニカル指標だけではありません。以下は単純移動平均の交差で売買するシステムの一部です。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
    else
        cross = Crossover_next($Cross, ma_fast, ma_slow)

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

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

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

## 実装の詳細

コンストラクタはインスタンスとなる配列の要素を確保したあと、当日の値を計算します。`MA_new`の実装を以下に示します。指標を計算するクラスのインスタンスは、当日と前日の値のほかに@作用素の値とIndexの値を保持します。これはコンストラクタで指定された@作用素を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で用いる@作用素の値
        obj[5] = Index # Indexを保存
        # days日前に@作用素を指定して、days日分進めることで当日の値を計算する。
        while obj[4] < 0
            MA_next(obj)
        end
        obj[4] = at # コンストラクタに渡された@作用素の値
        obj[5] = {-at}Index # @作用素を打ち消した本来のIndexを保存
        return obj
    end

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

    # nextメソッド
    def MA_next(obj)
        obj[4] = obj[4] + 1 + obj[5] - Index # 計算で用いる@作用素の値を計算
        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クラスはさまざまな値の単純移動平均を計算するほかに、計算した値を何日間か保存するためにも用います。たとえば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

Copyright (C) 2008, 2010 panacoran.
This document may be used under the term of the GNU General Public License version 3 or higher.