EMA Crossover Strategy with Tunable Short and Long Periods
Summary
This proof-of-concept strategy uses two exponential moving averages with tunable short and long periods. It enters a long position when the shorter average crosses above the longer one and exits when the longer average crosses above the shorter. The implementation calculates candidate averages across parameter ranges, then uses the selected pair for signal generation. It also requires nonzero volume for both entry and exit signals.
The author characterizes the approach as a basic crossover idea that does not perform especially well, and the document supplies no backtest results or evidence to quantify that assessment. The code specifies a four-hour timeframe, a fixed return-on-investment target, and a stop-loss setting, but those configuration values alone do not demonstrate risk-adjusted performance. Moving-average crossovers can lag and may produce repeated signals in sideways markets; the example is best treated as an illustrative baseline rather than a validated trading system.
Key ideas
- A short-period EMA crossing above a longer-period EMA triggers a long entry.
- The reverse crossover generates an exit signal for the long position.
- Both signals require trading volume to be positive.
- The short and long EMA periods are tunable within specified ranges.
- The author presents the strategy as a poorly performing proof of concept and gives no quantified test evidence.
Tags
Full text
# AverageStrategy
# AverageStrategy
author@: Gert Wohlgemuth
idea:
buys and sells on crossovers - doesn't really perfom that well and its just a proof of concept
## Source (GPL-3.0)
```python
# --- Do not remove these libs ---
from functools import reduce
from freqtrade.strategy import IStrategy
from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter
from pandas import DataFrame
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class AverageStrategy(IStrategy):
"""
author@: Gert Wohlgemuth
idea:
buys and sells on crossovers - doesn't really perfom that well and its just a proof of concept
"""
INTERFACE_VERSION: int = 3
# Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
"0": 0.5
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.2
# Optimal timeframe for the strategy
timeframe = '4h'
buy_range_short = IntParameter(5, 20, default=8)
buy_range_long = IntParameter(20, 120, default=21)
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
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
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
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
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
```Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.