AverageStrategy
Tested 1 Jun 2023 – 30 May 2025
Rank 2 of 27 by return
AverageStrategy turned $1,000 into $2,233 (+123.3%) 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 13.1%. It lost more trades than it won (31% win rate) but its winners were large enough for a profit factor of 1.52.
How AverageStrategy trades
A classic two-line crossover on the 4-hour chart. AverageStrategy buys when the 8-period EMA crosses above the 21-period EMA and sells on the opposite cross. A 50% take-profit and a 20% stoploss act as outer limits.
Crossover systems lose often but let winners run, which is why the win rate is low while the total return can still be positive. Expect long flat or losing stretches in sideways markets.
Rules at a glance
- Indicators
- EMA 8, EMA 21
- Take-profit (minimal ROI)
- 50% from entry
- Stoploss
- −20%
- 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 |
|---|---|---|---|---|
| SOL/USDT | 75 | 36.0% | +298.16 | 3d 17h |
| BNB/USDT | 76 | 36.8% | +199.06 | 4d 3h |
| AVAX/USDT | 33 | 39.4% | +181.46 | 3d 18h |
| BTC/USDT | 77 | 33.8% | +145.50 | 4d 8h |
| ETH/USDT | 74 | 29.7% | +127.70 | 3d 14h |
| DOGE/USDT | 51 | 29.4% | +126.66 | 2d 20h |
| DOT/USDT | 43 | 32.6% | +119.67 | 3d 14h |
| XRP/USDT | 54 | 25.9% | +109.86 | 3d 0h |
| ADA/USDT | 51 | 25.5% | +33.48 | 2d 17h |
| LINK/USDT | 58 | 20.7% | −108.14 | 2d 16h |
Why trades closed
| Exit reason | Trades | Win rate | Profit (USDT) |
|---|---|---|---|
| Take-profit (ROI) | 15 | 100.0% | +1,501.55 |
| Still open at end of test | 2 | 50.0% | +6.12 |
| Stoploss | 2 | 0.0% | −80.71 |
| Exit signal | 573 | 29.3% | −193.54 |
The code
AverageStrategy comes from the community freqtrade-strategies repository (GPL-3.0). These are the parts that decide when it trades; the full file is 79 lines.
Entry rules
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( qtpylib.crossed_above( dataframe[f'ema{self.buy_range_short.value}'], dataframe[f'ema{self.buy_range_long.value}'] ) & (dataframe['volume'] > 0) ), 'enter_long'] = 1 return dataframe
Exit rules
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( qtpylib.crossed_above( dataframe[f'ema{self.buy_range_long.value}'], dataframe[f'ema{self.buy_range_short.value}'] ) & (dataframe['volume'] > 0) ), 'exit_long'] = 1 return dataframe
Indicators
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Combine all ranges ... to avoid duplicate calculation for val in list(set(list(self.buy_range_short.range) + list(self.buy_range_long.range))): dataframe[f'ema{val}'] = ta.EMA(dataframe, timeperiod=val) 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 4h --timerange 20230501-20250530
freqtrade backtesting --strategy AverageStrategy --timeframe 4h \
--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.