DD(drawdown)와 MDD(Maximum Drawdown)를 계산하고 그리기

3 minute read

퀀트 전략을 평가할 때, CAGR(Compound Annual Growth Rate)로 얼마나 수익을 올릴 수 있는지 확인한다. 이익만 계산하는 거다. 위험도는 어떻게 계산할까? MDD(Maximum Drawdown)로 위험도를 측정한다. MDD는 이전 최대 누적 수익률에서 얼마나 떨어지는지를 알려준다. CAGR이 80%이지만 MDD가 40%인 전략과 CAGR이 10%이지만 MDD가 10%인 전략 중 어떤 걸 선택해야 할까? 이런 선택 기준을 제공하는 중요한 지표다.

MDD(Maximum Drawdown)를 최대 낙폭, 최대 손실 폭 정도로 번역해서 쓰는 것 같다. 난 둘 중 구글 검색 결과가 더 많은 ’최대 낙폭’을 사용한다.

DD(drawdown)를 계산하는 방법 - 수식을 곁들인

MDD는 DD(Drawdown)의 최대값이다. DD부터 알아보자.

The drawdown is the measure of the decline from a historical peak in some variable (typically the cumulative profit or total open equity of a financial trading strategy).

낙폭은 특정 변수(일반적으로 금융 트레이딩 전략의 누적 수익 또는 총미결제 잔고)의 과거 최고점 대비 하락 폭을 측정한 것입니다.

Drawdown (economics) - en.wikipedia.org

기준이 중요하다. 전고점 대비 하락 폭을 구해야 한다. 사람의 심리를 너무 잘 나타낸 측정값이다. 나는 현재 가격이 아쉬울 때, 항상 전고점을 바라본다. “그때는 저런 가격이었는데, 지금은 이게 뭐람. 많이 귀여워졌네.” DD가 딱 이 상실감과 절망감을 측정한다.

그럼 어떻게 계산해야 할까? 수식이 나올 차례다.

\begin{equation} D(T) = \max\left[\max_{t\in(0,T)}X(t)-X(T),0 \right] \end{equation}

항목을 하나씩 살펴보자

  • \(D(T)\) : \(T\) 시점의 DD(Drawdown)을 구하는 함수다
  • \(\max_{t\in(0,T)}X(t)\) : \(0 < t < T\) 를 만족하는 \(X(t)\) 의 최대 값이다. 즉, 전고점이다
  • \(\max\left[\max_{t\in(0,T)}X(t)-X(T),0 \right]\) : 전고점보다 구하는 시점의 가격이 더 높으면 마이너스 값이 나오는데, 이걸 0으로 만든다.

전고점에서 얼마나 떨어졌는지를 구하는 함수다. 이제 MDD(Maximum Drawdown)를 구할 차례다. 앞에 Maximum이니깐 제일 높은 값? 스포일러다.

MDD(Maximum Drawdown)를 계산하는 방법 - 수식을 곁들인

스포일러를 당했으니 바로 수식을 보자

\begin{equation} \operatorname{MDD}(T)=\max_{\tau\in (0,T)}D(\tau)=\max_{\tau\in (0,T)} \left[ \max \left[\max_{t \in (0,\tau)} X(t)- X(\tau),0 \right] \right] \end{equation}

계산하는 시점의 과거 DD(Drawdown) 중 최댓값을 구하면 된다.

DD와 MDD를 손실 퍼센트로 계산하는 방법 - 수식을 곁들인

Google에서 검색해보면 마이너스 비율(proportion)로 그린 그래프를 쉽게 볼 수 있다. Wikipeda Drawdown와 다른 공식을 사용했다. 출처를 찾기 힘들어서 wallstreetmojo.com에 있는 글을 참고했다.

\begin{equation} D(T) = \frac{X(T) - \max_{t\in(0,T]}X(t)}{\max_{t\in(0,T]}} \end{equation}

현재 가격에서 전고점을 빼고 그 값을 전고점으로 나눈다. 전고점 대비 얼마나 떨어졌는지 비율로 값을 구한다. \(t\in(0,T]\) 전고점을 구할 때, 현재 가격도 포함하게 변경했다. 이렇게 하면 현재 가격이 전고점보다 높아지면 0이 나온다. 0보다 큰지 작은지 편단할 필요가 없으니 편하다.

Kodex 코스피(226490) 과거 데이터 일부만 가져와서 DD를 계산한 결과를 보자.

date price recent_high DD
2015-09-08 18790 19395 -0.031193606599639084
2015-09-09 19305 19395 -0.004640371229698376
2015-09-10 19570 19570 0.0
2015-09-11 19455 19570 -0.005876341338783853
2015-09-14 19310 19570 -0.013285641287685232
2015-09-15 19340 19570 -0.011752682677567705
2015-09-16 19740 19740 0.0
2015-09-17 19745 19745 0.0
2015-09-18 19955 19955 0.0
2015-09-21 19655 19955 -0.015033826108744675

