"""Charge favourites more than draws: does the book balance, or just stop filling?

Favourites fill at 49.6% and draws at 17.7% (queue-aware, at the touch). That
imbalance is the whole loss mechanism: the favourite goes first, the draw never
comes, and we are left short the leg most likely to win. Today that cost -78c
against completions worth +2 to +4c.

If favourites fill twice as easily, we are underpricing them. Charging a premium
on the favourite alone would, in principle, do two good things at once: collect
more, and slow the favourite down so the legs finish together instead of the
easy one running ahead.

The counter-argument is the strongest single result in the fill data: cents
above the touch is the best predictor of not filling (AUC 0.690). A premium may
simply stop the favourite filling at all, which does not balance the book, it
empties it.

So measure fill rate against premium SEPARATELY BY LEG TYPE. The question is
whether the favourite's fill curve is flatter than the draw's -- if it is, there
is a premium that equalises them; if both collapse together, there is not.

Queue-aware fills from real Kalshi prints throughout.
"""
import csv, collections, json, statistics, sys

sys.path.insert(0, r'E:\soccer\main')
sys.path.insert(0, r'E:\soccer\research')
import mm_settle as S
import mm_maker_live as ML
from mm_volatility_clean import load_legs, CACHE


def _f(x, lo=0.0, hi=1.0):
    try:
        v = float(x)
        return v if lo < v < hi else None
    except (TypeError, ValueError):
        return None


def load_cols():
    """{leg: (touch, our_ask, side)} at the first quote with depth logged."""
    out = {}
    with open(r'E:\soccer\research\mm_paper_quotes.csv',
              encoding='utf-8', errors='replace') as fh:
        for r in csv.DictReader(fh):
            t = r.get('leg_ticker')
            if not t or t in out or not (r.get('ask_size') or '').strip():
                continue
            k, o = _f(r.get('k_ask')), _f(r.get('our_ask'))
            side = (r.get('side') or '').lower()
            if k and o and side in ('home', 'draw', 'away'):
                out[t] = (k, o, side)
    return out


def filled_at(trades, key, price, queue, ko):
    if key not in trades:
        return None
    cum = 0.0
    for ts_s, p, c in trades[key]:
        ts = S._pt(ts_s)
        if not ts:
            break
        if (ts.tzinfo is None) != (ko.tzinfo is None):
            ts = ts.astimezone() if ts.tzinfo else ts.replace(tzinfo=ko.tzinfo)
        if ts >= ko:
            break
        if p >= price - 1e-9:
            cum += c
            if cum > queue:
                return True
    return False


def main():
    legs, cols = load_legs(), load_cols()
    trades = json.load(open(CACHE, encoding='utf-8'))

    # For each leg, simulate resting at touch + premium, for several premiums.
    prems = (0, 1, 2, 3)
    rows = []
    for t, v in legs.items():
        if t not in cols:
            continue
        touch, _, side = cols[t]
        key = f"{t}|{v['ts'].isoformat()}"
        if key not in trades:
            continue
        r = {'side': side, 'touch': touch}
        ok = True
        for c in prems:
            px = touch + c / 100.0
            if px >= 0.99:
                ok = False
                break
            f = filled_at(trades, key, px, v['queue'], v['ko'])
            if f is None:
                ok = False
                break
            r[c] = f
        if ok:
            rows.append(r)

    if len(rows) < 200:
        print(f'only {len(rows)} legs -- too few')
        return
    print(f'{len(rows)} legs, queue-aware, priced at touch + premium\n')

    # favourite = the dearest leg of its book; draws called out separately
    print(f'{"leg type":>10} {"legs":>6} ' +
          ' '.join(f'{"+"+str(c)+"c":>8}' for c in prems))
    groups = {'favourite': lambda r: r['side'] != 'draw' and r['touch'] >= 0.45,
              'mid H/A': lambda r: r['side'] != 'draw' and r['touch'] < 0.45,
              'draw': lambda r: r['side'] == 'draw'}
    curves = {}
    for name, sel in groups.items():
        g = [r for r in rows if sel(r)]
        if len(g) < 40:
            continue
        cur = [100.0 * sum(1 for r in g if r[c]) / len(g) for c in prems]
        curves[name] = cur
        print(f'{name:>10} {len(g):>6} ' +
              ' '.join(f'{x:>7.1f}%' for x in cur))

    if 'favourite' in curves and 'draw' in curves:
        fav, draw = curves['favourite'], curves['draw']
        print(f'\n  favourite decay 0->+3c : {fav[0]:.1f}% -> {fav[-1]:.1f}% '
              f'({fav[-1]/max(0.1,fav[0]):.2f}x retained)')
        print(f'  draw at the touch      : {draw[0]:.1f}%')
        # is there a premium where the favourite matches the draw?
        match = next((c for c, x in zip(prems, fav) if x <= draw[0]), None)
        if match is not None:
            print(f'  -> favourite fill rate meets the draw\'s at +{match}c')
            print(f'     which would add {match}c to every completed book')
        else:
            print(f'  -> even at +{prems[-1]}c the favourite still fills faster '
                  f'than the draw does at the touch')

    print('\n=== what it does to the BOOK ===')
    print('  A premium only helps if books still complete. Completion needs all')
    print('  three legs, so it is the PRODUCT of the three fill rates.\n')
    if len(curves) >= 2:
        # ONLY the favourite is repriced. Indexing the mid leg at the same
        # premium (the first version of this) charges every non-draw leg and
        # so measures a different, much worse policy.
        d = curves.get('draw', [0])[0] / 100.0
        mid0 = curves.get('mid H/A', curves.get('favourite'))[0] / 100.0
        base = None
        for c, f in zip(prems, curves.get('favourite', [])):
            comp = (f / 100.0) * mid0 * d
            val = comp * (2.0 + c) * 100
            base = val if base is None else base
            print(f'   favourite +{c}c: completion ~{comp*100:>5.2f}%  '
                  f'book pays {2.0 + c:>4.1f}c  ->  {val:>6.3f}c '
                  f'per book quoted   ({val-base:+.3f}c vs touch)')
        print('')
        print('   and what it does to the STRANDING that actually cost us:')
        for c, f in zip(prems, curves.get('favourite', [])):
            # P(favourite fills but book does not complete)
            strand = (f / 100.0) * (1 - mid0 * d)
            print(f'   favourite +{c}c: P(short a favourite, book unfinished) '
                  f'{strand*100:>5.1f}%')


if __name__ == '__main__':
    main()
