Repository Public
1"""Donchian channel breakout: enter on a new N-bar high, leave on a new M-bar low.23Universe-generic (version 3): the symbols are whatever the agent trades; same rule and parameters as version 1.4"""56import numpy as np78META = {9 "name": "donchian_breakout",10 "version": "3",11 "universe": ["*"],12 "interval": "1h",13 "lookback": 2200,14 "params": {"entry": 480, "exit": 240},15}161718def _is_long(df, entry, exit_):19 """Replay the channel over the window: long if the most recent event is an upside breakout."""20 close = df["close"]21 upper = df["high"].rolling(entry).max().shift(1) # highest high of the previous `entry` bars22 lower = df["low"].rolling(exit_).min().shift(1) # lowest low of the previous `exit_` bars23 up = np.nonzero((close > upper).to_numpy())[0]24 down = np.nonzero((close < lower).to_numpy())[0]25 if len(up) == 0:26 return False27 return len(down) == 0 or up[-1] > down[-1]282930def target_weights(data, params):31 entry, exit_ = int(params["entry"]), int(params["exit"])32 n = len(data)33 weights = {}34 for symbol, df in data.items():35 if len(df) <= max(entry, exit_) or entry < 1 or exit_ < 1:36 weights[symbol] = 0.037 continue38 weights[symbol] = 1.0 / n if _is_long(df, entry, exit_) else 0.039 return weights40