"""
orb_test.py -- [2026-10-03] Opening-range breakout from the user's uploaded bot.py, tested on our
library (reports/open_window/bars, every day's 9:30 top-30 small caps, ~503 days).
  OR     = high/low of 9:30-9:44
  LONG   first 1-min close above the OR high AND above VWAP, gap up (open > prev close),
         RVOL so far >= R x the 14-day normal (per-minute profile), OR range >= 0.15%;
         entries 9:45-11:29 and 13:30-15:15 (their schedule) or 9:45-15:15 (no lunch block)
         buy at the next minute's open; one trade per stock per day
  EXITS  "theirs" stop 1.5 x daily ATR, target 2R, else 15:55
         "or_low" stop at the OR low, target 2R, else 15:55
         "or_mid" stop at the OR middle, no target, 15:55
  cost 0.1% per trade; results by halves of the period
    python3 orb_test.py
"""
import gzip
import json
import sys
from pathlib import Path

import numpy as np

HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE)); sys.path.insert(0, "/var/www/screener/trade")
import lib_v2 as L2
import reference as REF

COST = 0.1


def run_trade(a, m, stop, target):
    e = a["O"][m + 1]
    for k in range(m + 1, 385):
        if a["L"][k] <= stop:
            return (min(stop, a["O"][k]) / e - 1) * 100 - COST, k
        if target and a["H"][k] >= target:
            return (max(target, a["O"][k]) / e - 1) * 100 - COST, k
    return (a["C"][385] / e - 1) * 100 - COST, 385


def main():
    res = {}
    files = sorted(L2.BARS.glob("bars_20*.json.gz"))
    for p in files:
        d = json.load(gzip.open(p, "rt")); day = d["date"]
        for s in d["top30_current"]:
            ref = REF.load(day, s)
            if not ref or not ref.get("prev_close"):
                continue
            a = L2.day_arrays(d["symbols"][s]["bars"], ref, day, (np.zeros(390), np.zeros(390)))
            if a is None:
                continue
            o = a["o"]
            orh, orl = a["H"][:15].max(), a["L"][:15].min()
            if (orh - orl) / o * 100 < 0.15 or o <= ref["prev_close"]:
                continue
            by = L2._minutes(d["symbols"][s]["bars"])
            pv = cv = 0.0; cum = norm = 0.0
            for m in range(390):
                b = by.get(m)
                if b:
                    pv += (b[2] + b[3] + b[4]) / 3 * b[5]; cv += b[5]; cum += b[5]
                norm += max(ref["vol_per_min"][m], 1)
                if m < 15 or m > 344:
                    continue
                c = a["C"][m]; vw = pv / cv if cv else c
                if not (c > orh and c > vw):
                    continue
                rv = cum / norm
                for R in (1.3, 2.0):
                    if rv < R:
                        continue
                    for sched in ("theirs", "nolunch"):
                        if sched == "theirs" and 120 <= m < 240:
                            continue
                        e = a["O"][m + 1]
                        atr = ref.get("atr14") or 0
                        for ex in ("theirs", "or_low", "or_mid"):
                            key = (R, sched, ex)
                            if (day, s) in res.setdefault(key, {}):
                                continue
                            if ex == "theirs":
                                if not atr:
                                    continue
                                stop = e - 1.5 * atr; tgt = e + 3 * atr
                            elif ex == "or_low":
                                stop = orl; tgt = e + 2 * (e - orl)
                            else:
                                stop = (orh + orl) / 2; tgt = None
                            if stop >= e:
                                continue
                            res[key][(day, s)] = run_trade(a, m, stop, tgt)[0]
                break_ = all((day, s) in res.get((R, sc, ex), {}) for R in (1.3, 2.0) for sc in ("theirs", "nolunch") for ex in ("or_low", "or_mid"))
                if break_:
                    break
        REF._CACHE.clear()
    days = sorted({p.name[5:15] for p in files}); half = days[len(days) // 2]
    print(f"{len(days)} days {days[0]}..{days[-1]}, top-30 lists, gap-up only, 0.1% cost")
    for key in sorted(res):
        v = res[key]; p = np.array(list(v.values()))
        h = np.array([k[0] < half for k in v])
        print(f"RVOL>={key[0]} sched={key[1]:7} exit={key[2]:6}: {len(p):5} trades, win {(p > 0).mean():.0%}, avg {p.mean():+.3f}%, "
              f"sum {p.sum():+.0f}% (halves {p[h].sum():+.0f}% / {p[~h].sum():+.0f}%)")


if __name__ == "__main__":
    main()
