Skip to content
All library documents

EMA and Bollinger Band Rules for Avoiding Pump Conditions

Code Freqtrade

Summary

This five-minute long-only strategy is presented as an attempt to avoid pump-and-dump conditions. It calculates short, medium, and long exponential moving averages, 20-period Bollinger Bands, and rolling price extremes. An entry is signaled when the close is below the short and medium averages, equals the medium-period low, and is at or below the lower band, while volume remains below a threshold based on the prior 30-bar average. The exit requires the close to exceed the short and medium averages, reach the medium-period high, and be at or above the upper band.

The configuration sets a 5% stop loss and a minimum return-on-investment threshold of 10% before selling, unless the exit conditions occur first. The long EMA is calculated but does not appear in the displayed entry or exit rules. The code supplies no backtest results, market universe, or evidence that the filters identify pump activity reliably. Its rules are a testable example, not demonstrated performance guidance.

Key ideas

  • The strategy uses a five-minute timeframe and evaluates long entries using EMAs, Bollinger Bands, rolling lows, and volume.
  • Entry requires price weakness relative to short and medium EMAs and a close at the medium-period low near or below the lower band.
  • Exit requires strength relative to the short and medium EMAs, the medium-period high, and the upper Bollinger Band.
  • The configuration specifies a 5% stop loss and a 10% minimum ROI threshold unless exit conditions occur sooner.
  • The code provides no backtest evidence that its rules avoid pump-and-dump conditions.

Tags

Full text
# EMASkipPump.py


```py
from freqtrade.strategy import IStrategy
from typing import Dict, List
from functools import reduce
from pandas import DataFrame
# --------------------------------

import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy  # noqa


class EMASkipPump(IStrategy):

    """
        basic strategy, which trys to avoid pump and dump market conditions. Shared from the tradingview
        slack
    """
    INTERFACE_VERSION: int = 3
    EMA_SHORT_TERM = 5
    EMA_MEDIUM_TERM = 12
    EMA_LONG_TERM = 21

    # Minimal ROI designed for the strategy.
    # we only sell after 100%, unless our sell points are found before
    minimal_roi = {
        "0": 0.1
    }

    # Optimal stoploss designed for the strategy
    # This attribute will be overridden if the config file contains "stoploss"
    # should be converted to a trailing stop loss
    stoploss = -0.05

    # Optimal timeframe for the strategy
    timeframe = '5m'

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """ Adds several different TA indicators to the given DataFrame
        """

        dataframe['ema_{}'.format(self.EMA_SHORT_TERM)] = ta.EMA(
            dataframe, timeperiod=self.EMA_SHORT_TERM
        )
        dataframe['ema_{}'.format(self.EMA_MEDIUM_TERM)] = ta.EMA(
            dataframe, timeperiod=self.EMA_MEDIUM_TERM
        )
        dataframe['ema_{}'.format(self.EMA_LONG_TERM)] = ta.EMA(
            dataframe, timeperiod=self.EMA_LONG_TERM
        )

        bollinger = qtpylib.bollinger_bands(
            qtpylib.typical_price(dataframe), window=20, stds=2
        )
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']

        dataframe['min'] = ta.MIN(dataframe, timeperiod=self.EMA_MEDIUM_TERM)
        dataframe['max'] = ta.MAX(dataframe, timeperiod=self.EMA_MEDIUM_TERM)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe.loc[
            (dataframe['volume'] < (dataframe['volume'].rolling(window=30).mean().shift(1) * 20)) &
            (dataframe['close'] < dataframe['ema_{}'.format(self.EMA_SHORT_TERM)]) &
            (dataframe['close'] < dataframe['ema_{}'.format(self.EMA_MEDIUM_TERM)]) &
            (dataframe['close'] == dataframe['min']) &
            (dataframe['close'] <= dataframe['bb_lowerband']),
            'enter_long'
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe.loc[
            (dataframe['close'] > dataframe['ema_{}'.format(self.EMA_SHORT_TERM)]) &
            (dataframe['close'] > dataframe['ema_{}'.format(self.EMA_MEDIUM_TERM)]) &
            (dataframe['close'] >= dataframe['max']) &
            (dataframe['close'] >= dataframe['bb_upperband']),
            '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.