15 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 | """ MainStrategy — FreqTrade strategy with FreqAI + P1-3 confluence + Kelly sizing. Uses: - FreqAI LightGBMRegressor for ML predictions - Custom P1-3 confluence scoring for signal gating - Kelly criterion for position sizing - ATR-based SL/TP """ import sys sys.path.insert(0, '/home/sandro/.openclaw/workspace/trading') from freqtrade.strategy import IStrategy, merge_informative_pair from freqtrade.persistence import Trade import talib import numpy as np import pandas as pd from pandas import DataFrame class MainStrategy(IStrategy): """FreqTrade strategy wrapping P1-3 + ML system.""" # Strategy settings INTERFACE_VERSION = 3 timeframe = '1h' informative_timeframes = ['4h', '15m'] # Can short can_short = True # Stoploss stoploss = -0.03 # 3% max (ATR-based SL is tighter) trailing_stop = False # Take profit (handled by custom exit) minimal_roi = {"0": 0.05} # 5% max, but ATR-based TP is tighter # Position sizing position_adjustment_enable = False # FreqAI freqai_info = { "enabled": True, "purge_old_models": 4, "train_period_days": 90, "backtest_period_days": 7, "identifier": "sandro_v1", "feature_parameters": { "include_timeframes": ["1h"], "include_corr_pairlist": ["BTC/USDT"], "label_period_candles": 10, "include_shifted_candles": 5, "indicator_periods_candles": [7, 14, 21], "DI_threshold": 0.9, "weight_factor": 0.9, }, "data_split_parameters": { "test_size": 0.25, "random_state": 42, }, "model_training_parameters": { "n_estimators": 200, "learning_rate": 0.05, "max_depth": 7, }, } # === FEATURE ENGINEERING === def feature_engineering_expand_all(self, dataframe: DataFrame, period, metadata, **kwargs) -> DataFrame: """Features that expand across periods, timeframes, and correlated pairs.""" dataframe["%-rsi-period"] = talib.RSI(dataframe["close"], timeperiod=period) dataframe["%-mfi-period"] = talib.MFI(dataframe["high"], dataframe["low"], dataframe["close"], dataframe["volume"], timeperiod=period) dataframe["%-adx-period"] = talib.ADX(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=period) dataframe["%-roc-period"] = talib.ROC(dataframe["close"], timeperiod=period) bollinger = talib.BBANDS(dataframe["close"], timeperiod=period) dataframe["%-bb_width-period"] = (bollinger[0] - bollinger[2]) / bollinger[1] dataframe["%-bb_pctb-period"] = (dataframe["close"] - bollinger[2]) / (bollinger[0] - bollinger[2]) dataframe["%-relative_volume-period"] = dataframe["volume"] / dataframe["volume"].rolling(period).mean() return dataframe def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame: """Features that expand across timeframes and correlated pairs (not periods).""" dataframe["%-pct-change"] = dataframe["close"].pct_change() dataframe["%-raw_volume"] = dataframe["volume"] # EMA ratios ema8 = talib.EMA(dataframe["close"], timeperiod=8) ema21 = talib.EMA(dataframe["close"], timeperiod=21) ema55 = talib.EMA(dataframe["close"], timeperiod=55) dataframe["%-ema_8_21_ratio"] = ema8 / ema21 dataframe["%-ema_21_55_ratio"] = ema21 / ema55 # ATR as percentage dataframe["%-atr_pct"] = talib.ATR(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14) / dataframe["close"] * 100 # MACD macd, signal, hist = talib.MACD(dataframe["close"]) dataframe["%-macd_hist"] = hist # Stochastic dataframe["%-stoch_k"], dataframe["%-stoch_d"] = talib.STOCH(dataframe["high"], dataframe["low"], dataframe["close"]) return dataframe def feature_engineering_standard(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame: """Non-expanded features (temporal, custom).""" # Temporal dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25 dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 return dataframe def set_freqai_targets(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame: """Set prediction target: forward return 10 bars ahead minus fees.""" dataframe["&-target"] = ( dataframe["close"] .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) / dataframe["close"] - 1 - 0.001 # subtract fees ) return dataframe # === ENTRY/EXIT LOGIC === def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """Calculate ATR for SL/TP and confluence score.""" dataframe["atr"] = talib.ATR(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14) # P1-3 Confluence approximation (pre-feature-engineering) rsi = talib.RSI(dataframe["close"], timeperiod=14) adx = talib.ADX(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14) ema8 = talib.EMA(dataframe["close"], timeperiod=8) ema21 = talib.EMA(dataframe["close"], timeperiod=21) vol_ratio = dataframe["volume"] / dataframe["volume"].rolling(20).mean() bb = talib.BBANDS(dataframe["close"], timeperiod=20) bb_pctb = (dataframe["close"] - bb[2]) / (bb[0] - bb[2]) score = pd.Series(0, index=dataframe.index) score += ((rsi < 30) | (rsi > 70)).astype(int) * 20 score += (((rsi >= 30) & (rsi < 40)) | ((rsi > 60) & (rsi <= 70))).astype(int) * 10 score += (adx > 25).astype(int) * 15 score += (((bb_pctb < 0.1) | (bb_pctb > 0.9))).astype(int) * 15 score += (abs(ema8 / ema21 - 1) > 0.002).astype(int) * 15 score += (vol_ratio > 1.5).astype(int) * 15 dataframe["%-confluence_score"] = score.clip(0, 100) # FreqAI predictions (populated by FreqAI framework) # dataframe["&-target"] will contain the predicted return return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """Entry signals: P1-3 confluence gate + basic volume/trend filter.""" # Additional trend filter: EMA alignment ema8 = talib.EMA(dataframe["close"], timeperiod=8) ema21 = talib.EMA(dataframe["close"], timeperiod=21) ema_bullish = ema8 > ema21 ema_bearish = ema8 < ema21 # Long entries: confluence + bullish EMA trend + volume dataframe.loc[ ( (dataframe["%-confluence_score"] >= 40) & # P1-3 confluence gate ema_bullish & # EMA trend filter (dataframe["volume"] > 0) ), ["enter_long", "enter_tag"] ] = (1, "p13_long") # Short entries: confluence + bearish EMA trend + volume dataframe.loc[ ( (dataframe["%-confluence_score"] >= 40) & # P1-3 confluence gate ema_bearish & # EMA trend filter (dataframe["volume"] > 0) ), ["enter_short", "enter_tag"] ] = (1, "p13_short") return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """Exit handled by stoploss and ROI.""" dataframe["exit_long"] = 0 dataframe["exit_short"] = 0 return dataframe def custom_stoploss(self, pair: str, trade: Trade, current_time, current_rate, current_profit, after_fill, **kwargs) -> float: """ATR-based dynamic stoploss: 1.5 × ATR.""" dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if len(dataframe) > 0: last = dataframe.iloc[-1] atr = last.get("atr", 0) if atr > 0: sl_pct = (1.5 * atr) / current_rate return -sl_pct return self.stoploss def custom_stake_amount(self, pair: str, current_time, current_rate, proposed_stake, min_stake, max_stake, leverage, entry_tag, side, **kwargs) -> float: """Kelly criterion position sizing (f*/4 conservative).""" # Simple Kelly: use backtest WR 52% and R:R 1.67 win_rate = 0.52 rr_ratio = 2.5 / 1.5 # TP/SL = 1.67 kelly_f = (win_rate * rr_ratio - (1 - win_rate)) / rr_ratio conservative_f = kelly_f / 4 # f*/4 if conservative_f <= 0: return min_stake # Adjust by regime (reduce in low-confidence setups) stake = proposed_stake * min(conservative_f * 4, 1.0) # scale return max(min(stake, max_stake), min_stake) |
Strategy League — fixed backtest that feeds the ranking
Export report Freqtrade logsRun finished · took 352.0s
ℹ️ 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 →
- did not beat simply holding the market
- statistically significant edge (p=0.00)
- 100% of resampled runs stayed profitable
- profitable across 80% of rolling 3-month windows
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 |
|---|---|---|---|---|---|---|---|---|---|
| Jan 2026 | bullish trending low vol | 10 | +0.32 | 1.11 | 7 | 3 | 70.0 | -1.81 | 152h 06m |
| Dec 2025 | bearish trending low vol | 231 | +0.72 | 0.27 | 96 | 135 | 41.6 | -1.95 | 27h 36m |
| Nov 2025 | bearish trending high vol | 444 | +5.33 | 0.39 | 191 | 253 | 43.0 | -3.4 | 15h 40m |
| Oct 2025 | bearish trending low vol | 482 | +2.19 | 0.18 | 195 | 287 | 40.5 | -3.61 | 16h 24m |
| Sep 2025 | bullish choppy low vol | 173 | +4.74 | 1.16 | 91 | 82 | 52.6 | -1.66 | 40h 04m |
| Aug 2025 | bearish choppy low vol | 264 | +2.65 | 0.34 | 112 | 152 | 42.4 | -2.3 | 27h 16m |
| Jul 2025 | bullish choppy low vol | 318 | +2.71 | 0.24 | 131 | 187 | 41.2 | -4.19 | 24h 13m |
| Jun 2025 | bearish choppy low vol | 218 | +5.69 | 1.17 | 115 | 103 | 52.8 | -7.55 | 30h 34m |
| May 2025 | bullish trending low vol | 354 | +1.24 | 0.10 | 140 | 214 | 39.5 | -8.6 | 21h 52m |
| Apr 2025 | bullish choppy low vol | 419 | -0.29 | -0.08 | 156 | 263 | 37.2 | -8.67 | 15h 49m |
| Mar 2025 | bearish trending high vol | 613 | -1.28 | -0.04 | 231 | 382 | 37.7 | -8.98 | 11h 37m |
| Feb 2025 | bearish trending low vol | 711 | +4.36 | 0.35 | 303 | 408 | 42.6 | -9.39 | 9h 22m |
| Jan 2025 | bearish choppy low vol | 463 | +4.67 | 0.42 | 201 | 262 | 43.4 | -6.37 | 16h 14m |
| Dec 2024 | bullish trending low vol | 996 | +2.67 | 0.18 | 404 | 592 | 40.6 | -12.66 | 6h 54m |
| Nov 2024 | bullish trending low vol | 897 | -6.27 | -0.33 | 306 | 591 | 34.1 | -8.66 | 7h 50m |
| Oct 2024 | bullish choppy low vol | 221 | +5.26 | 1.01 | 112 | 109 | 50.7 | -1.27 | 32h 58m |
| Sep 2024 | bearish choppy low vol | 166 | +4.27 | 0.94 | 83 | 83 | 50.0 | -3.19 | 45h 06m |
| Aug 2024 | bearish choppy high vol | 475 | +9.30 | 0.81 | 229 | 246 | 48.2 | -5.03 | 14h 51m |
| Jul 2024 | bearish trending low vol | 359 | +7.61 | 0.96 | 180 | 179 | 50.1 | -3.14 | 22h 36m |
| Jun 2024 | bearish choppy low vol | 211 | +4.02 | 0.89 | 104 | 107 | 49.3 | -5.41 | 33h 32m |
| May 2024 | bullish choppy high vol | 218 | +0.77 | 0.20 | 89 | 129 | 40.8 | -5.59 | 30h 29m |
| Apr 2024 | bearish choppy high vol | 539 | +12.04 | 0.94 | 269 | 270 | 49.9 | -9.8 | 14h 05m |
| Mar 2024 | bullish trending high vol | 711 | -8.55 | -0.38 | 239 | 472 | 33.6 | -9.76 | 9h 09m |
| Feb 2024 | bullish trending low vol | 234 | +0.60 | 0.08 | 92 | 142 | 39.3 | -3.47 | 32h 28m |
| Jan 2024 | bearish choppy high vol | 367 | +0.71 | 0.02 | 141 | 226 | 38.4 | -5.23 | 19h 08m |
| Dec 2023 | bullish trending low vol | 357 | -2.17 | -0.31 | 123 | 234 | 34.5 | -3.82 | 22h 56m |
| Nov 2023 | bullish trending low vol | 371 | -0.71 | -0.06 | 139 | 232 | 37.5 | -3.19 | 17h 54m |
| Oct 2023 | bullish trending low vol | 188 | +2.17 | 0.44 | 82 | 106 | 43.6 | -1.91 | 41h 50m |
| Sep 2023 | bearish choppy low vol | 101 | +0.92 | 0.43 | 44 | 57 | 43.6 | -2.44 | 62h 23m |
| Aug 2023 | bearish choppy low vol | 213 | +2.96 | 0.70 | 100 | 113 | 46.9 | -4.05 | 39h 12m |
| Jul 2023 | bullish trending low vol | 218 | -0.41 | -0.09 | 81 | 137 | 37.2 | -4.97 | 31h 14m |
| Jun 2023 | bullish trending low vol | 330 | +1.29 | 0.16 | 133 | 197 | 40.3 | -5.1 | 22h 13m |
| May 2023 | bearish choppy low vol | 118 | -0.94 | -0.28 | 41 | 77 | 34.7 | -5.01 | 62h 27m |
| Apr 2023 | bullish trending low vol | 197 | -2.13 | -0.44 | 65 | 132 | 33.0 | -4.09 | 35h 51m |
| Mar 2023 | bullish trending high vol | 345 | +3.93 | 0.47 | 152 | 193 | 44.1 | -4.02 | 22h 10m |
| Feb 2023 | bullish trending low vol | 229 | +1.08 | 0.18 | 93 | 136 | 40.6 | -6.79 | 27h 25m |
| Jan 2023 | bullish trending low vol | 381 | +1.60 | 0.14 | 152 | 229 | 39.9 | -5.11 | 21h 09m |
| Dec 2022 | bearish trending low vol | 159 | +4.77 | 1.33 | 87 | 72 | 54.7 | -7.53 | 41h 17m |
| Nov 2022 | bearish trending high vol | 689 | +5.87 | 0.39 | 298 | 391 | 43.3 | -9.35 | 10h 07m |
| Oct 2022 | bullish choppy low vol | 220 | +1.09 | 0.29 | 92 | 128 | 41.8 | -9.53 | 33h 53m |
| Sep 2022 | bearish choppy high vol | 347 | -3.13 | -0.37 | 117 | 230 | 33.7 | -8.83 | 20h 28m |
| Aug 2022 | bullish choppy high vol | 338 | +2.48 | 0.38 | 145 | 193 | 42.9 | -10.51 | 21h 33m |
| Jul 2022 | bullish trending high vol | 514 | +2.82 | 0.24 | 212 | 302 | 41.2 | -13.31 | 13h 49m |
| Jun 2022 | bearish trending high vol | 927 | +1.59 | 0.08 | 363 | 564 | 39.2 | -13.78 | 7h 09m |
| May 2022 | bearish trending high vol | 1119 | -3.98 | -0.17 | 405 | 714 | 36.2 | -14.19 | 5h 41m |
| Apr 2022 | bearish choppy high vol | 324 | +5.96 | 0.82 | 157 | 167 | 48.5 | -9.02 | 22h 14m |
| Mar 2022 | bullish choppy high vol | 358 | +0.38 | 0.22 | 147 | 211 | 41.1 | -8.11 | 20h 00m |
| Feb 2022 | bearish trending high vol | 536 | +3.06 | 0.18 | 217 | 319 | 40.5 | -11.02 | 12h 39m |
| Jan 2022 | bearish trending high vol | 777 | +8.07 | 0.34 | 330 | 447 | 42.5 | -15.38 | 8h 51m |
| Dec 2021 | bearish trending high vol | 589 | +4.43 | 0.32 | 249 | 340 | 42.3 | -20.26 | 12h 18m |
| Nov 2021 | bearish trending high vol | 527 | +1.28 | 0.22 | 216 | 311 | 41.0 | -22.14 | 13h 50m |
| Oct 2021 | bullish trending high vol | 424 | -1.41 | -0.30 | 147 | 277 | 34.7 | -20.97 | 17h 12m |
| Sep 2021 | bearish trending high vol | 942 | +3.21 | 0.20 | 385 | 557 | 40.9 | -24.33 | 7h 07m |
| Aug 2021 | bullish trending high vol | 627 | -2.01 | -0.06 | 236 | 391 | 37.6 | -22.9 | 11h 22m |
| Jul 2021 | bullish trending high vol | 511 | +3.57 | 0.29 | 214 | 297 | 41.9 | -26.33 | 13h 45m |
| Jun 2021 | bearish trending high vol | 1093 | +7.43 | 0.28 | 456 | 637 | 41.7 | -33.21 | 6h 05m |
| May 2021 | bearish trending high vol | 2455 | -10.47 | -0.16 | 894 | 1561 | 36.4 | -38.98 | 2h 18m |
| Apr 2021 | bearish choppy high vol | 1490 | -5.79 | -0.18 | 538 | 952 | 36.1 | -22.32 | 4h 12m |
| Mar 2021 | bullish choppy high vol | 748 | -1.01 | -0.04 | 284 | 464 | 38.0 | -18.64 | 9h 34m |
| Feb 2021 | bullish trending high vol | 1773 | -6.03 | -0.10 | 658 | 1115 | 37.1 | -17.03 | 3h 10m |
| Jan 2021 | bullish trending high vol | 1850 | -7.07 | -0.17 | 670 | 1180 | 36.2 | -9.94 | 3h 00m |
Yearly breakdown
| Year | Trades | Profit % | Avg % | Win | Loss | Win % | DD % | Avg dur |
|---|---|---|---|---|---|---|---|---|
| 2026 | 10 | +0.32 | 1.11 | 7 | 3 | 70.0 | -1.81 | 152h 06m |
| 2025 | 4690 | +32.73 | 0.29 | 1962 | 2728 | 41.8 | -9.39 | 18h 12m |
| 2024 | 5394 | +32.43 | 0.28 | 2248 | 3146 | 41.7 | -12.66 | 16h 00m |
| 2023 | 3048 | +7.59 | 0.10 | 1205 | 1843 | 39.5 | -6.79 | 28h 50m |
| 2022 | 6308 | +28.98 | 0.20 | 2570 | 3738 | 40.7 | -15.38 | 13h 14m |
| 2021 | 13029 | -13.87 | -0.03 | 4947 | 8082 | 38.0 | -38.98 | 6h 06m |
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, but 1 backtest-realism warning(s) — results may not reflect real trading · 3 to review
| Line | Pattern | Detail | |
|---|---|---|---|
| 22 | review | missing_startup_candles | uses recursive indicators (ADX, ATR, EMA, RSI) 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=21), so it needs at least that many. Set it to a few times the longest period and confirm with `freqtrade recursive-analysis` |
| 197 | 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 |
| 72 | realism | freqai_not_started | defines FreqAI hooks (feature_engineering_expand_all, feature_engineering_expand_basic, feature_engineering_standard, set_freqai_targets) but never calls self.freqai.start(dataframe, metadata, self) in populate_indicators -- that call is what runs the model, so the features here are computed and thrown away and no prediction column is ever produced |
| 170 | review | enter_tag_overwrite | enter_tag/exit_tag is written by 2 separate assignments -- they share one column and run in source order, so a row matching more than one condition keeps only the LAST tag. Per-tag statistics won't mean what they appear to |
ran by Ron · took s
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.