ReinforcedAverageStrategy
Tested 1 Jun 2023 – 30 May 2025
Rank 1 of 27 by return
ReinforcedAverageStrategy turned $1,000 into $2,265 (+126.5%) over 729 days, while holding Bitcoin returned +288.1% and an equal mix of the ten coins +194.8%. Its worst drawdown was a relatively contained 9.7%. It lost more trades than it won (33% win rate) but its winners were large enough for a profit factor of 1.93.
How ReinforcedAverageStrategy trades
This is AverageStrategy with a trend filter. It still buys the 8/21 EMA cross on the 4-hour chart, but only when price is above a 50-period SMA calculated on a 12-times-longer timeframe (2 days).
Compare it directly with AverageStrategy on this site to see what a higher-timeframe filter does to trade count, win rate and drawdown.
Rules at a glance
- Indicators
- EMA 8 / 21, Resampled SMA 50
- Take-profit (minimal ROI)
- 50% from entry
- Stoploss
- −20%
- Trailing stop
- Off
- Exit signal
- Used
Results by month
Profit or loss in USDT for trades closed in each month.
Results by coin
| Pair | Trades | Win rate | Profit (USDT) | Avg. hold |
|---|---|---|---|---|
| DOGE/USDT | 28 | 42.9% | +212.75 | 3d 10h |
| SOL/USDT | 45 | 33.3% | +208.98 | 3d 12h |
| BNB/USDT | 50 | 38.0% | +197.37 | 4d 2h |
| AVAX/USDT | 16 | 37.5% | +175.64 | 4d 3h |
| DOT/USDT | 16 | 31.2% | +148.04 | 4d 5h |
| BTC/USDT | 46 | 37.0% | +120.25 | 4d 12h |
| XRP/USDT | 33 | 33.3% | +87.94 | 3d 13h |
| ADA/USDT | 25 | 32.0% | +69.07 | 2d 20h |
| LINK/USDT | 30 | 16.7% | +25.21 | 2d 16h |
| ETH/USDT | 39 | 28.2% | +19.57 | 3d 3h |
Why trades closed
| Exit reason | Trades | Win rate | Profit (USDT) |
|---|---|---|---|
| Take-profit (ROI) | 14 | 100.0% | +1,401.52 |
| Still open at end of test | 2 | 50.0% | +6.12 |
| Exit signal | 310 | 30.3% | −62.11 |
| Stoploss | 2 | 0.0% | −80.71 |
The code
ReinforcedAverageStrategy comes from the community freqtrade-strategies repository (GPL-3.0). These are the parts that decide when it trades; the full file is 97 lines.
Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( qtpylib.crossed_above(dataframe['maShort'], dataframe['maMedium']) & (dataframe['close'] > dataframe[f'resample_{self.resample_interval}_sma']) & (dataframe['volume'] > 0) ), 'enter_long'] = 1 return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( qtpylib.crossed_above(dataframe['maMedium'], dataframe['maShort']) & (dataframe['volume'] > 0) ), 'exit_long'] = 1 return dataframe
Indicators
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['maShort'] = ta.EMA(dataframe, timeperiod=8) dataframe['maMedium'] = ta.EMA(dataframe, timeperiod=21) ################################################################################## # required for graphing bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_upperband'] = bollinger['upper'] dataframe['bb_middleband'] = bollinger['mid'] self.resample_interval = timeframe_to_minutes(self.timeframe) * 12 dataframe_long = resample_to_interval(dataframe, self.resample_interval) dataframe_long['sma'] = ta.SMA(dataframe_long, timeperiod=50, price='close') dataframe = resampled_merge(dataframe, dataframe_long, fill_na=True) return dataframe
Reproduce this test
With freqtrade installed (see our Docker guide), download the data and run the same backtest:
freqtrade download-data --exchange binance --pairs ADA/USDT AVAX/USDT BNB/USDT BTC/USDT DOGE/USDT DOT/USDT ETH/USDT LINK/USDT SOL/USDT XRP/USDT --timeframes 4h --timerange 20230501-20250530
freqtrade backtesting --strategy ReinforcedAverageStrategy --timeframe 4h \
--timerange 20230601-20250530 --dry-run-wallet 1000 --stake-amount 200 --max-open-trades 5 --fee 0.001
Results can differ slightly with a different data source or freqtrade version. See how we test.