2 related strategies (⧉ identical code, ≈ similar name)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | # -*- coding: utf-8 -*- """ BearPullbackShortBearV3 - 熊市回调做空策略(强化版 Regime 过滤) 核心思想: - 只在“真正的熊市”做空: - EMA20 < EMA50 < EMA200 - MACD < 0 且在 0 轴下持续一段时间 - ADX > 阈值(趋势强度) - 熊市条件连续维持 N 根 K(regime_persistence)后才允许交易 - 熊市中等待反弹到布林上轨附近 + RSI 过热才开空; - 止损:ATR * mult(2~4),再加 1%~2.2% 的全局上限; - 止盈:超卖信号 + 盈利 >= 3% / 6% 分两档; - 移动止损:在大盈利时锁定部分收益; - 额外的 soft_stop:开仓后短时间内明显走反,在 -1% 附近直接认错。 用途: - 专门在类似 2022 这种趋势性熊市中使用; - 对 2021 这种“伪熊市 + 宽震荡”应当被 Regime 过滤掉,不交易。 """ from datetime import datetime import numpy as np import talib.abstract as ta from pandas import DataFrame from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter from freqtrade.persistence import Trade class BearPullbackShortBearV3(IStrategy): """熊市回调做空策略(熊市 Regime 强化版)""" INTERFACE_VERSION = 3 timeframe = "1h" can_short = True # 兜底硬止损(真正逻辑由 custom_exit / custom_stoploss 接管) stoploss = -0.15 # ROI 完全交给 custom_exit minimal_roi = {"0": 100} # === 参数区(后续可用 hyperopt 调优) === # 判趋势用 ADX 阈值(熊市要求适中,不必极强) adx_threshold = IntParameter(15, 30, default=18, space="buy") # 入场过热条件:RSI 阈值(熊市反弹一般 55~70 区间) rsi_entry = IntParameter(55, 75, default=60, space="buy") # 布林带标准差 bb_std = DecimalParameter(1.8, 2.6, default=2.0, space="buy") # 超时(小时):超过这个时间,盈利或小亏就平仓 timeout_hours = IntParameter(12, 48, default=24, space="sell") # ATR 止损倍数(最大允许亏损 = min(ATR_pct * mult, cap)) atr_mult = DecimalParameter(2.0, 4.0, default=3.0, space="sell") # Regime 持续性要求:熊市条件至少连续多少根 K 后才算真正进入熊市 regime_persistence = IntParameter(12, 72, default=24, space="buy") # === 保护机制 === @property def protections(self): return [ {"method": "CooldownPeriod", "stop_duration_candles": 1}, { "method": "StoplossGuard", "lookback_period_candles": 24, "trade_limit": 5, "stop_duration_candles": 6, "only_per_pair": False, }, ] # === 指标计算 === def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 布林带 bb = ta.BBANDS( dataframe, timeperiod=20, nbdevup=float(self.bb_std.value), nbdevdn=float(self.bb_std.value), ) dataframe["bb_upper"] = bb["upperband"] dataframe["bb_middle"] = bb["middleband"] dataframe["bb_lower"] = bb["lowerband"] # RSI / ADX dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) dataframe["adx"] = ta.ADX(dataframe) # EMA 趋势线 dataframe["ema_20"] = ta.EMA(dataframe, timeperiod=20) dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50) dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200) # MACD(用于确认长期处于 0 轴下方) macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9) dataframe["macd"] = macd["macd"] dataframe["macdsignal"] = macd["macdsignal"] dataframe["macdhist"] = macd["macdhist"] # ATR 用于动态止损 dataframe["atr"] = ta.ATR(dataframe, timeperiod=14) # --- 熊市 Regime 检测 --- adx_thr = int(self.adx_threshold.value) pers = int(self.regime_persistence.value) # 基础熊市条件:价格与均线空头排列 + MACD < 0 base_bear = ( (dataframe["close"] < dataframe["ema_200"]) & (dataframe["ema_20"] < dataframe["ema_50"]) & (dataframe["ema_50"] < dataframe["ema_200"]) & (dataframe["macd"] < 0) ) # 趋势强度:ADX > 阈值 strong_trend = dataframe["adx"] > adx_thr # 原始熊市判定(未考虑持续性) raw_bear = base_bear & strong_trend dataframe["bear_raw"] = np.where(raw_bear, 1, 0) # 持续性过滤:要求 raw_bear 连续 pers 根 K 都为 True if pers > 1: bear_regime = ( raw_bear.astype("int").rolling(window=pers, min_periods=pers).sum() == pers ) else: bear_regime = raw_bear dataframe["bear_regime"] = np.where(bear_regime, 1, 0) # 预备列 if "enter_tag" not in dataframe.columns: dataframe["enter_tag"] = "" if "exit_short" not in dataframe.columns: dataframe["exit_short"] = 0 return dataframe # === 入场:熊市中的反弹 + 过热 === def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 只做首次突破上轨的K线,避免上轨附近连环抄顶 cross_upper = ( (dataframe["close"] > dataframe["bb_upper"]) & (dataframe["close"].shift(1) <= dataframe["bb_upper"].shift(1)) ) rsi_thr = int(self.rsi_entry.value) # 仅在“已确认熊市 Regime”下允许做空 cond = ( (dataframe["bear_regime"] == 1) & cross_upper & (dataframe["rsi"] > rsi_thr) & (dataframe["volume"] > 0) ) dataframe.loc[cond, ["enter_short", "enter_tag"]] = (1, "bear_strong") return dataframe # === 出场信号基准(超卖位置) === def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """给出一个“超卖”信号,供 custom_exit 使用: 价格跌破布林下轨 或 RSI < 30。 """ if "exit_short" not in dataframe.columns: dataframe["exit_short"] = 0 oversold = ( (dataframe["close"] < dataframe["bb_lower"]) | (dataframe["rsi"] < 30) ) & (dataframe["volume"] > 0) dataframe.loc[oversold, "exit_short"] = 1 return dataframe # === 工具:ATR 动态止损(返回最大允许亏损百分比,正数) === def _calc_max_loss_pct(self, candle) -> float: atr = float(candle.get("atr", 0) or 0) close = float(candle.get("close", 0) or 1) atr_pct = atr / close if close > 0 else 0.01 mult = float(self.atr_mult.value) raw = atr_pct * mult # 根据 enter_tag 设置不同的上限(预留扩展位,当前只用 bear_strong) enter_tag = candle.get("enter_tag", "") or "bear_strong" if enter_tag == "bear_strong": cap = 0.022 # 强熊市允许稍宽 else: cap = 0.018 # 预留:弱熊市更窄 floor = 0.010 # 至少 1% return max(floor, min(raw, cap)) # === 核心:自定义出场逻辑 === def custom_exit( self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs, ): dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) try: df = dataframe.loc[dataframe["date"] <= current_time] last = df.iloc[-1] if not df.empty else dataframe.iloc[-1] except Exception: last = dataframe.iloc[-1] is_bear = int(last.get("bear_regime", 0)) == 1 exit_signal = int(last.get("exit_short", 0)) max_loss_pct = self._calc_max_loss_pct(last) # 交易存活时长(小时) duration_hrs = (current_time - trade.open_date_utc).total_seconds() / 3600 timeout = int(self.timeout_hours.value) # 1) 硬止损:动态 ATR 止损 if current_profit <= -max_loss_pct: return "bear_stop_loss" # 2) soft_stop:开仓后不久就走反,-1% 附近直接认错 # 避免拖到最大止损 if is_bear and duration_hrs < 6: # 开仓 6 小时内 if current_profit <= -0.01: return "soft_stop" # 3) Regime Flip:不再是熊市时,盈利或小亏就走 if not is_bear: if current_profit > -0.005: # -0.5% 以内 return "regime_flip" # 更深亏损交由硬止损处理 # 4) 正常熊市中,根据盈利和超卖信号出场 if exit_signal == 1: # 第二目标:盈利 >= 6% if current_profit >= 0.06: return "tp2_exit" # 第一目标:盈利 >= 3% if current_profit >= 0.03: return "tp1_exit" # 5) 超时处理:超过 timeout,小亏/小赢也离场 if duration_hrs > timeout: tag = f"timeout_exit_({timeout}h)" if current_profit > 0: return tag if current_profit > -0.005: return tag # 其它情况不强制退出 return None # === 动态移动止盈:在大盈利时保护仓位 === def custom_stoploss( self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs, ) -> float: """根据浮盈动态上调止损价位: - 盈利 > 3% -> 锁定 0.5% - 盈利 > 5% -> 锁定 2% - 盈利 > 8% -> 锁定 4% - 盈利 > 12% -> 锁定 6% 其它情况返回 1,让 custom_exit / 兜底止损处理。 """ if current_profit > 0.12: return 0.06 if current_profit > 0.08: return 0.04 if current_profit > 0.05: return 0.02 if current_profit > 0.03: return 0.005 return 1 |
Strategy League — fixed backtest that feeds the ranking
Export report Freqtrade logsRun finished · took 40.5s
ℹ️ This strategy uses custom_stoploss() — freqtrade only
re-checks these once per 1h candle by default, not against the price movement within it.
For a more accurate read, re-run this backtest locally with --timeframe-detail 1m
(or 5m — freqtrade's own docs use 5m detail for an hourly strategy as a lighter
alternative). Freqle doesn't do this for every check here: multiplying every
League/sweep backtest by a finer detail timeframe is more compute than the sandbox can sustain
across every indexed strategy. why this matters →
- profit isn't statistically significant (p=0.13) — hard to tell apart from luck
- did not beat simply holding the market
- 87% of resampled runs stayed profitable
Resampling the trade sequence 2,000× shows the spread of results this edge could plausibly produce — separating a dependable strategy from one that got lucky once.
Loading charts…
Monthly breakdown
| Month | Regime | Trades | Profit % | Avg % | Win | Loss | Win % | DD % | Avg dur |
|---|---|---|---|---|---|---|---|---|---|
| Dec 2025 | bearish trending low vol | 19 | +2.06 | 1.06 | 11 | 8 | 57.9 | -4.77 | 1h 51m |
| Nov 2025 | bearish trending high vol | 8 | -0.91 | -1.14 | 3 | 5 | 37.5 | -3.13 | 2h 38m |
| Oct 2025 | bearish trending low vol | 10 | -0.54 | -0.51 | 5 | 5 | 50.0 | -2.25 | 2h 42m |
| Sep 2025 | bullish choppy low vol | 11 | -0.06 | -0.04 | 6 | 5 | 54.5 | -1.71 | 2h 00m |
| Aug 2025 | bearish choppy low vol | 13 | -1.70 | -1.31 | 3 | 10 | 23.1 | -1.65 | 1h 51m |
| Jul 2025 | bullish choppy low vol | 5 | +0.18 | 0.37 | 3 | 2 | 60.0 | -0.24 | 1h 36m |
| Jun 2025 | bearish choppy low vol | 16 | +0.03 | 0.02 | 10 | 6 | 62.5 | -0.26 | 3h 34m |
| May 2025 | bullish trending low vol | 25 | +0.04 | 0.02 | 11 | 14 | 44.0 | -0.38 | 2h 05m |
| Apr 2025 | bullish choppy low vol | 5 | +0.70 | 1.47 | 5 | 0 | 100.0 | -0.1 | 2h 48m |
| Mar 2025 | bearish trending high vol | 7 | +1.98 | 2.85 | 6 | 1 | 85.7 | -2.01 | 3h 09m |
| Feb 2025 | bearish trending low vol | 7 | +0.32 | 0.54 | 4 | 3 | 57.1 | -2.28 | 2h 17m |
| Jan 2025 | bearish choppy low vol | 8 | +0.23 | 0.30 | 5 | 3 | 62.5 | -2.57 | 2h 30m |
| Dec 2024 | bullish trending low vol | 14 | +0.30 | 0.29 | 10 | 4 | 71.4 | -3.22 | 1h 56m |
| Nov 2024 | bullish trending low vol | 2 | -0.33 | -1.65 | 1 | 1 | 50.0 | -2.86 | 2h 30m |
| Oct 2024 | bullish choppy low vol | 8 | +0.05 | 0.06 | 4 | 4 | 50.0 | -2.61 | 1h 22m |
| Sep 2024 | bearish choppy low vol | 16 | +0.23 | 0.14 | 10 | 6 | 62.5 | -3.12 | 2h 15m |
| Aug 2024 | bearish choppy high vol | 3 | +0.05 | 0.17 | 2 | 1 | 66.7 | -2.81 | 1h 20m |
| Jul 2024 | bearish trending low vol | 9 | -0.23 | -0.26 | 4 | 5 | 44.4 | -2.92 | 1h 27m |
| Jun 2024 | bearish choppy low vol | 27 | -0.90 | -0.33 | 11 | 16 | 40.7 | -2.72 | 4h 44m |
| May 2024 | bullish choppy high vol | 21 | -1.07 | -0.49 | 10 | 11 | 47.6 | -1.95 | 3h 54m |
| Apr 2024 | bearish choppy high vol | 14 | +0.38 | 0.28 | 11 | 3 | 78.6 | -0.26 | 2h 30m |
| Mar 2024 | bullish trending high vol | 6 | +0.54 | 0.92 | 6 | 0 | 100.0 | 0.0 | 1h 50m |
| Feb 2024 | bullish trending low vol | 1 | -0.04 | -0.42 | 0 | 1 | 0.0 | -0.06 | 3h 00m |
| Jan 2024 | bearish choppy high vol | 7 | +0.29 | 0.42 | 4 | 3 | 57.1 | -0.25 | 1h 51m |
| Dec 2023 | bullish trending low vol | 7 | +0.26 | 0.39 | 5 | 2 | 71.4 | -0.29 | 1h 51m |
| Nov 2023 | bullish trending low vol | 5 | -0.15 | -0.31 | 3 | 2 | 60.0 | -0.32 | 4h 24m |
| Oct 2023 | bullish trending low vol | 12 | +0.31 | 0.26 | 5 | 7 | 41.7 | -0.56 | 3h 40m |
| Sep 2023 | bearish choppy low vol | 13 | +0.47 | 0.36 | 12 | 1 | 92.3 | -0.73 | 4h 18m |
| Aug 2023 | bearish choppy low vol | 14 | +0.31 | 0.22 | 10 | 4 | 71.4 | -1.17 | 2h 04m |
| Jul 2023 | bullish trending low vol | 7 | +0.25 | 0.36 | 3 | 4 | 42.9 | -1.12 | 3h 00m |
| Jun 2023 | bullish trending low vol | 13 | -0.07 | -0.06 | 5 | 8 | 38.5 | -1.38 | 2h 28m |
| May 2023 | bearish choppy low vol | 32 | -0.36 | -0.11 | 17 | 15 | 53.1 | -1.46 | 1h 52m |
| Apr 2023 | bullish trending low vol | 14 | -0.39 | -0.28 | 7 | 7 | 50.0 | -0.95 | 2h 47m |
| Mar 2023 | bullish trending high vol | 21 | +1.39 | 0.68 | 18 | 3 | 85.7 | -1.84 | 5h 23m |
| Feb 2023 | bullish trending low vol | 6 | +0.01 | -0.00 | 2 | 4 | 33.3 | -1.97 | 3h 50m |
| Jan 2023 | bullish trending low vol | 4 | +0.34 | 0.88 | 2 | 2 | 50.0 | -1.94 | 5h 00m |
| Dec 2022 | bearish trending low vol | 16 | -0.42 | -0.26 | 6 | 10 | 37.5 | -2.41 | 3h 08m |
| Nov 2022 | bearish trending high vol | 28 | +0.52 | 0.20 | 15 | 13 | 53.6 | -2.95 | 2h 13m |
| Oct 2022 | bullish choppy low vol | 11 | -0.78 | -0.70 | 6 | 5 | 54.5 | -2.35 | 1h 16m |
| Sep 2022 | bearish choppy high vol | 20 | -1.12 | -0.56 | 9 | 11 | 45.0 | -1.59 | 1h 54m |
| Aug 2022 | bullish choppy high vol | 5 | -0.34 | -0.68 | 2 | 3 | 40.0 | -0.5 | 3h 24m |
| Jul 2022 | bullish trending high vol | 13 | +1.05 | 0.86 | 9 | 4 | 69.2 | -0.26 | 4h 28m |
| Jun 2022 | bearish trending high vol | 8 | +0.77 | 0.97 | 8 | 0 | 100.0 | -0.35 | 3h 08m |
| May 2022 | bearish trending high vol | 11 | +1.25 | 1.14 | 8 | 3 | 72.7 | -1.59 | 2h 05m |
| Apr 2022 | bearish choppy high vol | 16 | -0.24 | -0.16 | 8 | 8 | 50.0 | -1.85 | 4h 00m |
| Mar 2022 | bullish choppy high vol | 6 | +0.09 | 0.16 | 5 | 1 | 83.3 | -1.5 | 1h 50m |
| Feb 2022 | bearish trending high vol | 6 | +0.62 | 1.04 | 6 | 0 | 100.0 | -1.91 | 1h 20m |
| Jan 2022 | bearish trending high vol | 12 | +0.31 | 0.30 | 7 | 5 | 58.3 | -2.34 | 3h 05m |
| Dec 2021 | bearish trending high vol | 18 | +0.23 | 0.13 | 12 | 6 | 66.7 | -3.08 | 3h 23m |
| Nov 2021 | bearish trending high vol | 20 | -0.61 | -0.30 | 6 | 14 | 30.0 | -2.64 | 3h 54m |
| Oct 2021 | bullish trending high vol | 7 | +0.84 | 1.28 | 7 | 0 | 100.0 | -2.71 | 3h 43m |
| Sep 2021 | bearish trending high vol | 6 | -0.73 | -1.29 | 2 | 4 | 33.3 | -2.87 | 2h 50m |
| Aug 2021 | bullish trending high vol | 4 | +0.07 | 0.17 | 3 | 1 | 75.0 | -2.21 | 1h 15m |
| Jul 2021 | bullish trending high vol | 19 | -0.96 | -0.52 | 7 | 12 | 36.8 | -2.22 | 1h 54m |
| Jun 2021 | bearish trending high vol | 14 | -0.86 | -0.62 | 5 | 9 | 35.7 | -1.29 | 3h 09m |
| May 2021 | bearish trending high vol | 25 | +0.65 | 0.28 | 15 | 10 | 60.0 | -0.56 | 2h 07m |
| Apr 2021 | bearish choppy high vol | 2 | +0.20 | 1.02 | 1 | 1 | 50.0 | -0.02 | 2h 30m |
| Mar 2021 | bullish choppy high vol | 14 | +0.20 | 0.18 | 6 | 8 | 42.9 | -0.83 | 3h 17m |
| Feb 2021 | bullish trending high vol | 5 | -0.07 | -0.13 | 2 | 3 | 40.0 | -0.53 | 2h 00m |
| Jan 2021 | bullish trending high vol | 1 | +0.21 | 2.15 | 1 | 0 | 100.0 | 0.0 | 1h 00m |
Yearly breakdown
| Year | Trades | Profit % | Avg % | Win | Loss | Win % | DD % | Avg dur |
|---|---|---|---|---|---|---|---|---|
| 2025 | 134 | +2.33 | 0.18 | 72 | 62 | 53.7 | -4.77 | 2h 23m |
| 2024 | 128 | -0.73 | -0.04 | 73 | 55 | 57.0 | -3.22 | 2h 52m |
| 2023 | 148 | +2.37 | 0.16 | 89 | 59 | 60.1 | -1.97 | 3h 11m |
| 2022 | 152 | +1.71 | 0.12 | 89 | 63 | 58.6 | -2.95 | 2h 41m |
| 2021 | 135 | -0.83 | -0.05 | 67 | 68 | 49.6 | -3.08 | 2h 50m |
Trade charts — best 2 and worst 2 performing pairs (full OHLC candles are expensive to render for every pair)
Backtests — over a market period
Backtest this strategy over a chosen crypto-cycle period. These don't affect the League ranking, and need that period's candle data downloaded.
Log in or sign up to run backtests.
| Period | Range | Total % | Win % | Max DD | Trades | |
|---|---|---|---|---|---|---|
| 2020 · DeFi Summer & Pre-Halving Rally | 20200101-20210101 | not run | ||||
| 2021 · Institutional Bull Market | 20210101-20220101 | not run | ||||
| 2022 · Post-Bull Crash & Macro Tightening | 20220101-20230101 | not run | ||||
| 2023–2024 · Recovery & ETF Anticipation | 20230101-20250101 | not run | ||||
| 2025–2026 · Current Cycle | 20250101-20260101 | not run | ||||
Walk forward
Out-of-sample backtest on recent data · 33 pairs · 20260101-20260701.
Backtest trust check
no lookahead patterns · 2 thing(s) worth reviewing before trusting the numbers
| Line | Pattern | Detail | |
|---|---|---|---|
| 32 | review | missing_startup_candles | uses recursive indicators (ADX, ATR, EMA, MACD) but startup_candle_count is not set (default 0). Their value at a bar depends on all bars before it, so freqtrade trims no warmup and the backtest opens with unwarmed values that can't occur live. The longest lookback visible here is EMA(timeperiod=200), so it needs at least that many. Set it to a few times the longest period and confirm with `freqtrade recursive-analysis` |
| 267 | review | dead_callback | custom_stoploss() is defined but use_custom_stoploss isn't True, and freqtrade only calls it when that flag is set -- the method never runs and every trade uses the static stoploss |
ran by Ron · took s
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.