"""
Five strategy-improvement probes on the live Kalshi CLV strategy (BOOK=kalshi,
band [5%,12%), home/away, both-sides dedupe). Reconstructs the signal set from
live_data keeping extra columns (b365 CLV, kalshi bid) so cross-book + spread
analyses are possible. Pure analysis -- nothing here changes the live pipeline.
"""
import os, sys
import numpy as np, pandas as pd
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
os.chdir(r'E:\soccer')
pd.set_option('display.width', 140)

EDGE_LO, EDGE_HI = 0.05, 0.12
SIDES = ['home', 'away']
EXCLUDE = ['USL', 'BSA']
GAME = ['game_date', 'home', 'away']
SIDE_LETTER = {'home': 'H', 'away': 'A'}

# ---- load + reconstruct the exact signal set (mirrors strategy.ipynb) ----
dfs = [pd.read_csv('live_data/' + f) for f in os.listdir('live_data')
       if f.endswith('.csv') and f != 'test.csv']
df = pd.concat(dfs, ignore_index=True)
df['ts'] = pd.to_datetime(df['timestamp'], format='mixed', errors='coerce')
df = df[df['ts'].notna()]
pre = df[(df['status'] == 'PRE') & (df['elapsed'] < 0)].copy()
pre = pre[~pre['league'].isin(EXCLUDE)]

entries = []
for side in SIDES:
    c = f'clv_kalshi_{side}'
    q = pre[(pre[c] >= EDGE_LO) & (pre[c] < EDGE_HI)].copy()
    q = q.sort_values('ts').drop_duplicates(GAME, keep='first')
    q['side']      = side
    q['clv']       = q[c]
    q['clv_b365']  = q[f'clv_b365_{side}']
    q['bid']       = q[f'kalshi_{side}_bid']
    q['ask']       = q[f'kalshi_{side}_ask']
    q['p_win']     = q[f'pin_fair_{side}']
    entries.append(q)
sig = pd.concat(entries, ignore_index=True)
sig = sig.sort_values('clv', ascending=False).drop_duplicates(GAME, keep='first')
sig = sig.sort_values('ts').reset_index(drop=True)

sig['odds'] = np.where((sig['ask'] > 0), 1.0 / sig['ask'], np.nan)
sig['won']  = (sig['ftr'] == sig['side'].map(SIDE_LETTER)).astype(int)
sig = sig.dropna(subset=['odds', 'p_win', 'ftr']).reset_index(drop=True)
sig['net']  = np.where(sig['won'] == 1, sig['odds'] - 1, -1.0)
sig['spread'] = sig['ask'] - sig['bid']

def line(name, s):
    if len(s) == 0:
        print(f'  {name:<22} n=  0'); return
    print(f'  {name:<22} n={len(s):>4} | WR {s.won.mean():>5.1%} | flat ROI {s.net.mean():>+7.1%} | net {s.net.sum():>+7.1f}u')

print(f"reconstructed {len(sig)} settled signals | WR {sig.won.mean():.1%} | flat ROI {sig.net.mean():+.1%} | net {sig.net.sum():+.1f}u")
print("(should match the strategy.ipynb baseline of 365 / 26.3% / +18.6% / +67.8u)\n")

# ================= #1  CLV SUB-BAND =================
print("="*70)
print("#1  CLV SUB-BAND  -- is the edge uniform across [5%,12%)?")
for lo, hi in [(0.05,0.06),(0.06,0.07),(0.07,0.08),(0.08,0.10),(0.10,0.12)]:
    line(f'[{lo:.0%},{hi:.0%})', sig[(sig.clv>=lo)&(sig.clv<hi)])
print("  --- coarse halves ---")
line('[5%,8%)',  sig[(sig.clv>=0.05)&(sig.clv<0.08)])
line('[8%,12%)', sig[(sig.clv>=0.08)&(sig.clv<0.12)])

