"""
Fill probability: a resting limit at the line only fills if price actually trades
AT or THROUGH it. This measures, on real ticks, how often a line touch would fill
a post-only order instead of bouncing away without giving you the trade.

  python fillprob.py --data data3

For every 0.001% brick that spans the 1hr line, we look at what price does in the
next N seconds:
  fills      - price ticks to the line or beyond it in the brick's direction
  missed     - price reverses away without reaching the line again
"""
import argparse
import glob
import os
import statistics
from decimal import Decimal as D

import backtest as bt
import zec_renko_bot as z

WINDOWS = (1, 2, 5, 10, 30)      # seconds allowed for the fill


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--data", default="data3")
    ap.add_argument("--limit-files", type=int, default=3)
    a = ap.parse_args()

    files = sorted(glob.glob(os.path.join(a.data, "*.zip")))[: a.limit_files]
    ticks = []
    for f in files:
        ticks.extend(bt.iter_ticks(f))
    ticks.sort(key=lambda t: t[1])
    print(f"{len(files)} files | {len(ticks):,} ticks")

    r1h, r3 = z.Renko(D("0.001")), z.Renko(D("0.00001"))
    line, side = None, 0
    hour = None
    touches = []          # (index, direction) of bricks that touched the line
    prev = None

    for i, (price, ts) in enumerate(ticks):
        h = ts // 3_600_000
        if hour is None:
            hour = h
        elif h != hour:
            hour = h
            if prev is not None:
                for b in r1h.feed(prev):
                    line, side = b[0], b[2]
        for b in r3.feed(price):
            if line is None:
                continue
            lo, hi = min(b[0], b[1]), max(b[0], b[1])
            if lo <= line <= hi:
                touches.append((i, b[2], line))
        prev = price

    print(f"line touches: {len(touches):,}")
    if not touches:
        return

    for w in WINDOWS:
        filled = 0
        for idx, d, ln in touches:
            t0 = ticks[idx][1]
            filled_here = False
            for j in range(idx + 1, min(idx + 4000, len(ticks))):
                p, t = ticks[j]
                if t - t0 > w * 1000:
                    break
                if (d == 1 and p >= ln) or (d == -1 and p <= ln):
                    filled_here = True
                    break
            if filled_here:
                filled += 1
        print(f"  fill window {w:>2}s: {filled:>6}/{len(touches)} = {filled / len(touches) * 100:.1f}%")

    # how far price runs past the line when it does fill, in bricks of 0.1%
    runs = []
    for idx, d, ln in touches[:4000]:
        best = 0
        for j in range(idx + 1, min(idx + 3000, len(ticks))):
            p, _ = ticks[j]
            best = max(best, d * (p - ln) / ln / D("0.001"))
            if best > 40:
                break
        runs.append(best)
    if runs:
        print(f"  run past the line when filled: median {statistics.median(runs):.2f} bricks "
              f"| mean {statistics.mean(runs):.2f} | p90 {sorted(runs)[int(len(runs) * 0.9)]:.1f}")


if __name__ == "__main__":
    main()
