ADX(平均方向性指数)
Average Directional Movement Index(ADX)は、平均方向性指数と呼ばれ、価格にトレンドがあるかどうかを決定する際に役立ちます。
Welles Wilderの書籍「New Concepts in Technical Trading Systems」で述べられています。
計算式
ADXを計算するのためには複数のテクニカル指標が必要になります。
DX(方向性指数)
最初に方向性指数(Directional Movement Index, DX)を計算します。ADXはDXを平均したものになります。
DXでは3種類のラインによって構成されます。
- \(DI_{+}\): プラスの価格変動値(positive directional index, 上昇の強さ)
- \(DI_{-}\): マイナスの価格変動値(negative directional index,下降の強さ)
- ADXはトレンドの強さ(上昇/下降を問わない)を表す
となります。
True Range (TR)
方向性指数を計算するためには、True Rangeを利用します。
TRは特定の期間での価格レンジを分解することによってボラティリティを測定しようとする方法です。
ある時点\(t\)での計算式は以下の通りになります。
$$ TR_t = \max((H_t-L_t), |H_t – C_{t-1}|, |L_t-C_{t-1}|) $$
ここで、\(H_t\): 高値、\(L_t\): 底値、\(C_{t-1}\): 前日のクローズ値
となります。すなわち、TRとは
- 「高値-底値」
- 「高値-昨日のクローズ値の絶対値」
- 「底値-昨日のクローズ値の絶対値」
のレンジであるとします。これらの最大値から平均を算出したものがAverage True Range (ATR)です。
ATRの値が、相場ボラティリティであるとみなします(参考サイト)。
def calculate_true_range(data):
"""1日の最大の値動きを計算する"""
prev_close = data.close.shift(1)
A = data.high - data.low
B = (data.high - prev_close).abs()
C = (data.low - prev_close).abs()
true_range = pd.concat([A, B, C], axis=1)
true_range = true_range.max(axis=1)
return true_range
Average True Range (ATR)
Average True Range(ATR)は、True Rangeに対して移動平均したものです。以下の表はTrue Rangeで最大値を求める前の表です。
time H-L H-Cp L-Cp
2023-04-03 00:10:00 0.017 0.001 0.016
2023-04-03 00:20:00 0.001 0.001 0.000
2023-04-03 00:30:00 0.120 0.119 0.001
2023-04-03 00:40:00 0.065 0.042 0.023
2023-04-03 00:50:00 0.087 0.003 0.084
True Rangeでは、上記表から最大値のみを選択します。
2023年4月3日0時10分から50分までのAverage True Rangeは平均を取れば良いので
$$ \text{ATR(5)} = \frac{0.017+0.001+0.120+0.65+0.087}{5} = 0.058 $$
となります。
また、ATRはSupertrendの計算式にも利用されます。Supertrendでは、
- UP = (High + Low) / 2 + multiplier * ATR
- Down = (High + Low) / 2 – multiplier * ATR
で計算されます。デフォルトでmultiplierは3, ATRの期間は14がよく使われていそうです。
Direction Movement Index (DX)
平均方向性指数(ADX)を計算するために方向性指数(DX)を計算します。
上昇と下落の方向性\(DM_{+}, DM_{-}\)をそれぞれ計算します。
- \(H_t – H_{t-1}>0\)ならば、\(DM_{+}=H_t-H_{t-1}\)、それ以外の場合は\(DM_{+}=0\)
- \(L_{t-1}-L_{t}>0\)ならば、\(DM_{-}=L_{t-1}-L_{t}\)、それ以外の場合\(DM_{-}=0\)
これらを求めた後、平滑移動平均(Smoothed Moving Average, SMMA)をとります。
$$ DM_{+} = SMMA(DM_{+}, \text{period}) $$
SSMAは次の式で表されます。
$$ SMMA_t = \frac{N-1}{N}SMMA_{t-1} + \frac{1}{N}c_t $$
最初の値は、SMAとして計算されます。以上の値を使って、上昇方向と下落方向のDirectional Index (DI)を計算します。
$$ DI_{+} = \frac{DM_{+}}{ATR_i} \times 100 $$
$$ DI_{-} = \frac{DM_{-}}{ATR_i} \times 100 $$
そして、方向性指数は以下の式になります。
$$ DX = \frac{|DI_{+} – DI_{-}|}{DI_{+} + DI_{-}} \times 100 $$
DXを平滑化したものがADXです。
$$ ADX = SMMA(DX, \text{period}) $$
Pythonでの実装
def directional_movement_index(data, period=14):
"""Directional Movement Index"""
# +DM (上昇幅): 当日の高値ー前日の高値
dm_plus = data.high.diff()
# -DM (下落幅): 前日の安値ー当日の安値
dm_minus = -data.low.diff()
# condition1: 前日の高値の方が高いなら+DMは0
dm_plus[dm_plus < 0] = 0
# condition1: 当日の安値の方が高いなら-DMは0
dm_minus[dm_minus < 0] = 0
# condition2: +DMが-DMより値が大きい時刻は-DMは0
dm_minus[dm_plus > dm_minus] = 0
# condition2: +DMが-DMより値が小さい時刻は+DMは0
dm_plus[dm_plus < dm_minus] = 0
# 先ほど求めたTRを利用する
true_range = calculate_true_range(data)
# +DI:
sum_tr = true_range.rolling(window=period).sum()
di_plus = dm_plus.rolling(window=period).sum() / sum_tr
di_minus = dm_minus.rolling(window=period).sum() / sum_tr
# ADX
dx = (di_plus - di_minus).abs() / (di_plus + di_minus)
adx = dx.rolling(window=period).mean()
return {"plus_di": di_plus * 100, "minus_di": di_minus * 100, "adx": adx * 100}
ADXの見方

Directional Movement Index (DX)の見方は簡単です。基本は±DIの位置関係をチェックします。
+DIが-DIよりも上に位置すれば上昇トレンド
+DIが-DIよりも下に位置すれば下降トレンド
と判断することができます。上図のドル円のADXのチャートを見ると、上昇トレンドと下降トレンドが入り混じっていることがわかります。
これは10分足を使っているためであると考えられます。ADXが大きく、+DIが-DIは上回っている期間は買いが強くなっていることが示唆されます。
また、ワイルダーは書籍の中で、売買のタイミングを以下のように推奨しています。
+DI が -DIを上に抜けた時に買い
-DI が +DIを上に抜けた時に売る
このような一般的な手法に加えて、ワイルダーは極値点による方法を推奨しています。
シグナルのダマシを減らし、取引回数を増やすために利用されます。
極値点の原理によると「極値点」は +DI と -DI がお互いにクロスした際に発生します。
- +DI が -DI よりも高い場合、その後クロスする時のその日の高値が極値点
- +DI が -DI よりも低い場合、その後クロスする時のその日の安値が極値点
極値点はマーケットがエントリーレベルになった時に使用されます。
そのため、買いシグナルの後( +DI が -DI より上)、価格が極値点を超えるまで待ってから買う必要があります。
しかし、もし価格が極値点を超えなかった場合は売りポジションを保持した方が賢明です。