recent_high 컬럼은 전고점을 나타낸다. 전고점이 갱신되면 DD 계산 결과가 0이 된다. 전고점 대비 1.1%가 떨어진 2015-09-15 가격을 계산해 보자.

\begin{equation} D(T) = \frac{19340 - 19570}{19570} = -0.011752682677567705 \end{equation}

DD를 계산했으니 이제 MDD를 계산해 보자.

\begin{equation} \operatorname{MDD}(T)=\min_{\tau\in (0,T)}D(\tau)=\min_{\tau\in (0,T)}\frac{X(\tau) - \max_{t\in(0,\tau]}X(t)}{\max_{t\in(0,\tau]}} \end{equation}

\(\max\) 대신 \(\min\) 으로 변경해야 한다. 최대 마이너스 비율을 계산해야 하기 때문이다.

위 표에 있는 데이터로만 계산하면 MDD는 가장 많이 떨어진 2015-09-21의 -0.015033826108744675가 나온다. 백분율로 나타나면 -0.15%이다.

Elixir와 Explorer 라이브러리로 DD와 MDD를 계산

Elixir 프로그래밍 언어와 Explorer 라이브러리를 사용해서 DD와 MDD를 계산한다. Elixir는 Erlang VM 위에서 실행하는 함수형 프로그래밍 언어다. Explorer 라이브러리는 dplyr 라이브러리에 영향을 받은 DataFrame 라이브러리다.

Kodex 코스피(226490)의 DD와 MDD를 구해보자. 과거 데이터는 investing.com에서 받았다. Elixir 언어를 설명하는 글이 아니니 계산식이 프로그래밍 언어로 어떻게 연결되는지만 간단히 설명한다.

누적 최댓값을 구하는 Series.cumulative_max/2 함수와 누적 최솟값을 구하는 Series.cumulative_min/2 함수를 사용했다. 이런 함수가 없다면 지난 값들을 순회하며 비교해서 min, max 값을 구해야 한다. 하지만 많이 사용하는 함수라서 DataFrame 라이브러리라면 구현이 되어 있을 것이다.

require Explorer.DataFrame, as: Df
alias Explorer.Series

df =
  df
  |> Df.mutate_with(&[recent_high: Series.cumulative_max(&1[:price])])
  |> Df.mutate_with(fn lf ->
  [
    dd:
    Series.divide(
      Series.subtract(lf[:price], lf[:recent_high]),
      lf[:recent_high]
    )
  ]
end)
|> Df.mutate_with(&[mdd: Series.cumulative_min(&1[:dd])])

수식이랑 별반 다른 점이 없다. 마지막 row의 mdd 컬럼값을 보면 MDD를 알 수 있다.

Df.tail(df, 1)[:mdd]

Kodex 코스피(226490)의 MDD는 -43.3%이다.

Elixir와 VegaLite 라이브러리로 DD를 시각화

VegaLiteVega-Lite를 Elixir 언어에서 사용할 수 있게 래핑한 라이브러리다.

한 번쯤은 본 법한 DD(drawdown) 그래프를 비슷하게 흉내 내보자. DD를 핑크색으로 꽉 채운 영역으로 그린다. 그리고 MDD가 대략 어떤 값인지 보기 쉽게 가로로 빨간 줄을 긋는다.

alias VegaLite, as: Vl

Vl.new(width: 800)
|> Vl.data_from_values(df)
|> Vl.layers([
  Vl.new()
  |> Vl.mark(:area)
  |> Vl.encode_field(:x, "date", type: :temporal)
  |> Vl.encode_field(:y, "dd", type: :quantitative)
  |> Vl.encode(:color, value: "pink"),
  Vl.new()
  |> Vl.mark(:rule)
  |> Vl.encode(:y, field: "dd", aggregate: "min", type: :quantitative)
  |> Vl.encode(:color, value: "red")
])

nil

이런 그래프를 그릴 수 있다.

마치며

DD(Drawdown)와 MDD(Maximum Drawdown)를 계산하는 방법을 살펴봤다. 위키피디아 Drawdown 페이지에는 절대 수치를 구하는 방법만 나와 있어서 인터넷에서 다른 글을 참고해서 손실 비율로 DD를 구하고 그래프도 그려봤다.

퀀트 전략을 선택할 때, 도움 되는 수치 중 위험도를 가늠할 수 있는 MDD(Maximum Drawdown)에 대해 알아봤다. CAGR(Compound Annual Growth Rate)을 계산하는 데 그치지 않고 항상 MDD도 같이 계산해서 손실 비율이 최대 얼마나 되는지를 알아야 한다.