日経平均の日足データからゴールデンクロスとデッドクロスを検出してみました。
プログラムは python で、pandas の DataFrame にほぼおまかせです。
検出
手順は単純で
- ① csv の 日足データを DataFrame に読み込み
- ② 短期と長期の移動平均を求め
- ③ 両者がクロスするポイントを探す
だけです。
入力は csv
日足の csv ファイルはこんな感じです。
1 2 3 4 5 6 7 8 |
日付 始値 高値 安値 終値 0 2014-01-06 16147.54 16164.01 15864.44 15908.88 1 2014-01-07 15835.41 15935.37 15784.25 15814.37 2 2014-01-08 15943.68 16121.45 15906.57 16121.45 3 2014-01-09 16002.88 16004.56 15838.44 15880.33 4 2014-01-10 15785.15 15922.14 15754.70 15912.06 5 2014-01-14 15657.20 15661.71 15383.69 15422.40 .. ... ... ... ... |
プログラム
移動平均の日数(短期と長期)を冒頭で指定します。複数のパターンを指定でき、下記の例では、5日と25日、25日と75日、13日と26日の 3 つを指定しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import numpy as np import pandas as pd # [[短期, 長期], [短期, 長期] ・・・] types = [[5, 25], [25, 75], [13, 26]] df = pd.read_csv(u'日経平均_2014.csv', encoding='shift-jis') df = df.sort_values(by=u'日付').reset_index(drop=True) for short_day, long_day in types: # 移動平均 rolling_s = df[u'終値'].rolling(short_day).mean().fillna(0) rolling_l = df[u'終値'].rolling(long_day).mean().fillna(0) # 移動平均のクロス確認 over_s_l = rolling_s > rolling_l golden = (over_s_l != over_s_l.shift(1)) & (over_s_l == True) dead = (over_s_l != over_s_l.shift(1)) & (over_s_l == False) # 列 追加 # 0:クロスなし # 1:ゴールデンクロス # -1:デッドクロス col_name = 'cross_' + str(short_day) + '_' + str(long_day) df[col_name] = [x+y*-1 for x,y in zip(golden, dead)] df[col_name] = np.append(np.array([0] * (long_day+1)), df[col_name][long_day+1:]) # |
実行すると
カラムが追加され、ゴールデンクロスが出れば 1、デッドクロスが出れば -1、それ以外は 0 がセットされます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
>>> df[[u'日付','cross_5_25','cross_25_75','cross_13_26']] 日付 cross_5_25 cross_25_75 cross_13_26 0 2014-01-06 0 0 0 1 2014-01-07 0 0 0 2 2014-01-08 0 0 0 3 2014-01-09 0 0 0 4 2014-01-10 0 0 0 5 2014-01-14 0 0 0 6 2014-01-15 0 0 0 .. ... ... ... ... >>> df[93:100][[u'日付','cross_5_25','cross_25_75','cross_13_26']] 日付 cross_5_25 cross_25_75 cross_13_26 93 2014-05-23 0 0 0 94 2014-05-26 0 0 0 95 2014-05-27 1 0 0 ← ゴールデン 96 2014-05-28 0 0 0 97 2014-05-29 0 0 1 ← ゴールデン 98 2014-05-30 0 0 0 99 2014-06-02 0 0 0 |
チャートで確認
チャート上にプロットしてみます。



ちょっと改善
上昇、下降をバタバタ繰り返す日が続くと、ゴールデンクロスとデッドクロスが短期間に連続して出てしまいます。たとえば上の図を見ると、7/25 にデッドクロス、翌営業日の 7/28 にゴールデンクロスが出ています。
プログラム上、短期と長期の移動平均を比較し、値の大小が逆転すればすぐにサインを出しているのが原因です。

これだと、方向が曖昧なままあわててサインを出すことになり、指標としてイマイチです。
そこで対策として、3 日分の推移を見て、多数決を取ることにします。どういうことかというと、当日をふくめて過去 3日を確認し、2 日以上の状態を優先するように変更します。

