ATR Volatility Filter, RSI Entries, and Trailing Stops for Trend Following
Summary
This Python strategy combines an ATR-based volatility condition with RSI thresholds to enter long or short positions. It compares the latest ATR with a moving average of recent ATR values and only considers entries when current volatility is above that average. The five-period RSI is compared with thresholds derived from a configurable entry parameter; the default setting produces an upper threshold of 66 and a lower threshold of 34. Entries are placed a fixed price offset from the latest bar close.
For open positions, the strategy tracks the highest price reached during a long trade or the lowest during a short trade, then exits if the close crosses a trailing level set 0.8% from that extreme by default. Position size is fixed at one unit, and the code cancels outstanding orders on each bar update. The document provides implementation details but no backtest results, instrument, bar interval, transaction-cost analysis, or evidence that the thresholds generalize. Execution behavior may also depend on the framework and market conventions.
Key ideas
- The strategy filters entries by requiring ATR to exceed its recent average.
- It uses a five-period RSI with configurable upper and lower entry thresholds.
- Long and short entries are placed at a fixed offset from the bar close.
- Exits use a trailing stop based on the highest or lowest price reached during the trade.
- The code specifies a fixed position size but provides no performance or market-specific evidence.
Tags
Full text
# TrendFollowingStrategy
# TrendFollowingStrategy
## Source (MIT)
```python
from typing import List, Dict
from howtrader.app.portfolio_strategy import StrategyTemplate, StrategyEngine
from howtrader.trader.utility import BarGenerator, ArrayManager
from howtrader.trader.object import TickData, BarData
class TrendFollowingStrategy(StrategyTemplate):
""""""
author = "用Python的交易员"
atr_window = 22
atr_ma_window = 10
rsi_window = 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_window",
"atr_ma_window",
"rsi_window",
"rsi_entry",
"trailing_percent",
"fixed_size"
]
variables = [
"atr_value",
"atr_ma",
"rsi_value",
"rsi_buy",
"rsi_sell"
]
def __init__(
self,
strategy_engine: StrategyEngine,
strategy_name: str,
vt_symbols: List[str],
setting: dict
):
""""""
super().__init__(strategy_engine, strategy_name, vt_symbols, setting)
self.vt_symbol = vt_symbols[0]
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_bars(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.
"""
bars = {bar.vt_symbol: bar}
self.on_bars(bars)
def on_bars(self, bars: Dict[str, BarData]):
""""""
self.cancel_all()
bar = bars[self.vt_symbol]
am = self.am
am.update_bar(bar)
if not am.inited:
return
atr_array = am.atr(self.atr_window, array=True)
self.atr_value = atr_array[-1]
self.atr_ma = atr_array[-self.atr_ma_window:].mean()
self.rsi_value = am.rsi(self.rsi_window)
pos = self.get_pos(self.vt_symbol)
if 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:
self.buy(self.vt_symbol, bar.close_price + 5, self.fixed_size)
elif self.rsi_value < self.rsi_sell:
self.short(self.vt_symbol, bar.close_price - 5, self.fixed_size)
elif 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)
if bar.close_price <= long_stop:
self.sell(self.vt_symbol, bar.close_price - 5, abs(pos))
elif 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)
if bar.close_price >= short_stop:
self.cover(self.vt_symbol, bar.close_price + 5, abs(pos))
self.put_event()
```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.