"""
bar_data_build.py

[2026-09-25] Data for bar_backtest.py: for each of the 20 opening-window days,
the 9:28 top-30 under two scanner rankings (the current score with FIXED
prior-session data, and the proposed |gap| + daily ATR% + RVOL ranking), with
each symbol's full-day 1-minute bars (9:30-16:00) and the levels the bot uses:
premarket high (4:00-9:28), previous-day high, 20-day high, previous-day
volume (the bot's volume baseline), daily ATR(14) and the 5-day reference.

Writes bars_<date>.json.gz next to this file (one per day).
"""
import csv
import glob
import gzip
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path
from zoneinfo import ZoneInfo

HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parents[1]))
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
from alpaca_client import get_client
import volatility

ET = ZoneInfo("America/New_York")
OUT = HERE / "bars"
OUT.mkdir(exist_ok=True)


def pct_rank(rows, key):
    s = sorted(rows, key=key)
    n = max(1, len(s) - 1)
    return {r["symbol"]: i / n for i, r in enumerate(s)}


def main():
    feats = list(csv.DictReader(open(sorted(glob.glob(str(HERE / "scanner_features_*.csv")))[-1])))
    for r in feats:
        for k in ("score", "rvol", "gap_pct", "daily_atr_pct"):
            r[k] = float(r[k]) if r[k] not in ("", None) else 0.0
    days = sorted({r["date"] for r in feats})
    client = get_client()
    for d in days:
        path = OUT / f"bars_{d}.json.gz"
        if path.exists():
            print(d, "cached"); continue
        rows = [r for r in feats if r["date"] == d]
        cur = [r["symbol"] for r in sorted(rows, key=lambda r: -r["score"])[:30]]
        rk = [pct_rank(rows, f) for f in (lambda r: abs(r["gap_pct"]), lambda r: r["daily_atr_pct"], lambda r: r["rvol"])]
        alt = [r["symbol"] for r in sorted(rows, key=lambda r: -sum(x[r["symbol"]] for x in rk))[:30]]
        syms = sorted(set(cur) | set(alt))
        dd = datetime.fromisoformat(d).replace(tzinfo=ET)
        daily = client.get_daily_bars_bulk(syms, dd - timedelta(days=45), dd - timedelta(seconds=1))
        mins = {}
        for i in range(0, len(syms), 50):
            req = StockBarsRequest(symbol_or_symbols=syms[i:i + 50], timeframe=TimeFrame(1, TimeFrameUnit.Minute),
                                   start=dd.replace(hour=4), end=dd.replace(hour=16), limit=None, feed=client._feed)
            mins.update(client.hist_data.get_stock_bars(req).data)
        data = {}
        for s in syms:
            hist = [b for b in daily.get(s, []) if b["t"] < dd]
            bl = mins.get(s, [])
            pm = [b for b in bl if b.timestamp.astimezone(ET).strftime("%H:%M") < "09:28"]
            reg = [b for b in bl if "09:30" <= b.timestamp.astimezone(ET).strftime("%H:%M") < "16:00"]
            if len(hist) < 2 or len(reg) < 10:
                continue
            data[s] = {
                "bars": [[int(b.timestamp.timestamp()), float(b.open), float(b.high), float(b.low),
                          float(b.close), float(b.volume)] for b in reg],
                "levels": {"premarket_high": max((float(b.high) for b in pm), default=None),
                           "prev_day_high": hist[-1]["h"],
                           "range_20d_high": max(b["h"] for b in hist[-20:]),
                           "daily_atr": volatility.daily_atr(hist),
                           **volatility.five_day_reference(hist)},
                "baseline": hist[-1]["v"],
            }
        with gzip.open(path, "wt") as f:
            json.dump({"date": d, "top30_current": [s for s in cur if s in data],
                       "top30_alt": [s for s in alt if s in data], "symbols": data}, f)
        print(d, len(syms), "symbols, kept", len(data), flush=True)


if __name__ == "__main__":
    main()