チャート描画も含めたコーディングはこうなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
import numpy as np import pandas as pd import matplotlib.pyplot as plt # [[短期, 長期], [短期, 長期] ・・・] types = [[5, 25], [25, 75], [13, 26]] df = pd.read_csv(u'日経平均_2014.csv', encoding='shift-jis') df = df.sort_values(by=u'日付').reset_index(drop=True) for short_day, long_day in types: # 移動平均 rolling_s = df[u'終値'].rolling(short_day).mean().fillna(0) rolling_l = df[u'終値'].rolling(long_day).mean().fillna(0) # 移動平均のクロス確認 # 多数決を取り入れる over = rolling_s > rolling_l over_major = (over + over.shift(1) + over.shift(2)) >= 2 golden = (over_major != over_major.shift(1)) & (over_major == True) dead = (over_major != over_major.shift(1)) & (over_major == False) # 列 追加 # 0:クロスなし # 1:ゴールデンクロス # -1:デッドクロス col_name = 'cross_' + str(short_day) + '_' + str(long_day) df[col_name] = [x+y*-1 for x,y in zip(golden, dead)] df[col_name] = np.append(np.array([0] * (long_day+3)), df[col_name][long_day+3:]) # # チャート描画 # plt.plot(range(len(df)), df[u'終値'], label='N255') plt.plot(range(short_day-1, len(df)), rolling_s[short_day-1:], color='r', label='MA '+ str(short_day)) plt.plot(range(long_day-1, len(df)), rolling_l[long_day-1:], color='g', label='MA '+ str(long_day)) plt.legend(loc='upper left') plt.xlim(0, len(df)+1) crosses = df[df[col_name] != 0] golden_x = [] golden_y = [] golden_day = [] dead_x = [] dead_y = [] dead_day = [] for i in range(len(crosses)): if (crosses.iloc[i][col_name] == 1): golden_x.append(crosses.index[i]) golden_y.append(crosses.iloc[i][u'終値']) golden_day.append(crosses.iloc[i][u'日付']) else: dead_x.append(crosses.index[i]) dead_y.append(crosses.iloc[i][u'終値']) dead_day.append(crosses.iloc[i][u'日付']) plt.scatter(golden_x, golden_y, c='black', s=80, marker='^') plt.scatter(dead_x, dead_y, c='green', s=80, marker='v') for i in range(len(golden_x)): plt.text(golden_x[i], golden_y[i]+500, '{0}-{1}'.format(int(golden_day[i][-5:-3]),int(golden_day[i][-2:])), ha = 'center', va = 'bottom') for i in range(len(dead_x)): plt.text(dead_x[i], dead_y[i]-500, '{0}-{1}'.format(int(dead_day[i][-5:-3]),int(dead_day[i][-2:])), ha = 'center', va = 'top', color='red') plt.title(df.iloc[0][u'日付'][0:4]+' ('+str(short_day) + ' / ' + str(long_day) + ')') plt.show() # |
結果
チャートはこうなります。
7月後半の小刻みな出力は消えています。



メリット、デメリット
こちらの方法だと曖昧なサインを出すことはなくなりますが、1日ないし 2日、サインが遅れます。
とおる says:
> golden = (over_s_l != over_s_l.shift(1)) & (over_s_l == True)
これはいったい何をやっているのでしょうか?
説明いただけないでしょうか。。。
管理人 says:
とおるさん、こんにちわ。
over_s_l は短期移動平均が長期移動平均を上回るかどうかを示します。
over_s_l.shift(1) は前日の状態です。
over_s_l != over_s_l.shift(1)
前日と当日で変化があるか?
over_s_l == True
短期移動平均が長期移動平均を上回るか?
つまり、前日から当日にかけて短期移動平均が長期移動平均を追い抜いたかどうかを判定しています。
芭蕉 says:
df[col_name] = np.append(np.array([0] * (long_day+1)), df[col_name][long_day+1:])
この部分ですが、どういった処理を行っているのかわかりません。
特に
np.array([0] * (long_day+1))
の部分が何を表しているのか教えていただけますでしょうか?
はっちゃん says:
ここでエラーが出るんですが。。。。どこを直したらよいでしょうか?
ValueError Traceback (most recent call last)
in
60
61 for i in range(len(golden_x)):
—> 62 plt.text(golden_x[i], golden_y[i]+500, '{0}-{1}'.format(int(golden_day[i][-5:-3]),int(golden_day[i][-2:])), ha = 'center', va = 'bottom')
63
64 for i in range(len(dead_x)):
ValueError: invalid literal for int() with base 10: '0/'