Вход по Bollinger Bands и Heikin Ashi
Стратегия использует свечи Heikin Ashi и полосы Боллинджера.
Система ищет две или три подряд медвежьи свечи Heikin Ashi, касающиеся нижней полосы. Зеленая свеча, закрывшаяся выше полосы, подает сигнал на покупку. Короткие сделки формируются зеркально. Половина позиции фиксируется на первой цели, остаток сопровождается трейлинг-стопом.
Подробности
- Критерии входа: разворот Heikin Ashi у полос Боллинджера.
- Длинные/короткие: оба направления.
- Критерии выхода: частичная фиксация и трейлинг-стоп.
- Стопы: да.
- Параметры по умолчанию:
BollingerLength= 20BollingerWidth= 2CandleType= TimeSpan.FromMinutes(15)
- Фильтры:
- Категория: Разворот
- Направление: Оба
- Индикаторы: Heikin Ashi, Bollinger Bands
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной (15м)
- Сезонность: Нет
- Нейронные сети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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()