シリーズも7回目なので今回は少し難易度を上げて、DMI という七面倒な計算が必要なオシレータ系指標にチャレンジ。それに、これまでは日足の終値を材料として計算する指標ばかりでしたが、DMI は高値・安値を積極的に活用するという特色があります。DMI については下記が詳しいので参照下さい。
計算に使う株価データは毎度お馴染み、日経平均プロフィルさんから。CSV ファイルを読み出して Pandas データフレームに格納する処理のコードはシリーズ第1回目を参照ください。
まずは、真の値幅を計算しておきます。
def calc_tr(close, high, low): close_prv = close.shift() # 前日終値 thigh = np.maximum(high, close_prv) # 真の高値 tlow = np.minimum(low, close_prv) # 真の安値 return thigh - tlow df['tr'] = calc_tr(df.close, df.high, df.low)
次に、指標計算の元になる、上昇幅(+DM)と下降幅(-DM)を計算するのですが、ただ計算するだけでなく +DM と -DM を比較した際の状況に応じてゼロに補正するという処理も必要なので、これが面倒。プログラムではこの補正ロジックを calc_dm_scalar という関数にしておき、それを NumPy のユニバーサル関数に登録してベクトル演算する、という手法を取っています。
def adjust_dm_scalar(d_high, d_low): if np.isnan(d_high) or np.isnan(d_low): return np.nan, np.nan elif d_high < 0 and d_low < 0: return 0, 0 elif d_high > d_low: return d_high, 0 elif d_high < d_low: return 0, d_low else: return 0, 0 def calc_dm(high, low): d_high = high - high.shift() # 当日の高値 - 前日の高値 d_low = low.shift() - low # 前日の安値 - 本日の安値 adjust_dm_vec = np.frompyfunc(adjust_dm_scalar, 2, 2) # adjust_dm_scalar をユニバーサル関数登録 return adjust_dm_vec(d_high, d_low) df['dm_high'], df['dm_low'] = calc_dm(df.high, df.low)
上記で算出した +DM/-DM を直近の日数において合計し、さらに真の値幅合計で割ると、+DI/-DI という指標が導出されます。
DI_DAYS = 14 df['di_high'] = df.dm_high.rolling(DI_DAYS).sum() / df.tr.rolling(DI_DAYS).sum() df['di_low'] = df.dm_low.rolling(DI_DAYS).sum() / df.tr.rolling(DI_DAYS).sum()
さらに補助的な指標として、+DI と -DI の差分を表す DX を計算し、さらに移動平均をとって ADX とします。
ADX_DAYS = 14 def calc_dx(di_high, di_low): a = np.abs(di_high - di_low) b = di_high + di_low return a / b df['dx'] = calc_dx(df.di_high, df.di_low) df['adx'] = df.dx.rolling(ADX_DAYS).mean()
ふー、やっと計算が終わったので可視化してみます。今回は高値・安値が計算材料となっているので、直近250日の日足ローソク足チャートと、+DI/-DI/ADX の折れ線グラフを上下に作画してみます。
指標の使い方は、+DI が -DI を上抜けたら買いサイン、その逆が売りサインとするのが標準的らしいですが、どうも上のグラフを見る限りではダマシの連続ですね… ADX も線の上昇・下降がトレンドを表す筈なのですが、ここ1年くらいはボックス相場的な様相のせいか、うまく追従できてない印象。自分もこの指標は今回が初めてなので、もうちょっと付き合ってみる必要がありそうです。
という訳で、今回もコード一式と実行結果は Google Colaboratory でどうぞ↓。