【Python】株価のテクニカル分析指標から売買タイミングを自動計算
以前に「pandas-datareader」と「mplfinance」を用いて、テクニカル分析チャートを作成しました。その際に「MACD」や「RSI」などのテクニカル分析指標を算出しました。
今回は、これらの指標から売買のタイミングを計算します。
1銘柄ずつチャートを目視していくのは大変です。注目した方がよい銘柄を自動で割り出して、その銘柄のチャートだけを見るようにして手間を省きたいのです。
- [1] 株価の取得、テクニカル分析指標の算出
- [2] 今回使用するデータ
- [3] ゴールデンクロス・デッドクロス(MACD)
- [4] ボトムアウト・ピークアウト(ヒストグラム)
- [5] RSI
- 終わりに
- 出典
[1] 株価の取得、テクニカル分析指標の算出
以前の記事で紹介した内容で行います。
ここまで済めば、テクニカル分析チャートまでは作れます。
[2] 今回使用するデータ
ソフトバンク(9984.JP)の2021/04/01〜07/13の株価を使用します。
[3] ゴールデンクロス・デッドクロス(MACD)
MACDとシグナルが重なるときが、売買のタイミングと言われています。
詳しい説明は、下記サイト等を参考にしてみてください。
[3-1] ソースコード
MACDとシグナルが重なるのは差が0になるタイミングです。つまりヒストグラムが0を跨ぐタイミングです。
# ヒストグラムと日付を取り出す hists = df['Hist'] dates = df.index # 日数分ループ for i in range(1, hists.size): # 2日分取り出し h1 = hists.iloc[i-1] h2 = hists.iloc[i] # ゴールデンクロス・デッドクロスを判定 if h1 < 0 < h2: # ゴールデンクロス print(f'{dates[i]:%Y-%m-%d} {code} ゴールデンクロス {h1:.1f},{h2:.1f}') elif h1 > 0 > h2: # デッドクロス print(f'{dates[i]:%Y-%m-%d} {code} デッドクロス {h1:.1f},{h2:.1f}')
以下の出力結果が得られます。
# 2021-04-20 9984.JP デッドクロス 13.8,-12.3 # 2021-06-03 9984.JP ゴールデンクロス -0.1,15.5 # 2021-07-07 9984.JP デッドクロス 1.6,-10.4 # 2021-07-13 9984.JP ゴールデンクロス -6.9
[3-2] チャート
出力結果をチャート上に示すと、赤枠と青枠の部分になります。赤枠がゴールデンクロス、青枠がデッドクロスです。
[3-3] 計算通りに売買したら成功したか
04/20の売りは、5月に入り株価が下落したため成功と言えます。一方、06/03の売りは7月中旬時点では成功とは言えません。
また、07/07, 07/13と短期間にデッドクロス・ゴールデンクロスが発生しています。頻繁に発生するような場合は、売買タイミングと考えるのは難しそうです。
[4] ボトムアウト・ピークアウト(ヒストグラム)
ヒストグラムが天井・天底をうつときも売買の目安と言われています。
ゴールデンクロス・デッドクロスよりも反応が早いと言われています。詳しい説明は、下記サイト等を参考にしてみてください。
[4-1] ソースコード
ヒストグラムを用いて計算しますが、小さな変化を拾いすぎないように以下の条件としています。
- ボトムアウト:2日連続で減少後に2日連続で上昇 and 負値
- ピークアウト:2日連続で上昇後に2日連続で減少 and 正値
減少・上昇は日毎の差分をh5d.diff()
で取得し、差分の正負で判断しています。
# ヒストグラムと日付を取り出す hists = df['Hist'] dates = df.index # 日数分ループ for i in range(0, hists.size-5): # 5日分取り出し h5d = hists.iloc[i:i+5] # 日毎の差分を取得 hdiff = h5d.diff() # ボトムアウト・ピークアウトを判定 if (hdiff[1] < 0) and (hdiff[2] < 0) and \ (hdiff[3] > 0) and (hdiff[4] > 0) and \ (h5d[2] < 0): # ボトムアウト hist_values = f'{h5d[0]:.1f},{h5d[1]:.1f},{h5d[2]:.1f},{h5d[3]:.1f},{h5d[4]:.1f}' print(f'{dates[i+2]:%Y-%m-%d} {code} ボトムアウト {hist_values}') elif (hdiff[1] > 0) and (hdiff[2] > 0) and \ (hdiff[3] < 0) and (hdiff[4] < 0) and \ (h5d[2] > 0): # ピークアウト hist_values = f'{h5d[0]:.1f},{h5d[1]:.1f},{h5d[2]:.1f},{h5d[3]:.1f},{h5d[4]:.1f}' print(f'{dates[i+2]:%Y-%m-%d} {code} ピークアウト {hist_values}')```
以下の出力結果が得られます。
# 2021-04-30 9984.JP ボトムアウト -18.8,-30.0,-37.2,-30.4,-29.4 # 2021-05-17 9984.JP ボトムアウト -180.7,-221.7,-246.1,-234.2,-222.5 # 2021-06-16 9984.JP ピークアウト 41.9,45.6,47.1,40.6,32.8
参考までに、条件を2日連続ではなく1日とすると、ボトムアウト5回・ピークアウト5回という結果になります。
[4-2] チャート
出力結果をチャート上に示すと、赤枠と青枠の部分になります。
[4-3] 計算通りに売買したら成功したか
- 2021/04/30 ボトムアウトで買い:失敗
- 2021/05/17 ボトムアウトで買い:失敗
- 2021/06/16 ピークアウトで売り:成功
少なくとも今回の例では、良い結果が得られませんでした
[4-4] 条件を変えたらどうなるか
2日連続上昇後に減少の条件から「2日連続」を無くしてみます。
# 2021-04-21 9984.JP ボトムアウト -12.3,-23.9,-19.8 # 2021-04-23 9984.JP ボトムアウト -19.8,-23.8,-16.4 # 2021-04-30 9984.JP ボトムアウト -30.0,-37.2,-30.4 # 2021-05-17 9984.JP ボトムアウト -221.7,-246.1,-234.2 # 2021-06-07 9984.JP ピークアウト 20.9,30.9,29.9 # 2021-06-16 9984.JP ピークアウト 45.6,47.1,40.6 # 2021-06-28 9984.JP ピークアウト 33.4,47.1,45.9 # 2021-06-30 9984.JP ピークアウト 45.9,47.2,44.7 # 2021-07-02 9984.JP ピークアウト 44.7,46.8,17.1 # 2021-07-09 9984.JP ボトムアウト -17.9,-22.5,-6.9
売買のタイミングは判断できませんが、傾向を見るのには役立つかもしれません。ボトムアウト・ピークアウトは傾向を見るにとどめるのが良さそうです。
ソースコードは次の通りです。
# ヒストグラムと日付を取り出す hists = df['Hist'] dates = df.index # 日数分ループ for i in range(0, hists.size-3): # 3日分取り出し h3d = hists.iloc[i:i+3] # 日毎の差分を取得 hdiff = h3d.diff() # ボトムアウト・ピークアウトを判定 if (hdiff[1] < 0 < hdiff[2]) and (h3d[1] < 0): # ボトムアウト hist_values = f'{h3d[0]:.1f},{h3d[1]:.1f},{h3d[2]:.1f}' print(f'{dates[i+1]:%Y-%m-%d} {code} ボトムアウト {hist_values}') elif (hdiff[1] > 0 > hdiff[2]) and (h3d[1] > 0): # ピークアウト hist_values = f'{h3d[0]:.1f},{h3d[1]:.1f},{h3d[2]:.1f}' print(f'{dates[i+1]:%Y-%m-%d} {code} ピークアウト {hist_values}')
[5] RSI
RSIは70%以上だと買われすぎ、30%以下だと売られすぎと言われています。
- RSIが70%以上:買われすぎなので売りのタイミング
- RSIが30%以下:売られすぎなので買いのタイミング
詳しい説明は、下記サイト等を参考にしてみてください。
[5-1] ソースコード
単純にRSIの値で判断すれば良いので、これまで紹介した2つに比べてだいぶシンプルです。
# 日数分ループ for i, rsi in enumerate(df['RSI']): if rsi > 70: # RSI > 70% print(f'{df.index[i]} RSI>70% {rsi:.1f}') elif rsi < 30: # RSI < 30% print(f'{df.index[i]} RSI<30% {rsi:.1f}')
以下の出力結果が得られます。この例ではRSIが70%以上になりませんでした。
# 2021-05-13 00:00:00 RSI<30% 24.4 # 2021-05-14 00:00:00 RSI<30% 27.6 # 2021-05-17 00:00:00 RSI<30% 24.1 # 2021-05-18 00:00:00 RSI<30% 25.1 # 2021-05-19 00:00:00 RSI<30% 24.4 # 2021-05-20 00:00:00 RSI<30% 21.8 # 2021-05-21 00:00:00 RSI<30% 22.7 # 2021-05-24 00:00:00 RSI<30% 22.4 # 2021-05-25 00:00:00 RSI<30% 23.6 # 2021-05-26 00:00:00 RSI<30% 18.7 # 2021-05-27 00:00:00 RSI<30% 17.8 # 2021-05-28 00:00:00 RSI<30% 21.1 # 2021-05-31 00:00:00 RSI<30% 25.5 # 2021-06-17 00:00:00 RSI<30% 25.7 # 2021-06-18 00:00:00 RSI<30% 27.6 # 2021-06-21 00:00:00 RSI<30% 23.2 # 2021-06-23 00:00:00 RSI<30% 27.0
[5-2] チャート
出力結果をチャート上に示すと、赤線がRSI=30%のラインです。
[5-3] 計算通りに売買したら成功したか
- 2021/05/13〜05/31 RSI<30%で買い:失敗
- 2021/06/17〜06/23 RSI<30%で買い:失敗
売られすぎ(RSI<30%)の期間が二度ありました。このタイミングで買っていたら、いずれも失敗でした。
RSIは横ばいの相場で有効と言われています。この例では下降トレンドであったため効力を発揮しなかったものと思われます。
[5-4] 別銘柄の場合はどうか
JR東日本(9020.JP)なら、どういう結果になったのか見てみます。
- 2021/04/21〜04/22 RSI<30%で買い:成功
- 2021/06/02〜06/14 RSI>70%で売り:成功
- 2021/06/29〜06/30 RSI<30%で買い:成功
逆に上手くいき過ぎていて気持ちが悪いです。
MACDを用いた指標で売買しても成功していました。
終わりに
テクニカル分析指標だけで売買を判断するは難しいようです。ですが、判断材料としては十分に使えそうです。
売買のタイミングを感情で左右してまうと失敗します。とくに慣れない頃はそうでした。客観的な指標も用いて、冷静に判断できればと思いました。