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 | # --- Do not remove these libs --- # --- Do not remove these libs --- from freqtrade.strategy.interface import IStrategy from typing import Dict, List from functools import reduce from pandas import DataFrame # -------------------------------- import talib.abstract as ta import numpy as np import freqtrade.vendor.qtpylib.indicators as qtpylib import datetime from technical.util import resample_to_interval, resampled_merge from datetime import datetime, timedelta from freqtrade.persistence import Trade from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter import technical.indicators as ftt # @Rallipanos # Buy hyperspace params: entry_params = {'base_nb_candles_entry': 14, 'ewo_high': 2.327, 'ewo_high_2': -2.327, 'ewo_low': -20.988, 'low_offset': 0.975, 'low_offset_2': 0.955, 'rsi_entry': 69} # Sell hyperspace params: exit_params = {'base_nb_candles_exit': 24, 'high_offset': 0.998, 'high_offset_2': 1} def EWO(dataframe, ema_length=5, ema2_length=35): df = dataframe.copy() ema1 = ta.EMA(df, timeperiod=ema_length) ema2 = ta.EMA(df, timeperiod=ema2_length) emadif = (ema1 - ema2) / df['low'] * 100 return emadif class NotAnotherSMAOffSetStrategy_V2(IStrategy): INTERFACE_VERSION = 3 # ROI table: minimal_roi = {'0': 0.215, '40': 0.032, '87': 0.016, '201': 0} # Stoploss: stoploss = -0.35 # SMAOffset base_nb_candles_entry = IntParameter(5, 80, default=entry_params['base_nb_candles_entry'], space='entry', optimize=True) base_nb_candles_exit = IntParameter(5, 80, default=exit_params['base_nb_candles_exit'], space='exit', optimize=True) low_offset = DecimalParameter(0.9, 0.99, default=entry_params['low_offset'], space='entry', optimize=True) low_offset_2 = DecimalParameter(0.9, 0.99, default=entry_params['low_offset_2'], space='entry', optimize=True) high_offset = DecimalParameter(0.95, 1.1, default=exit_params['high_offset'], space='exit', optimize=True) high_offset_2 = DecimalParameter(0.99, 1.5, default=exit_params['high_offset_2'], space='exit', optimize=True) # Protection fast_ewo = 50 slow_ewo = 200 ewo_low = DecimalParameter(-20.0, -8.0, default=entry_params['ewo_low'], space='entry', optimize=True) ewo_high = DecimalParameter(2.0, 12.0, default=entry_params['ewo_high'], space='entry', optimize=True) ewo_high_2 = DecimalParameter(-6.0, 12.0, default=entry_params['ewo_high_2'], space='entry', optimize=True) rsi_entry = IntParameter(30, 70, default=entry_params['rsi_entry'], space='entry', optimize=True) # Trailing stop: trailing_stop = True trailing_stop_positive = 0.005 trailing_stop_positive_offset = 0.025 trailing_only_offset_is_reached = True # Sell signal use_exit_signal = True exit_profit_only = False exit_profit_offset = 0.01 ignore_roi_if_entry_signal = False ## Optional order time in force. order_time_in_force = {'entry': 'gtc', 'exit': 'gtc'} # Optimal timeframe for the strategy timeframe = '5m' inf_1h = '1h' process_only_new_candles = True startup_candle_count = 200 plot_config = {'main_plot': {'ma_entry': {'color': 'orange'}, 'ma_exit': {'color': 'orange'}}} def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, exit_reason: str, current_time: datetime, **kwargs) -> bool: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1] if last_candle is not None: if exit_reason in ['exit_signal']: if last_candle['hma_50'] > last_candle['ema_100'] and last_candle['rsi'] < 45: #*1.2 return False if last_candle is not None: if exit_reason in ['exit_signal']: if last_candle['hma_50'] * 1.149 > last_candle['ema_100'] and last_candle['close'] < last_candle['ema_100'] * 0.951: #*1.2 return False return True def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Calculate all ma_entry values for val in self.base_nb_candles_entry.range: dataframe[f'ma_entry_{val}'] = ta.EMA(dataframe, timeperiod=val) # Calculate all ma_exit values for val in self.base_nb_candles_exit.range: dataframe[f'ma_exit_{val}'] = ta.EMA(dataframe, timeperiod=val) dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) # Elliot dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) dataframe['vol_7_max'] = dataframe['volume'].rolling(window=20).max() dataframe['vol_14_max'] = dataframe['volume'].rolling(window=14).max() dataframe['vol_7_min'] = dataframe['volume'].rolling(window=20).min() dataframe['vol_14_min'] = dataframe['volume'].rolling(window=14).min() dataframe['roll_7'] = 100 * ((dataframe['volume'] - dataframe['vol_7_max']) / (dataframe['vol_7_max'] - dataframe['vol_7_min'])) dataframe['vol_base'] = ta.SMA(dataframe['roll_7'], timeperiod=5) dataframe['vol_ma_26'] = ta.SMA(dataframe['volume'], timeperiod=26) dataframe['vol_ma_200'] = ta.SMA(dataframe['volume'], timeperiod=100) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[(dataframe['vol_base'] > -96) & (dataframe['vol_base'] < -77) & (dataframe['rsi_fast'] < 35) & (dataframe['close'] < dataframe[f'ma_entry_{self.base_nb_candles_entry.value}'] * self.low_offset.value) & (dataframe['EWO'] > self.ewo_high.value) & (dataframe['rsi'] < self.rsi_entry.value) & (dataframe['volume'] > 0) & (dataframe['close'] < dataframe[f'ma_exit_{self.base_nb_candles_exit.value}'] * self.high_offset.value), ['enter_long', 'enter_tag']] = (1, 'ewo1') dataframe.loc[(dataframe['vol_base'] > -96) & (dataframe['vol_base'] > -20) & (dataframe['rsi_fast'] < 35) & (dataframe['close'] < dataframe[f'ma_entry_{self.base_nb_candles_entry.value}'] * self.low_offset.value) & (dataframe['EWO'] > self.ewo_high.value) & (dataframe['rsi'] < self.rsi_entry.value) & (dataframe['volume'] > 0) & (dataframe['close'] < dataframe[f'ma_exit_{self.base_nb_candles_exit.value}'] * self.high_offset.value), ['enter_long', 'enter_tag']] = (1, 'ewo3') dataframe.loc[(dataframe['vol_base'] > -96) & (dataframe['vol_base'] < -77) & (dataframe['rsi_fast'] < 35) & (dataframe['close'] < dataframe[f'ma_entry_{self.base_nb_candles_entry.value}'] * self.low_offset_2.value) & (dataframe['EWO'] > self.ewo_high_2.value) & (dataframe['rsi'] < self.rsi_entry.value) & (dataframe['volume'] > 0) & (dataframe['close'] < dataframe[f'ma_exit_{self.base_nb_candles_exit.value}'] * self.high_offset.value) & (dataframe['rsi'] < 25), ['enter_long', 'enter_tag']] = (1, 'ewo2') dataframe.loc[(dataframe['vol_base'] > -96) & (dataframe['vol_base'] < -77) & (dataframe['rsi_fast'] < 35) & (dataframe['close'] < dataframe[f'ma_entry_{self.base_nb_candles_entry.value}'] * self.low_offset.value) & (dataframe['EWO'] < self.ewo_low.value) & (dataframe['volume'] > 0) & (dataframe['close'] < dataframe[f'ma_exit_{self.base_nb_candles_exit.value}'] * self.high_offset.value), ['enter_long', 'enter_tag']] = (1, 'ewolow') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append((dataframe['close'] > dataframe['sma_9']) & (dataframe['close'] > dataframe[f'ma_exit_{self.base_nb_candles_exit.value}'] * self.high_offset_2.value) & (dataframe['rsi'] > 50) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) | (dataframe['close'] < dataframe['hma_50']) & (dataframe['close'] > dataframe[f'ma_exit_{self.base_nb_candles_exit.value}'] * self.high_offset.value) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_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 280.6s
ℹ️ This strategy uses a trailing stop — freqtrade only
re-checks these once per 5m 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 →
- 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 |
|---|---|---|---|---|---|---|---|---|---|
| Jun 2025 | bearish choppy low vol | 1 | +0.23 | 2.30 | 1 | 0 | 100.0 | -0.44 | 0h 40m |
| May 2025 | bullish trending low vol | 1 | +0.18 | 1.80 | 1 | 0 | 100.0 | -0.63 | 0h 00m |
| Feb 2025 | bearish trending low vol | 1 | -0.21 | -2.14 | 0 | 1 | 0.0 | -0.78 | 2h 40m |
| Dec 2024 | bullish trending low vol | 2 | +0.29 | 1.45 | 2 | 0 | 100.0 | -0.69 | 1h 10m |
| Jan 2024 | bearish choppy high vol | 4 | +0.38 | 0.95 | 3 | 1 | 75.0 | -1.23 | 0h 44m |
| Dec 2023 | bullish trending low vol | 1 | +0.11 | 1.07 | 1 | 0 | 100.0 | -1.16 | 0h 50m |
| Aug 2023 | bearish choppy low vol | 4 | -1.05 | -2.62 | 3 | 1 | 75.0 | -1.43 | 2h 45m |
| Jul 2023 | bullish trending low vol | 1 | +0.17 | 1.74 | 1 | 0 | 100.0 | 0.0 | 1h 15m |
| Jan 2023 | bullish trending low vol | 6 | +1.20 | 2.00 | 6 | 0 | 100.0 | 0.0 | 0h 39m |
| Nov 2022 | bearish trending high vol | 1 | -0.18 | -1.84 | 0 | 1 | 0.0 | -0.15 | 1h 15m |
| Jul 2022 | bearish trending high vol | 5 | +0.70 | 1.40 | 4 | 1 | 80.0 | -0.58 | 0h 58m |
| Jun 2022 | bearish trending high vol | 4 | +0.08 | 0.21 | 2 | 2 | 50.0 | -0.84 | 1h 06m |
| Jul 2021 | bearish trending high vol | 1 | +0.25 | 2.47 | 1 | 0 | 100.0 | -0.57 | 1h 00m |
| Jun 2021 | bearish trending high vol | 2 | +0.17 | 0.85 | 1 | 1 | 50.0 | -0.77 | 1h 55m |
| May 2021 | bearish trending high vol | 7 | -0.73 | -1.06 | 4 | 3 | 57.1 | -1.0 | 1h 11m |
| Feb 2021 | bullish trending high vol | 45 | +4.68 | 1.04 | 36 | 9 | 80.0 | -0.41 | 0h 52m |
| Jan 2021 | bullish trending high vol | 129 | +13.94 | 1.08 | 104 | 25 | 80.6 | -3.12 | 0h 43m |
Yearly breakdown
| Year | Trades | Profit % | Avg % | Win | Loss | Win % | DD % | Avg dur |
|---|---|---|---|---|---|---|---|---|
| 2025 | 3 | +0.20 | 0.65 | 2 | 1 | 66.7 | -0.78 | 1h 07m |
| 2024 | 6 | +0.67 | 1.12 | 5 | 1 | 83.3 | -1.23 | 0h 53m |
| 2023 | 12 | +0.43 | 0.36 | 11 | 1 | 91.7 | -1.43 | 1h 25m |
| 2022 | 10 | +0.60 | 0.60 | 6 | 4 | 60.0 | -0.84 | 1h 03m |
| 2021 | 184 | +18.31 | 0.99 | 146 | 38 | 79.3 | -3.12 | 0h 47m |
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 | |
|---|---|---|---|
| 66 | review | startup_candles_too_small | startup_candle_count is 200, but EMA(timeperiod=100) needing 3x warmup needs at least 300 candles -- so the first 100+ 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 |
| 109 | review | enter_tag_overwrite | enter_tag/exit_tag is written by 4 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.