자동매매 로그 분석 성과 측정

왜 매매 로그를 분석해야 하는가

자동매매 봇을 만들고 실행하는 것은 시작에 불과합니다. 진짜 수익을 만드는 과정은 매매 로그를 분석하고 전략을 개선하는 반복에서 나옵니다. 로그 없는 자동매매는 계기판 없이 비행기를 모는 것과 같습니다.

많은 트레이더가 총 수익률만 확인하고 넘어가지만, 실제로는 어떤 시간대에 수익이 났는지, 어떤 조건에서 손실이 컸는지, 승률과 손익비의 균형은 맞는지를 세밀하게 파악해야 전략을 지속적으로 개선할 수 있습니다.

매매 로그 설계

자동매매 봇에서 기록해야 할 핵심 데이터 항목입니다. 처음부터 구조화된 로그를 설계하면 이후 분석이 훨씬 수월합니다.

필드 설명 예시
timestamp 주문 시각 (UTC) 2026-03-05T10:30:00Z
symbol 거래 종목 BTC/USDT
side 매수/매도 buy / sell
entry_price 진입 가격 85,200
exit_price 청산 가격 86,100
quantity 수량 0.05
pnl 실현 손익 (수수료 포함) +38.5 USDT
fee 수수료 3.42 USDT
strategy 사용 전략명 momentum_20
signal_reason 진입/청산 사유 MA crossover
import pandas as pd
import json
from datetime import datetime

class TradeLogger:
    def __init__(self, filepath='trades.csv'):
        self.filepath = filepath
        self.columns = [
            'timestamp', 'symbol', 'side', 'entry_price',
            'exit_price', 'quantity', 'pnl', 'fee',
            'strategy', 'signal_reason', 'holding_seconds'
        ]
    
    def log_trade(self, trade_data):
        """거래 1건 기록"""
        trade_data['timestamp'] = datetime.utcnow().isoformat()
        df = pd.DataFrame([trade_data])
        df.to_csv(self.filepath, mode='a',
                  header=not pd.io.common.file_exists(self.filepath),
                  index=False)
    
    def load_trades(self):
        """전체 거래 내역 로드"""
        return pd.read_csv(self.filepath, parse_dates=['timestamp'])

핵심 성과 지표(KPI) 계산

매매 로그가 쌓이면 다음 지표들을 자동으로 계산하여 전략 건강도를 진단합니다.

기본 지표

def calculate_metrics(trades_df):
    """핵심 성과 지표 계산"""
    total_trades = len(trades_df)
    winners = trades_df[trades_df['pnl'] > 0]
    losers = trades_df[trades_df['pnl'] < 0]
    
    # 승률
    win_rate = len(winners) / total_trades * 100
    
    # 평균 수익 / 평균 손실
    avg_win = winners['pnl'].mean() if len(winners) > 0 else 0
    avg_loss = abs(losers['pnl'].mean()) if len(losers) > 0 else 0
    
    # 손익비 (Reward/Risk Ratio)
    rr_ratio = avg_win / avg_loss if avg_loss > 0 else float('inf')
    
    # Profit Factor
    gross_profit = winners['pnl'].sum()
    gross_loss = abs(losers['pnl'].sum())
    profit_factor = gross_profit / gross_loss if gross_loss > 0 else float('inf')
    
    # 기대값 (Expectancy)
    expectancy = (win_rate/100 * avg_win) - ((1 - win_rate/100) * avg_loss)
    
    return {
        'total_trades': total_trades,
        'win_rate': f"{win_rate:.1f}%",
        'avg_win': f"{avg_win:.2f}",
        'avg_loss': f"{avg_loss:.2f}",
        'rr_ratio': f"{rr_ratio:.2f}",
        'profit_factor': f"{profit_factor:.2f}",
        'expectancy': f"{expectancy:.2f}",
        'total_pnl': f"{trades_df['pnl'].sum():.2f}",
        'total_fees': f"{trades_df['fee'].sum():.2f}"
    }

최대 낙폭(MDD) 계산

최대 낙폭은 전략의 최악의 순간을 보여줍니다. 이 값이 심리적으로 감당 가능한 수준인지 반드시 확인해야 합니다.

def calculate_mdd(trades_df, initial_capital=10000):
    """최대 낙폭(MDD) 계산"""
    cumulative_pnl = trades_df['pnl'].cumsum()
    equity = initial_capital + cumulative_pnl
    
    running_max = equity.cummax()
    drawdown = (equity - running_max) / running_max * 100
    
    mdd = drawdown.min()
    mdd_end_idx = drawdown.idxmin()
    mdd_start_idx = equity[:mdd_end_idx].idxmax()
    
    print(f"최대 낙폭: {mdd:.2f}%")
    print(f"낙폭 시작: {trades_df.iloc[mdd_start_idx]['timestamp']}")
    print(f"낙폭 최저: {trades_df.iloc[mdd_end_idx]['timestamp']}")
    
    return mdd

