Estrategia Bollinger Stochastic
Bollinger Stochastic combina las Bandas de Bollinger con el oscilador estocástico para identificar movimientos sobreextendidos. Que el precio toque la banda exterior mientras el oscilador está en una zona extrema sugiere un posible retroceso.
Las pruebas indican un rendimiento anual promedio de aproximadamente 133%. Funciona mejor en el mercado de criptomonedas.
El sistema opera contra esos extremos, yendo largo cuando el precio toca la banda inferior con el estocástico en sobreventa, y vendiendo en corto en la banda superior con el estocástico en sobrecompra.
Un stop basado en porcentaje limita el riesgo si la reversión a la media no ocurre.
Detalles
- Criterios de entrada: señal de indicador
- Largo/Corto: Ambos
- Criterios de salida: stop-loss o señal opuesta
- Stops: Sí, basado en porcentaje
- Valores predeterminados:
CandleType= 15 minuteStopLoss= 2%
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Bollinger Bands, Stochastic
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining Bollinger Bands and Stochastic oscillator for mean-reversion.
/// Buys when price touches lower band with oversold stochastic, sells at upper band with overbought.
/// </summary>
public class BollingerStochasticStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<int> _cooldownBars;
private decimal _stochK;
private int _cooldown;
/// <summary>
/// Data type for candles.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Period for Bollinger Bands calculation.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for Bollinger Bands.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Stochastic oversold level.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// Stochastic overbought level.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BollingerStochasticStrategy"/>.
/// </summary>
public BollingerStochasticStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetRange(10, 50)
.SetDisplay("BB Period", "Period for Bollinger Bands", "Bollinger Settings");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2.0m)
.SetDisplay("BB Deviation", "Standard deviation multiplier", "Bollinger Settings");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Oversold Level", "Stochastic oversold level", "Stochastic Settings");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Overbought Level", "Stochastic overbought level", "Stochastic Settings");
_cooldownBars = Param(nameof(CooldownBars), 50)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stochK = 50;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
var stochastic = new StochasticOscillator();
var subscription = SubscribeCandles(CandleType);
// Bind stochastic with BindEx
subscription.BindEx(stochastic, OnStochastic);
// Bind bollinger bands with BindEx
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
var stochArea = CreateChartArea();
if (stochArea != null)
DrawIndicator(stochArea, stochastic);
}
}
private void OnStochastic(ICandleMessage candle, IIndicatorValue stochValue)
{
var stoch = (IStochasticOscillatorValue)stochValue;
if (stoch.K is decimal k)
_stochK = k;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bb = (BollingerBandsValue)bbValue;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
var close = candle.ClosePrice;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Buy: price at lower band + stochastic oversold
if (close <= lower && _stochK < StochOversold && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Sell: price at upper band + stochastic overbought
else if (close >= upper && _stochK > StochOverbought && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long at middle band
if (Position > 0 && close > middle)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short at middle band
else if (Position < 0 && close < middle)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class bollinger_stochastic_strategy(Strategy):
def __init__(self):
super(bollinger_stochastic_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetDisplay("BB Period", "Period for Bollinger Bands", "Bollinger Settings")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetDisplay("BB Deviation", "Standard deviation multiplier", "Bollinger Settings")
self._stoch_oversold = self.Param("StochOversold", 20.0) \
.SetDisplay("Oversold Level", "Stochastic oversold level", "Stochastic Settings")
self._stoch_overbought = self.Param("StochOverbought", 80.0) \
.SetDisplay("Overbought Level", "Stochastic overbought level", "Stochastic Settings")
self._cooldown_bars = self.Param("CooldownBars", 50) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._stoch_k = 50.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def bollinger_period(self):
return self._bollinger_period.Value
@property
def bollinger_deviation(self):
return self._bollinger_deviation.Value
@property
def stoch_oversold(self):
return self._stoch_oversold.Value
@property
def stoch_overbought(self):
return self._stoch_overbought.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(bollinger_stochastic_strategy, self).OnReseted()
self._stoch_k = 50.0
self._cooldown = 0
def OnStarted2(self, time):
super(bollinger_stochastic_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.bollinger_period
bollinger.Width = self.bollinger_deviation
stochastic = StochasticOscillator()
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stochastic, self._on_stochastic)
subscription.BindEx(bollinger, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
stoch_area = self.CreateChartArea()
if stoch_area is not None:
self.DrawIndicator(stoch_area, stochastic)
def _on_stochastic(self, candle, stoch_value):
if stoch_value.K is not None:
self._stoch_k = float(stoch_value.K)
def OnProcess(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
middle = float(bb_value.MovingAverage)
close = float(candle.ClosePrice)
if self._cooldown > 0:
self._cooldown -= 1
return
if close <= lower and self._stoch_k < self.stoch_oversold and self.Position == 0:
self.BuyMarket()
self._cooldown = self.cooldown_bars
elif close >= upper and self._stoch_k > self.stoch_overbought and self.Position == 0:
self.SellMarket()
self._cooldown = self.cooldown_bars
if self.Position > 0 and close > middle:
self.SellMarket()
self._cooldown = self.cooldown_bars
elif self.Position < 0 and close < middle:
self.BuyMarket()
self._cooldown = self.cooldown_bars
def CreateClone(self):
return bollinger_stochastic_strategy()