# ================= #4  FAVORITE vs LONGSHOT =================
print("\n"+"="*70)
print("#4  FAVORITE vs LONGSHOT  -- where does the edge live? (by decimal odds)")
for lo, hi, nm in [(1.0,2.0,'fav   (<2.0)'),(2.0,4.0,'mid   (2-4)'),
                   (4.0,8.0,'long  (4-8)'),(8.0,1e9,'bomb  (>8)')]:
    line(nm, sig[(sig.odds>=lo)&(sig.odds<hi)])
print("  --- by p_win (Pinnacle fair) ---")
for lo, hi, nm in [(0.0,0.15,'p<15%'),(0.15,0.25,'15-25%'),(0.25,0.40,'25-40%'),(0.40,1.0,'p>=40%')]:
    line(nm, sig[(sig.p_win>=lo)&(sig.p_win<hi)])

# ================= #2  CROSS-BOOK AGREEMENT =================
print("\n"+"="*70)
print("#2  CROSS-BOOK AGREEMENT  -- require B365 CLV to agree (currently Kalshi-only)")
both_band = sig[(sig.clv_b365>=EDGE_LO)&(sig.clv_b365<EDGE_HI)]
b365_pos  = sig[sig.clv_b365>0]
b365_neg  = sig[sig.clv_b365<=0]
line('all (Kalshi signal)', sig)
line('B365 CLV also in band', both_band)
line('B365 CLV > 0 (soft)',  b365_pos)
line('B365 CLV <= 0',        b365_neg)
print(f"  (B365 CLV available on {sig.clv_b365.notna().sum()}/{len(sig)} signals)")

# ================= #3  SPREAD-QUALITY FILTER =================
print("\n"+"="*70)
print("#3  SPREAD-QUALITY FILTER  -- skip wide Kalshi bid-ask (spread eats edge)?")
sp = sig.dropna(subset=['spread'])
print(f"  spread (prob pts): median {sp.spread.median()*100:.1f} | mean {sp.spread.mean()*100:.1f} | p90 {sp.spread.quantile(.9)*100:.1f}  (n={len(sp)})")
for lo, hi, nm in [(0,0.02,'<=2c'),(0.02,0.04,'2-4c'),(0.04,0.06,'4-6c'),(0.06,1.0,'>6c')]:
    line(f'spread {nm}', sp[(sp.spread>=lo)&(sp.spread<hi)])
print("  --- cumulative: keep only signals with spread <= X ---")
for thr in (0.02,0.03,0.04,0.06):
    line(f'spread <= {thr*100:.0f}c', sp[sp.spread<=thr])

# ================= #5  SIZING / SHRINKAGE =================
print("\n"+"="*70)
print("#5  SIZING / SHRINKAGE  -- final $ and MAX DRAWDOWN (variance) from $1000")
BR0, CAP, FLOOR = 1000.0, 0.25, 1.0
def kfrac(p, odds, mult):
    b = odds-1; f = max((p*b-(1-p))/b, 0.0); return min(f*mult, CAP)
def sim(mult, shrink=0.0, flat=None):
    br=BR0; peak=BR0; mdd=0.0
    for _,r in sig.iterrows():
        if flat is not None:
            stake = round(flat*br, 2)
        else:
            p = r.ask + (1-shrink)*(r.p_win - r.ask)   # shrink edge toward the ask
            stake = round(kfrac(p, r.odds, mult)*br, 2)
            if stake>0: stake=max(stake, FLOOR)
        br += stake*(r.odds-1) if r.won else -stake
        peak=max(peak,br); mdd=max(mdd,(peak-br)/peak)
    return br, mdd
for nm, kw in [('quarter-Kelly (base)',dict(mult=0.25)),
               ('eighth-Kelly',        dict(mult=0.125)),
               ('qKelly, 50% edge-shrink',dict(mult=0.25,shrink=0.5)),
               ('qKelly, 75% edge-shrink',dict(mult=0.25,shrink=0.75)),
               ('flat 1% of bankroll',  dict(mult=0,flat=0.01)),
               ('flat 2% of bankroll',  dict(mult=0,flat=0.02))]:
    f,dd = sim(**kw)
    print(f'  {nm:<26} final ${f:>8,.0f}  ({f/BR0-1:>+6.1%})  | max drawdown {dd:>5.1%}')
