2020/5/3

ゼロから始めるBitcoinの自動取引②


この記事は全4回中の第2回です。他の回についてはこちら( #1 / #3 / #4 )。 bitFlyerを利用したBitcoinの自動取引について、Pythonと裁定取引についてはある程度わかるけど他はわからないという人のために概要を簡潔に書いていきます。 第2回の今回はテクニカル指標編です。

テクニカル指標とは

トレーダーの人が参考にしている値動きの指標です。様々な種類があります。 それぞれの指標にそれぞれの根拠と強みがありますが、必ずダマシも存在し万能な指標は存在しません。

実装

テクニカル指標を元に自動取引ルールを決めるとき、プログラム上でテクニカル指標を計算しなければなりません。 今回は有名な指標をいくつか実装して見ます。

SMA, EMA

移動平均線のことです。SMAが単純移動平均線、EMAが指数移動平均線です。 SMAの値は、現在までのロウソク足 \(n\) 本分の終値の平均なので計算方法は省略します。 EMAについては次のように計算されます。 終値の経過が \((c_1,c_2,\cdots,c_n,c_{n+1},\cdots)\) のとき、期間 \(n\) 、 \(k\) 番目の \({\rm EMA}(n)_k\) は \(n\) 番目以降で定義され、 \(k=n\) のとき \[{\rm EMA}(n)_k= \dfrac{c_1+c_2+\cdots+c_n}{n}\] \(k\geqq n+1\) のとき \[{\rm EMA}(n)_k=(1-\alpha)\times{\rm EMA}(n)_{k-1}+\alpha\times c_{k}\] ただし \[\alpha= \dfrac{2}{n+1}\] 期間の違う二つのSMA,EMAがクロスする時点(ゴールデンクロス、デッドクロス)などを売買の判断材料にします。 EMAの方がSMAより値動きへの反応が早いです。 実装してみると次のようになります。

[コードを表示]

Python

import pandas as pd
import numpy as np

ohlc = np.random.randint(700000,900000,(100,6))
closeprice = ohlc[:,4]

def SMA(n):
    """
    return : SMAのndarray(length=len(ohlc))
    """
    return pd.Series(closeprice).rolling(n).mean().values

def EMA(n):
    """
    return : EMAのndarray(length=len(ohlc))
    """
    alpha = 2/(n+1)
    x0 = closeprice[:n].mean()
    ema = [x0]
    for i in range(len(closeprice)-n):
        ema.append(ema[-1]*(1-alpha)+closeprice[n+i]*alpha)

    nanlist = np.zeros(n-1)
    nanlist[:] = np.nan
    return np.concatenate([nanlist,np.array(ema)])

print(SMA(10).shape,SMA(10)[-5:])
print(EMA(10).shape,EMA(10)[-5:])

前回実装した getohlc() を意識して、 [[timestamp,open,high,low,close,volume],...] の配列をohlcに代入したと想定してSMA,EMAを計算します。 ただし、今回は70万〜90万の数値で作った100×6のランダム配列を入れています。 引数 n は期間です。 実行したらわかるように、入れたOHLCデータの本数と等しい長さのnumpy.ndarrayを返します。 ただし、初めのn-1個は計算不可能なためnanが入っています。

MACD

これも有名なテクニカル指標です。期間の違うEMA二つの差として定義されます(短期EMA-長期EMA)。 また、これの単純移動平均をとったものがsignalと呼ばれます(指数移動平均をとる場合もあります)。 これもゴールデンクロス、デッドクロスなどを売買の判断材料にします。EMAよりも反応が早いです。 これは先ほど作ったEMA関数を利用しましょう。

[コードを表示]

Python

import pandas as pd
import numpy as np

ohlc = np.random.randint(700000,900000,(100,6))
closeprice = ohlc[:,4]

def EMA(n):
    省略

def MACD(n1,n2,n3):
    """
    return : MACDのndarray,signalのndarray
    """
    macd = EMA(n1) - EMA(n2)
    macd[:n2] = np.nan
    signal = pd.Series(macd).rolling(n3).mean().values

    return macd,signal

print(MACD(12,26,9))

n1 が短期EMAの期間、 n2 が長期EMAの期間、 n3 がシグナルの期間です。 MACDとSignalの二つを入れたOHLC配列と同じ長さのnumpy.ndarrayで返します。 計算できない部分についてはnanが入ります。

RSI

期間中の買われすぎ、売られすぎの度合いを客観的に判断するオシレーター系指標(振り子のようにある値の範囲を行ったり来たりする指標)です。0〜100の値を推移します。 計算方法は次のようになります。少し複雑です。 \[{\rm RSI}(n)=100- \dfrac{100}{1+RS}\] ただし \[RS= \dfrac{\text{期間中の平均上昇幅}}{\text{期間中の平均下落幅}}\] ※平均上昇(下落)幅は「一つ前の平均上昇(下落)幅 \(\times \dfrac{n-1}{n}+\) 現在の上昇(下落)幅 \(\times \dfrac1n\) 」で計算される。 RSIが低い値から回復してきたときに値段が上がりそうだから買いだな、というように使うのが一例です。 実装は次のようになります。

[コードを表示]

Python

import pandas as pd
import numpy as np

ohlc = np.random.randint(700000,900000,(100,6))
closeprice = ohlc[:,4]

def RSI(n):
    """
    return : RSIのndarray
    """
    RSI_period = n
    diff = pd.Series(closeprice).diff(1)
    positive = diff.clip(lower=0).ewm(alpha=1.0/RSI_period).mean()
    negative = diff.clip(upper=0).ewm(alpha=1.0/RSI_period).mean()
    rsi = 100 - 100/(1-positive/negative)
    return rsi.values

print(RSI(14)[-5:])

これまでと同様、入れたOHLC配列と同じ長さのnumpy.ndarrayで返します。 計算できない部分についてはnanが入ります。

BB

ボリンジャーバンドです。 終値が正規分布に従うと仮定し、過去 \(n\) 本の終値から平均 \(\mu\) と分散 \(\sigma\) を計算します。 順張り逆張り両方に使えます。 終値が正規分布に従うというのはそもそも間違いだと思うので(ファットテールのため)個人的にはあまり信用したことがありません。 実装は次のようになります。

[コードを表示]

Python

import pandas as pd
import numpy as np

ohlc = np.random.randint(700000,900000,(100,6))
closeprice = ohlc[:,4]

def BB(n,sigma=2):
    base = pd.Series(closeprice).rolling(n).mean().values
    sig = pd.Series(closeprice).rolling(n).std(ddof=1).values

    upper_band = base + sigma*sig
    lower_band = base - sigma*sig

    return upper_band, lower_band

upper,lower = BB(14)
print(upper.shape,upper[-5:])
print(lower.shape,lower[-5:])

上のバンドラインと下のバンドラインを、入れたOHLC配列と同じ長さのnumpy.ndarrayで返します。 計算できない部分についてはnanが入ります。 sigma はデフォルトでは2が入っていますが変更もできます。

RCI

RSI同様、期間中の買われすぎ、売られすぎの度合いを客観的に判断するオシレーター系指標です。-100〜100の値を推移します。 計算方法は次のようになります。独特です。 \[{\rm RCI}(n)=\left(1- \dfrac{6d}{n^3-n}\right)\times100\] ただし \(d\) は \(n\) 期間の日付の順位(現在を1として遡っていく)と価格の順位の二乗和。\\ 例えば直近5期間の価格が \((90,89,87,91,100)\) と変化した場合、価格の順位は \((3,4,5,2,1)\) で、日付の順位 \((5,4,3,2,1)\) なので、二乗和は \[(-2)^2+0^2+2^2+0^2+0^2=8\] となる。 これまでの指標とは少し雰囲気の違う指標ですが、これを使って大儲けしているトレーダーさんもいるみたいなので侮れないです。 実装は次のようになります。

[コードを表示]

Python

import pandas as pd
import numpy as np

ohlc = np.random.randint(700000,900000,(100,6))
closeprice = ohlc[:,4]

def RCI(n):
    """
    return: RCIのndarray
    """
    rci = []
    hm = len(closeprice) - (n-1)
    for j in range(len(closeprice) - (n-1)):
        table = np.zeros([2,n])
        # closeprice[-n:0]になるのを回避
        if j == len(closeprice)-n:
            table[0] = closeprice[-n:]
        else:
            table[0] = closeprice[-len(closeprice)+j:-len(closeprice)+n+j]
        table[1] = np.arange(n,0,-1)

        sortedtabel = table[:,np.argsort(table[0])]

        index = np.arange(n,0,-1)
        d = 0
        for i in range(n):
            d += (index[i]-sortedtabel[1][i])**2
        rci.append((1-6*d/(n*(n*n-1)))*100)
    nanlist = np.zeros(n-1)
    nanlist[:] = np.nan
    return np.concatenate([nanlist,np.array(rci)])

入れたOHLC配列と同じ長さの(省略) 計算できない部分につい(省略)

ATR

これまでと少し系統が違う指標です。ボラティリティの大きさを示唆します。 true rangeというものをmax(最新の確定足の高値,最新の確定足の一つ前のローソク足の終値)-min(最新の確定足の安値,最新の確定足の一つ前のローソク足の終値)で定義し、その移動平均をとります。 実装は次のようになります。

[コードを表示]

Python

import pandas as pd
import numpy as np

ohlc = np.random.randint(700000,900000,(100,6))
closeprice = ohlc[:,4]

def ATR(n):
    """
    return : ATRのndarray(length=len(ohlc))
    """
    tr = []
    for i in range(1,len(ohlc)):
        tr.append(max(ohlc[-i][2],ohlc[-i-1][4]) - min(ohlc[-i][3],ohlc[-i-1][4]))
    tr.reverse()
    atr = pd.Series(tr).ewm(span=n).mean().values
    atr = np.insert(atr,0,np.nan)
    return atr

print(ATR(14)[-5:])

ohlc のデータをそのまま使います。 返り値はこれまでと同様です。

talibの利用

ここまで必死に作ってきたテクニカル指標ですが、実はtalibというライブラリを使えばいとも簡単に同じものが得られます。

[コードを表示]

Python

import talib

ohlc = np.random.randint(700000,900000,(100,6)).astype('float') # talibに与える配列はint型だとエラーが出る
closeprice = ohlc[:,4]

SMA = talib.SMA(closeprice,timeperiod=10)
EMA = talib.EMA(closeprice,timeperiod=10)
MACD = talib.MACD(closeprice,fastperiod=12,slowperiod=26,signalperiod=9)
RSI = talib.RSI(closeprice,timeperiod=14)
upper, middle, lower = talib.BBANDS(closeprice, timeperiod=25, nbdevup=2, nbdevdn=2, matype=0)
ATR = talib.ATR(ohlc[:,2],ohlc[:,3],closeprice,timeperiod=14)

RCIはないものの、多くの指標が実装されていてとても充実しています。 talibがインストールできる人はこちらを使ったほうが簡単です(じゃあ今までの実装はなんだったんだという話ですが)。 ちなみに上で実装してきたものはtalibの返り値と比較して概ね合致する事を確認済みです。 さて、今回はここまでにします。テクニカル指標が扱えるようになったところで次回はいよいよ実際に取引する部分について説明していきます。

今回のまとめ

  • テクニカル指標計算にはtalibを利用する

参考

特になし

back