"""v3frag.py (A1.14): fragility of the A1.13 rule (v3 WETH, mc $250k-1M at +5 min, $100, hold 2 h, stop 0.5x, no TP).
(1) day bootstrap, (2) worst rolling 3 days, (3) burns after entry on the top coins, (4) entry +4/+6 min and band $200k-$1.2M."""
import random, collections, statistics as st, datetime, numpy as np
exec(open("/root/fwa1/v3simrep.py").read().split("alld = sorted")[0])
def series_t(p, usd, te):
    pth, pull, pf, sup = CACHE[p["pool"]]
    i = state(pth, te)
    if i is None: return None
    L = Lat(pth, i)
    if not L: return None
    eu = p["eu"]; gas = 0.05 / eu; x = usd / eu
    mc = pth[i][1] ** 2 * sup * eu
    q = buy(pth[i][1], L, x * (1 - pf) * 0.99 - gas)
    T = []; M = []
    for k in range(i + 1, len(pth)):
        Lk = Lat(pth, k)
        if not Lk or pth[k][3] < 0.01: continue
        out = min(sell(pth[k][1], Lk, q), 0.9 * pth[k][4])
        T.append(pth[k][0]); M.append(max(0, out * (1 - pf) * 0.99 - gas) / x)
    return np.array(T), np.array(M), pull, (sell(pth[i][1], L, q) * (1 - pf) * 0.99 - gas) / x, mc
def tr(s, sl=0.5, tstop=7200):
    T, M, pull, m0, mc = s
    ok = T <= tstop; Tn, Mn = T[ok], M[ok]
    hit = Mn <= sl if sl else np.zeros(len(Mn), bool)
    if hit.any():
        k = int(np.argmax(hit)); t = Tn[k]
        if pull is not None and pull <= t + 3: return 0.0, "pull", pull
        j = max(k, int(np.searchsorted(T, t + 3, "right")) - 1); return float(M[j]), "sl", t
    if pull is not None and pull <= tstop: return 0.0, "pull", pull
    return (float(Mn[-1]) if len(Mn) else m0), "time", tstop
SC = {}
def book(te, lo, hi, usd=100):
    R = []
    for p in pools:
        k = (p["pool"], usd, te)
        if k not in SC: SC[k] = series_t(p, usd, te)
        s = SC[k]
        if not s or not (lo <= s[4] < hi): continue
        v, why, t = tr(s); R.append((p, v, why, t))
    return R
def stats(R, label):
    byd = collections.defaultdict(lambda: [0.0, 0])
    for p, v, why, t in R: byd[day(p["t"])][0] += (v - 1) * 100; byd[day(p["t"])][1] += 1
    days = sorted(byd)
    for h, sel_ in (("H1", lambda d: d < day(CUT)), ("H2", lambda d: d >= day(CUT)), ("all", lambda d: True)):
        Rh = [r for r in R if sel_(day(r[0]["t"]))]
        if not Rh: continue
        v = [r[1] for r in Rh]
        print("  %s %s n%d mean %.2fx med %.2fx drop-top3 %.2fx $%.0f total" % (label, h, len(v), st.mean(v), st.median(v), st.mean(sorted(v)[:-3]) if len(v) > 3 else 0, sum(x - 1 for x in v) * 100))
    return byd, days
random.seed(7)
print("== base rule +5 min $250k-1M, stop 0.5x, hold 2 h, $100")
R = book(300, 2.5e5, 1e6); byd, days = stats(R, "base")
print("  per day $:", " ".join("%s:%+.0f(%d)" % (d, byd[d][0], byd[d][1]) for d in days))
for h, dd in (("H1", [d for d in days if d < day(CUT)]), ("H2", [d for d in days if d >= day(CUT)]), ("all", days)):
    under = 0; res = []
    for _ in range(1000):
        smp = [random.choice(dd) for _ in dd]
        pnl = sum(byd[d][0] for d in smp); cost = sum(byd[d][1] for d in smp) * 100
        m = 1 + pnl / cost if cost else 1; res.append(m); under += m < 1
    res.sort()
    print("  bootstrap %s (%d days): under 1.0x in %.1f%% of 1000, 5th pct %.2fx, median %.2fx" % (h, len(dd), under / 10, res[50], res[500]))
dl = [datetime.date(2026, int(d[:2]), int(d[3:])) for d in days]
allday = [dl[0] + datetime.timedelta(k) for k in range((dl[-1] - dl[0]).days + 1)]
pn = {d: byd[days[dl.index(d)]][0] if d in dl else 0 for d in allday}
roll = [(sum(pn[allday[k + j]] for j in range(3)), allday[k]) for k in range(len(allday) - 2)]
roll.sort()
print("  worst rolling 3 days $:", ["%s %+.0f" % (d.strftime("%d %b"), v) for v, d in roll[:3]], " best:", ["%s %+.0f" % (d.strftime("%d %b"), v) for v, d in roll[-2:]])
print("  worst single day $: %+.0f" % min(v[0] for v in byd.values()))
print("== burns after entry on top coins")
Rs = sorted(R, key=lambda r: -r[1])[:8]
for p, v, why, t in Rs:
    pool, tok, is0, fee, cb, sup, dec = meta[p["pool"]]; fb = p["fb"]
    added = 0.0; rows = []
    for b, li, kind, data in D.execute("SELECT b, li, kind, data FROM lp WHERE pool=? ORDER BY b, li", (pool,)):
        w = words(data); amt = (w[3] if is0 else w[2]) / 1e18 if kind == "m" else (w[2] if is0 else w[1]) / 1e18
        liq = w[1] if kind == "m" else w[0]
        s = (b - fb) * SPB
        if kind == "m": added += liq
        elif s > 300: rows.append("%ds %.2f ETH out (%.0f%% of liquidity)" % (s, amt, 100 * liq / added if added else 0))
    print("  %s x%.1f burns after entry: %s" % (pool, v, rows or "none"))
print("== robustness grid (entry time x band), $100, stop 0.5x, hold 2 h")
for te in (240, 300, 360):
    for lo, hi in ((2e5, 1e6), (2.5e5, 1e6), (2.5e5, 1.2e6), (2e5, 1.2e6), (3e5, 1e6), (2.5e5, 8e5)):
        stats(book(te, lo, hi), "+%dm %dk-%dk" % (te // 60, lo / 1e3, hi / 1e3))
