Skip to content
All library documents

Bollinger Channel Entries with CCI Direction and ATR Trailing Stops

Article Strategy library · Author: 用Python的交易员

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.