ATR Volatility Filter, RSI Entries, and Trailing Stops
Summary
This event-driven trading strategy combines an average true range (ATR) activity filter with relative strength index (RSI) entries. It calculates ATR and compares the latest value with a moving average of recent ATR readings; new trades are considered only when ATR is above that average. The default RSI period is short, and thresholds around the midpoint determine whether to enter long or short. Orders are submitted slightly away from the bar close, while position size is fixed by a configurable quantity.
For open positions, the strategy updates the trade's highest or lowest observed price and places a trailing stop based on a configurable percentage. It initializes its indicator calculations from historical bars and manages orders through the framework's bar and tick callbacks. The document provides implementation code and default parameters, but no asset specification, backtest, transaction-cost assumptions, or performance evidence. The percentage-based stop and order offsets may behave differently across instruments and volatility regimes, so the code alone does not establish suitability or profitability.
Key ideas
- New entries require ATR to exceed its recent moving average.
- RSI readings above or below thresholds around 50 trigger long or short orders.
- Position size is fixed by a configurable parameter, and entry orders are offset from the bar close.
- Open trades use a trailing stop measured from the best observed price since entry.
- The code gives no market-specific results or backtest evidence.
Tags
Full text
# AtrRsiStrategy
# AtrRsiStrategy
## 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 AtrRsiStrategy(CtaTemplate):
""""""
author = "用Python的交易员"
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)
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.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.