import lab, rules as Q, rotmap as R, math, json, datetime as dt, statistics as st R.INDEX_NEED = 1 R.RISK_MA = 50 # baseline; the script sets its own risk series where it matters M = lab.load(); M._risk = Q.trend_state(M.idx["BTC"], 100, 0.01) S = {dt.datetime.utcfromtimestamp(int(r["date"])).strftime("%Y-%m-%d"): r["totalCirculatingUSD"].get("peggedUSD") for r in json.load(open("stables.json"))} sc = [S.get(d) for d in M.days] for i in range(1, M.N): if sc[i] is None: sc[i] = sc[i - 1] def g(i, n): return (sc[i] / sc[i - n] - 1) if i >= n and sc[i] and sc[i - n] else None P = [("2020-06-01", "2024-06-30"), ("2024-07-01", None)] def both(rule): out = [] for a, z in P: s = R.stats(M.backtest(rule(), M.pos[a], M.pos[z] if z else M.N - 1)[0]); out.append("%+6.0f%% dd %4.0f%%" % (s["total"], s["maxdd"])) return " | ".join(out) print("LIQUIDITY LAYER (stablecoin supply growth) as an extra condition") print(" G on 100d (base) ", both(lambda: M.book_rule()[0])) for n in (30, 60, 90): base = list(M._risk) M._risk = [base[i] and (g(i, n) or 0) > 0 for i in range(M.N)] print(" G, risk also needs stables %2dd > 0" % n, both(lambda: M.book_rule()[0])) M._risk = base # how often is stables growth negative while BTC>100d? informative neg = sum(1 for i in range(M.pos["2020-06-01"], M.N) if M._risk[i] and (g(i, 60) or 0) <= 0) print(" days risk-on with stables 60d shrinking:", neg) # forward alt vs BTC return by stablecoin growth tercile print("\nSTABLES 60d growth vs next 60d ALTS minus BTC (alts basket)") A = lab.alts_idx(M) rows = [] for i in range(M.pos["2020-06-01"], M.N - 60, 5): x = g(i, 60) if x is None or not A[i] or not A[i + 60]: continue rows.append((x, math.log(A[i + 60] / A[i]) - math.log(M.idx["BTC"][i + 60] / M.idx["BTC"][i]), M.days[i])) rows.sort() k = len(rows) // 3 for lab_, part in (("low", rows[:k]), ("mid", rows[k:2*k]), ("high", rows[2*k:])): for per, f in (("hold", lambda d: d < "2024-07-01"), ("recent", lambda d: d >= "2024-07-01")): v = [r[1] for r in part if f(r[2])] if v: print(" %-4s tercile %-6s n=%3d median alts-vs-BTC %+5.1f%% win %2.0f%%" % (lab_, per, len(v), 100 * (math.exp(st.median(v)) - 1), 100 * sum(x > 0 for x in v) / len(v))) # TRENCH window back-test on the MEMES basket (tradeable proxy, 2020+) print("\nTRENCH WINDOW PROXY: BTC 7d crosses +10%, then MEMES vs BTC over 14d") b = M.idx["BTC"]; mm = M.idx["MEMES"] r7 = [(b[i] / b[i - 7] - 1) if i >= 7 and b[i] and b[i - 7] else None for i in range(M.N)] ev = [i for i in range(M.pos["2020-06-01"], M.N - 14) if r7[i] and r7[i - 1] is not None and r7[i] >= 0.10 > r7[i - 1]] ev2 = [] for i in ev: if not ev2 or i - ev2[-1] > 14: ev2.append(i) res = [] for i in ev2: if mm[i] and mm[i + 14]: rel = (mm[i + 14] / mm[i]) / (b[i + 14] / b[i]) - 1 res.append((M.days[i], rel, mm[i + 14] / mm[i] - 1)) for per, f in (("hold", lambda d: d < "2024-07-01"), ("recent", lambda d: d >= "2024-07-01")): v = [r for r in res if f(r[0])] print(" %-6s n=%2d memes beat BTC %d/%d median rel %+.1f%% memes up %d/%d" % (per, len(v), sum(r[1] > 0 for r in v), len(v), 100 * st.median([r[1] for r in v]), sum(r[2] > 0 for r in v), len(v))) # base rate: any random 14d window base = [(mm[i + 14] / mm[i]) / (b[i + 14] / b[i]) - 1 for i in range(M.pos["2020-06-01"], M.N - 14) if mm[i] and mm[i + 14]] print(" base rate: memes beat BTC in any 14d window %.0f%%, median %+.1f%%" % (100 * sum(x > 0 for x in base) / len(base), 100 * st.median(base)))