시간대별·요일별 분석

시장은 시간대와 요일에 따라 다른 성격을 보입니다. 자동매매 봇의 성과를 시간 축으로 분석하면 어떤 시간에 전략이 잘 작동하고 어떤 시간에 손실이 나는지 파악할 수 있습니다.

def time_analysis(trades_df):
    """시간대별·요일별 손익 분석"""
    df = trades_df.copy()
    df['hour'] = df['timestamp'].dt.hour
    df['weekday'] = df['timestamp'].dt.day_name()
    
    # 시간대별 손익
    hourly = df.groupby('hour')['pnl'].agg(['sum', 'count', 'mean'])
    hourly.columns = ['총손익', '거래수', '평균손익']
    print("=== 시간대별 성과 ===")
    print(hourly.sort_values('총손익', ascending=False))
    
    # 요일별 손익
    daily = df.groupby('weekday')['pnl'].agg(['sum', 'count', 'mean'])
    daily.columns = ['총손익', '거래수', '평균손익']
    print("n=== 요일별 성과 ===")
    print(daily.sort_values('총손익', ascending=False))
    
    # 손실 집중 시간대 경고
    worst_hours = hourly[hourly['총손익'] < 0].index.tolist()
    if worst_hours:
        print(f"n⚠️ 손실 집중 시간대: {worst_hours}")
        print("해당 시간대 거래 제한을 고려하세요")
    
    return hourly, daily

분석 결과 특정 시간대에 손실이 집중된다면, 해당 시간의 거래를 비활성화하는 것만으로도 전체 성과가 크게 개선될 수 있습니다.

연속 손실 분석

연속 손실(losing streak)은 자동매매의 심리적·재무적 최대 위협입니다. 복구매매 패턴에서 다루었듯, 연속 손실 후의 과잉 대응이 계좌를 파괴합니다.

def streak_analysis(trades_df):
    """연속 승/패 분석"""
    results = (trades_df['pnl'] > 0).astype(int)
    
    streaks = []
    current_streak = 1
    for i in range(1, len(results)):
        if results.iloc[i] == results.iloc[i-1]:
            current_streak += 1
        else:
            streaks.append({
                'type': 'win' if results.iloc[i-1] == 1 else 'loss',
                'length': current_streak
            })
            current_streak = 1
    
    streak_df = pd.DataFrame(streaks)
    losses = streak_df[streak_df['type'] == 'loss']
    
    print(f"최대 연속 손실: {losses['length'].max()}회")
    print(f"평균 연속 손실: {losses['length'].mean():.1f}회")
    print(f"연속 5회 이상 손실 발생: {(losses['length'] >= 5).sum()}번")
    
    return streak_df

자동 리포트 생성

매일 또는 매주 자동으로 성과 리포트를 생성하여 텔레그램이나 이메일로 발송하면 전략 상태를 상시 모니터링할 수 있습니다.

def generate_daily_report(trades_df, date=None):
    """일일 성과 리포트 생성"""
    if date is None:
        date = datetime.utcnow().date()
    
    daily = trades_df[trades_df['timestamp'].dt.date == date]
    
    if len(daily) == 0:
        return "거래 없음"
    
    metrics = calculate_metrics(daily)
    
    report = f"""
📊 일일 매매 리포트 ({date})
━━━━━━━━━━━━━━━━━━
거래 횟수: {metrics['total_trades']}
승률: {metrics['win_rate']}
총 손익: {metrics['total_pnl']} USDT
수수료: {metrics['total_fees']} USDT
손익비: {metrics['rr_ratio']}
Profit Factor: {metrics['profit_factor']}
━━━━━━━━━━━━━━━━━━
"""
    return report

분석 결과를 전략 개선에 반영하기

로그 분석의 최종 목표는 전략 개선입니다. 분석에서 발견한 패턴을 구체적인 액션으로 연결하는 프레임워크를 소개합니다.

  • 승률은 높은데 손익비가 낮다면: 익절을 너무 빨리 하고 있습니다. 트레일링 스탑으로 수익을 더 키우세요.
  • 승률이 낮은데 손익비가 높다면: 추세추종 전략 특성상 정상입니다. 진입 필터를 추가해 가짜 시그널을 줄이세요.
  • 특정 시간대에 손실 집중: 해당 시간 거래를 비활성화하거나 포지션 크기를 줄이세요.
  • MDD가 허용 범위 초과: 손실 한도 규칙을 재점검하고 레버리지를 낮추세요.
  • 수수료가 총수익의 30% 이상: 거래 빈도를 줄이거나 메이커 주문 비율을 높이세요.

마무리

자동매매의 진짜 경쟁력은 코드가 아니라 데이터 기반의 지속적 개선에 있습니다. 매매 로그를 체계적으로 기록하고, 핵심 지표를 자동으로 산출하며, 분석 결과를 전략에 피드백하는 사이클을 구축하세요. 이 반복이 장기적으로 수익을 만드는 유일한 방법입니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux