テクニカル分析ライブラリTA-Lib
April 28, 2021
テクニカル分析 pythonはじめに
pythonでテクニカル分析を行う場合、各種指標の算出にTA-Libを利用するケースがあります。ネット上にはTA-Libを利用したサンプルソースも数多く存在しています。本稿では、TA-Libとpythonで自前で用意したテクニカル指標算出関数の比較(答え合わせ)を行います。
TA-Libのインストール
TA-Libはpython専用のライブラリという訳ではなく、各種言語用のインターフェースを持つライブラリのようで、pythonのインターフェースとTA-Lib本体のバイナリの両方インストールする必要があります。
TA-Lib自体は、ご本尊のTA-Lib : Technical Analysis Libraryにあります。ここからソースコードを取得し、自分でバイナリをビルドすることも可能ですが、めんどいので、野良ビルドされた私設パッケージを使うのが吉。
Windowsの場合は、pip一発でインストールを可能にする私設パッケージがこちらに置かれています。
ここから、使用するpythonのバージョン用の私設パッケージを入手します。例えばwindowsがx64で、pythonのバージョンが3,8の場合、
TA_Lib‑0.4.19‑cp38‑cp38‑win_amd64.whl
をダウンロードします。ダウンロード後、以下のコマンドでインストールします。
C:\> pip install "c:\download_path\TA_Lib‑0.4.19‑cp38‑cp38‑win_amd64.whl"
TA-Libの利用
例えば、以下のようなcsvファイルを読み込み、macdを算出するコードは、次の通りです。
2015/01/05,375,376,366,373,1831000000
2015/01/06,367,372,362,363,1782900000
,,,,,.
2020/01/16,621,624,613,616,2468700000
2020/01/17,618,622,613,618,1676800000
2020/01/20,618,621,616,616,1231700000
import talib
import pandas as pd
path = './1332.csv'
df = pd.read_csv(path, header=None,
names=['Date','Open','High','Low','Close','Volume'], encoding='UTF-8')
df = df.set_index("Date")
macd = talib.MACD(df['Close'], fastperiod=26, slowperiod=12, signalperiod=9)
macd[0]がmacdの値、macd[1]がsignalの値、macd[2]がmacd-signalの値となります。
TA-Libの利用の注意点
テクニカル分析は、あくまで私見ですが、分析結果そのものに意味がある訳ではなく、他の市場参加者も自分と同じ指標に基づいて売買を行っているというところに意味があります。つまり、テクニカル信者がある指標での買いシグナルを根拠にある銘柄を買うとすると、同じテクニカルを信じる別の信者も同じタイミングで買いを入れるはずなので、買うから上がる、上がるから買うのレバレッジが働き、信者達の目論見通りに株価が動く(はず)という訳です。
そのため、「他の信者と同じ指標値」という点が特に重要です。
TA-Libはブラックボックスとして容易に利用できてしまうので、中でどういう計算が行われているのかは留意すべきです。つまり、「TA-Libで算出した指標が、ほんとうに他の信者と同じなのか?」を留意すべきです。これはTA-Libに限った話ではありませんが。
そこで、次節からはTA-Libとそれ以外の方法の指標の比較を行っていきます。
macd
移動平均線のゴールデンクロスよりもシグナルが早く出るということで人気のmacdを見ていきます。 pythonでmacdを自前で計算させると、次のようなコードになるでしょう。そりゃ、意識高い系プログラマーならもっと簡潔にエレガントに書くのでしょうが、泥グラマーが書けばこんなものでしょう。
def calc_macd(df, es, el, sg):
macd = pd.DataFrame()
macd['ema_s'] = df['Close'].ewm(span=es).mean()
macd['ema_l'] = df['Close'].ewm(span=el).mean()
macd['macd'] = macd['ema_s'] - macd['ema_l']
macd['signal'] = macd['macd'].ewm(span=sg).mean()
macd['diff'] = macd['macd'] - macd['signal']
f_plus = lambda x: x if x > 0 else 0
f_minus = lambda x: x if x < 0 else 0
macd['diff+'] = macd['diff'].map(f_plus)
macd['diff-'] = macd['diff'].map(f_minus)
return macd
これと、TA-Libの計算結果を比較してみます。
import numpy as np
import talib
import pandas as pd
def compare_macd(df):
macd = calc_macd(df, 12, 26, 9)
close = np.array(df['Close']*1.0)
tamacd = talib.MACD(close, fastperiod=26, slowperiod=12, signalperiod=9)
compare = pd.DataFrame()
compare['my_macd'] = macd['macd']
compare['ta_macd'] = tamacd[0]
compare['my_signal'] = macd['signal']
compare['ta_signal'] = tamacd[1]
compare['my_diff'] = macd['diff']
compare['ta_diff'] = tamacd[2]
pd.set_option('display.max_rows', 2000)
print(compare)
def main():
path = './1332.csv'
df = pd.read_csv(path, header=None,
names=['Date','Open','High','Low','Close','Volume'], encoding='UTF-8')
df = df.set_index("Date")
compare_macd(df)
if __name__ == '__main__':
main()
比較結果は以下の通り。
my_macd ta_macd my_signal ta_signal my_diff ta_diff
Date
2015/01/05 0.000000 NaN 0.000000 NaN 0.000000 NaN
2015/01/06 -0.224359 NaN -0.124644 NaN -0.099715 NaN
2015/01/07 -0.130828 NaN -0.127178 NaN -0.003650 NaN
... ... ... ... ... ... ...
2015/02/19 -4.321830 NaN -4.100060 NaN -0.221770 NaN
2015/02/20 -3.521985 NaN -3.984371 NaN 0.462387 NaN
2015/02/23 -1.813523 -0.809359 -3.549981 -1.676602 1.736458 0.867243
2015/02/24 -0.231990 0.594899 -2.886114 -1.222302 2.654124 1.817201
2015/02/25 1.233390 1.927638 -2.061946 -0.592314 3.295335 2.519952
2015/02/26 3.120983 3.747560 -1.025091 0.275661 4.146074 3.471899
... ... ... ... ... ... ...
2015/10/30 10.144012 10.144011 6.522003 6.521999 3.622010 3.622012
2015/11/02 9.210587 9.210586 7.059719 7.059716 2.150867 2.150869
2015/11/04 8.613623 8.613622 7.370500 7.370498 1.243123 1.243125
2015/11/05 8.685932 8.685931 7.633586 7.633584 1.052345 1.052347
2015/11/06 8.164967 8.164966 7.739862 7.739861 0.425104 0.425106
... ... ... ... ... ... ...
2016/01/14 50.708566 50.708567 54.114835 54.114836 -3.406269 -3.406269
2016/01/15 46.351386 46.351387 52.562146 52.562146 -6.210759 -6.210759
2016/01/18 42.249876 42.249877 50.499692 50.499692 -8.249815 -8.249816
2016/01/19 38.156102 38.156103 48.030974 48.030974 -9.874872 -9.874872
2016/01/20 34.593674 34.593674 45.343514 45.343514 -10.749840 -10.749840
... ... ... ... ... ... ...
2020/01/15 -4.451281 -4.451281 -0.986967 -0.986967 -3.464313 -3.464313
2020/01/16 -5.508248 -5.508248 -1.891224 -1.891224 -3.617025 -3.617025
2020/01/17 -6.114041 -6.114041 -2.735787 -2.735787 -3.378254 -3.378254
2020/01/20 -6.678533 -6.678533 -3.524336 -3.524336 -3.154196 -3.154196
- 2015/01/05~2015/02/20:最初の算出不能期間の扱いが異なる。
- 2015/02/23~2016/01/19:最初は差が大きいが徐々に差がなくなる
- 2016/01/20~:一致
いまいち意味不明な結果に。
直近の結果はTA-Libと自前計算は一致するものの、算出期間の初期は微妙に異なる。バックテストなんかでTA-Libを利用する場合は要注意か。
stochastic
ストキャスティクスは、売られすぎ/買われすぎをあらわすオシレーター。
泥グラマーが書いたコードはコチラ。
def calc_stochastic(df, term):
stochastic = pd.DataFrame()
stochastic['%K'] = ((df['Close'] - df['Low'].rolling(term).min()) \
/ (df['High'].rolling(term).max() - df['Low'].rolling(term).min())) * 100
stochastic['%D'] = stochastic['%K'].rolling(3).mean()
stochastic['%SD'] = stochastic['%D'].rolling(3).mean()
return stochastic
これと、TA-Libの計算結果を比較してみます。
def compare_stochastic(df):
term = 14
stochastic = calc_stochastic(df, term)
high = np.array(df['High']*1.0)
low = np.array(df['Low']*1.0)
close = np.array(df['Close']*1.0)
tastoch = talib.STOCH(high, low, close,
fastk_period=term, slowk_period=3)
compare = pd.DataFrame()
compare['my_%D'] = stochastic['%D']
compare['ta_%D'] = tastoch[0]
compare['my_%SD'] = stochastic['%SD']
compare['ta_%SD'] = tastoch[1]
pd.set_option('display.max_rows', 2000)
print(compare)
比較結果は以下の通り。
$ python test_compare_ta.py
my_%D ta_%D my_%SD ta_%SD
Date
2015/01/05 NaN NaN NaN NaN
2015/01/06 NaN NaN NaN NaN
... ... ... ... ...
2015/01/23 NaN NaN NaN NaN
2015/01/26 NaN NaN NaN NaN
2015/01/27 62.183236 NaN NaN NaN
2015/01/28 81.098580 NaN NaN NaN
2015/01/29 87.844612 87.844612 77.042142 77.042142
2015/01/30 88.553114 88.553114 85.832102 85.832102
2015/02/02 80.476550 80.476550 85.624758 85.624758
... ... ... ... ...
2020/01/16 13.388334 13.388334 18.542362 18.542362
2020/01/17 8.988276 8.988276 13.962464 13.962464
2020/01/20 7.482993 7.482993 9.953201 9.953201
- 2015/01/27~2015/02/28:算出不能期間の扱いが異なる。
- 2015/01/29~:一致
TA-Libと自前計算の結果は一致している。
rsi
rsiは、売られすぎ/買われすぎをあらわすオシレーター。
泥グラマーが書いたコードはコチラ。
def calc_rsi(df, term):
rsi = pd.DataFrame()
rsi['diff'] = df['Close'].diff()
rsi['diff+'] = rsi['diff'].copy()
rsi['diff-'] = rsi['diff'].copy()
rsi['diff+'][rsi['diff']<0] = 0
rsi['diff-'][rsi['diff']>0] = 0
rsi['up-sum']=rsi['diff+'].rolling(term).sum() # ※
rsi['down-sum']=rsi['diff-'].abs().rolling(term).sum() # ※
rsi['rsi']=rsi['up-sum']/(rsi['up-sum']+rsi['down-sum'])*100.0
return rsi
これと、TA-Libの計算結果を比較してみます。
def compare_rsi(df):
term = 14
rsi = calc_rsi(df, term)
close = np.array(df['Close']*1.0)
tarsi = talib.RSI(close, timeperiod=term)
compare = pd.DataFrame()
compare['my_rsi'] = rsi['rsi']
compare['ta_rsi'] = tarsi
pd.set_option('display.max_rows', 2000)
print(compare)
比較結果は以下の通り。
$ python test_compare_ta.py
my_rsi ta_rsi
Date
2015/01/05 NaN NaN
2015/01/06 NaN NaN
... ... ...
2015/01/22 NaN NaN
2015/01/23 NaN NaN
2015/01/26 54.000000 54.000000
2015/01/27 65.384615 60.026738
2015/01/28 65.048544 61.575722
2015/01/29 63.207547 57.948333
... ... ...
2020/01/14 38.938053 44.722191
2020/01/15 41.904762 42.685062
2020/01/16 38.095238 41.333334
2020/01/17 36.893204 42.316929
2020/01/20 27.956989 41.566426
- 2015/01/05~2015/01/23:算出不能期間は一致。
- 2015/01/26:1日目の算出結果は一致。
- 2015/01/27~:以降は全部異なる
おそらく算出方法が異なるのだとは思うが、2015/01/26だけ一致しているのがとても不思議。
直近の200日分を可視化してみる。
TA-Libのrsiの方が、振れ幅が小さく、シグナルがより出にくいようなグラフです。
TA-Libを利用してシグナルを出すようなことを考える場合は要注意です。
Money Partnersのサイトの解説によれば、「rsiの算出方法として、平均上昇幅・下落幅を「指数平滑移動平均線」で算出する方法もある」という記載がありますが、そこには計算式は書かれておらず、TA-Libがその方法を採用しているのかもわからない。
まとめ
WindowsにTA-Libをインストールする方法と、使用上の注意点をまとめました。
ライブラリを利用してテクニカル分析による売買シグナルを検討する場合、使用するライブラリがどうやって計算しているかはよく調べるべし。TA-Libしかり。