코인

[자동매매로봇 만들기][7편] 백테스트 결과 시각화 – 수익 흐름을 눈으로 확인하기

stupidsoft 2025. 6. 11. 20:21
반응형

 

앞선 글에서 간단한 전략(볼린저 밴드 하단 + RSI < 30 진입, 중심선 청산)을 적용해
과거 데이터를 기반으로 수익률과 승률 등을 확인해봤다.

이번 글에서는 그 결과를 그래프로 시각화해보자.
숫자로 보는 수익률도 중요하지만, 시각화된 수익곡선은 전략의 성향을 훨씬 직관적으로 보여준다.


1. 백테스트 전략 코드 (요약 포함)

먼저 [6편]에서 만들었던 전략 백테스트 코드를 그대로 쓰되,
수익률 기록뿐만 아니라 각 트레이드의 순서에 따라 누적 수익률을 계산하도록 정리해보자.

import ccxt
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 데이터 수집
binance = ccxt.binance({'enableRateLimit': True})
symbol = 'BTC/USDT'
ohlcv = binance.fetch_ohlcv(symbol, timeframe='5m', limit=5000)
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')

# RSI 계산
def calculate_rsi(series, period=14):
    delta = series.diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)
    avg_gain = gain.rolling(window=period).mean()
    avg_loss = loss.rolling(window=period).mean()
    rs = avg_gain / avg_loss
    return 100 - (100 / (1 + rs))

df['rsi14'] = calculate_rsi(df['close'])

# 볼린저 밴드
period = 20
df['ma20'] = df['close'].rolling(window=period).mean()
std = df['close'].rolling(window=period).std()
df['bb_upper'] = df['ma20'] + 2 * std
df['bb_lower'] = df['ma20'] - 2 * std

# 전략 백테스트
position = None
entry_price = 0
returns = []
entry_times = []

for i in range(1, len(df)):
    row = df.iloc[i]
    prev = df.iloc[i - 1]

    # 진입 조건
    if position is None:
        if row['close'] < row['bb_lower'] and row['rsi14'] < 30:
            position = 'long'
            entry_price = row['close']
            entry_times.append(row['timestamp'])

    # 청산 조건
    elif position == 'long':
        if row['close'] > row['ma20']:
            exit_price = row['close']
            ret = (exit_price - entry_price) / entry_price
            returns.append(ret)
            position = None

2. 수익곡선 계산

returns 리스트를 누적해서 수익곡선을 만들자.
각 수익률은 개별 트레이드의 수익이고, 이를 누적해서 곡선을 만들면 된다.

# 누적 수익률 시리즈 생성
cumulative_returns = pd.Series([1 + r for r in returns]).cumprod() - 1

3. 수익곡선 시각화

plt.figure(figsize=(10, 5))
plt.plot(cumulative_returns, marker='o')
plt.title('백테스트 누적 수익률 곡선')
plt.xlabel('트레이드 순번')
plt.ylabel('누적 수익률')
plt.grid(True)
plt.tight_layout()
plt.show()

이렇게 하면 전략이 수익을 얼마나 쌓았는지 한눈에 확인할 수 있다.


4. 최대 낙폭(Drawdown) 시각화

낙폭은 고점 대비 얼마나 손실이 컸는지를 보여주는 지표다.
수익률만 보면 좋아 보여도, 큰 낙폭이 있으면 위험한 전략일 수 있다.

equity_curve = pd.Series([1 + r for r in returns]).cumprod()
running_max = equity_curve.cummax()
drawdown = (equity_curve - running_max) / running_max

plt.figure(figsize=(10, 4))
plt.plot(drawdown, color='red')
plt.title('최대 낙폭 (Drawdown)')
plt.xlabel('트레이드 순번')
plt.ylabel('낙폭 비율')
plt.grid(True)
plt.tight_layout()
plt.show()

5. 수익률 요약 지표 출력

if returns:
    total_return = cumulative_returns.iloc[-1]
    win_rate = sum(1 for r in returns if r > 0) / len(returns)
    avg_return = np.mean(returns)

    print(f"총 거래 횟수: {len(returns)}")
    print(f"승률: {win_rate * 100:.2f}%")
    print(f"평균 수익률: {avg_return * 100:.2f}%")
    print(f"누적 수익률: {total_return * 100:.2f}%")
else:
    print("조건에 맞는 거래가 발생하지 않았습니다.")

6. 출력 결과

총 거래 횟수: 7
승률: 85.71%
평균 수익률: 0.17%
누적 수익률: 1.22%

 

 

마무리하며

 

이번 글에서는 앞서 구현한 전략의 결과를 수익곡선과 낙폭 그래프로 시각화해보았다.
단순한 숫자보다는 이렇게 흐름을 그래프로 보는 것이 훨씬 전략의 안정성과 특징을 잘 보여준다.

이제 이 전략이 꾸준한 수익을 내는지, 손실이 얼마나 큰지를 눈으로 직접 확인할 수 있게 되었다.

사실 전략이라기엔 너무 초라한 내용이지만 어쨌거나 틀을 잡았으니 이제 전략을 조금 구체화 하면 될 것 같다.


 

반응형