Freqtrade Strategy

Supertrend

1h candlesTrend followingSpot, long only

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

Supertrend turned $1,000 into $1,683 (+68.3%) over 729 days, while holding Bitcoin returned +288.1% and an equal mix of the ten coins +194.8%. Its worst drawdown was 27.0%, a substantial dip to sit through. It won 61% of trades for a profit factor of 1.13.

Supertrend account balance compared with holding Bitcoin$0$1,000$2,000$3,000$4,000JulOctJan 2024AprJulOctJan 2025AprSupertrend $1,683BTC hold $3,880
Account balance from $1,000 (closed trades, daily). Dashed line: $1,000 held in BTC over the same days.
Total return+68.3%$1,683 final balance
Max drawdown27.0%4 Dec 2024 to 2 Apr 2025
Win rate60.9%952 wins / 610 losses
Profit factor1.13Gross wins ÷ gross losses
Trades1,5632.1 per day
Average trade+0.22%after fees
Average hold1d 8hwinners 1d 1h, losers 1d 19h
Annualised (CAGR)+29.8%Sharpe 1.92 · Sortino 2.35

How Supertrend trades

Supertrend enters on the 1-hour chart only when three Supertrend indicators with different settings all point up, and exits only when three separate sell-side Supertrends all point down. A trailing stop locks in gains after a 14.4% move.

Requiring three indicators to agree reduces false signals but delays entries and exits. The author notes the Supertrend implementation has not been independently validated.

Rules at a glance

Indicators
3 × Supertrend (buy), 3 × Supertrend (sell)
Take-profit (minimal ROI)
8.7% from entry; 5.8% after 6h 12m; 2.9% after 14h 21m; 0% after 1d 13h
Stoploss
−26.5%
Trailing stop
Trails 5% behind the high once profit passes 14.4%
Exit signal
Used

Results by month

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

JFMAMJJASOND2023-90+35-120+18+234+33+2032024-87+252-38-107+66-133+39-66+110-22+601-1202025+62-155-157+43+81

Results by coin

PairTradesWin rateProfit (USDT)Avg. hold
DOGE/USDT14967.1%+343.131d 4h
SOL/USDT21768.2%+194.831d 6h
AVAX/USDT13664.7%+116.981d 2h
BTC/USDT20058.5%+84.461d 12h
LINK/USDT13461.9%+63.771d 6h
ETH/USDT18058.9%+41.251d 9h
XRP/USDT14456.2%−4.891d 12h
ADA/USDT14658.9%−33.561d 7h
BNB/USDT16953.8%−51.681d 13h
DOT/USDT8859.1%−71.171d 7h

Why trades closed

Exit reasonTradesWin rateProfit (USDT)
Take-profit (ROI)110086.5%+5,873.78
Still open at end of test30.0%−21.53
Trailing stop10.0%−50.05
Exit signal4590.0%−5,119.08

The code

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

Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
           (dataframe[f'supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}'] == 'up') &
           (dataframe[f'supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}'] == 'up') &
           (dataframe[f'supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}'] == 'up') & # The three indicators are 'up' for the current candle
           (dataframe['volume'] > 0) # There is at least some trading volume
    ),
        'enter_long'] = 1

    return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
           (dataframe[f'supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}'] == 'down') &
           (dataframe[f'supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}'] == 'down') &
           (dataframe[f'supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}'] == 'down') & # The three indicators are 'down' for the current candle
           (dataframe['volume'] > 0) # There is at least some trading volume
        ),
        'exit_long'] = 1

    return dataframe
Indicators
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    new_cols = []

    for multiplier in self.buy_m1.range:
        for period in self.buy_p1.range:
            new_cols.append(self.supertrend_direction(
                dataframe, multiplier, period, f'supertrend_1_buy_{multiplier}_{period}'))

    for multiplier in self.buy_m2.range:
        for period in self.buy_p2.range:
            new_cols.append(self.supertrend_direction(
                dataframe, multiplier, period, f'supertrend_2_buy_{multiplier}_{period}'))

    for multiplier in self.buy_m3.range:
        for period in self.buy_p3.range:
            new_cols.append(self.supertrend_direction(
                dataframe, multiplier, period, f'supertrend_3_buy_{multiplier}_{period}'))

    for multiplier in self.sell_m1.range:
        for period in self.sell_p1.range:
            new_cols.append(self.supertrend_direction(
                dataframe, multiplier, period, f'supertrend_1_sell_{multiplier}_{period}'))

    for multiplier in self.sell_m2.range:
        for period in self.sell_p2.range:
            new_cols.append(self.supertrend_direction(
                dataframe, multiplier, period, f'supertrend_2_sell_{multiplier}_{period}'))

    for multiplier in self.sell_m3.range:
        for period in self.sell_p3.range:
            new_cols.append(self.supertrend_direction(
                dataframe, multiplier, period, f'supertrend_3_sell_{multiplier}_{period}'))

    if new_cols:
        dataframe = pd.concat([dataframe] + new_cols, axis=1)

    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 1h --timerange 20230501-20250530
freqtrade backtesting --strategy Supertrend --timeframe 1h \
  --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.