코인

[자동매매로봇 만들기] [8편] 전략 고도화 – 백테스트 구조화 & 볼린저 밴드 전략 모듈화

stupidsoft 2025. 6. 12. 19:38
반응형

 

 

백테스트를 통해 가벼운 전략의 결과를 보았으니

이제 전략을 다듬어가며 나에게 맞는, 더 좋은 결과를 내는 전략을 찾아가는 과정이 필요하겠다.

그러기 위해서 먼저 코드를 구조화 하는 것도 필요하다.

 

1. 전체 코드 개요

backtest/
├── backtest.py         ← 실행 진입점
├── strategy.py         ← 전략 정의 (볼린저 밴드, RSI 등)
├── analyzer.py         ← 수익률, 승률, 낙폭 계산
├── plotter.py          ← 수익곡선 시각화

2. strategy.py – 전략 모듈화

# strategy.py

def calculate_indicators(df):
    import pandas as pd
    import numpy as np

    df['ma20'] = df['close'].rolling(window=20).mean()
    std = df['close'].rolling(window=20).std()
    df['bb_upper'] = df['ma20'] + 2 * std
    df['bb_lower'] = df['ma20'] - 2 * std

    delta = df['close'].diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)
    avg_gain = gain.rolling(14).mean()
    avg_loss = loss.rolling(14).mean()
    rs = avg_gain / avg_loss
    df['rsi14'] = 100 - (100 / (1 + rs))

    return df

def bollinger_rsi_strategy(df):
    position = None
    entry_price = 0
    trades = []

    for i in range(1, len(df)):
        row = df.iloc[i]
        if position is None:
            if row['close'] < row['bb_lower'] and row['rsi14'] < 30:
                position = 'long'
                entry_price = row['close']
                entry_time = row['timestamp']
        elif position == 'long':
            if row['close'] > row['ma20']:
                exit_price = row['close']
                exit_time = row['timestamp']
                ret = (exit_price - entry_price) / entry_price
                trades.append({'entry': entry_time, 'exit': exit_time, 'return': ret})
                position = None
    return trades

3. analyzer.py – 수익률 분석

# analyzer.py

import pandas as pd
import numpy as np

def analyze_trades(trades):
    if not trades:
        return None

    returns = [t['return'] for t in trades]
    cum_returns = pd.Series([1 + r for r in returns]).cumprod() - 1
    win_rate = sum(1 for r in returns if r > 0) / len(returns)
    avg_return = np.mean(returns)
    total_return = cum_returns.iloc[-1]

    return {
        'count': len(returns),
        'win_rate': win_rate,
        'avg_return': avg_return,
        'total_return': total_return,
        'cum_returns': cum_returns
    }

4. plotter.py – 수익곡선 그리기

# plotter.py

import matplotlib.pyplot as plt

def plot_cumulative_returns(cum_returns):
    plt.figure(figsize=(10, 5))
    plt.plot(cum_returns, marker='o')
    plt.title('누적 수익률')
    plt.xlabel('트레이드 순번')
    plt.ylabel('누적 수익률')
    plt.grid(True)
    plt.tight_layout()
    plt.show()

5. backtest.py – 실행 스크립트

# backtest.py

import ccxt
import pandas as pd
from strategy import calculate_indicators, bollinger_rsi_strategy
from analyzer import analyze_trades
from plotter import plot_cumulative_returns

# 데이터 로딩
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')

# 지표 계산 및 전략 실행
df = calculate_indicators(df)
trades = bollinger_rsi_strategy(df)

# 분석 및 시각화
result = analyze_trades(trades)
if result:
    print(f"총 거래 수: {result['count']}")
    print(f"승률: {result['win_rate']*100:.2f}%")
    print(f"평균 수익률: {result['avg_return']*100:.2f}%")
    print(f"누적 수익률: {result['total_return']*100:.2f}%")
    plot_cumulative_returns(result['cum_returns'])
else:
    print("거래 없음")

마무리하며

이번 글에서는 백테스트 코드를 구조화하고,
볼린저 밴드 + RSI 전략을 모듈로 정리해서 확장 가능하고 재사용 가능한 형태로 발전시켰다.
이제 다른 전략도 쉽게 추가할 수 있고, 여러 종목에 동시에 테스트하는 것도 가능하다.

 

반응형