본문 바로가기

주식 분석

기술적 분석 (5) - DMI (Directional Movement Index)

 이번에 살펴볼 지수는 DMI 입니다. 시장의 방향성과 추세의 강도를 계량화 한 지표로, 전일 대비 현재 주식 가격을 비교한 선이라고 할 수 있습니다. 구하는 원리나 공식이 복잡하므로 천천히 알아보도록 하겠습니다.

 

삼성전자 차트(영웅문)

 DMI 지수에서 확인할 부분은 위에 표현된 DI Plus, DI Minus 입니다. ADX는 DMI를 확인할 때 보통 같이 확인하는 지표인데 뒤에서 설명 드리겠습니다. DI 두개를 구하기 위해서는 DM, TR 이라는 것을 계산해야 합니다.

 

- DM (Directional Movement)

 상승 추세 일때는 금일의 고가가 반드시 전일의 고가 보다 높아야 하고, 하락 추세 일때는 금일의 저가가 반드시 전일의 저가 보다 낮아야 한다고 가정하고 계산합니다.

 

 - 전일의 고가 보다 금일의 고가가 높을시 : +DM

 - 전일의 저가 보다 금일의 저가가 낮을시 : -DM

 - 위 상황이 둘다 발생했을 때,

   - 고가 상승 범위가 더 크다면 : +DM

   - 저가 하락 범위가 더 크다면 : -DM

 - 변동 자체가 전날의 변동보다 작다면 : DM = 0

 

- TR (True Range)

 아래의 세가지 경우 중 절대값이 가장 큰 값을 TR로 지정합니다.

  1. 금일의 고가 - 금일의 저가

  2. 금일의 고가 - 전일의 종가

  3. 금일의 저가 - 전일의 종가

 

위의 두가지를 각각 계산한 뒤에 DI를 계산합니다.

 

- DI(Directional Indicator)

   - DI = DM / TR

   - DI는 DM의 값에 따라 부호가 바뀌게 됩니다.

   - DM과 TR 하루치를 통해 DI를 계산하는게 아니라, 보통 M일 동안의 이동평균으로 계산합니다.

   - +DI = +DM의 M일 이동평균 / TR의 M일 이동평균

           = DI Plus

           = PDI

   - -DI = -DM의 M일 이동평균 / TR의 M일 이동평균

           = DI Minus

           = MDI

   - 이렇게 계산된 DI는 M일 동안 상승 추세, 또는 하락 추세에서 금일 주가의 비율 이라고 볼 수 있습니다.

 

 즉 DI Plus가 위에 있다면 상승 추세에 있다고 볼 수 있고, DI Minus가 더 위에 있다면 하락 추세에 있다고 볼 수 있죠. 그래서 DMI 지수를 이용한 매매 기법은 간단합니다.

 

 - PDI가 MDI를 상향 돌파하는 골든 크로스에 매수 포지션

 - PDI가 MDI를 하향 돌파하는 데드 크로스에 매도 포지션

 

 오르고 있는 분위기에 사고, 떨어지는 분위기에 판다고 할 수 있습니다. 그러나 단순히 DMI 지수만을 사용해서 매매를 하는 방법은 좋지 않다고 합니다. DMI 지수는 가짜 신호가 꽤 많고, 신호 자체도 자주 일어납니다. 그래서 DMI에서 도출한 지표인 ADX를 같이 사용하는 경우가 많습니다.

 

 - ADX = | (PDI - MDI) | / (PDI + MDI) * 100

 

 ADX는 현재 추세의 강한 정도를 나타냅니다. 그 추세가 상승 추세인지, 하락 추세인지는 모릅니다. 그래서 다른 지표랑 사용을 하게 되는데 보통 DMI, ADX 조합으로 사용을 합니다. 

 두개의 조합을 사용하는 방법은 이렇습니다.

 PDI가 DMI를 상향 돌파하는 골든 크로스 상황에서, ADX의 크기도 커지고 있다면 상승 추세가 높아진다고 예상 할 수 있습니다. 그럴 경우에 강한 매수 신호로 볼 수 있습니다. DMI의 의미 없는 신호를 거를 수 있게 됩니다.

 


Python으로 구현하기 위해 데이터를 불러줍니다.

.
import pandas as pd
import numpy as np
import plotly.graph_objects as go

path_dir = './data'
data = np.loadtxt(path_dir + '/' + '000660_from_2010.csv', delimiter = ',')
data = pd.DataFrame(data)
data.columns = ['Open', 'High', "Low", "Close", "Volumn", "Adj"]

DMI와 ADX를 구합니다. 이전에 계산했던 지표들에 비하면 코드가 조금 더럽...습니다

.
def cal_dmi(data, n=14, n_ADX=14) :
    #https://github.com/Crypto-toolbox/pandas-technical-indicators/blob/master/technical_indicators.py : ADX
    i = 0
    UpI = [0]
    DoI = [0]
    while i + 1 <= data.index[-1] :
        UpMove = data.loc[i + 1, "High"] - data.loc[i, "High"]
        DoMove = data.loc[i, "Low"] - data.loc[i+1, "Low"]
        if UpMove > DoMove and UpMove > 0 :
            UpD = UpMove
        else :
            UpD = 0
        UpI.append(UpD)
        if DoMove > UpMove and DoMove > 0 :
            DoD = DoMove
        else :
            DoD = 0
        DoI.append(DoD)
        i = i + 1

    i = 0
    TR_l = [0]
    while i < data.index[-1]:
        TR = max(data.loc[i + 1, 'High'], data.loc[i, 'Close']) - min(data.loc[i + 1, 'Low'], data.loc[i, 'Close'])
        TR_l.append(TR)
        i = i + 1
    TR_s = pd.Series(TR_l)
    ATR = pd.Series(TR_s.ewm(span=n, min_periods=1).mean())
    UpI = pd.Series(UpI)
    DoI = pd.Series(DoI)
    PosDI = pd.Series(UpI.ewm(span=n, min_periods=1).mean() / ATR)
    NegDI = pd.Series(DoI.ewm(span=n, min_periods=1).mean() / ATR)
    ADX = pd.Series((abs(PosDI - NegDI) / (PosDI + NegDI)).ewm(span=n_ADX, min_periods=1).mean(),
                    name='ADX_' + str(n) + '_' + str(n_ADX))

    return PosDI, NegDI, ADX
    
    data["PDI"],data["MDI"],data["ADX"] = cal_dmi(data)

그림을 그렸습니다.

 

Index를 20부터 한 이유는, 위의 DMI 계산 코드상 가장 이른 시간대의 값들의 계산이 정확하지 않기 때문입니다.

fig = go.Figure()
fig.add_trace(go.Scatter(x=data.index[20:], y=data["PDI"][20:],
                        mode='lines',
                        name="PDI"))
fig.add_trace(go.Scatter(x=data.index[20:], y=data["MDI"][20:],
                        mode='lines',
                        name="MDI"))
fig.add_trace(go.Scatter(x=data.index[20:], y=data["ADX"][20:],
                        mode='lines',
                        name="ADX"))

fig.update_layout(title='DMI and ADX',
                   xaxis_title='days',
                   yaxis_title='Value')

fig.show()

보시다시피 PDI와 MDI만 보면 서로 크로스 되는 경우가 꽤 있습니다. ADX의 움직임 까지 같이 확인해보면 신호의 빈도가 적어집니다. ADX의 경우 0.25 이상이면 추세가 약한 추세 0.5 이상이면 꽤 강한 추세라고 볼 수 있습니다.