Skip to content
All library documents

ATR Volatility Filter, RSI Entries, and Trailing Stops on 15-Minute Bars

Article Strategy library · Author: 51bitquant

Summary

This event-driven trading strategy builds 15-minute bars from incoming market data and calculates ATR and RSI. It only considers entries when current ATR is above the recent average of ATR values, using that condition as a volatility filter. RSI thresholds around its midpoint then trigger a long or short order, with the order price set slightly above or below the bar close. Position size is fixed by a configurable quantity.

Once a position is open, the strategy tracks the most favorable price reached and places a trailing stop at a configurable percentage distance. The code cancels outstanding orders on each new bar and refreshes the stop as the trade evolves. The document contains implementation code but no backtest setup, performance evidence, or discussion of market-specific behavior. It also does not specify a separate profit target or show how slippage and order fills affect the strategy, so profitability and execution behavior cannot be inferred from the source alone.

Key ideas

  • The strategy aggregates incoming data into 15-minute bars before calculating signals.
  • Entries require ATR to exceed its recent average and RSI to cross a threshold around the midpoint.
  • Entry orders are placed at prices offset from the signal bar's close.
  • Open positions use a percentage-based trailing stop that follows favorable price movement.
  • The source provides no results to establish performance or execution quality.

Tags

Full text
# AtrRsi15MinStrategy


# AtrRsi15MinStrategy









## Source (MIT)

```python
from howtrader.app.cta_strategy import (
    CtaTemplate,
    StopOrder
)

from howtrader.trader.object import TickData, BarData, TradeData, OrderData
from howtrader.trader.utility import BarGenerator, ArrayManager
from decimal import Decimal


class AtrRsi15MinStrategy(CtaTemplate):
    """"""

    author = "51bitquant"

    atr_length = 22
    atr_ma_length = 10
    rsi_length = 5
    rsi_entry = 16
    trailing_percent = 0.8
    fixed_size = 1

    atr_value = 0
    atr_ma = 0
    rsi_value = 0
    rsi_buy = 0
    rsi_sell = 0
    intra_trade_high = 0
    intra_trade_low = 0

    parameters = [
        "atr_length",
        "atr_ma_length",
        "rsi_length",
        "rsi_entry",
        "trailing_percent",
        "fixed_size"
    ]
    variables = [
        "atr_value",
        "atr_ma",
        "rsi_value",
        "rsi_buy",
        "rsi_sell",
        "intra_trade_high",
        "intra_trade_low"
    ]

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        """"""
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)
        self.bg = BarGenerator(self.on_bar, 15, self.on_15min_bar)
        self.am = ArrayManager()

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("策略初始化")

        self.rsi_buy = 50 + self.rsi_entry
        self.rsi_sell = 50 - self.rsi_entry

        self.load_bar(10)

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.bg.update_bar(bar)

    def on_15min_bar(self, bar: BarData):
        self.cancel_all()

        am = self.am
        am.update_bar(bar)
        if not am.inited:
            return

        atr_array = am.atr(self.atr_length, array=True)
        self.atr_value = atr_array[-1]
        self.atr_ma = atr_array[-self.atr_ma_length:].mean()
        self.rsi_value = am.rsi(self.rsi_length)

        if self.pos == 0:
            self.intra_trade_high = bar.high_price
            self.intra_trade_low = bar.low_price

            if self.atr_value > self.atr_ma:
                if self.rsi_value > self.rsi_buy:
                    price = bar.close_price * 1.01
                    self.buy(Decimal(price), Decimal(self.fixed_size))
                elif self.rsi_value < self.rsi_sell:
                    price = bar.close_price * 0.99
                    self.short(Decimal(price), Decimal(self.fixed_size))

        elif self.pos > 0:
            self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
            self.intra_trade_low = bar.low_price
            long_stop = self.intra_trade_high * (1 - self.trailing_percent / 100)
            self.sell(Decimal(long_stop), Decimal(abs(self.pos)), stop=True)

        elif self.pos < 0:
            self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
            self.intra_trade_high = bar.high_price

            short_stop = self.intra_trade_low * (1 + self.trailing_percent / 100)
            self.cover(Decimal(short_stop), Decimal(abs(self.pos)), stop=True)

        self.put_event()

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        self.put_event()

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        pass
```

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.