EMA Trend Crossovers Confirmed by On-Balance Volume
Summary
This short-timeframe strategy uses a 20-period exponential moving average as a trend line and on-balance volume (OBV) as confirmation. It enters long when price crosses above the EMA and OBV rises from the prior candle; it enters short when price crosses below the EMA and OBV falls. Exit conditions also use price crossings, with the OBV direction specified separately for long and short exits. The framework sets a five-minute timeframe, a tiered return-on-investment schedule, a fixed stop loss, and a trailing stop configuration.
The source is an implementation example rather than a research report: it provides no market or exchange, backtest period, performance results, or evidence for the signal choices. The entry and exit rules can also coincide only under particular OBV directions, so the volume confirmation affects both opening and closing behavior. The stated risk controls are configuration values, not proof that losses or drawdowns will be limited in practice. Users would need to evaluate the strategy across assets, fees, and market conditions before drawing conclusions.
Key ideas
- A 20-period EMA defines the price trend threshold on a five-minute timeframe.
- Long and short entries require price to cross the EMA with OBV moving in the same directional sense.
- Exit signals use EMA crossings paired with distinct OBV conditions.
- The implementation includes tiered profit targets, a fixed stop loss, and trailing-stop settings.
- No backtest results or tested market are provided.
Tags
Full text
# TrendFollowingStrategy
# TrendFollowingStrategy
## Source (GPL-3.0)
```python
from functools import reduce
from pandas import DataFrame
from freqtrade.strategy import IStrategy
import talib.abstract as ta
from freqtrade.strategy.interface import IStrategy
class TrendFollowingStrategy(IStrategy):
INTERFACE_VERSION: int = 3
# ROI table:
minimal_roi = {"0": 0.15, "30": 0.1, "60": 0.05}
# minimal_roi = {"0": 1}
# Stoploss:
stoploss = -0.265
# Trailing stop:
trailing_stop = True
trailing_stop_positive = 0.05
trailing_stop_positive_offset = 0.1
trailing_only_offset_is_reached = False
timeframe = "5m"
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Calculate OBV
dataframe['obv'] = ta.OBV(dataframe['close'], dataframe['volume'])
# Add your trend following indicators here
dataframe['trend'] = dataframe['close'].ewm(span=20, adjust=False).mean()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Add your trend following buy signals here
dataframe.loc[
(dataframe['close'] > dataframe['trend']) &
(dataframe['close'].shift(1) <= dataframe['trend'].shift(1)) &
(dataframe['obv'] > dataframe['obv'].shift(1)),
'enter_long'] = 1
# Add your trend following sell signals here
dataframe.loc[
(dataframe['close'] < dataframe['trend']) &
(dataframe['close'].shift(1) >= dataframe['trend'].shift(1)) &
(dataframe['obv'] < dataframe['obv'].shift(1)),
'enter_short'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Add your trend following exit signals for long positions here
dataframe.loc[
(dataframe['close'] < dataframe['trend']) &
(dataframe['close'].shift(1) >= dataframe['trend'].shift(1)) &
(dataframe['obv'] > dataframe['obv'].shift(1)),
'exit_long'] = 1
# Add your trend following exit signals for short positions here
dataframe.loc[
(dataframe['close'] > dataframe['trend']) &
(dataframe['close'].shift(1) <= dataframe['trend'].shift(1)) &
(dataframe['obv'] < dataframe['obv'].shift(1)),
'exit_short'] = 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.