MACDStrategy
Tested 1 Jun 2023 – 30 May 2025
Rank 9 of 27 by return
MACDStrategy turned $1,000 into $1,229 (+22.9%) over 729 days, while holding Bitcoin returned +288.1% and an equal mix of the ten coins +194.8%. Its worst drawdown was 39.2%, a substantial dip to sit through. It won 97% of trades with small targets; a profit factor of 1.11 shows how thin that edge is.
How MACDStrategy trades
MACDStrategy buys when the MACD line is above its signal line while CCI is at or below -48, catching a momentum turn from a weak reading. It sells when MACD falls below the signal line with CCI above 687.
The sell threshold of 687 is so high that the exit signal almost never fires. In practice trades close through the stepped take-profit (5% falling to 1%) or the 30% stoploss.
Rules at a glance
- Indicators
- MACD, CCI
- Take-profit (minimal ROI)
- 5% from entry; 4% after 20m; 3% after 30m; 1% after 1h 0m
- Stoploss
- −30%
- 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 |
|---|---|---|---|---|
| BNB/USDT | 113 | 98.2% | +104.65 | 3d 11h |
| ADA/USDT | 124 | 97.6% | +90.96 | 1d 19h |
| AVAX/USDT | 127 | 96.9% | +66.11 | 1d 22h |
| LINK/USDT | 111 | 96.4% | +40.27 | 2d 13h |
| DOGE/USDT | 109 | 96.3% | +32.00 | 2d 3h |
| BTC/USDT | 79 | 96.2% | +25.98 | 6d 20h |
| XRP/USDT | 127 | 96.1% | +6.66 | 2d 17h |
| ETH/USDT | 72 | 95.8% | −41.56 | 7d 4h |
| SOL/USDT | 155 | 96.1% | −44.97 | 1d 15h |
| DOT/USDT | 123 | 95.9% | −50.90 | 2d 3h |
Why trades closed
| Exit reason | Trades | Win rate | Profit (USDT) |
|---|---|---|---|
| Take-profit (ROI) | 1101 | 100.0% | +2,379.48 |
| Still open at end of test | 5 | 0.0% | −99.07 |
| Stoploss | 34 | 0.0% | −2,051.22 |
The code
MACDStrategy comes from the community freqtrade-strategies repository (GPL-3.0). These are the parts that decide when it trades; the full file is 103 lines.
Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['macd'] > dataframe['macdsignal']) & (dataframe['cci'] <= self.buy_cci.value) & (dataframe['volume'] > 0) # Make sure Volume is not 0 ), 'enter_long'] = 1 return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['macd'] < dataframe['macdsignal']) & (dataframe['cci'] >= self.sell_cci.value) & (dataframe['volume'] > 0) # Make sure Volume is not 0 ), 'exit_long'] = 1 return dataframe
Indicators
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] dataframe['macdhist'] = macd['macdhist'] dataframe['cci'] = ta.CCI(dataframe) 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 MACDStrategy --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.