Simple
Tested 1 Jun 2023 – 30 May 2025
Rank 12 of 27 by return
Simple turned $1,000 into $906 (−9.4%) 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 53.4%, deep enough that most people would have switched it off. It won 86% of its trades yet still lost money, because its few losing trades were much larger than its many small wins.
How Simple trades
Simple buys strength rather than weakness: MACD positive and above its signal, the upper Bollinger Band expanding and a fast RSI above 70. It sells when RSI passes 80, or at a 1% take-profit.
Buying when RSI is already above 70 means entering after a move has started. The tiny 1% target against a 25% stoploss is a poor payoff ratio.
Rules at a glance
- Indicators
- MACD, RSI 7, Bollinger Bands 12/2
- Take-profit (minimal ROI)
- 1% from entry
- Stoploss
- −25%
- 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 |
|---|---|---|---|---|
| BTC/USDT | 308 | 84.1% | +131.58 | 1d 13h |
| SOL/USDT | 399 | 91.5% | +108.37 | 19h 5m |
| ETH/USDT | 361 | 84.5% | +96.94 | 1d 3h |
| LINK/USDT | 251 | 88.8% | +63.38 | 18h 42m |
| ADA/USDT | 324 | 88.0% | −6.99 | 22h 47m |
| BNB/USDT | 276 | 79.0% | −20.20 | 1d 12h |
| DOT/USDT | 243 | 85.6% | −49.12 | 19h 38m |
| XRP/USDT | 318 | 84.9% | −74.26 | 1d 2h |
| DOGE/USDT | 290 | 90.0% | −75.46 | 21h 53m |
| AVAX/USDT | 280 | 85.7% | −268.15 | 22h 13m |
Why trades closed
| Exit reason | Trades | Win rate | Profit (USDT) |
|---|---|---|---|
| Take-profit (ROI) | 2566 | 100.0% | +5,137.21 |
| Still open at end of test | 4 | 0.0% | −40.50 |
| Stoploss | 30 | 0.0% | −1,510.13 |
| Exit signal | 450 | 15.1% | −3,680.49 |
The code
Simple comes from the community freqtrade-strategies repository (GPL-3.0). These are the parts that decide when it trades; the full file is 76 lines.
Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( ( (dataframe['macd'] > 0) # over 0 & (dataframe['macd'] > dataframe['macdsignal']) # over signal & (dataframe['bb_upperband'] > dataframe['bb_upperband'].shift(1)) # pointed up & (dataframe['rsi'] > 70) # optional filter, need to investigate ) ), 'enter_long'] = 1 return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # different strategy used for sell points, due to be able to duplicate it to 100% dataframe.loc[ ( (dataframe['rsi'] > 80) ), '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'] dataframe['macdhist'] = macd['macdhist'] # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=7) # required for graphing bollinger = qtpylib.bollinger_bands(dataframe['close'], window=12, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_upperband'] = bollinger['upper'] dataframe['bb_middleband'] = bollinger['mid'] 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 Simple --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.