Freqtrade Strategy

Strategy005

5m candlesVolume + oscillatorSpot, long only

Tested 1 Jun 2023 – 30 May 2025
Rank 22 of 27 by return

Strategy005 turned $1,000 into $380 (−62.0%) over 729 days, while holding Bitcoin returned +288.1% and an equal mix of the ten coins +194.8%. Its worst drawdown was a severe 68.3%, deep enough that most people would have switched it off. It won 83% of its trades yet still lost money, because its few losing trades were much larger than its many small wins.

Strategy005 account balance compared with holding Bitcoin$0$1,000$2,000$3,000$4,000JulOctJan 2024AprJulOctJan 2025AprStrategy005 $380BTC hold $3,880
Account balance from $1,000 (closed trades, daily). Dashed line: $1,000 held in BTC over the same days.
Total return−62.0%$380 final balance
Max drawdown68.3%11 Dec 2023 to 6 Apr 2025
Win rate83.1%755 wins / 154 losses
Profit factor0.80Gross wins ÷ gross losses
Trades9091.2 per day
Average trade−0.34%after fees
Average hold1d 17hwinners 1d 9h, losers 3d 9h
Annualised (CAGR)−38.4%Sharpe -1.82 · Sortino -13.26

How Strategy005 trades

Strategy005 waits for a volume spike of four times the 150-candle average while price is below its 40-period SMA and the Fisher RSI is deeply oversold. It exits when RSI crosses above 74 while MACD is negative.

Volume spikes on a falling price can mark capitulation or the start of a larger drop. The ROI table stretches to 24 hours, the longest of the sample strategies.

Rules at a glance

Indicators
Volume vs. 150-candle average, RSI, Fisher RSI, Fast Stochastic, MACD, -DI
Take-profit (minimal ROI)
5% from entry; 4% after 20m; 3% after 40m; 2% after 1h 20m; 1% after 1d 0h
Stoploss
−10%
Trailing stop
Off
Exit signal
Used

Results by month

Profit or loss in USDT for trades closed in each month.

JFMAMJJASOND2023-95+51-69-25+55+49+542024-168+74+91-254+40-102+8-119+37-26+96-762025-48-166-63+16+19

Results by coin

PairTradesWin rateProfit (USDT)Avg. hold
DOGE/USDT10190.1%+121.431d 3h
BTC/USDT8491.7%+78.451d 13h
ETH/USDT8289.0%+27.062d 18h
LINK/USDT9486.2%+8.481d 8h
BNB/USDT9986.9%−11.462d 23h
ADA/USDT9580.0%−126.571d 13h
XRP/USDT6876.5%−129.932d 7h
SOL/USDT9277.2%−166.301d 1h
DOT/USDT10078.0%−180.541d 11h
AVAX/USDT9474.5%−241.111d 5h

Why trades closed

Exit reasonTradesWin rateProfit (USDT)
Take-profit (ROI)755100.0%+2,500.03
Still open at end of test10.0%−5.09
Stoploss1530.0%−3,115.42

The code

Strategy005 comes from the community freqtrade-strategies repository (GPL-3.0). These are the parts that decide when it trades; the full file is 185 lines.

Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        # Prod
        (
            (dataframe['close'] > 0.00000200) &
            (dataframe['volume'] > dataframe['volume'].rolling(self.buy_volumeAVG.value).mean() * 4) &
            (dataframe['close'] < dataframe['sma']) &
            (dataframe['fastd'] > dataframe['fastk']) &
            (dataframe['rsi'] > self.buy_rsi.value) &
            (dataframe['fastd'] > self.buy_fastd.value) &
            (dataframe['fisher_rsi_norma'] < self.buy_fishRsiNorma.value)
        ),
        'enter_long'] = 1

    return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

    conditions = []
    if self.sell_trigger.value == 'rsi-macd-minusdi':
        conditions.append(qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value))
        conditions.append(dataframe['macd'] < 0)
        conditions.append(dataframe['minus_di'] > self.sell_minusDI.value)
    if self.sell_trigger.value == 'sar-fisherRsi':
        conditions.append(dataframe['sar'] > dataframe['close'])
        conditions.append(dataframe['fisher_rsi'] > self.sell_fishRsiNorma.value)

    if conditions:
        dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1

    return dataframe
Indicators
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

    # MACD
    macd = ta.MACD(dataframe)
    dataframe['macd'] = macd['macd']
    dataframe['macdsignal'] = macd['macdsignal']

    # Minus Directional Indicator / Movement
    dataframe['minus_di'] = ta.MINUS_DI(dataframe)

    # RSI
    dataframe['rsi'] = ta.RSI(dataframe)

    # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
    rsi = 0.1 * (dataframe['rsi'] - 50)
    dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1)
    # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
    dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)

    # Stoch fast
    stoch_fast = ta.STOCHF(dataframe)
    dataframe['fastd'] = stoch_fast['fastd']
    dataframe['fastk'] = stoch_fast['fastk']

    # Overlap Studies
    # ------------------------------------

    # SAR Parabol
    dataframe['sar'] = ta.SAR(dataframe)

    # SMA - Simple Moving Average
    dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)

    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 5m --timerange 20230501-20250530
freqtrade backtesting --strategy Strategy005 --timeframe 5m \
  --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.