
지금까지 다룬 이동평균은 가격의 평균을 기준으로 추세를 판단했습니다. 이번에 소개할 돈치안 채널은 평균이 아니라 극값 — 일정 기간 동안의 최고가와 최저가 — 을 기준으로 합니다. 가격이 최고점을 갱신하면 상승 추세로, 최저점을 갱신하면 하락 추세로 보는, 굉장히 직관적인 접근입니다.
지표 소개
돈치안 채널은 Richard Donchian이 개발한 지표로, 세 개의 선으로 구성됩니다.
상단 (Upper) = N일 최고가
하단 (Lower) = N일 최저가
중심 (Mid) = (Upper + Lower) / 2
기본 기간은 20일입니다. 차트에 그리면 가격을 감싸는 채널 형태가 됩니다.

상단선(파랑)과 하단선(빨강)이 계단식으로 움직이는 것이 특징입니다. 이동평균처럼 부드럽게 흘러가는 게 아니라, 신고가/신저가가 나올 때만 값이 바뀝니다. 가격이 상단을 뚫으면 "N일 만에 가장 높은 가격"이라는 뜻이고, 하단을 뚫으면 "N일 만에 가장 낮은 가격"이라는 뜻입니다.
가설 설정
돈치안 채널의 가장 기본적인 전략은 브레이크아웃입니다.
- 매수 — 가격이 상단선을 돌파 (N일 최고가 갱신)
- 매도 — 가격이 하단선을 돌파 (N일 최저가 갱신)
"신고가를 내면 들어가고, 신저가를 내면 나온다." 단순하지만, 이 논리가 유명한 터틀트레이딩의 출발점이기도 합니다.
백테스트 조건
| 항목 | 설정 |
|---|---|
| 종목 | BTC/USDT (Binance Spot) |
| 기간 | 2018.01.01 ~ 2025.12.31 |
| 타임프레임 | 일봉 (1D) |
| 돈치안 기간 | 20일 |
| 진입 | 상단 돌파 시 매수 |
| 청산 | 하단 돌파 시 매도 |
| 초기 자본 | $10,000 |
| 수수료 / 슬리피지 | 미적용 |
진입 신호는 전일까지의 채널을 기준으로 판단합니다. 당일 데이터로 당일 채널을 만들고 그걸로 판단하면 미래 참조가 되기 때문입니다.
코드 구현
import pandas as pd
import mplfinance as mpf
# ===== 설정 =====
DATA_FILE = "BTCUSDT_1d.csv"
DC_PERIOD = 20 # 돈치안 채널 기간
INITIAL_CAPITAL = 10000
ZOOM_DAYS = 180
# ================
# 데이터 로드
df = pd.read_csv(DATA_FILE, index_col="timestamp", parse_dates=True)
# 돈치안 채널 계산
# 진입 신호는 "전일까지의" 채널을 기준으로 판단 → shift(1)
df["dc_upper"] = df["high"].rolling(window=DC_PERIOD).max().shift(1)
df["dc_lower"] = df["low"].rolling(window=DC_PERIOD).min().shift(1)
df["dc_mid"] = (df["dc_upper"] + df["dc_lower"]) / 2
# 백테스트
capital = INITIAL_CAPITAL
holdings = 0
buy_price = 0
in_position = False
trades = []
for i in range(DC_PERIOD, len(df)):
high = df["high"].iloc[i]
low = df["low"].iloc[i]
close = df["close"].iloc[i]
upper = df["dc_upper"].iloc[i]
lower = df["dc_lower"].iloc[i]
if not in_position:
# 상단 돌파 → 매수
if high > upper:
buy_price = close
holdings = capital / buy_price
capital = 0
in_position = True
else:
# 하단 돌파 → 매도
if low < lower:
sell_price = close
capital = holdings * sell_price
pnl = (sell_price - buy_price) / buy_price * 100
trades.append({
"buy": buy_price,
"sell": sell_price,
"pnl_pct": round(pnl, 2)
})
holdings = 0
in_position = False
# 마지막 포지션 정리
if in_position:
last_price = df["close"].iloc[-1]
capital = holdings * last_price
pnl = (last_price - buy_price) / buy_price * 100
trades.append({
"buy": buy_price,
"sell": last_price,
"pnl_pct": round(pnl, 2)
})
# 결과 출력
final_capital = capital
total_return = (final_capital - INITIAL_CAPITAL) / INITIAL_CAPITAL * 100
win_trades = [t for t in trades if t["pnl_pct"] > 0]
lose_trades = [t for t in trades if t["pnl_pct"] <= 0]
first_price = df["close"].dropna().iloc[0]
last_price = df["close"].dropna().iloc[-1]
bnh_return = (last_price - first_price) / first_price * 100
bnh_capital = INITIAL_CAPITAL * (1 + bnh_return / 100)
print("=" * 40)
print(f"Donchian Channel {DC_PERIOD} 백테스트 결과")
print("=" * 40)
print(f"초기 자본: ${INITIAL_CAPITAL:,.0f}")
print(f"최종 자본: ${final_capital:,.0f}")
print(f"총 수익률: {total_return:+.2f}%")
print(f"총 거래 수: {len(trades)}회")
print(f"승리: {len(win_trades)}회")
print(f"패배: {len(lose_trades)}회")
if trades:
print(f"승률: {len(win_trades)/len(trades)*100:.1f}%")
print("-" * 40)
print(f"바이앤홀드: ${bnh_capital:,.0f} ({bnh_return:+.2f}%)")
print("=" * 40)
# 차트 1 — 전체 기간 라인 차트
ap_full = [
mpf.make_addplot(df["dc_upper"], color="#1E88E5", width=1.2, label=f"DC Upper {DC_PERIOD}"),
mpf.make_addplot(df["dc_lower"], color="#E53935", width=1.2, label=f"DC Lower {DC_PERIOD}"),
]
mpf.plot(df, type="line", style="charles",
title=f"BTC/USDT Donchian Channel {DC_PERIOD} (Full)",
addplot=ap_full, volume=False, figsize=(14, 6), linecolor="#333333")
# 차트 2 — 최근 구간 캔들 차트
df_zoom = df.tail(ZOOM_DAYS).copy()
ap_zoom = [
mpf.make_addplot(df_zoom["dc_upper"], color="#1E88E5", width=1.2, label=f"DC Upper {DC_PERIOD}"),
mpf.make_addplot(df_zoom["dc_lower"], color="#E53935", width=1.2, label=f"DC Lower {DC_PERIOD}"),
]
mpf.plot(df_zoom, type="candle", style="charles",
title=f"BTC/USDT Donchian Channel {DC_PERIOD} (Last {ZOOM_DAYS}D)",
addplot=ap_zoom, volume=False, figsize=(14, 6))
사용법
DC_PERIOD를 바꾸면 채널 기간을 조절할 수 있습니다.
결과 분석


