"""MIC04 — Consecutive-days continuation vs fade. IDEA: Compute net of last-k daily close returns (streak). - FOLLOWING: go long when streak is positive (sign = +1), flat when negative. - FADING: go long when streak is negative (mean-reversion), flat when positive. Both are long-flat. We try k in {3, 5} and compare following vs fading. Position is vol-targeted (20% target, 2x cap). Grid: 4 configs (2 k-values × 2 directions), TFs: 1d, 12h. Total backtests: 4 configs × 2 TFs × 2 assets = 16 — but we only call study_weights per config (each call does 2 TFs × 2 assets internally) → 4 calls = 16 backtests (fine). Actually we pick the best config manually. To stay <= 6 total calls we test 2 configs (k=3 follow, k=5 follow) and present the best, then also run the fading variants if promising. We run all 4 configs (each on tfs=("1d","12h")) → 4 calls, well within budget. """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt") import altlib as al import numpy as np def streak_target(df, k: int, follow: bool) -> np.ndarray: """ For each bar i, compute net of last-k close returns (causal: uses close[i-k..i]). streak[i] = close[i] / close[i-k] - 1 (sign of cumulative k-bar return) If follow=True: position = +1 when streak > 0, else 0 (long-flat continuation). If fading=True: position = +1 when streak < 0, else 0 (long-flat mean-reversion). Then vol-target the direction. """ c = df["close"].values.astype(float) n = len(c) # Cumulative k-bar return ending at i: c[i]/c[i-k] - 1 streak = np.full(n, np.nan) for i in range(k, n): streak[i] = c[i] / c[i - k] - 1.0 if follow: direction = np.where(streak > 0, 1.0, 0.0) else: direction = np.where(streak < 0, 1.0, 0.0) # Fill NaN with 0 before vol_target direction = np.nan_to_num(direction, nan=0.0) # Apply vol targeting tgt = al.vol_target(direction, df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0) return tgt configs = [ ("MIC04-k3-follow", 3, True), ("MIC04-k5-follow", 5, True), ("MIC04-k3-fade", 3, False), ("MIC04-k5-fade", 5, False), ] results = {} for name, k, follow in configs: print(f"\n{'='*60}") print(f"Running {name} (k={k}, follow={follow})") print('='*60) rep = al.study_weights( name, lambda df, k=k, follow=follow: streak_target(df, k, follow), tfs=("1d", "12h"), ) results[name] = rep print(al.fmt(rep)) # Pick best config by holdout Sharpe (min across assets in best TF) best_name = max(results, key=lambda n: results[n]["verdict"].get("best_holdout_sharpe", -99)) best_rep = results[best_name] print("\n" + "="*60) print(f"BEST CONFIG: {best_name}") print("="*60) print(al.fmt(best_rep)) print("JSON:", al.as_json(best_rep))