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 | # 多时间框架策略 # 原理:用更高时间框架确认趋势,低时间框架入场 # 适合:趋势市场,减少假信号 from freqtrade.strategy import IStrategy, IntParameter from pandas import DataFrame import talib.abstract as ta from functools import reduce class MultiTFStrategy(IStrategy): INTERFACE_VERSION = 3 # 参数 ema_fast = IntParameter(5, 15, default=9, space="buy", optimize=True) ema_slow = IntParameter(20, 40, default=21, space="buy", optimize=True) minimal_roi = {"0": 0.10} stoploss = -0.05 timeframe = '15m' trailing_stop = True trailing_stop_positive = 0.03 trailing_stop_positive_offset = 0.04 startup_candle_count = 200 order_types = { 'entry': 'limit', 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } def informative_pairs(self): # 1小时时间框架用于趋势确认 return [ ("BTC/USDT", "1h"), ("ETH/USDT", "1h"), ("SOL/USDT", "1h"), ("DOGE/USDT", "1h"), ] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 15m 指标 dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=self.ema_fast.value) dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=self.ema_slow.value) dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['adx'] = ta.ADX(dataframe, timeperiod=14) dataframe['volume_ma'] = dataframe['volume'].rolling(20).mean() return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[:, 'enter_long'] = 0 # 获取 1h 数据 informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1h') if informative is not None and len(informative) > 0: # 1h 趋势 informative['ema_50'] = ta.EMA(informative, timeperiod=50) informative['ema_200'] = ta.EMA(informative, timeperiod=200) last_1h = informative.iloc[-1] trend_up = last_1h['ema_50'] > last_1h['ema_200'] else: trend_up = True # 默认允许 conditions = [ # 15m EMA 多头 dataframe['ema_fast'] > dataframe['ema_slow'], # RSI 不超买 dataframe['rsi'] < 70, # ADX 有趋势 dataframe['adx'] > 20, # 成交量 dataframe['volume'] > dataframe['volume_ma'], ] if conditions and trend_up: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[:, 'exit_long'] = 0 conditions = [ # EMA 死叉 dataframe['ema_fast'] < dataframe['ema_slow'], ] if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1 return dataframe |
Strategy League — fixed backtest that feeds the ranking
Export report Freqtrade logsRun finished · took 115.3s
ℹ️ This strategy uses a trailing stop — freqtrade only
re-checks these once per 15m candle by default, not against the price movement within it.
For a more accurate read, re-run this backtest locally with --timeframe-detail 1m. 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=1.00) — hard to tell apart from luck
- only 0% of resampled runs were profitable
- profitable in only 0% of rolling 3-month windows
- did not beat simply holding the market
- very deep drawdown (-90%)
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 |
|---|---|---|---|---|---|---|---|---|---|
| Oct 2021 | bullish trending high vol | 40 | -0.59 | -0.15 | 15 | 25 | 37.5 | -90.03 | 4h 49m |
| Sep 2021 | bearish trending high vol | 135 | -6.93 | -0.51 | 44 | 91 | 32.6 | -89.47 | 3h 27m |
| Aug 2021 | bullish trending high vol | 134 | +3.69 | 0.28 | 49 | 85 | 36.6 | -87.22 | 4h 02m |
| Jul 2021 | bearish trending high vol | 97 | -4.13 | -0.43 | 24 | 73 | 24.7 | -86.23 | 4h 12m |
| Jun 2021 | bearish trending high vol | 171 | -9.61 | -0.56 | 45 | 126 | 26.3 | -82.11 | 3h 59m |
| May 2021 | bearish trending high vol | 325 | -11.67 | -0.36 | 114 | 211 | 35.1 | -74.75 | 3h 15m |
| Apr 2021 | bearish choppy high vol | 420 | -13.74 | -0.33 | 150 | 270 | 35.7 | -61.92 | 3h 43m |
| Mar 2021 | bullish choppy high vol | 405 | -0.60 | -0.01 | 143 | 262 | 35.3 | -51.79 | 4h 36m |
| Feb 2021 | bullish trending high vol | 538 | -15.52 | -0.29 | 211 | 327 | 39.2 | -47.77 | 3h 09m |
| Jan 2021 | bullish trending high vol | 539 | -30.89 | -0.57 | 185 | 354 | 34.3 | -32.53 | 3h 25m |
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
1 potential lookahead pattern(s) found · 2 to review
| Line | Pattern | Detail | |
|---|---|---|---|
| 63 | leak | iloc_last | .iloc[-1] in populate_* applies the newest candle to all rows |
| 25 | review | startup_candles_too_small | startup_candle_count is 200, but EMA(timeperiod=200) needing 3x warmup needs at least 600 candles -- so the first 400+ candles of every backtest use an indicator that hasn't warmed up. Recursive indicators (EMA/RSI/ADX/ATR) want several times their period, not exactly it |
| 34 | review | unused_informative | informative_pairs() declares an extra timeframe, but nothing merges it into the dataframe (no merge_informative_pair, no @informative) -- that data is fetched and discarded, and any higher-timeframe filter you think is running isn't |
ran by Ron · took s
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.