"""
Limit-order fill-capture sim. Signal = first PRE snapshot where Kalshi CLV >= 5%.
At that moment record the ask A0. For each offset N cents, place a resting buy
limit at P = A0 - N/100. It FILLS if any later PRE snapshot's ask <= P (price
came down to us) before kickoff. Filled bets enter at P; unfilled = no bet ($0).
Settle via ftr. Flat 1-unit stake, ROI uses fill price. Compares net to N=0
(market-at-ask baseline), netting out the winners we miss by not filling.
"""
import csv, glob, sys
from collections import defaultdict
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
files = sorted(glob.glob(r'E:\soccer\live_data\*.csv'))

def fval(r, k):
    try: return float(r.get(k, ''))
    except (TypeError, ValueError): return None

# per fixture/side: time-sorted [(mins_to_ko, clv%, ask)], ftr, win
series = defaultdict(lambda: {'rows': [], 'ftr': None})
for fp in files:
    with open(fp, newline='', encoding='utf-8', errors='replace') as f:
        for r in csv.DictReader(f):
            if r.get('status') != 'PRE':
                continue
            el = fval(r, 'elapsed')
            if el is None or el >= 0:
                continue
            ftr = r.get('ftr') or None
            for side in ('home', 'away'):
                clv = fval(r, f'clv_kalshi_{side}')
                ask = fval(r, f'kalshi_{side}_ask')
                d = series[(r['game_date'], r['home'], r['away'], side)]
                if clv is not None and ask is not None and 0 < ask < 1:
                    d['rows'].append((-el, clv * 100, ask))
                if ftr:
                    d['ftr'] = ftr
for d in series.values():
    d['rows'].sort(reverse=True)   # descending mins: earliest first -> chronological

FLOOR, CAP = 5.0, 12.0
# build signal list: first time CLV in [5,12], with entry ask + future ask path + outcome
signals = []
for key, d in series.items():
    if not d['ftr'] or not d['rows']:
        continue
    win = (d['ftr'] == ('H' if key[3] == 'home' else 'A'))
    rows = d['rows']
    ei = next((i for i, (m, c, a) in enumerate(rows) if FLOOR <= c <= CAP), None)
    if ei is None:
        continue
    A0 = rows[ei][2]
    future_asks = [a for (m, c, a) in rows[ei:]]      # entry onward
    signals.append((A0, future_asks, win))

print(f"signals (first CLV in [{FLOOR:.0f},{CAP:.0f}]): {len(signals)}\n")
print(f"{'offset':>7}{'fill%':>7}{'avgEntry':>9}{'filledW':>9}{'fillROI%':>9}"
      f"{'missW':>7}{'netUnits':>10}{'vsMkt':>8}")
print('-' * 66)

base_units = None
for N in range(0, 6):
    nfill = nwin_fill = nwin_miss = 0
    entry_sum = 0.0
    units = 0.0
    for A0, fut, win in signals:
        P = round(A0 - N / 100.0, 2)
        if P <= 0:
            # can't place below 0; treat as no fill
            if win: nwin_miss += 1
            continue
        filled = any(a <= P + 1e-9 for a in fut) if N > 0 else True
        if filled:
            nfill += 1
            entry_sum += P
            b = (1 - P) / P
            if win:
                nwin_fill += 1
                units += b
            else:
                units -= 1
        else:
            if win:
                nwin_miss += 1
    fillpct = 100 * nfill / len(signals)
    avgentry = entry_sum / nfill if nfill else 0
    fillroi = 100 * units / nfill if nfill else 0
    if N == 0:
        base_units = units
    vs = units - base_units
    print(f"{('-'+str(N)+'c'):>7}{fillpct:>6.0f}%{avgentry:>9.3f}{nwin_fill:>9}"
          f"{fillroi:>+8.1f}{nwin_miss:>7}{units:>+10.2f}{vs:>+8.2f}")

print("\nnetUnits = total flat-stake profit over the WHOLE signal set "
      "(unfilled = 0). vsMkt = vs the N=0 market-at-ask baseline.")
