최초 프로젝트 업로드 (Script Auto Commit)

This commit is contained in:
2025-12-03 22:36:00 +09:00
commit 4745ab3c28
33 changed files with 4251 additions and 0 deletions

132
analyze_backtest_log.py Normal file
View File

@@ -0,0 +1,132 @@
# analyze_backtest_log.py
# 백테스트 로그 분석 스크립트: exit_reason별 성과, 승률, 평균 손익 계산
import re
from typing import Dict, List, Tuple
from collections import defaultdict
def parse_sell_trades(log_path: str) -> List[Dict[str, any]]:
"""로그 파일에서 SELL 거래 파싱"""
trades = []
line_count = 0
match_count = 0
try:
with open(log_path, 'r', encoding='utf-16-le', errors='ignore') as f:
for line in f:
line_count += 1
# SELL 패턴: [날짜] SELL (exit_reason): 종목명(티커) @ 가격 / 수량주 (수익률%)
# 예: [2022-01-10] SELL (STOP_LOSS_TECH): Delta Air Lines(DAL) @ 40 / 247,260주 (-2.12%)
match = re.search(
r'\[(\d{4}-\d{2}-\d{2})\]\s+SELL\s+\(([^)]+)\):\s+(.+?)\s+@\s+[\d,]+\s+/\s+[\d,]+주\s+\(([+-]?\d+\.\d+)%\)',
line
)
if match:
match_count += 1
date, exit_reason, ticker, return_pct = match.groups()
trades.append({
'date': date,
'exit_reason': exit_reason,
'ticker': ticker.strip(),
'return_pct': float(return_pct)
})
except Exception as e:
print(f"Error reading file: {e}")
print(f"Debug: Scanned {line_count} lines, found {match_count} SELL trades")
return trades
def analyze_by_exit_reason(trades: List[Dict[str, any]]) -> None:
"""exit_reason별 통계 분석"""
stats: Dict[str, List[float]] = defaultdict(list)
for trade in trades:
stats[trade['exit_reason']].append(trade['return_pct'])
print("\n" + "="*70)
print("📊 EXIT REASON별 성과 분석")
print("="*70)
print(f"{'Exit Reason':<30} {'횟수':>6} {'승률':>7} {'평균':>8} {'누적':>8}")
print("-"*70)
total_trades = len(trades)
total_profit = sum(t['return_pct'] for t in trades)
total_wins = sum(1 for t in trades if t['return_pct'] > 0)
for reason in sorted(stats.keys()):
returns = stats[reason]
count = len(returns)
win_rate = sum(1 for r in returns if r > 0) / count * 100
avg_return = sum(returns) / count
total_return = sum(returns)
print(f"{reason:<30} {count:>6} {win_rate:>6.1f}% {avg_return:>7.2f}% {total_return:>7.2f}%")
print("-"*70)
if total_trades > 0:
print(f"{'TOTAL':<30} {total_trades:>6} {total_wins/total_trades*100:>6.1f}% "
f"{total_profit/total_trades:>7.2f}% {total_profit:>7.2f}%")
print("="*70)
def analyze_losers(trades: List[Dict[str, any]], top_n: int = 10) -> None:
"""손실 거래 TOP N 분석"""
losers = sorted([t for t in trades if t['return_pct'] < 0],
key=lambda x: x['return_pct'])
print(f"\n❌ 손실 TOP {top_n} 거래")
print("-"*70)
for i, trade in enumerate(losers[:top_n], 1):
print(f"{i:2}. [{trade['date']}] {trade['ticker']:<30} "
f"{trade['exit_reason']:<25} {trade['return_pct']:>7.2f}%")
def analyze_winners(trades: List[Dict[str, any]], top_n: int = 10) -> None:
"""수익 거래 TOP N 분석"""
winners = sorted([t for t in trades if t['return_pct'] > 0],
key=lambda x: x['return_pct'], reverse=True)
print(f"\n✅ 수익 TOP {top_n} 거래")
print("-"*70)
for i, trade in enumerate(winners[:top_n], 1):
print(f"{i:2}. [{trade['date']}] {trade['ticker']:<30} "
f"{trade['exit_reason']:<25} {trade['return_pct']:>7.2f}%")
def main() -> None:
log_path = 'latest_backtest.log'
print("백테스트 로그 분석 시작...")
trades = parse_sell_trades(log_path)
print(f"{len(trades)}개 거래 파싱 완료")
analyze_by_exit_reason(trades)
analyze_losers(trades, top_n=15)
analyze_winners(trades, top_n=15)
# 추가 인사이트
print("\n" + "="*70)
print("💡 최적화 힌트")
print("="*70)
stop_losses = [t for t in trades if 'STOP_LOSS' in t['exit_reason']]
trailing_stops = [t for t in trades if 'TRAILING_STOP' in t['exit_reason']]
profit_takes = [t for t in trades if 'PROFIT_TAKE' in t['exit_reason']]
if stop_losses:
avg_sl = sum(t['return_pct'] for t in stop_losses) / len(stop_losses)
print(f"• STOP_LOSS 평균 손실: {avg_sl:.2f}% → 너무 타이트하면 완화 고려")
if trailing_stops:
ts_win_rate = sum(1 for t in trailing_stops if t['return_pct'] > 0) / len(trailing_stops) * 100
print(f"• TRAILING_STOP 승률: {ts_win_rate:.1f}% → 50% 이하면 파라미터 조정 필요")
if profit_takes:
avg_pt = sum(t['return_pct'] for t in profit_takes) / len(profit_takes)
print(f"• PROFIT_TAKE 평균 수익: {avg_pt:.2f}% → 10% 이상이면 목표가 상향 고려")
if __name__ == '__main__':
main()