"""v3simrep.py: grid over the A1.13 rule, pick on first half, test on second half. Vectorised: per pool the net sell multiple at every swap."""
import sys, pickle, collections, statistics as st, datetime, numpy as np
exec(open("/root/fwa1/v3sim.py").read().split("CACHE = {}")[0])
pools, CACHE = pickle.load(open("/root/fwa1/v3/simcache.pkl", "rb"))
def series(p, usd, lfac):
    pth, pull, pf, sup = CACHE[p["pool"]]
    i = state(pth, 300)
    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
    q = buy(pth[i][1], L * lfac, x * (1 - pf) * 0.99 - gas)
    T = []; M = []
    for k in range(i + 1, len(pth)):
        Lk = Lat(pth, k)
        if not Lk: continue
        if pth[k][3] < 0.01: continue          # only price prints from real swaps of 0.01+ ETH
        out = min(sell(pth[k][1], Lk * lfac, q), 0.9 * pth[k][4])   # never more than 90% of the WETH really in the pool
        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 * lfac, q) * (1 - pf) * 0.99 - gas) / x
SER = {}
def trade2(p, usd, tp, sl, tstop, lfac=1.0):
    key = (p["pool"], usd, lfac)
    if key not in SER: SER[key] = series(p, usd, lfac)
    s = SER[key]
    if s is None: return None
    T, M, pull, m0 = s
    ok = T <= tstop; Tn, Mn = T[ok], M[ok]
    hit = np.zeros(len(Mn), bool)
    if tp: hit |= Mn >= tp
    if sl: hit |= Mn <= sl
    if hit.any():
        k = int(np.argmax(hit)); t = Tn[k]
        if pull is not None and pull <= t + 3: return dict(v=0.0, why="pull", t=pull)
        j = max(k, int(np.searchsorted(T, t + 3, "right")) - 1)
        return dict(v=float(M[j]), why="tp" if tp and Mn[k] >= tp else "sl", t=t)
    if pull is not None and pull <= tstop: return dict(v=0.0, why="pull", t=pull)
    return dict(v=float(Mn[-1]) if len(Mn) else m0, why="time", t=tstop)
def day(ts): return datetime.datetime.utcfromtimestamp(ts + 7 * 3600).strftime("%m-%d")
def sel(p, lo, hi, filt):
    f = p["feats"][300]
    if not (lo <= f["mc"] < hi): return False
    if filt and not (f["nnew"] >= 20 and f["rep1"] < 0.05): return False
    return True
EX = [(tp, sl, ts) for tp in (1.5, 2, 3, None) for sl in (0.6, 0.5, None) for ts in (3600, 7200)]
RNG = [(2.5e5, 1e6), (1e5, 2.5e5), (2.5e5, 5e5), (5e5, 1e6), (1e5, 1e6)]
def run(lo, hi, filt, ex, usd, half, lfac=1.0):
    R = []
    for p in pools:
        if half and (p["t"] < CUT) != (half == 1): continue
        if not sel(p, lo, hi, filt): continue
        r = trade2(p, usd, *ex, lfac=lfac)
        if r: R.append((p, r))
    return R
def summ(R, ndays):
    if not R: return "n0"
    m = [r["v"] for p, r in R]; byd = collections.defaultdict(float)
    for p, r in R: byd[day(p["t"])] += r["v"] - 1
    wh = collections.Counter(r["why"] for p, r in R)
    return "n%d %.1f/day mean %.2fx med %.2fx win %d%% green %d/%d %s" % (len(m), len(m) / ndays, st.mean(m), st.median(m),
        100 * sum(v > 1 for v in m) / len(m), sum(v > 0 for v in byd.values()), len(byd), dict(wh))
alld = sorted({day(p["t"]) for p in pools}); d1 = [d for d in alld if d < day(CUT)]; d2 = [d for d in alld if d >= day(CUT)]
print("days", len(d1), len(d2), "pools", len(pools), flush=True)
for usd in (100, 500):
    print("== $%d" % usd)
    res = []
    for lo, hi in RNG:
        for filt in (0, 1):
            for ex in EX:
                R1 = run(lo, hi, filt, ex, usd, 1)
                if len(R1) < 15: continue
                res.append((st.mean([r["v"] for p, r in R1]), lo, hi, filt, ex, len(R1)))
    res.sort(key=lambda x: -x[0])
    for mn, lo, hi, filt, ex, n in res[:5]:
        print("H1 best %.2fx n%d mc %dk-%dk filt%d ex%s" % (mn, n, lo / 1e3, hi / 1e3, filt, ex))
    mn, lo, hi, filt, ex, n = res[0]
    print(" pick ->", lo, hi, filt, ex)
    print("  H1:", summ(run(lo, hi, filt, ex, usd, 1), len(d1)))
    print("  H2:", summ(run(lo, hi, filt, ex, usd, 2), len(d2)))
    print("  H2 half depth:", summ(run(lo, hi, filt, ex, usd, 2, 0.5), len(d2)))
    print(" supervisor base (250k-1M) both halves:")
    for filt in (0, 1):
        for ex in EX:
            a = run(2.5e5, 1e6, filt, ex, usd, 1); b = run(2.5e5, 1e6, filt, ex, usd, 2)
            if a and b: print("  f%d %s | H1 %s | H2 %s" % (filt, ex, summ(a, len(d1)), summ(b, len(d2))), flush=True)
