SPB Git

Repository Public

strategy.py 1.8 KB 48 lines python

1"""Volatility breakout: follow a move that is large against the volatility that preceded it, for a fixed time."""23import numpy as np45META = {6    "name": "vol_breakout_perp",7    "version": "1",8    "kind": "perp",9    "universe": ["BTC", "ETH", "SOL", "HYPE"],10    "interval": "1h",11    "lookback": 500,12    "max_leverage": 2.0,13    "max_gross_exposure": 2.0,14    "params": {"move_window": 24, "vol_window": 168, "k": 2.0, "hold": 48, "gross": 1.0},15}161718def _last_breakout(close, move_window, vol_window, k, hold):19    """Side (+1 / -1) of the most recent breakout among the last `hold` bars, 0 if there is none.2021    A breakout at bar t: |log(close[t] / close[t - move_window])| > k x sigma, where sigma is the standard22    deviation of the `vol_window` one-bar log-returns that END where the move starts, scaled to the move's length.23    """24    logp = np.log(close)25    n = len(logp)26    for t in range(n - 1, max(n - 1 - hold, move_window + vol_window - 1), -1):27        start = t - move_window28        move = logp[t] - logp[start]29        returns = np.diff(logp[start - vol_window:start + 1])30        sigma = returns.std(ddof=1) * np.sqrt(move_window)31        if sigma > 0 and abs(move) > k * sigma:32            return 1.0 if move > 0 else -1.033    return 0.0343536def target_weights(data, params):37    move_window, vol_window, hold = int(params["move_window"]), int(params["vol_window"]), int(params["hold"])38    k, gross = float(params["k"]), float(params["gross"])39    n = len(data)40    weights = {}41    for symbol, df in data.items():42        close = df["close"].to_numpy()43        if move_window < 1 or vol_window < 3 or hold < 1 or k <= 0 or len(close) < move_window + vol_window + 1:44            weights[symbol] = 0.045            continue46        weights[symbol] = _last_breakout(close, move_window, vol_window, k, hold) * gross / n47    return weights48
Files main
Menu

Theme