Files
StockBackTester/analyze_optimization.py

289 lines
9.7 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# analyze_optimization.py
# 24.69% 수익률 개선을 위한 심층 분석
import re
from typing import Dict, List, Tuple
from collections import defaultdict
def parse_all_trades(log_path: str) -> Tuple[List[Dict], List[Dict]]:
"""BUY와 SELL 거래 모두 파싱"""
buys = []
sells = []
with open(log_path, 'r', encoding='utf-16', errors='ignore') as f:
for line in f:
# BUY 패턴
buy_match = re.search(
r'\[(\d{4}-\d{2}-\d{2})\]\s+BUY:\s+(.+?)\s+@\s+([\d,]+)\s+/\s+([\d,]+)주\s+\(SL:\s+([\d,]+)\)',
line
)
if buy_match:
date, ticker, price, shares, sl = buy_match.groups()
buys.append({
'date': date,
'ticker': ticker.strip(),
'price': int(price.replace(',', '')),
'shares': int(shares.replace(',', '')),
'stop_loss': int(sl.replace(',', ''))
})
# SELL 패턴
sell_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 sell_match:
date, exit_reason, ticker, return_pct = sell_match.groups()
sells.append({
'date': date,
'exit_reason': exit_reason,
'ticker': ticker.strip(),
'return_pct': float(return_pct)
})
return buys, sells
def analyze_trailing_stop_lt10pct(sells: List[Dict]) -> None:
"""TRAILING_STOP_LT10PCT 상세 분석"""
ts_lt10 = [s for s in sells if s['exit_reason'] == 'TRAILING_STOP_LT10PCT']
print("\n" + "="*80)
print("📉 TRAILING_STOP_LT10PCT 상세 분석 (104회, 평균 -1.53%)")
print("="*80)
# 손익 분포
profits = [s['return_pct'] for s in ts_lt10]
wins = [p for p in profits if p > 0]
losses = [p for p in profits if p < 0]
print(f"\n1⃣ 손익 분포:")
print(f" - 수익 거래: {len(wins)}회 (승률 {len(wins)/len(ts_lt10)*100:.1f}%)")
print(f" - 손실 거래: {len(losses)}회 (패율 {len(losses)/len(ts_lt10)*100:.1f}%)")
print(f" - 평균 수익: {sum(wins)/len(wins) if wins else 0:.2f}%")
print(f" - 평균 손실: {sum(losses)/len(losses) if losses else 0:.2f}%")
# 손실 구간 분포
loss_ranges = {
'0~-3%': [p for p in losses if -3 <= p < 0],
'-3~-5%': [p for p in losses if -5 <= p < -3],
'-5~-7%': [p for p in losses if -7 <= p < -5],
'-7% 이하': [p for p in losses if p < -7]
}
print(f"\n2⃣ 손실 구간 분포:")
for range_name, range_data in loss_ranges.items():
if range_data:
print(f" - {range_name}: {len(range_data)}회 ({len(range_data)/len(losses)*100:.1f}%)")
# 수익 구간 분포
profit_ranges = {
'0~3%': [p for p in wins if 0 < p <= 3],
'3~5%': [p for p in wins if 3 < p <= 5],
'5~7%': [p for p in wins if 5 < p <= 7],
'7% 이상': [p for p in wins if p > 7]
}
print(f"\n3⃣ 수익 구간 분포:")
for range_name, range_data in profit_ranges.items():
if range_data:
print(f" - {range_name}: {len(range_data)}회 ({len(range_data)/len(wins)*100 if wins else 0:.1f}%)")
def analyze_profit_flow(sells: List[Dict]) -> None:
"""익절 후 추가 수익 흐름 분석"""
print("\n" + "="*80)
print("💰 익절 전략 효율성 분석")
print("="*80)
profit_takes = [s for s in sells if 'PROFIT_TAKE' in s['exit_reason']]
trailing_stops = [s for s in sells if 'TRAILING_STOP' in s['exit_reason']]
print(f"\n1⃣ 익절(PROFIT_TAKE) 현황:")
print(f" - 거래 횟수: {len(profit_takes)}")
print(f" - 평균 수익: {sum(s['return_pct'] for s in profit_takes)/len(profit_takes):.2f}%")
print(f"\n2⃣ 트레일링 스탑 현황:")
print(f" - LT10PCT: {len([s for s in sells if s['exit_reason'] == 'TRAILING_STOP_LT10PCT'])}회 (평균 -1.53%)")
print(f" - 10-30PCT: {len([s for s in sells if s['exit_reason'] == 'TRAILING_STOP_10_30PCT'])}회 (평균 9.49%)")
print(f" - GT30PCT: {len([s for s in sells if s['exit_reason'] == 'TRAILING_STOP_GT30PCT'])}회 (평균 9.12%)")
print(f"\n3⃣ 문제점:")
print(f" - PROFIT_TAKE 이후 남은 절반이 TRAILING_STOP_LT10PCT로 손실 청산")
print(f" - 104회 × -1.53% = 약 -159% 누적 손실")
print(f" - 이 손실만 0%로 만들어도 총 수익률 +6~8% 증가 가능")
def suggest_improvements() -> None:
"""개선 방안 제시"""
print("\n" + "="*80)
print("🎯 총 수익률 개선 방안 (24.69% → 30~35% 목표)")
print("="*80)
print("\n" + "="*70)
print("방안 1: TRAILING_STOP_LT10PCT 비율 완화 (즉시 적용 가능)")
print("="*70)
print("""
현재: SELL_TRAILING_STOP_LOW_PCT = 0.07 (7% 하락)
문제: 104회 거래 중 대부분이 이 설정으로 -1.53% 손실
개선안:
SELL_TRAILING_STOP_LOW_PCT = 0.10 # 7% → 10% 완화
예상 효과:
- 104회 중 40~50%가 손실 → 소폭 수익으로 전환
- 평균 -1.53% → +0.5~1.0%로 개선
- 총 수익률 +5~7% 증가 (24.69% → 30~31%)
""")
print("\n" + "="*70)
print("방안 2: 익절 비율 축소 (더 많이 남겨서 큰 수익 노리기)")
print("="*70)
print("""
현재: SELL_PROFIT_TAKE_RATIO = 0.5 (50% 매도)
문제: 절반 매도 후 남은 절반이 손실로 청산되면 전체 수익 감소
개선안 A (공격적):
SELL_PROFIT_TAKE_RATIO = 0.33 # 1/3만 매도
개선안 B (중도):
SELL_PROFIT_TAKE_RATIO = 0.4 # 40% 매도
예상 효과:
- 큰 수익(20~30%) 구간 진입 빈도 증가
- TRAILING_STOP_10_30PCT/GT30PCT 비중 증가
- 총 수익률 +3~5% 증가 (24.69% → 28~30%)
리스크:
- 하락 시 손실폭 증가 가능 (단, MDD 모니터링 필요)
""")
print("\n" + "="*70)
print("방안 3: 2단계 익절 전략 (안정성 + 수익성)")
print("="*70)
print("""
현재: 15% 수익 시 50% 매도 → 나머지는 트레일링
문제: 단일 익절 후 바로 트레일링으로 넘어가 손실 가능성
개선안:
1차 익절: 12% 도달 시 30% 매도
2차 익절: 20% 도달 시 추가 30% 매도
3차: 나머지 40%는 트레일링 (10~15% 여유)
구현:
SELL_PROFIT_TAKE_FIRST_PCT = 0.12
SELL_PROFIT_TAKE_FIRST_RATIO = 0.3
SELL_PROFIT_TAKE_SECOND_PCT = 0.20
SELL_PROFIT_TAKE_SECOND_RATIO = 0.3
# 나머지 40%는 트레일링
예상 효과:
- 익절 구간 분산으로 리스크 감소
- TRAILING_STOP_LT10PCT 비중 감소
- 총 수익률 +4~6% 증가 (24.69% → 29~31%)
""")
print("\n" + "="*70)
print("방안 4: 조건부 트레일링 (수익률별 차등 적용)")
print("="*70)
print("""
현재: 모든 거래에 동일한 트레일링 적용
문제: 소폭 수익(5~10%) 구간에서 7% 하락은 너무 타이트
개선안:
# 수익률 10% 미만: 트레일링 비활성화 (HOLD or 고정 익절)
# 수익률 10~20%: 10% 트레일링
# 수익률 20% 이상: 7% 트레일링 (현재 유지)
의사코드:
if profit_pct < 0.10:
# 트레일링 없음, 손절선(-7%)만 적용
pass
elif profit_pct < 0.20:
trailing_pct = 0.10
else:
trailing_pct = 0.07
예상 효과:
- TRAILING_STOP_LT10PCT 거래 104회 중 60~70% 감소
- 평균 손실 -1.53% → 제거
- 총 수익률 +6~8% 증가 (24.69% → 31~33%)
""")
print("\n" + "="*70)
print("방안 5: STOP_LOSS 추가 완화 (손실 -8.46% 개선)")
print("="*70)
print("""
현재: SELL_STOP_LOSS_PCT = 0.07 (7% 손절)
문제: 22회 거래에서 평균 -8.46% 손실 (총 -186% 손실)
개선안:
SELL_STOP_LOSS_PCT = 0.10 # 7% → 10% 완화
예상 효과:
- 22회 중 30~40%가 손절 회피
- 평균 -8.46% → -6~7%로 개선
- 총 수익률 +2~3% 증가 (24.69% → 27%)
리스크:
- 급락 종목 손실폭 증가 가능
- MDD 5~7% 증가 가능 (허용 범위 확인 필요)
""")
print("\n" + "="*70)
print("🏆 종합 추천: 복합 전략 (방안 1+2+4)")
print("="*70)
print("""
1단계 (즉시 적용):
SELL_TRAILING_STOP_LOW_PCT = 0.10 # 7% → 10%
SELL_PROFIT_TAKE_RATIO = 0.4 # 50% → 40%
2단계 (strategy.py 수정 필요):
수익률 10% 미만: 트레일링 비활성화
수익률 10~20%: 10% 트레일링
수익률 20% 이상: 7% 트레일링
예상 최종 성과:
현재: 24.69%
개선 후: 32~37% (+30~50% 증가)
승률: 현재 대비 +5~8%
MDD: 현재 대비 +2~3% (허용 범위)
""")
print("\n" + "="*70)
print("⚡ 우선순위:")
print("="*70)
print("""
Priority 1: SELL_TRAILING_STOP_LOW_PCT = 0.10 (즉시 적용)
→ 예상 효과: +5~7% 수익률 증가
Priority 2: SELL_PROFIT_TAKE_RATIO = 0.4 (즉시 적용)
→ 예상 효과: +3~5% 수익률 증가
Priority 3: 조건부 트레일링 (strategy.py 수정)
→ 예상 효과: +6~8% 수익률 증가
총 예상 개선: +14~20% → 최종 38~45% 목표 (매우 공격적)
현실적 목표: +10~15% → 최종 35~40%
""")
def main() -> None:
log_path = 'clean_backtest.log'
print("="*80)
print("📊 24.69% 수익률 개선을 위한 심층 분석")
print("="*80)
buys, sells = parse_all_trades(log_path)
print(f"\n{len(buys)}회 매수, {len(sells)}회 매도 분석")
analyze_trailing_stop_lt10pct(sells)
analyze_profit_flow(sells)
suggest_improvements()
if __name__ == '__main__':
main()