+1117%로 바이앤홀드(+555%)의 약 두 배입니다.
승률이 40.5%입니다. 42번 거래해서 25번 졌습니다. 그런데도 +1117%입니다. 이건 소수의 큰 승리가 다수의 작은 손실을 커버하는 전형적인 추세추종 패턴입니다. 신고가를 돌파한 뒤 큰 추세가 이어지면 큰 수익을 얻고, 돌파가 실패하면 하단까지 내려올 때 빠져나오면서 작은 손실로 끝나는 구조입니다.
이동평균 크로스와 비교하면, 돈치안 브레이크아웃은 진입 조건이 명확합니다. "N일 최고가를 넘었는가" — 계산할 것도 없고, 해석할 여지도 없습니다. 대신 청산이 느립니다. 매도 조건이 "N일 최저가 돌파"인데, 상단 돌파 후 이익이 발생한 상태에서 다시 하단까지 내려오려면 상당한 되돌림을 감수해야 합니다. 이 부분이 수익률을 깎는 주된 원인입니다.
정리
돈치안 채널은 "최고가를 넘으면 사고, 최저가를 깨면 판다"는 심플한 논리의 브레이크아웃 지표입니다. 승률은 낮지만 추세를 잡았을 때의 수익이 크기 때문에, 전형적인 추세추종 전략의 출발점이 됩니다.
다만 현재 전략에는 두 가지 개선 여지가 있습니다. 첫째, 손절이 없습니다. 상단 돌파 후 가격이 급락해도 하단까지 내려올 때까지 기다려야 합니다. 둘째, 포지션 크기가 고정입니다. 변동성에 상관없이 항상 자본 전액을 투입합니다.
다음 글에서는 이 두 가지를 개선한 터틀트레이딩을 다룹니다. 앞서 소개한 ATR을 활용해 손절 라인을 설정하고, 변동성에 맞게 포지션 크기를 조절하는 방법을 적용합니다.
본 포스팅은 지표의 원리와 백테스트 과정을 정리한 기술 블로그이며, 특정 자산의 매매를 권유하지 않습니다.
백테스트 결과는 과거 데이터 기반이며 실제 수익을 보장하지 않습니다. 모든 투자의 책임은 본인에게 있습니다.
'Development > Indicator Lab' 카테고리의 다른 글
| 터틀트레이딩 — 피라미딩 (0) | 2026.03.11 |
|---|---|
| 터틀트레이딩 — 돈치안 채널 + ATR 리스크 관리 (0) | 2026.03.10 |
| 같은 전략, 다른 설정 — MA 크로스 통합 비교 (2) | 2026.03.08 |
| ATR (Average True Range, 평균진폭) (0) | 2026.03.07 |
| HMA (Hull Moving Average, 헐 이동평균) (0) | 2026.03.06 |