"""Test the two remaining ideas: leg the draw first, and raise the gate margin.

Rebates are settled by research rather than code -- Kalshi's open Liquidity
Incentive Program excludes the Sports category outright, and the Liquidity
Provider Program that does cover sports needs an executed Market Maker Agreement
and an auction. Neither is reachable here. That was the lever with real
magnitude, so what is left is the two policy changes.

  DRAW FIRST   Offer only the draw; add home and away once it fills. Legs fill
               independently (0.352^3 = 4.4% against 3.8% observed), so
               completion should not move. What changes is WHAT WE ARE LEFT
               HOLDING when it fails -- never a stranded favourite, which is
               where the live -78c came from.

  MARGIN       The gate needs sum > 1.00 + fees + TARGET_MARGIN. AJAHEE opened
               on 0.63c of margin and had nothing to absorb a goal. Raising the
               bar refuses those, at the cost of volume.

Both scored the same way: queue-aware fills from real trade prints, completions
priced by the gate and ratchet, and PARTIALS CARRIED TO SETTLEMENT. Scoring
completions only is what made the strategy look profitable earlier today.
"""
import csv, collections, json, os, 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
from mm_partial_pnl import load_meta, load_results, WINS


def fills_for(legs, meta, trades):
    """{leg_ticker: bool} queue-aware, priced at the touch."""
    out = {}
    for t, v in legs.items():
        px = meta[t][0]
        key = f"{t}|{v['ts'].isoformat()}"
        if key not in trades:
            return None
        cum, hit = 0.0, False
        for ts_s, p, c in trades[key]:
            ts = S._pt(ts_s)
            if not ts:
                break
            ko = v['ko']
            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 >= px - 1e-9:
                cum += c
                if cum > v['queue']:
                    hit = True
                    break
        out[t] = hit
    return out


def score(books, margin, draw_first):
    """(n_books, completions, total_pnl_cents, partial_legs, partial_pnl)."""
    n = done = 0
    pnl = 0.0
    plegs = 0
    ppnl = 0.0
    for ev, d in books.items():
        asks, legs, meta, filled, res = (d['asks'], d['legs'], d['meta'],
                                         d['filled'], d['res'])
        fees = sum(ML.maker_fee(a) for a in asks.values())
        if sum(asks.values()) <= 1.0 + fees + margin:
            continue                          # gate
        n += 1
        # which legs do we actually END UP short?
        if draw_first:
            draw = [t for t in legs if meta[t][1] == 'draw']
            if not draw or not filled[draw[0]]:
                # draw never filled -> we offered nothing else, so no exposure
                continue
            short = [draw[0]] + [t for t in legs
                                 if meta[t][1] != 'draw' and filled[t]]
        else:
            short = [t for t in legs if filled[t]]
        if len(short) == 3:
            done += 1
            pnl += sum(asks.values()) - fees - 1.0
            continue
        if not short or res is None:
            continue
        for t in short:
            px, side = meta[t][0], meta[t][1]
            p = px - ML.maker_fee(px) - (1.0 if WINS[res] == side else 0.0)
            pnl += p
            ppnl += p
            plegs += 1
    return n, done, pnl * 100, plegs, ppnl * 100


def main():
    legs_all, meta = load_legs(), load_meta()
    res_map = load_results()
    trades = json.load(open(CACHE, encoding='utf-8'))

    grouped = collections.defaultdict(dict)
    for t, v in legs_all.items():
        if t in meta:
            grouped[t.rsplit('-', 1)[0]][t] = v

    books = {}
    for ev, legs in grouped.items():
        if len(legs) != 3:
            continue
        if not all(meta[t][1] in ('home', 'draw', 'away') for t in legs):
            continue
        f = fills_for(legs, meta, trades)
        if f is None:
            continue
        _, _, gd, home, away = meta[next(iter(legs))]
        books[ev] = {'asks': {t: meta[t][0] for t in legs}, 'legs': list(legs),
                     'meta': meta, 'filled': f,
                     'res': res_map.get((gd, home, away))}
    print(f'{len(books)} three-leg books with fills and results\n')

    print(f'{"policy":>14} {"margin":>7} {"books":>6} {"done":>5} {"rate":>6} '
          f'{"partial legs":>13} {"partial P&L":>12} {"TOTAL":>9}')
    for draw_first in (False, True):
        lbl = 'draw first' if draw_first else 'all three'
        for margin in (0.005, 0.010, 0.015, 0.020):
            n, done, total, plegs, ppnl = score(books, margin, draw_first)
            if not n:
                continue
            print(f'{lbl:>14} {margin*100:>6.1f}c {n:>6} {done:>5} '
                  f'{100.0*done/n:>5.1f}% {plegs:>13} {ppnl:>11.1f}c '
                  f'{total:>8.1f}c')
    print('\nTOTAL includes partials carried to settlement, which is the only')
    print('honest basis -- completions alone flattered this all morning.')


if __name__ == '__main__':
    main()
