import lab, rules as Q, rotmap as R, math, json, copy R.INDEX_NEED = 1 R.RISK_MA = 50 # baseline; the script sets its own risk series where it matters px = json.load(open(lab.ST + "/px.json")); fd = json.load(open(lab.ST + "/fund.json")) def build(px, n=100, band=0.01, top=0.70, wash=0.05): R.CLOCK_TOP, R.CLOCK_WASH = top, wash M = R.Model(px, fd); M._risk = Q.trend_state(M.idx["BTC"], n, band); return M def run(M, a="2020-06-01", z="2024-06-30", fee=None, lag=1): if fee is not None: R.FEE = fee g = M.book_rule()[0] if lag > 1: g0 = g; g = lambda i: g0(i - (lag - 1)) if i - (lag - 1) >= 0 else {} s = R.stats(M.backtest(g, M.pos[a], M.pos[z] if z else M.N - 1)[0]); R.FEE = 0.003 return "%+6.0f%% dd %4.0f%%" % (s["total"], s["maxdd"]) P = [("2020-06-01", "2024-06-30"), ("2024-07-01", None)] def both(M, **k): return " | ".join(run(M, a, z, **k) for a, z in P) print("PARAMS (100d b1% risk)") for top in (0.6, 0.7, 0.8): for wash in (0.03, 0.05, 0.10): print(" top %.2f wash %.2f %s" % (top, wash, both(build(px, top=top, wash=wash)))) M = build(px) print("COSTS fee 1%%: %s" % both(M, fee=0.01)) print(" fee 2%%: %s" % both(M, fee=0.02)) print("LAG 2 days: %s" % both(M, lag=2)) print(" 4 days: %s" % both(M, lag=4)) # survivorship stress: drop each basket's best performers over 2020-06..2021-12 print("SURVIVORSHIP STRESS: remove the top performers of 2020-21 from every alt basket") for k in (1, 2, 3): p2 = copy.deepcopy(px) for b in R.ALT_BASKETS: mem = [s for s in R.BASKETS[b] if s in px and len(R.BASKETS[b]) > 1] def perf(s): d = sorted(d for d in px[s] if "2020-06-01" <= d <= "2021-12-31") return px[s][d[-1]] / px[s][d[0]] if len(d) > 60 else 0 for s in sorted(mem, key=perf)[-k:]: if perf(s) > 0: p2.pop(s, None) M2 = build(p2) hold_alts = R.stats(M2.backtest(lambda i: Q.alts_w(M2, i), M2.pos["2020-06-01"], M2.pos["2024-06-30"])[0]) print(" drop top %d each: book %s (alts buy-and-hold holdout %+.0f%%)" % (k, both(M2), hold_alts["total"]))