"""
Drawdown comparison: YES-underdog vs NO-favorite using the SAME signal selection.
Computes max drawdown, daily volatility, Sharpe-like ratio, longest losing streak.
"""
import json
import math
from pathlib import Path
from collections import defaultdict

h = json.loads(Path("E:/soccer/bet_history.json").read_text())

bets = [b for b in h
        if b.get("kalshi_ask") and b.get("kalshi_stake") and b.get("won") is not None and b.get("ftr")]
# Sort by resolution date, then date
bets.sort(key=lambda b: (b.get("resolved_at") or b["date"], b["date"]))

DRAW_PRICE = 0.25


def per_bet_pnl_yes(b):
    yes_win = b["won"]
    if yes_win:
        return b["kalshi_stake"] * (1 - b["kalshi_ask"]) / b["kalshi_ask"]
    return -b["kalshi_stake"]


def per_bet_pnl_no(b):
    side = b["bet"]; is_home_bet = "HOME" in side.upper()
    ftr = b["ftr"]
    no_price = b["kalshi_ask"] + DRAW_PRICE
    if no_price >= 0.99:
        return 0.0
    no_win = (is_home_bet and ftr != "A") or (not is_home_bet and ftr != "H")
    if no_win:
        return b["kalshi_stake"] * (1 - no_price) / no_price
    return -b["kalshi_stake"]


def drawdown_stats(pnls, label):
    cum = []
    s = 0.0
    for p in pnls:
        s += p
        cum.append(s)

    peak = cum[0]
    max_dd_abs = 0.0
    max_dd_peak = peak
    max_dd_trough = peak
    current_peak = cum[0]
    for v in cum:
        if v > current_peak:
            current_peak = v
        dd = current_peak - v
        if dd > max_dd_abs:
            max_dd_abs = dd
            max_dd_peak = current_peak
            max_dd_trough = v

    final = cum[-1]
    # Volatility per-bet
    mean_pnl = sum(pnls) / len(pnls)
    var_pnl = sum((p - mean_pnl) ** 2 for p in pnls) / len(pnls)
    std_pnl = math.sqrt(var_pnl)
    sharpe_per_bet = mean_pnl / std_pnl if std_pnl else 0

    # Longest losing streak
    longest_loss_streak = 0
    cur_streak = 0
    for p in pnls:
        if p < 0:
            cur_streak += 1
            longest_loss_streak = max(longest_loss_streak, cur_streak)
        else:
            cur_streak = 0

    return {
        "label": label,
        "n": len(pnls),
        "final_pnl": final,
        "max_dd": max_dd_abs,
        "max_dd_pct_of_final": (max_dd_abs / final * 100) if final > 0 else float("nan"),
        "max_dd_peak": max_dd_peak,
        "max_dd_trough": max_dd_trough,
        "mean": mean_pnl,
        "std": std_pnl,
        "sharpe_per_bet": sharpe_per_bet,
        "sharpe_annual": sharpe_per_bet * math.sqrt(252 * (len(pnls) / 35)),  # rough: ~35 days of data
        "longest_loss_streak": longest_loss_streak,
        "cum": cum,
    }


yes_pnls = [per_bet_pnl_yes(b) for b in bets]
no_pnls = [per_bet_pnl_no(b) for b in bets]

yes_stats = drawdown_stats(yes_pnls, "YES underdog")
no_stats = drawdown_stats(no_pnls, "NO favorite")

print("=" * 78)
print(f"DRAWDOWN COMPARISON ({len(bets)} bets, chronological)")
print("=" * 78)
print(f"{'Metric':<32} {'YES underdog':>16} {'NO favorite':>16}")
print("-" * 78)
for key, label, fmt in [
    ("n", "Sample size", "{:>16d}"),
    ("final_pnl", "Final P&L", "${:>+15.2f}"),
    ("mean", "Avg P&L per bet", "${:>+15.4f}"),
    ("std", "Stdev per bet", "${:>15.2f}"),
    ("sharpe_per_bet", "Sharpe per bet", "{:>16.3f}"),
    ("max_dd", "Max drawdown ($)", "${:>15.2f}"),
    ("max_dd_pct_of_final", "Max DD as % of final P&L", "{:>15.1f}%"),
    ("longest_loss_streak", "Longest losing streak", "{:>16d}"),
]:
    yv = yes_stats[key]; nv = no_stats[key]
    if isinstance(yv, float) and (math.isnan(yv) or math.isinf(yv)):
        ys = "n/a"
    else:
        ys = fmt.format(yv)
    if isinstance(nv, float) and (math.isnan(nv) or math.isinf(nv)):
        ns = "n/a"
    else:
        ns = fmt.format(nv)
    print(f"{label:<32} {ys:>16} {ns:>16}")

