テクニカル分析は信じてよいものなのか

# はじめに

株価のテクニカル分析を行うにあたり、各種テクニカル指標をpythonで独自計算（というかネットで拾ってきたソースを使って計算）させていたが、いまいち自信が持てないので、TA-Libで答え合わせをしてみた。やってみると、驚きの事実が！！！！！

本稿は、macd, stochastic, rsiの答え合わせした結果のまとめ。

# 使用データ

以下の形式のcsvファイル。2015/01/05～2020/01/20のデータ。

~~~csv:title=1332.csv
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
~~~

このcsvファイルを以下のコードで読み込む。

~~~python
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

当方で使用しているmacd算出関数は以下。

~~~python
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の計算結果を比較する。

~~~python
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]

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()
~~~

比較結果

最初のうちは結果は異なるが...

![compare_macd_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/3e4114b3-eb28-3572-69d9-141846b86670.png)

2015-10-30以降は同じ結果

![compare_macd_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/4c2881f6-1c4f-6028-1e02-ddbd1d2e6e7e.png)

....

![compare_macd_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/906f52e7-f1b9-ba5d-bc0c-c4d59689e332.png)

なにか釈然としない

先頭から100日分を可視化してみる。

![compare_macd_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/228ac05c-def7-40fc-8e1a-33e762c48a4d.png)

よくわからないズレ方。
最新の結果が一致してるから、まぁいいか。

# stochastic

当方で使用しているstochastic算出関数は以下。

~~~python
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の計算結果を比較する。

~~~python
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]
~~~

比較結果

最初の計算不能期間の扱いは異なるものの...

![compare_stochastic_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/fdd4ca35-bfeb-cbf3-7509-f9ae09e3a3a3.png)

あとは一致した、

![compare_stochastic_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/ddfdb255-b5dc-6921-3bca-097e6dd40845.png)

ほっとした。

# rsi

当方で使用しているrsi算出関数は以下。rsiの定義は、※のところは、sum()ではなくてmean()だろう、という突っ込みはあるが、結局同じこと。

~~~python
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の計算結果を比較する。

~~~python
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
~~~

比較結果

一致したのは最初の日だけ。

![compare_rsi_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/469480c2-1d49-2433-2508-b1d0e5becf33.png)

あとは全然ちがう！

![compare_rsi_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/5b3efdbf-ad24-d31e-e070-7a9d9fa1430b.png)

先頭から200日分を可視化してみる。

![compare_rsi_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/4791dd7f-58f3-5e29-6b53-6daffeb4c188.png)

これはどう解釈したらよいのだろうか。

真っ先に、自分が書いたコードを疑う。が、所詮これはネットで拾ってきたコード。1日目だけ結果が一致しているのがなお不思議。

念のため、別のツールではどうなのか見てみる。例えば、Yahoo! Finance.

![compare_rsi_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483341/b97140bf-7ea9-a8ea-b320-7ee6873100a9.png)

1332 日本水産(株) 2015/1/30 のrsi(算出期間14日)は、

+ Yahoo! Finance.では、62.37%
+ TA-Libでは、64.0331%
+ 自力計算では、67.7868%

全部違う！


# まとめ

テクニカル分析指標macd, stochastic, rsiを自作コードとTA-Libとで比較してみたら、rsiは全く違う結果になった。

計算方法はいろいろあるんだろうから、違ってても別にいいんですよ。でも、ここで算出した指標をもとにシグナルを出して売買する場合は話は別。そもそも、テクニカル指標を元に売買するっていうことは、他の多くの市場参加者も同じ指標で売買するはず、ってことが大前提にある。要は、皆が同じ行動をとると、上がるものはより上がる。別にテクニカル指標そのものを信じている訳ではない。でも、ツールによって指標の値が異なるなら、同じ原理で行動しても、行動のタイミングは使うツールによって変わってしまうので意味はない。

少なくともrsiは通用しないということがわかった。

テクニカル分析なんて所詮そんなもの（個人の感想です）。