"""TRD09 — Aroon Trend Strategy Aroon(period): long when AroonUp > AroonDown AND AroonUp > 70. Uses vol-targeting (TP01-style) for position sizing. """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt") import altlib as al import numpy as np def aroon(df, period: int = 25): """Compute Aroon Up and Aroon Down (causal). AroonUp[i] = 100 * (bars since highest high in [i-period..i]) / period AroonDown[i] = 100 * (bars since lowest low in [i-period..i]) / period Both in [0, 100]. """ high = df["high"].values.astype(float) low = df["low"].values.astype(float) n = len(high) aroon_up = np.full(n, np.nan) aroon_down = np.full(n, np.nan) # Vectorized using pandas rolling argmax/argmin import pandas as pd h_series = pd.Series(high) l_series = pd.Series(low) for i in range(period, n): window_h = high[i - period: i + 1] window_l = low[i - period: i + 1] # position of max/min within window (0=oldest, period=current) idx_max = np.argmax(window_h) # periods ago = period - idx_max idx_min = np.argmin(window_l) aroon_up[i] = 100.0 * idx_max / period aroon_down[i] = 100.0 * idx_min / period return aroon_up, aroon_down def make_target(period: int = 25, threshold: float = 70.0, use_vol_target: bool = True): """Return a target function for al.study_weights.""" def target_fn(df): up, dn = aroon(df, period) # Long signal: AroonUp > AroonDown AND AroonUp > threshold direction = np.where( (up > dn) & (up > threshold), 1.0, 0.0 # flat otherwise (long-flat, no short) ) direction[~np.isfinite(up)] = 0.0 if use_vol_target: return al.vol_target(direction, df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0) else: return direction return target_fn if __name__ == "__main__": # Small grid: period x threshold (4 combos max) configs = [ {"period": 25, "threshold": 70.0}, {"period": 14, "threshold": 70.0}, {"period": 25, "threshold": 60.0}, {"period": 40, "threshold": 70.0}, ] best_rep = None best_score = -999.0 for cfg in configs: name = f"TRD09_p{cfg['period']}_t{int(cfg['threshold'])}" print(f"\n=== Running {name} ===") fn = make_target(period=cfg["period"], threshold=cfg["threshold"]) rep = al.study_weights(name, fn, tfs=("1d",)) print(al.fmt(rep)) # Score = min of BTC/ETH hold-out sharpe cells = rep.get("cells", []) if cells: cell = cells[0] # 1d pa = cell.get("per_asset", {}) btc_ho = pa.get("BTC", {}).get("holdout", {}).get("sharpe", -999) eth_ho = pa.get("ETH", {}).get("holdout", {}).get("sharpe", -999) score = min(btc_ho, eth_ho) if score > best_score: best_score = score best_rep = rep best_cfg = cfg print("\n\n=== BEST CONFIG ===") print(f"Config: {best_cfg}") print(al.fmt(best_rep)) print("JSON:", al.as_json(best_rep))