"""
어깨매도(Shoulder Cut) 백테스트 및 원인 분석

[문제] 어깨매도가 마이너스 수익률로 체결되는 이유
============================================================
현재 로직 (kis_short_ver2.py):
  - 어깨매도 = "보유 중 고점(max_price) 대비 3%(SHOULDER_CUT_PCT) 이상 하락 시"
    수익/손실 불문하고 즉시 매도.
  - max_price 초기값 = 매수가(buy_price). 루프마다 현재가로 갱신.

왜 마이너스로 나가는가?
  1) 매수 후 한 번도 안 올랐을 때
     → max_price = buy_price 유지 → 고점 대비 3% 하락 = 매수가 대비 -3% 하락
     → 이미 손실 구간에서 어깨매도 조건만 만족해 매도됨.

  2) 조금 올랐다가 3% 빠졌을 때
     → 예: 매수가 10,000원 → 10,100원 갱신(고점) → 9,800원으로 하락(고점 대비 ~3%)
     → 어깨매도 체결 시 매수가 대비 -2% (손실).

결론: "그 전에 팔아야 한다" = 수익이 났을 때 먼저 팔아야 하는데,
      현재는 "고점 대비 N% 하락"만 보고 있어서, 고점이 매수가 근처면
      손실로 어깨매도되는 것이 현재 설계 그대로 동작한 결과임.

개선 방향 (백테스트에서 검증 대상):
  - (A) 어깨매도를 "수익일 때만" 적용: profit_pct > 0 일 때만 고점 대비 N% 하락 시 매도.
  - (B) 고점이 매수가 + X% 이상일 때만 어깨매도 적용 (일단 수익 구간 진입한 경우만).
"""

import sqlite3
from pathlib import Path
from collections import defaultdict

SCRIPT_DIR = Path(__file__).resolve().parent
DB_PATH = SCRIPT_DIR / "quant_bot.db"


def get_conn():
    return sqlite3.connect(DB_PATH)


def report_shoulder_sell_analysis():
    """DB trade_history에서 어깨매도 건만 추출해 통계 및 원인 보고."""
    conn = get_conn()
    conn.row_factory = sqlite3.Row

    # 어깨매도만 필터
    rows = conn.execute("""
        SELECT code, name, buy_price, sell_price, qty, profit_rate, realized_pnl,
               buy_date, sell_date, sell_reason
        FROM trade_history
        WHERE sell_reason = '어깨매도'
        ORDER BY sell_date DESC
    """).fetchall()

    conn.close()

    if not rows:
        print("[어깨매도] trade_history에 '어깨매도' 건이 없습니다.")
        return []

    total = len(rows)
    wins = [r for r in rows if r["profit_rate"] > 0]
    losses = [r for r in rows if r["profit_rate"] <= 0]
    win_count = len(wins)
    loss_count = len(losses)
    avg_profit = sum(r["profit_rate"] for r in rows) / total
    avg_loss_when_loss = sum(r["profit_rate"] for r in losses) / loss_count if losses else 0
    total_pnl = sum(r["realized_pnl"] for r in rows)

    print("=" * 60)
    print("📊 어깨매도(Shoulder Cut) 실거래 통계 (trade_history)")
    print("=" * 60)
    print(f"  총 건수: {total}")
    print(f"  익절 건수: {win_count} ({100*win_count/total:.1f}%)")
    print(f"  손실 건수: {loss_count} ({100*loss_count/total:.1f}%)")
    print(f"  평균 수익률: {avg_profit:.2f}%")
    if losses:
        print(f"  손실 건 평균 수익률: {avg_loss_when_loss:.2f}%")
    print(f"  실현 손익 합계: {total_pnl:+,.0f}원")
    print()

    print("📌 왜 어깨매도가 마이너스로 나가는가?")
    print("   - 조건: 보유 고점(max_price) 대비 3% 하락 시 무조건 매도.")
    print("   - max_price 초기값 = 매수가 → 올라본 적 없으면 '매수가=고점'에서 3% 하락 = 이미 -3% 근처.")
    print("   - 조금 올랐다가 3% 빠져도, 매수가 대비하면 손실일 수 있음.")
    print("   → 개선: 수익일 때만 어깨매도 적용(profit_pct>0) 또는 고점이 매수가+X% 이상일 때만 적용.")
    print()

    return rows


