"""What premium over the touch maximises EXPECTED BOOK VALUE?

The maker's payoff is not per leg. A book pays only when all three legs sell,
and it then collects the overround plus whatever premium each leg was sold
above the touch. So the quantity to maximise is

    E[book] = P(all three fill at premium d) x (overround + 3d)

Quoting higher raises the second term and destroys the first. The live ladder
picks d by decaying it with time-to-kickoff; nothing has ever measured the
curve it is walking down.

MEASURED FROM THE TRADE TAPE, not from our own fills. For each leg we fetch
every print between posting and kickoff and take the maximum. A resting ask at
price P would have been lifted iff some print landed at or above P, so one tape
read gives the fill/no-fill answer at EVERY premium at once -- the actual arm
the quoter happened to choose is irrelevant.

THE CEILING CAVEAT STILL APPLIES: a print at or above our price means we could
have been hit, not that we would have been. Queue priority is assumed, exactly
as mm_settle assumes it. Read the SHAPE of the curve, not its level.

Tape reads are cached to research/.tape_cache.json, so re-runs are free.

Usage:  python research\\touch_premium_curve.py
"""
import csv
import datetime as dt
import json
import shutil
import sys
import tempfile
from collections import defaultdict
from pathlib import Path

sys.path.insert(0, r'E:\soccer\main')
import mm_settle as MS

QUOTES = Path(r'E:\soccer\research\mm_paper_quotes.csv')
CACHE = Path(r'E:\soccer\research\.tape_cache.json')
PREMIUMS = [0, 1, 2, 3, 4, 5]


def _f(x):
    try:
        return float(x)
    except (TypeError, ValueError):
        return None


def first_rows():
    """First genuine quote per leg (action rows carry a `reason`)."""
    tmp = Path(tempfile.gettempdir()) / 'mm_paper_quotes.snap.csv'
    shutil.copyfile(QUOTES, tmp)
    first = {}
    with tmp.open(newline='', encoding='utf-8', errors='replace') as fh:
        for r in csv.DictReader(fh):
            t = r.get('leg_ticker')
            if t and t not in first and not (r.get('reason') or ''):
                first[t] = r
    return first


def main():
    cache = json.loads(CACHE.read_text()) if CACHE.exists() else {}
    first = first_rows()
    print(f'{len(first)} legs; {len(cache)} tape reads cached')

    legs, n_new = {}, 0
    for i, (tkr, r) in enumerate(sorted(first.items()), 1):
        ts, ko = MS._pt(r.get('ts')), MS._pt(r.get('kickoff'))
        ka, over = _f(r.get('k_ask')), _f(r.get('over_ask'))
        if ts is None or ko is None or ka is None or over is None:
            continue
        if not (0 < ka < 1) or not (0 < (ko - ts).total_seconds() / 3600.0 <= 24):
            continue
        if tkr in cache:
            mx = cache[tkr]
        else:
            trades = MS.trades_tuples(tkr, ts)
            pre = [p for t, p in trades if t <= ko]
            mx = max(pre) if pre else 0.0
            cache[tkr] = mx
            n_new += 1
            if n_new % 25 == 0:
                CACHE.write_text(json.dumps(cache))
                print(f'  ... {n_new} new tape reads ({i}/{len(first)})')
        legs[tkr] = {'event': r.get('event'), 'touch': ka, 'max': mx,
                     'over': over, 'regime': r.get('regime') or '?'}
    CACHE.write_text(json.dumps(cache))
    print(f'{len(legs)} legs usable, {n_new} new reads\n')

    books = defaultdict(list)
    for v in legs.values():
        books[v['event']].append(v)
    full = {e: v for e, v in books.items() if len(v) == 3}
    print(f'{len(full)} three-leg books\n')
    if not full:
        return

    print(f'{"premium":>8} {"leg fill":>9} {"books complete":>15} '
          f'{"collected":>10} {"E[book]":>9}')
    for d in PREMIUMS:
        legfill = sum(1 for v in legs.values() if v['max'] >= v['touch'] + d / 100.0)
        comp, val = 0, 0.0
        for e, v3 in full.items():
            if all(x['max'] >= x['touch'] + d / 100.0 for x in v3):
                comp += 1
                over_c = (sum(x['touch'] for x in v3) - 1.0) * 100.0
                val += over_c + 3 * d
        n = len(full)
        ev = val / n
        print(f'{d:>7}c {100.0*legfill/len(legs):>8.0f}% '
              f'{comp:>7} / {n:<5} {100.0*comp/n:>3.0f}% '
              f'{val:>9.0f}c {ev:>8.2f}c')
    print('\nE[book] is per book QUOTED, not per book completed -- the number a')
    print('quoting policy actually earns. Fills assume queue priority (CEILING).')


if __name__ == '__main__':
    main()
