Skip to content
All library documents

Dual Thrust Breakout Entries with ATR Stop Losses

Article Strategy library · Author: Jesse community

Summary

This implementation of the Dual Thrust strategy sets upper and lower entry thresholds around the latest anchor-timeframe open. Each threshold is offset by a coefficient times the larger of two recent price ranges, calculated from rolling highs, lows, and closes over separately configurable lookback lengths. A move above the upper threshold signals a long entry; a move below the lower threshold signals a short entry.

Entries use an ATR-based stop distance and size positions through a risk-to-quantity function. An opposite signal liquidates an open position, and pending entries are canceled. The code exposes the lookback lengths, threshold coefficients, and stop multiplier as tunable parameters, but gives no market, timeframe, backtest, or performance evidence. The strategy therefore describes a configurable breakout method rather than demonstrating its profitability; the range calculations, anchor timeframe behavior, parameter selection, and execution assumptions require validation.

Key ideas

  • Upper and lower thresholds are offset from the latest anchor-timeframe open by scaled historical ranges.
  • The range calculation uses the larger of two high-low and close-based spans.
  • Crossing the upper threshold triggers a long entry, while crossing the lower threshold triggers a short entry.
  • ATR determines stop distance, and a risk-based sizing function sets quantity.
  • An opposite signal closes the position, but no backtest results or validation evidence are provided.

Tags

Full text
# DUAL_THRUST


# DUAL_THRUST









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

## Source (MIT)

```python
from jesse.strategies import Strategy, cached
import jesse.indicators as ta
from jesse import utils
import numpy as np

# https://medium.com/@gaea.enquiries/quantitative-strategy-research-series-one-the-dual-thrust-38380b38c2fa
# Dual Thrust by Michael Chalek

class DUAL_THRUST(Strategy):

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

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

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

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

    def update_position(self):
        if (self.is_long and self.short_cond) or (self.is_short and self.long_cond):
            self.liquidate()

    def should_cancel_entry(self) -> bool:
        return True

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

    @property
    def up_min_low(self):
        return np.min(self.candles[:, 4][-self.hp['up_length']:])

    @property
    def up_min_close(self):
        return np.min(self.candles[:, 2][-self.hp['up_length']:])

    @property
    def up_max_close(self):
        return np.max(self.candles[:, 2][-self.hp['up_length']:])

    @property
    def up_max_high(self):
        return np.max(self.candles[:, 3][-self.hp['up_length']:])

    @property
    def down_min_low(self):
        return np.min(self.candles[:, 4][-self.hp['down_length']:])

    @property
    def down_min_close(self):
        return np.min(self.candles[:, 2][-self.hp['down_length']:])

    @property
    def down_max_close(self):
        return np.max(self.candles[:, 2][-self.hp['down_length']:])

    @property
    def down_max_high(self):
        return np.max(self.candles[:, 4][-self.hp['down_length']:])

    @property
    def up_thurst(self):
        return self.anchor_candles[:, 1][-1] + self.hp['up_coeff'] * max(self.up_max_close - self.up_min_low, self.up_max_high - self.up_min_close)

    @property
    def down_thrust(self):
        return self.anchor_candles[:, 1][-1] - self.hp['down_coeff'] * max(self.down_max_close - self.down_min_low, self.down_max_high - self.down_min_close)

    @property
    @cached
    def anchor_candles(self):
        return self.get_candles(self.exchange, self.symbol, utils.anchor_timeframe(self.timeframe))

    @property
    def short_cond(self):
        return self.price < self.down_thrust

    @property
    def long_cond(self):
        return self.price > self.up_thurst

    @property
    def atr(self):
        return ta.atr(self.candles)


    # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # Genetic
    # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    def hyperparameters(self):
        return [
            {'name': 'stop_loss_atr_rate', 'type': float, 'min': 0.1, 'max': 2.0, 'default': 2},
            {'name': 'down_length', 'type': int, 'min': 3, 'max': 30, 'default': 21},
            {'name': 'up_length', 'type': int, 'min': 3, 'max': 30, 'default': 21},
            {'name': 'down_coeff', 'type': float, 'min': 0.1, 'max': 3.0, 'default': 0.67},
            {'name': 'up_coeff', 'type': float, 'min': 0.1, 'max': 3.0, 'default': 0.71},
        ]


```

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.