def backtest_shoulder_rule_current(rows):
    """현재 규칙 그대로: 이미 DB에 기록된 결과 요약 (재현 확인)."""
    if not rows:
        return
    print("=" * 60)
    print("🔬 백테스트 1: 현재 규칙 (고점 대비 3% 하락 시 무조건 매도)")
    print("=" * 60)
    print("   → 실거래와 동일. 위 실거래 통계가 곧 현재 규칙 결과.")
    print()


def backtest_shoulder_rule_profit_only(rows):
    """
    개선안 A: 수익일 때만 어깨매도 적용.
    (실제로는 매도 시점의 profit_rate만 있으므로, '그때 수익이었으면 어깨매도 적용했을 것'으로 가정.)
    여기서는 "손실로 어깨매도된 건"을 제외하면 몇 건이었을지, 그때 손익이 얼마였을지 보여줌.
    """
    if not rows:
        return
    # 손실로 어깨매도된 건 = 개선안 적용 시 어깨매도로 나가지 않았을 건
    would_skip = [r for r in rows if r["profit_rate"] <= 0]
    would_apply = [r for r in rows if r["profit_rate"] > 0]

    print("=" * 60)
    print("🔬 백테스트 2: 개선안 A — 수익일 때만 어깨매도 적용")
    print("=" * 60)
    print(f"   현재 규칙으로 손실로 어깨매도된 건: {len(would_skip)}건")
    print(f"   → 개선안 적용 시 이 건들은 어깨매도 조건에서 제외됨 (다른 손절/익절로 나갔을 가능성).")
    print(f"   수익으로 어깨매도된 건(유지): {len(would_apply)}건")
    if would_skip:
        skip_pnl = sum(r["realized_pnl"] for r in would_skip)
        print(f"   제외되는 건들의 실현손익 합계: {skip_pnl:+,.0f}원 (이 손실들은 다른 로직으로 나갈 때까지 보유됨)")
    print()


def backtest_shoulder_rule_high_above_entry(rows, min_high_pct=0.01):
    """
    개선안 B: 고점이 매수가 대비 min_high_pct 이상일 때만 어깨매도 적용.
    DB에는 고점이 없으므로, "매도가가 매수가보다 높았던 건" = 수익 매도만 해당.
    (실제 고점은 매도가보다 높았을 수 있음. 보수적으로 수익 건만 적용된 것처럼 집계.)
    """
    if not rows:
        return
    # 수익 매도 = 매도가 > 매수가 → 고점은 매수가 이상이었을 것
    above_entry = [r for r in rows if r["sell_price"] > r["buy_price"]]
    below_entry = [r for r in rows if r["sell_price"] <= r["buy_price"]]

    print("=" * 60)
    print(f"🔬 백테스트 3: 개선안 B — 고점이 매수가+{min_high_pct*100:.0f}% 이상일 때만 어깨매도")
    print("=" * 60)
    print("   (DB에 고점 미저장으로, 매도가>매수가인 수익 건만 '고점이 매수가 위'로 간주)")
    print(f"   적용될 건(수익 매도): {len(above_entry)}건")
    print(f"   제외될 건(손실 매도): {len(below_entry)}건")
    if below_entry:
        pnl_excluded = sum(r["realized_pnl"] for r in below_entry)
        print(f"   제외 건 실현손익 합계: {pnl_excluded:+,.0f}원")
    print()


def main():
    print("\n")
    rows = report_shoulder_sell_analysis()
    backtest_shoulder_rule_current(rows)
    backtest_shoulder_rule_profit_only(rows)
    backtest_shoulder_rule_high_above_entry(rows, min_high_pct=0.01)
    print("=" * 60)
    print("✅ 백테스트 완료. 개선안 반영 시 kis_short_ver2.py 매도 로직에서")
    print("   어깨매도 조건에 profit_pct > 0 또는 max_price >= buy_price * (1 + X) 추가 검토.")
    print("=" * 60)


if __name__ == "__main__":
    main()
