Bollinger Channel Entries with CCI Direction and ATR Trailing Stops
Summary
This 15-minute bar strategy combines Bollinger channel orders with the sign of the Commodity Channel Index (CCI). When flat, it places a buy stop-style order at the upper band if CCI is positive, or a short order at the lower band if CCI is negative. While holding a long position, it tracks the highest bar high and sets an exit level below that high by a multiple of ATR. For a short position, it tracks the lowest low and places an exit above it by the same ATR-based distance. The listed defaults are an 18-bar Bollinger window, 3.4 deviation setting, 10-bar CCI, 30-bar ATR, and 5.2 stop multiplier.
The source is an implementation example using Howtrader callbacks and order methods; it provides no backtest results, instrument specification, or discussion of execution assumptions. The entry orders sit at channel boundaries, so the code does not simply enter at market when CCI changes sign. Stop distance changes with ATR, but the example shows no separate position-sizing or portfolio risk logic. Performance and suitability cannot be inferred from the code alone.
Key ideas
- The strategy evaluates signals on 15-minute bars.
- Positive CCI prompts a buy order at the upper Bollinger band, while negative CCI prompts a short order at the lower band.
- Long and short exits trail from the trade's extreme using an ATR-based distance.
- The default stop multiplier is 5.2 times the ATR value.
- The source gives no backtest evidence or position-sizing framework.
Tags
Full text
# BollChannelStrategy
# BollChannelStrategy
## 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 BollChannelStrategy(CtaTemplate):
""""""
author = "用Python的交易员"
boll_window = 18
boll_dev = 3.4
cci_window = 10
atr_window = 30
sl_multiplier = 5.2
fixed_size = 1
boll_up = 0
boll_down = 0
cci_value = 0
atr_value = 0
intra_trade_high = 0
intra_trade_low = 0
long_stop = 0
short_stop = 0
parameters = [
"boll_window",
"boll_dev",
"cci_window",
"atr_window",
"sl_multiplier",
"fixed_size"
]
variables = [
"boll_up",
"boll_down",
"cci_value",
"atr_value",
"intra_trade_high",
"intra_trade_low",
"long_stop",
"short_stop"
]
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.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
self.boll_up, self.boll_down = am.boll(self.boll_window, self.boll_dev)
self.cci_value = am.cci(self.cci_window)
self.atr_value = am.atr(self.atr_window)
if self.pos == 0:
self.intra_trade_high = bar.high_price
self.intra_trade_low = bar.low_price
if self.cci_value > 0:
self.buy(Decimal(self.boll_up), Decimal(self.fixed_size), True)
elif self.cci_value < 0:
self.short(Decimal(self.boll_down), Decimal(self.fixed_size), True)
elif self.pos > 0:
self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
self.intra_trade_low = bar.low_price
self.long_stop = self.intra_trade_high - self.atr_value * self.sl_multiplier
self.sell(Decimal(self.long_stop), Decimal(abs(self.pos)), True)
elif self.pos < 0:
self.intra_trade_high = bar.high_price
self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
self.short_stop = self.intra_trade_low + self.atr_value * self.sl_multiplier
self.cover(Decimal(self.short_stop), Decimal(abs(self.pos)), 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.