Keltner Channel Breakouts with Percentage Trailing Stops
Summary
This event-driven strategy builds five-minute bars from incoming market data and calculates Keltner Channel boundaries using an 11-period length and a 1.6 deviation setting. When flat, it places linked buy-stop and short-stop orders at the upper and lower channel boundaries, respectively, aiming to enter when price breaks out of the channel. Once a position is open, it tracks the favorable extreme and places an exit order using a trailing distance of 0.8% from that extreme.
The code also cancels outstanding orders as new bars arrive and removes the opposing entry order after a trade. It gives configurable position size and indicator parameters, but no asset, test period, or performance evidence. The example does not explain how the settings were selected or address transaction costs, slippage, or behavior in range-bound markets, so its rules alone do not establish an edge.
Key ideas
- The strategy enters on breaks of the upper or lower Keltner Channel boundary.
- Channel settings use an 11-period length and a 1.6 deviation value.
- Open positions are exited with a trailing order set 0.8% from the best observed price extreme.
- The example provides order-handling logic but no backtest results or asset context.
Tags
Full text
# KingKeltnerStrategy
# KingKeltnerStrategy
## 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 KingKeltnerStrategy(CtaTemplate):
""""""
author = "用Python的交易员"
kk_length = 11
kk_dev = 1.6
trailing_percent = 0.8
fixed_size = 1
kk_up = 0
kk_down = 0
intra_trade_high = 0
intra_trade_low = 0
long_vt_orderids = []
short_vt_orderids = []
vt_orderids = []
parameters = ["kk_length", "kk_dev", "trailing_percent", "fixed_size"]
variables = ["kk_up", "kk_down"]
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, 5, self.on_5min_bar)
self.am = ArrayManager()
def on_init(self):
"""
Callback when strategy is inited.
"""
self.write_log("策略初始化")
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_5min_bar(self, bar: BarData):
""""""
for orderid in self.vt_orderids:
self.cancel_order(orderid)
self.vt_orderids.clear()
am = self.am
am.update_bar(bar)
if not am.inited:
return
self.kk_up, self.kk_down = am.keltner(self.kk_length, self.kk_dev)
if self.pos == 0:
self.intra_trade_high = bar.high_price
self.intra_trade_low = bar.low_price
self.send_oco_order(self.kk_up, self.kk_down, 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
price = self.intra_trade_high * (1 - self.trailing_percent / 100)
vt_orderids = self.sell(Decimal(price), Decimal(abs(self.pos)), True)
self.vt_orderids.extend(vt_orderids)
elif self.pos < 0:
self.intra_trade_high = bar.high_price
self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
price = self.intra_trade_low * (1 + self.trailing_percent / 100)
vt_orderids = self.cover(Decimal(price), Decimal(abs(self.pos)), True)
self.vt_orderids.extend(vt_orderids)
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.
"""
if self.pos != 0:
if self.pos > 0:
for short_orderid in self.short_vt_orderids:
self.cancel_order(short_orderid)
elif self.pos < 0:
for buy_orderid in self.long_vt_orderids:
self.cancel_order(buy_orderid)
for orderid in (self.long_vt_orderids + self.short_vt_orderids):
if orderid in self.vt_orderids:
self.vt_orderids.remove(orderid)
self.put_event()
def send_oco_order(self, buy_price, short_price, volume):
""""""
self.long_vt_orderids = self.buy(Decimal(buy_price), Decimal(volume), True)
self.short_vt_orderids = self.short(Decimal(short_price), Decimal(volume), True)
self.vt_orderids.extend(self.long_vt_orderids)
self.vt_orderids.extend(self.short_vt_orderids)
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.