import json, math, collections, statistics as S
from eng import *
KW = dict(solbtc_froth=0.20, breadth=0.25)
def weights(kw):
    W = {}; ENT = {}
    for k in KEYS:
        ds, L = SERIES[k]; w = {}; e = {}
        for t in run(k, **kw):
            i0 = ds.index(t['entry']); i1 = ds.index(t['exit'])
            # replay half: use stage HOT
            st = STAGES[k]; cur = 1.0
            for j in range(i0, i1):
                if j > i0 and cur == 1.0 and st[j][0] == 'HOT' and kw.get('half', 'hot') == 'hot': cur = 0.5
                w[ds[j]] = cur; e[ds[j]] = t['entry']
        W[k] = w; ENT[k] = e
    return W, ENT
RET = {k: {SERIES[k][0][i]: SERIES[k][1][i + 1] / SERIES[k][1][i] - 1 for i in range(len(SERIES[k][1]) - 1)} for k in KEYS}
R30 = {k: {SERIES[k][0][i]: SERIES[k][1][i] / SERIES[k][1][i - 30] - 1 for i in range(30, len(SERIES[k][1]))} for k in KEYS}
days = sorted({d for k in KEYS for d in SERIES[k][0]})
def sim(W, ENT, K=3, rank='r30', mode='any', chain_cap=None, start='2023-01-01'):
    eq = 0; pk = 0; mdd = 0; prev = {}; yr = collections.defaultdict(float); held = {}; ex = []
    for d in days:
        if d < start: continue
        opn = {k: W[k][d] for k in KEYS if W[k].get(d, 0) > 0 and d in RET[k]}
        if mode == 'buy_only':
            # keep what we hold while it is open; add new only on its entry day
            held = {k: v for k, v in held.items() if k in opn}
            for k in sorted(opn, key=lambda k: -R30[k].get(d, -9)):
                if len(held) >= K: break
                if k not in held and ENT[k].get(d) == d: held[k] = 1
            sel = list(held)
        else:
            cand = sorted(opn, key=lambda k: -(R30[k].get(d, -9) if rank == 'r30' else -((dt.date.fromisoformat(d) - dt.date.fromisoformat(ENT[k][d])).days)))
            sel = []; cc = collections.Counter()
            for k in cand:
                if chain_cap and cc[CHAIN[k]] >= chain_cap: continue
                sel.append(k); cc[CHAIN[k]] += 1
                if len(sel) >= K: break
        wt = {k: opn[k] / K for k in sel}
        turn = sum(abs(wt.get(k, 0) - prev.get(k, 0)) for k in set(wt) | set(prev))
        r = sum(x * RET[k][d] for k, x in wt.items()) - turn * 0.005; prev = wt
        lr = math.log(1 + r); eq += lr; yr[d[:4]] += lr; pk = max(pk, eq); mdd = min(mdd, eq - pk); ex.append(sum(wt.values()))
    return dict(total=round((math.exp(eq) - 1) * 100), worst=round((math.exp(mdd) - 1) * 100), money_in=round(S.mean(ex) * 100),
                by_year={y: round((math.exp(v) - 1) * 100) for y, v in yr.items()})
Wb, Eb = weights({}); W5, E5 = weights(KW)
out = {}
for nm, W, E, a in [("47 base rule, 3 strongest 30d", Wb, Eb, {}), ("48 new rule (35), 3 strongest 30d", W5, E5, {}),
                    ("49 new rule, 3 freshest (newest risk on)", W5, E5, dict(rank='fresh')),
                    ("50 new rule, money only on BUY day, hold till off", W5, E5, dict(mode='buy_only')),
                    ("51 new rule, 3 strongest, max 2 per chain", W5, E5, dict(chain_cap=2)),
                    ("52 new rule, 2 strongest", W5, E5, dict(K=2)), ("53 new rule, 4 strongest", W5, E5, dict(K=4))]:
    r = sim(W, E, **a); out[nm] = r; print(nm, r, flush=True)
b = {}; e = 0; pk = 0; mdd = 0; bd = sorted(BTC)
yb = collections.defaultdict(float)
for i in range(len(bd) - 1):
    if bd[i] < '2023-01-01': continue
    lr = math.log(BTC[bd[i + 1]] / BTC[bd[i]]); e += lr; yb[bd[i][:4]] += lr; pk = max(pk, e); mdd = min(mdd, e - pk)
out['BTC hold'] = dict(total=round((math.exp(e) - 1) * 100), worst=round((math.exp(mdd) - 1) * 100), by_year={y: round((math.exp(v) - 1) * 100) for y, v in yb.items()})
print('BTC hold', out['BTC hold'])
json.dump(out, open('res_c.json', 'w'))