# Show DD context
print()
print(f"YES underdog DD: peak ${yes_stats['max_dd_peak']:.2f} -> trough ${yes_stats['max_dd_trough']:.2f}")
print(f"NO favorite DD:  peak ${no_stats['max_dd_peak']:.2f} -> trough ${no_stats['max_dd_trough']:.2f}")

# Find when the max DD happened for each strategy (bet index)
def find_dd_window(pnls):
    cum = []
    s = 0
    for p in pnls:
        s += p; cum.append(s)
    peak = cum[0]; peak_i = 0
    max_dd = 0; trough_i = 0; max_peak_i = 0
    for i, v in enumerate(cum):
        if v > peak:
            peak = v; peak_i = i
        if peak - v > max_dd:
            max_dd = peak - v
            trough_i = i
            max_peak_i = peak_i
    return max_peak_i, trough_i

yes_pi, yes_ti = find_dd_window(yes_pnls)
no_pi, no_ti = find_dd_window(no_pnls)
print()
print(f"YES underdog max DD spanned bets #{yes_pi} -> #{yes_ti}  ({yes_ti - yes_pi} bets)")
print(f"  Dates: {bets[yes_pi].get('resolved_at') or bets[yes_pi]['date']}  ->  {bets[yes_ti].get('resolved_at') or bets[yes_ti]['date']}")
print(f"NO favorite  max DD spanned bets #{no_pi} -> #{no_ti}  ({no_ti - no_pi} bets)")
print(f"  Dates: {bets[no_pi].get('resolved_at') or bets[no_pi]['date']}  ->  {bets[no_ti].get('resolved_at') or bets[no_ti]['date']}")

# Daily P&L for both
def daily_pnls(pnls):
    by_day = defaultdict(float)
    for p, b in zip(pnls, bets):
        d = b.get("resolved_at") or b["date"]
        by_day[d] += p
    return sorted(by_day.values())

yes_daily = daily_pnls(yes_pnls)
no_daily = daily_pnls(no_pnls)

def pct(arr, p):
    if not arr: return 0
    arr = sorted(arr)
    return arr[max(0, min(len(arr)-1, int(p/100 * len(arr))))]

print()
print(f"{'Daily P&L metric':<32} {'YES underdog':>16} {'NO favorite':>16}")
print("-" * 78)
print(f"{'Trading days':<32} {len(yes_daily):>16} {len(no_daily):>16}")
print(f"{'Best day':<32} {'$'+format(max(yes_daily),'+.2f'):>16} {'$'+format(max(no_daily),'+.2f'):>16}")
print(f"{'Worst day':<32} {'$'+format(min(yes_daily),'+.2f'):>16} {'$'+format(min(no_daily),'+.2f'):>16}")
print(f"{'Median day':<32} {'$'+format(pct(yes_daily,50),'+.2f'):>16} {'$'+format(pct(no_daily,50),'+.2f'):>16}")
print(f"{'5th pctile (bad day)':<32} {'$'+format(pct(yes_daily,5),'+.2f'):>16} {'$'+format(pct(no_daily,5),'+.2f'):>16}")
print(f"{'95th pctile (great day)':<32} {'$'+format(pct(yes_daily,95),'+.2f'):>16} {'$'+format(pct(no_daily,95),'+.2f'):>16}")
print(f"{'% losing days':<32} {sum(1 for d in yes_daily if d<0)/len(yes_daily)*100:>15.1f}% {sum(1 for d in no_daily if d<0)/len(no_daily)*100:>15.1f}%")
