Freqtrade Strategy

UniversalMACD

5m candlesMomentumSpot, long only

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

UniversalMACD turned $1,000 into $1,747 (+74.7%) 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 7.2%. It won 77% of trades with small targets; a profit factor of 3.91 shows how thin that edge is.

UniversalMACD account balance compared with holding Bitcoin$0$1,000$2,000$3,000$4,000JulOctJan 2024AprJulOctJan 2025AprUniversalMACD $1,747BTC hold $3,880
Account balance from $1,000 (closed trades, daily). Dashed line: $1,000 held in BTC over the same days.
Total return+74.7%$1,747 final balance
Max drawdown7.2%7 Apr 2024 to 18 Jun 2024
Win rate77.3%307 wins / 90 losses
Profit factor3.91Gross wins ÷ gross losses
Trades3970.5 per day
Average trade+0.94%after fees
Average hold1d 13hwinners 1d 2h, losers 3d 4h
Annualised (CAGR)+32.2%Sharpe 2.69 · Sortino 1.49

How UniversalMACD trades

UniversalMACD replaces the usual MACD difference with a ratio (EMA12 / EMA26 − 1) so the signal means the same thing on every coin regardless of price. It buys when that ratio sits in a narrow tuned band just below zero. Its tuned sell band has a lower bound above its upper bound, so the exit signal can never fire; trades close through the stepped take-profit or the stoploss.

Normalising MACD is a sound idea, and the result here is one of the smoothest equity curves in the test. But the entry band is very narrow and the exit is effectively the ROI table alone, so small parameter changes can produce very different results.

Rules at a glance

Indicators
EMA 12 / 26 ratio
Take-profit (minimal ROI)
21.3% from entry; 9.9% after 27m; 3% after 1h 0m; 0% after 2h 44m
Stoploss
−31.8%
Trailing stop
Off
Exit signal
Used

Results by month

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

JFMAMJJASOND2023+45+10+39+1+14+57+582024+54+11+76+0-64-17+8+27+2+16+176+1242025+58+9-20+18+46

Results by coin

PairTradesWin rateProfit (USDT)Avg. hold
XRP/USDT4983.7%+156.904h 5m
DOT/USDT3688.9%+130.631d 16h
SOL/USDT4872.9%+125.1313h 42m
LINK/USDT4870.8%+100.231d 7h
AVAX/USDT6777.6%+89.741d 16h
DOGE/USDT6774.6%+81.4521h 7m
ETH/USDT1888.9%+44.537h 53m
BNB/USDT785.7%+17.1729d 5h
BTC/USDT475.0%+12.531h 55m
ADA/USDT5371.7%−10.951d 15h

Why trades closed

Exit reasonTradesWin rateProfit (USDT)
Take-profit (ROI)39378.1%+1,003.02
Stoploss40.0%−255.65

The code

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

Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            (dataframe['umacd'].between(self.buy_umacd_min.value, self.buy_umacd_max.value))

        ),
        'enter_long'] = 1

    return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            (dataframe['umacd'].between(self.sell_umacd_min.value, self.sell_umacd_max.value))
        ),
        'exit_long'] = 1

    return dataframe
Indicators
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe['ma12'] = ta.EMA(dataframe, timeperiod=12)
    dataframe['ma26'] = ta.EMA(dataframe, timeperiod=26)
    dataframe['umacd'] = (dataframe['ma12'] / dataframe['ma26']) - 1

    # Just for show user the min and max of indicator in different coins to set inside hyperoptable variables.cuz
    # in different timeframes should change the min and max in hyperoptable variables.
    # print(dataframe['umacd'].min(), dataframe['umacd'].max())

    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 UniversalMACD --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.