Entrada BB HeikinAshi
Estrategia de Bollinger Bands que utiliza velas Heikin Ashi.
El sistema espera dos o tres barras Heikin Ashi bajistas consecutivas que toquen la banda inferior de Bollinger. Una vela alcista que cierre de vuelta por encima de la banda activa una entrada larga. Los cortos funcionan en la dirección opuesta. La mitad de la posición se cierra en el primer objetivo y el resto se protege con un stop trailing.
Detalles
- Criterios de entrada: Reversión de velas Heikin Ashi consecutivas alrededor de Bollinger Bands.
- Largo/Corto: Ambos.
- Criterios de salida: Toma parcial de beneficios y stop trailing.
- Stops: Sí.
- Valores predeterminados:
BollingerLength= 20BollingerWidth= 2CandleType= TimeSpan.FromMinutes(15)
- Filtros:
- Categoría: Reversión
- Dirección: Ambos
- Indicadores: Heikin Ashi, Bollinger Bands
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía (15m)
- 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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bollinger Bands + Heikin Ashi entry strategy with partial profit and trailing stop.
/// </summary>
public class BbHeikinAshiEntryStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerWidth;
private BollingerBands _bollinger;
private decimal _haOpenPrev1, _haOpenPrev2, _haOpenPrev3;
private decimal _haClosePrev1, _haClosePrev2, _haClosePrev3;
private decimal _haHighPrev1, _haHighPrev2, _haHighPrev3;
private decimal _haLowPrev1, _haLowPrev2, _haLowPrev3;
private decimal _upperBbPrev1, _upperBbPrev2, _upperBbPrev3;
private decimal _lowerBbPrev1, _lowerBbPrev2, _lowerBbPrev3;
private decimal _prevRawLow;
private decimal _prevRawHigh;
/// <summary>
/// Initialize BB Heikin Ashi Entry strategy.
/// </summary>
public BbHeikinAshiEntryStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetDisplay("Bollinger Length", "Period of Bollinger Bands", "Bollinger")
.SetOptimize(10, 40, 5);
_bollingerWidth = Param(nameof(BollingerWidth), 2m)
.SetDisplay("Bollinger Width", "Standard deviation multiplier", "Bollinger")
.SetOptimize(1m, 3m, 0.5m);
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Bollinger period.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Bollinger width (standard deviation).
/// </summary>
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_haOpenPrev1 = _haOpenPrev2 = _haOpenPrev3 = default;
_haClosePrev1 = _haClosePrev2 = _haClosePrev3 = default;
_haHighPrev1 = _haHighPrev2 = _haHighPrev3 = default;
_haLowPrev1 = _haLowPrev2 = _haLowPrev3 = default;
_upperBbPrev1 = _upperBbPrev2 = _upperBbPrev3 = default;
_lowerBbPrev1 = _lowerBbPrev2 = _lowerBbPrev3 = default;
_prevRawLow = _prevRawHigh = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands
{
Length = BollingerLength,
Width = BollingerWidth
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bollinger, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbResult)
{
if (candle.State != CandleStates.Finished)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
var haOpen = _haOpenPrev1 == 0
? (candle.OpenPrice + candle.ClosePrice) / 2m
: (_haOpenPrev1 + _haClosePrev1) / 2m;
var haHigh = Math.Max(Math.Max(candle.HighPrice, haOpen), haClose);
var haLow = Math.Min(Math.Min(candle.LowPrice, haOpen), haClose);
if (bbResult is not BollingerBandsValue bbRaw ||
bbRaw.UpBand is not decimal upper ||
bbRaw.LowBand is not decimal lower)
{
Shift(haOpen, haClose, haHigh, haLow, 0m, 0m, candle);
return;
}
if (_haOpenPrev1 != 0)
{
var redCandle1 = _haClosePrev1 < _haOpenPrev1 && _haLowPrev1 <= _lowerBbPrev1;
var redCandle2 = _haClosePrev2 < _haOpenPrev2 && _haLowPrev2 <= _lowerBbPrev2;
var greenConfirmation = haClose > haOpen;
var buySignal = (redCandle1 || redCandle2) && greenConfirmation;
var greenCandle1 = _haClosePrev1 > _haOpenPrev1 && _haHighPrev1 >= _upperBbPrev1;
var greenCandle2 = _haClosePrev2 > _haOpenPrev2 && _haHighPrev2 >= _upperBbPrev2;
var redConfirmation = haClose < haOpen;
var sellSignal = (greenCandle1 || greenCandle2) && redConfirmation;
if (buySignal && Position == 0)
{
BuyMarket();
}
else if (sellSignal && Position == 0)
{
SellMarket();
}
}
Shift(haOpen, haClose, haHigh, haLow, upper, lower, candle);
}
private void Shift(decimal haOpen, decimal haClose, decimal haHigh, decimal haLow, decimal upper, decimal lower, ICandleMessage candle)
{
_haOpenPrev3 = _haOpenPrev2;
_haOpenPrev2 = _haOpenPrev1;
_haOpenPrev1 = haOpen;
_haClosePrev3 = _haClosePrev2;
_haClosePrev2 = _haClosePrev1;
_haClosePrev1 = haClose;
_haHighPrev3 = _haHighPrev2;
_haHighPrev2 = _haHighPrev1;
_haHighPrev1 = haHigh;
_haLowPrev3 = _haLowPrev2;
_haLowPrev2 = _haLowPrev1;
_haLowPrev1 = haLow;
_upperBbPrev3 = _upperBbPrev2;
_upperBbPrev2 = _upperBbPrev1;
_upperBbPrev1 = upper;
_lowerBbPrev3 = _lowerBbPrev2;
_lowerBbPrev2 = _lowerBbPrev1;
_lowerBbPrev1 = lower;
_prevRawLow = candle.LowPrice;
_prevRawHigh = candle.HighPrice;
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Indicators import BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bb_heikin_ashi_entry_strategy(Strategy):
def __init__(self):
super(bb_heikin_ashi_entry_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bb_length = self.Param("BollingerLength", 20) \
.SetDisplay("Bollinger Length", "Period of Bollinger Bands", "Bollinger")
self._bb_width = self.Param("BollingerWidth", 2.0) \
.SetDisplay("Bollinger Width", "Standard deviation multiplier", "Bollinger")
self._cooldown_bars = self.Param("CooldownBars", 20) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._ha_open_prev1 = 0.0
self._ha_close_prev1 = 0.0
self._ha_high_prev1 = 0.0
self._ha_low_prev1 = 0.0
self._ha_open_prev2 = 0.0
self._ha_close_prev2 = 0.0
self._ha_high_prev2 = 0.0
self._ha_low_prev2 = 0.0
self._upper_bb_prev1 = 0.0
self._lower_bb_prev1 = 0.0
self._upper_bb_prev2 = 0.0
self._lower_bb_prev2 = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@cooldown_bars.setter
def cooldown_bars(self, value):
self._cooldown_bars.Value = value
def OnReseted(self):
super(bb_heikin_ashi_entry_strategy, self).OnReseted()
self._ha_open_prev1 = self._ha_open_prev2 = 0.0
self._ha_close_prev1 = self._ha_close_prev2 = 0.0
self._ha_high_prev1 = self._ha_high_prev2 = 0.0
self._ha_low_prev1 = self._ha_low_prev2 = 0.0
self._upper_bb_prev1 = self._upper_bb_prev2 = 0.0
self._lower_bb_prev1 = self._lower_bb_prev2 = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(bb_heikin_ashi_entry_strategy, self).OnStarted2(time)
bb = BollingerBands()
bb.Length = self._bb_length.Value
bb.Width = self._bb_width.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, self.OnProcess).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
ha_close = (o + h + l + c) / 4.0
if self._ha_open_prev1 == 0:
ha_open = (o + c) / 2.0
else:
ha_open = (self._ha_open_prev1 + self._ha_close_prev1) / 2.0
ha_high = max(h, ha_open, ha_close)
ha_low = min(l, ha_open, ha_close)
bb = bb_value
upper = bb.UpBand
lower = bb.LowBand
if upper is None or lower is None:
self._shift(ha_open, ha_close, ha_high, ha_low, 0.0, 0.0)
return
upper_v = float(upper)
lower_v = float(lower)
if self._ha_open_prev1 != 0:
red1 = self._ha_close_prev1 < self._ha_open_prev1 and self._ha_low_prev1 <= self._lower_bb_prev1
red2 = self._ha_close_prev2 < self._ha_open_prev2 and self._ha_low_prev2 <= self._lower_bb_prev2
green_confirm = ha_close > ha_open
buy_signal = (red1 or red2) and green_confirm
green1 = self._ha_close_prev1 > self._ha_open_prev1 and self._ha_high_prev1 >= self._upper_bb_prev1
green2 = self._ha_close_prev2 > self._ha_open_prev2 and self._ha_high_prev2 >= self._upper_bb_prev2
red_confirm = ha_close < ha_open
sell_signal = (green1 or green2) and red_confirm
if buy_signal and self.Position == 0:
self.BuyMarket()
elif sell_signal and self.Position == 0:
self.SellMarket()
self._shift(ha_open, ha_close, ha_high, ha_low, upper_v, lower_v)
def _shift(self, ha_open, ha_close, ha_high, ha_low, upper, lower):
self._ha_open_prev2 = self._ha_open_prev1
self._ha_open_prev1 = ha_open
self._ha_close_prev2 = self._ha_close_prev1
self._ha_close_prev1 = ha_close
self._ha_high_prev2 = self._ha_high_prev1
self._ha_high_prev1 = ha_high
self._ha_low_prev2 = self._ha_low_prev1
self._ha_low_prev1 = ha_low
self._upper_bb_prev2 = self._upper_bb_prev1
self._upper_bb_prev1 = upper
self._lower_bb_prev2 = self._lower_bb_prev1
self._lower_bb_prev1 = lower
def CreateClone(self):
return bb_heikin_ashi_entry_strategy()