Estrategia Bollinger Cci
Implementación de la estrategia - Bollinger Bands + CCI. Compra cuando el precio está por debajo de la banda inferior de Bollinger y el CCI está por debajo de -100 (sobreventa). Vende cuando el precio está por encima de la banda superior de Bollinger y el CCI está por encima de 100 (sobrecompra).
Las pruebas indican un rendimiento anual promedio de aproximadamente 73%. Funciona mejor en el mercado de criptomonedas.
Las bandas de Bollinger mapean los límites de volatilidad, y el CCI mide la distancia desde la media. Las rupturas más allá de una banda con confirmación del CCI desencadenan operaciones.
Adecuado para mercados volátiles donde las tendencias se extienden rápidamente. Se aplican stops basados en ATR para mayor seguridad.
Detalles
- Criterios de entrada:
- Largo:
Close < LowerBand && CCI < CciOversold - Corto:
Close > UpperBand && CCI > CciOverbought
- Largo:
- Largo/Corto: Ambos
- Criterios de salida: El precio regresa a la banda media
- Stops: Basados en ATR usando
StopLoss - Valores predeterminados:
BollingerPeriod= 20BollingerDeviation= 2.0mCciPeriod= 20CciOversold= -100mCciOverbought= 100mStopLoss= new Unit(2, UnitTypes.Absolute)CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Bollinger Bands, CCI
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Implementation of strategy - Bollinger Bands + CCI.
/// Buy when price is below lower Bollinger Band and CCI is below -100 (oversold).
/// Sell when price is above upper Bollinger Band and CCI is above 100 (overbought).
/// </summary>
public class BollingerCciStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciOversold;
private readonly StrategyParam<decimal> _cciOverbought;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<Unit> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private int _cooldown;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands deviation multiplier.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// CCI oversold level.
/// </summary>
public decimal CciOversold
{
get => _cciOversold.Value;
set => _cciOversold.Value = value;
}
/// <summary>
/// CCI overbought level.
/// </summary>
public decimal CciOverbought
{
get => _cciOverbought.Value;
set => _cciOverbought.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop-loss value.
/// </summary>
public Unit StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Candle type used for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="BollingerCciStrategy"/>.
/// </summary>
public BollingerCciStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Period for Bollinger Bands", "Bollinger Parameters");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Deviation multiplier for Bollinger Bands", "Bollinger Parameters");
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Period for Commodity Channel Index", "CCI Parameters");
_cciOversold = Param(nameof(CciOversold), -100m)
.SetDisplay("CCI Oversold", "CCI level to consider market oversold", "CCI Parameters");
_cciOverbought = Param(nameof(CciOverbought), 100m)
.SetDisplay("CCI Overbought", "CCI level to consider market overbought", "CCI Parameters");
_cooldownBars = Param(nameof(CooldownBars), 80)
.SetRange(5, 500)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_stopLoss = Param(nameof(StopLoss), new Unit(2, UnitTypes.Absolute))
.SetDisplay("Stop Loss", "Stop loss in ATR or value", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var bollinger = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
var cci = new CommodityChannelIndex { Length = CciPeriod };
// Setup candle subscription
var subscription = SubscribeCandles(CandleType);
// Bind indicators to candles
subscription
.BindEx(bollinger, cci, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
// Create separate area for CCI
var cciArea = CreateChartArea();
if (cciArea != null)
{
DrawIndicator(cciArea, cci);
}
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!bollingerValue.IsFormed || !cciValue.IsFormed)
return;
// In this function we receive only the middle band value from the Bollinger Bands indicator
// We need to calculate the upper and lower bands ourselves or get them directly from the indicator
// Get Bollinger Bands values from the indicator
var bb = (BollingerBandsValue)bollingerValue;
var middleBand = bb.MovingAverage;
var upperBand = bb.UpBand;
var lowerBand = bb.LowBand;
var cciTyped = cciValue.ToDecimal();
// Current price
var price = candle.ClosePrice;
LogInfo($"Candle: {candle.OpenTime}, Close: {price}, " +
$"Upper Band: {upperBand}, Middle Band: {middleBand}, Lower Band: {lowerBand}, " +
$"CCI: {cciTyped}");
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Trading rules
var lowerTouch = price <= lowerBand * 1.002m;
var upperTouch = price >= upperBand * 0.998m;
if (lowerTouch && cciTyped < CciOversold && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Buy signal: Price below lower Bollinger Band and CCI oversold ({cciTyped} < {CciOversold}).");
}
else if (upperTouch && cciTyped > CciOverbought && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Sell signal: Price above upper Bollinger Band and CCI overbought ({cciTyped} > {CciOverbought}).");
}
// Exit conditions
else if (price > middleBand && Position > 0)
{
// Exit long position when price returns to the middle band
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Exit long: Price returned to middle band. Position: {Position}");
}
else if (price < middleBand && Position < 0)
{
// Exit short position when price returns to the middle band
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Exit short: Price returned to middle band. Position: {Position}");
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class bollinger_cci_strategy(Strategy):
"""
Bollinger Bands + CCI strategy.
Buy when price is below lower BB and CCI is oversold.
Sell when price is above upper BB and CCI is overbought.
"""
def __init__(self):
super(bollinger_cci_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetDisplay("Bollinger Period", "Period for Bollinger Bands", "Bollinger Parameters")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetDisplay("Bollinger Deviation", "Deviation multiplier for Bollinger Bands", "Bollinger Parameters")
self._cci_period = self.Param("CciPeriod", 20) \
.SetDisplay("CCI Period", "Period for Commodity Channel Index", "CCI Parameters")
self._cci_oversold = self.Param("CciOversold", -100.0) \
.SetDisplay("CCI Oversold", "CCI level to consider market oversold", "CCI Parameters")
self._cci_overbought = self.Param("CciOverbought", 100.0) \
.SetDisplay("CCI Overbought", "CCI level to consider market overbought", "CCI Parameters")
self._cooldown_bars = self.Param("CooldownBars", 80) \
.SetRange(5, 500) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle type for strategy", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(bollinger_cci_strategy, self).OnStarted2(time)
self._cooldown = 0
bollinger = BollingerBands()
bollinger.Length = self._bollinger_period.Value
bollinger.Width = self._bollinger_deviation.Value
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, cci, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
cci_area = self.CreateChartArea()
if cci_area is not None:
self.DrawIndicator(cci_area, cci)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, bb_value, cci_value):
if candle.State != CandleStates.Finished:
return
if not bb_value.IsFormed or not cci_value.IsFormed:
return
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
upper_band = float(bb_value.UpBand)
lower_band = float(bb_value.LowBand)
middle_band = float(bb_value.MovingAverage)
cci_dec = float(cci_value)
price = float(candle.ClosePrice)
if self._cooldown > 0:
self._cooldown -= 1
return
cd = self._cooldown_bars.Value
os_level = self._cci_oversold.Value
ob_level = self._cci_overbought.Value
lower_touch = price <= lower_band * 1.002
upper_touch = price >= upper_band * 0.998
if lower_touch and cci_dec < os_level and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
elif upper_touch and cci_dec > ob_level and self.Position == 0:
self.SellMarket()
self._cooldown = cd
elif price > middle_band and self.Position > 0:
self.SellMarket()
self._cooldown = cd
elif price < middle_band and self.Position < 0:
self.BuyMarket()
self._cooldown = cd
def OnReseted(self):
super(bollinger_cci_strategy, self).OnReseted()
self._cooldown = 0
def CreateClone(self):
return bollinger_cci_strategy()