Freqtrade Strategy

ReinforcedAverageStrategy

4h candlesMoving-average crossoverSpot, long only

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.

ReinforcedAverageStrategy account balance compared with holding Bitcoin$0$1,000$2,000$3,000$4,000JulOctJan 2024AprJulOctJan 2025AprReinforcedAverageStrategy $2,265BTC hold $3,880
Account balance from $1,000 (closed trades, daily). Dashed line: $1,000 held in BTC over the same days.
Total return+126.5%$2,265 final balance
Max drawdown9.7%9 Dec 2024 to 29 May 2025
Win rate33.2%109 wins / 219 losses
Profit factor1.93Gross wins ÷ gross losses
Trades3280.5 per day
Average trade+1.93%after fees
Average hold3d 15hwinners 7d 9h, losers 1d 18h
Annualised (CAGR)+50.6%Sharpe 1.39 · Sortino 6.51

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.

JFMAMJJASOND2023+183+82+1852024-103+247+259-29+3-10-13-24+30+40+416+1052025+15-58-14+0-47

Results by coin

PairTradesWin rateProfit (USDT)Avg. hold
DOGE/USDT2842.9%+212.753d 10h
SOL/USDT4533.3%+208.983d 12h
BNB/USDT5038.0%+197.374d 2h
AVAX/USDT1637.5%+175.644d 3h
DOT/USDT1631.2%+148.044d 5h
BTC/USDT4637.0%+120.254d 12h
XRP/USDT3333.3%+87.943d 13h
ADA/USDT2532.0%+69.072d 20h
LINK/USDT3016.7%+25.212d 16h
ETH/USDT3928.2%+19.573d 3h

Why trades closed

Exit reasonTradesWin rateProfit (USDT)
Take-profit (ROI)14100.0%+1,401.52
Still open at end of test250.0%+6.12
Exit signal31030.3%−62.11
Stoploss20.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.