Skip to content
All library documents

Moving Average Crossovers Filtered by ADX with ATR Risk Levels

Article Strategy library · Author: Jesse community

Summary

This strategy enters when a fast moving average crosses a slow one, with the crossover direction determining whether it takes a long or short position. It requires ADX to exceed a configurable entry threshold, using trend strength as a filter. The code allows different moving-average types, lookback periods, and price sources, so those choices can be varied through its hyperparameters.

Positions receive an ATR-based stop loss and take-profit level, with separate configurable multipliers. Position quantity is calculated from account balance and the distance to the stop, and a later moving-average relationship combined with a lower ADX reading can trigger liquidation. The strategy cancels pending entries. The document provides implementation details and parameter ranges, but no market, backtest results, or evidence that the settings generalize. Its performance and sensitivity to parameter selection therefore remain unestablished.

Key ideas

  • A fast and slow moving-average crossover sets the entry direction.
  • An ADX threshold filters entries based on trend strength.
  • ATR-based stop and target distances scale with recent volatility.
  • Position quantity is based on account balance and stop distance.
  • The document provides no backtest results or evidence of robustness across markets.

Tags

Full text
# MAGen


# MAGen









############################################################# #############################################################

## Source (MIT)

```python
import jesse.indicators as ta
from jesse import utils
from jesse.strategies import Strategy

class MAGen(Strategy):

    def should_long(self) -> bool:
        return self.longEntry

    def should_short(self) -> bool:
        return self.shortEntry

    def go_long(self):
        entry = self.price
        stop = entry - self.atr * self.hp['stop_loss_atr_rate']
        qty = utils.risk_to_qty(self.balance, 3, entry, stop)
        take_profit = entry + self.atr * self.hp['take_profit_atr_rate']
        self.buy = qty, entry
        self.stop_loss = qty, stop
        self.take_profit = qty, take_profit

    def go_short(self):
        entry = self.price
        stop = entry + self.atr * self.hp['stop_loss_atr_rate']
        qty = utils.risk_to_qty(self.balance, 3, entry, stop)
        take_profit = entry - self.atr * self.hp['take_profit_atr_rate']
        self.sell = qty, entry
        self.stop_loss = qty, stop
        self.take_profit = qty, take_profit

    def should_cancel_entry(self) -> bool:
        return True

    def update_position(self):
        if (self.is_short and self.shortExit) or (self.is_long and self.longExit):
            self.liquidate()

    ################################################################
    # # # # # # # # # # # # # indicators # # # # # # # # # # # # # #
    ################################################################

    @property
    def longEntry(self):
        return self.trend_direction_change == 1 and self.adx > self.hp['adx_entry']

    @property
    def shortEntry(self):
        return self.trend_direction_change == -1 and self.adx > self.hp['adx_entry']

    @property
    def longExit(self):
        return self.ma_fast[-1] < self.ma_slow[-1] and self.adx < self.hp['adx_exit']

    @property
    def shortExit(self):
        return self.ma_fast[-1] > self.ma_slow[-1] and self.adx < self.hp['adx_exit']

    @property
    def adx(self):
        return ta.adx(self.candles, period=self.hp['adx_period'])

    @property
    def trend_direction_change(self):
        direction = 0
        if self.ma_fast[-1] < self.ma_slow[-1] and self.ma_fast[-2] >= self.ma_slow[-2]:
            direction = -1
        if self.ma_fast[-1] > self.ma_slow[-1] and self.ma_fast[-2] <= self.ma_slow[-2]:
            direction = 1
        return direction


    @property
    def ma_slow(self):
        if self.hp['ma_source_slow'] == 0:
            source = "close"
        elif self.hp['ma_source_slow'] == 1:
            source = "high"
        elif self.hp['ma_source_slow'] == 2:
            source = "low"
        elif self.hp['ma_source_slow'] == 3:
            source = "open"
        elif self.hp['ma_source_slow'] == 4:
            source = "hl2"
        elif self.hp['ma_source_slow'] == 5:
            source = "hlc3"
        elif self.hp['ma_source_slow'] == 6:
            source = "ohlc4"

        return ta.ma(self.candles, matype=self.hp['ma_type_slow'], period=self.hp['ma_period_slow'], source_type=source, sequential=True)

    @property
    def ma_fast(self):

        if self.hp['ma_source_fast'] == 0:
            source = "close"
        elif self.hp['ma_source_fast'] == 1:
            source = "high"
        elif self.hp['ma_source_fast'] == 2:
            source = "low"
        elif self.hp['ma_source_fast'] == 3:
            source = "open"
        elif self.hp['ma_source_fast'] == 4:
            source = "hl2"
        elif self.hp['ma_source_fast'] == 5:
            source = "hlc3"
        elif self.hp['ma_source_fast'] == 6:
            source = "ohlc4"

        return ta.ma(self.candles, matype=self.hp['ma_type_fast'], period=self.hp['ma_period_fast'], source_type=source, sequential=True)

    @property
    def atr(self):
        return ta.atr(self.candles, period=self.hp['atr_period'])

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # # Genetic
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    def hyperparameters(self):
        return [
            {'name': 'stop_loss_atr_rate', 'type': float, 'min': 1, 'max': 4, 'default': 2},
            {'name': 'take_profit_atr_rate', 'type': float, 'min': 3, 'max': 20, 'default': 5},
            {'name': 'atr_period', 'type': int, 'min': 5, 'max': 40, 'default': 32},
            {'name': 'ma_period_slow', 'type': int, 'min': 3, 'max': 200, 'default': 20},
            {'name': 'ma_source_slow', 'type': int, 'min': 0, 'max': 6, 'default': 0},
            {'name': 'ma_period_fast', 'type': int, 'min': 3, 'max': 100, 'default': 5},
            {'name': 'ma_source_fast', 'type': int, 'min': 0, 'max': 6, 'default': 0},
            {'name': 'ma_type_slow', 'type': int, 'min': 0, 'max': 39, 'default': 11},
            {'name': 'ma_type_fast', 'type': int, 'min': 0, 'max': 39, 'default': 11},
            {'name': 'adx_period', 'type': int, 'min': 3, 'max': 60, 'default': 8},
            {'name': 'adx_exit', 'type': int, 'min': 3, 'max': 40, 'default': 15},
            {'name': 'adx_entry', 'type': int, 'min': 3, 'max': 40, 'default': 13},
        ]
```

Shown in full with attribution under the source's licence. Licence: MIT

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.