Стратегия BBTrend SuperTrend Decision
Стратегия вычисляет значение BBTrend на основе двух полос Боллинджера с различными периодами и передаёт его в расчёт SuperTrend. Полученное направление SuperTrend определяет открытие длинных или коротких позиций. Дополнительно можно включить защиту в виде тейк‑профита и стоп‑лосса в процентах.
Детали
- Критерии входа:
- Лонг: направление SuperTrend вверх.
- Шорт: направление SuperTrend вниз.
- Длинные/короткие: обе стороны, настраивается.
- Критерии выхода:
- Противоположное направление SuperTrend.
- Стопы: опциональные тейк‑профит и стоп‑лосс в процентах.
- Значения по умолчанию:
- Длина коротких BB = 20, длинных BB = 50, StdDev = 2.
- Длина SuperTrend = 10, множитель = 7.
- Тейк‑профит = 30%, стоп‑лосс = 20%.
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: Bollinger Bands, SuperTrend
- Стопы: Опциональные TP/SL
- Сложность: Средняя
- Таймфрейм: Краткосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// BBTrend with SuperTrend decision strategy.
/// Computes BBTrend from two Bollinger Bands and applies a SuperTrend
/// calculation to generate long and short entries.
/// </summary>
public class BbtrendSupertrendDecisionStrategy : Strategy
{
/// <summary>
/// Protection mode for take profit and stop loss.
/// </summary>
public enum TpSlModes
{
None,
TP,
SL,
Both
}
private readonly StrategyParam<int> _shortBbLength;
private readonly StrategyParam<int> _longBbLength;
private readonly StrategyParam<decimal> _stdDev;
private readonly StrategyParam<int> _supertrendLength;
private readonly StrategyParam<decimal> _supertrendMultiplier;
private readonly StrategyParam<Sides?> _tradeDirection;
private readonly StrategyParam<TpSlModes> _tpSlCondition;
private readonly StrategyParam<decimal> _takeProfitPerc;
private readonly StrategyParam<decimal> _stopLossPerc;
private readonly StrategyParam<DataType> _candleType;
private decimal? _previousBbTrend;
private decimal? _prevUp;
private decimal? _prevDn;
private decimal? _prevAtr;
private decimal? _prevSt;
/// <summary>
/// Short Bollinger Bands length.
/// </summary>
public int ShortBbLength
{
get => _shortBbLength.Value;
set => _shortBbLength.Value = value;
}
/// <summary>
/// Long Bollinger Bands length.
/// </summary>
public int LongBbLength
{
get => _longBbLength.Value;
set => _longBbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation.
/// </summary>
public decimal StdDev
{
get => _stdDev.Value;
set => _stdDev.Value = value;
}
/// <summary>
/// SuperTrend ATR period.
/// </summary>
public int SupertrendLength
{
get => _supertrendLength.Value;
set => _supertrendLength.Value = value;
}
/// <summary>
/// SuperTrend multiplier.
/// </summary>
public decimal SupertrendMultiplier
{
get => _supertrendMultiplier.Value;
set => _supertrendMultiplier.Value = value;
}
/// <summary>
/// Trading direction.
/// </summary>
public Sides? TradeDirection
{
get => _tradeDirection.Value;
set => _tradeDirection.Value = value;
}
/// <summary>
/// Take profit / stop loss mode.
/// </summary>
public TpSlModes TpSlCondition
{
get => _tpSlCondition.Value;
set => _tpSlCondition.Value = value;
}
/// <summary>
/// Take profit percentage.
/// </summary>
public decimal TakeProfitPerc
{
get => _takeProfitPerc.Value;
set => _takeProfitPerc.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPerc
{
get => _stopLossPerc.Value;
set => _stopLossPerc.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BbtrendSupertrendDecisionStrategy()
{
_shortBbLength = Param(nameof(ShortBbLength), 20)
.SetGreaterThanZero()
.SetDisplay("Short BB Length", "Short Bollinger Bands length", "BBTrend")
.SetOptimize(10, 40, 5);
_longBbLength = Param(nameof(LongBbLength), 50)
.SetGreaterThanZero()
.SetDisplay("Long BB Length", "Long Bollinger Bands length", "BBTrend")
.SetOptimize(30, 100, 5);
_stdDev = Param(nameof(StdDev), 2m)
.SetGreaterThanZero()
.SetDisplay("Std Dev", "Standard deviation", "BBTrend");
_supertrendLength = Param(nameof(SupertrendLength), 10)
.SetGreaterThanZero()
.SetDisplay("ST Length", "SuperTrend ATR period", "SuperTrend")
.SetOptimize(5, 20, 1);
_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 12m)
.SetGreaterThanZero()
.SetDisplay("ST Factor", "SuperTrend multiplier", "SuperTrend")
.SetOptimize(1m, 10m, 1m);
_tradeDirection = Param(nameof(TradeDirection), (Sides?)null)
.SetDisplay("Direction", "Allowed trading direction", "Trading");
_tpSlCondition = Param(nameof(TpSlCondition), TpSlModes.None)
.SetDisplay("TP/SL Mode", "Protection mode", "Risk");
_takeProfitPerc = Param(nameof(TakeProfitPerc), 30m)
.SetGreaterThanZero()
.SetDisplay("Take Profit (%)", "Take profit percentage", "Risk");
_stopLossPerc = Param(nameof(StopLossPerc), 20m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (%)", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousBbTrend = null;
_prevUp = null;
_prevDn = null;
_prevAtr = null;
_prevSt = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var shortBb = new BollingerBands { Length = ShortBbLength, Width = StdDev };
var longBb = new BollingerBands { Length = LongBbLength, Width = StdDev };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx([shortBb, longBb], ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, shortBb);
DrawIndicator(area, longBb);
DrawOwnTrades(area);
}
Unit tp = default;
Unit sl = default;
if (TpSlCondition == TpSlModes.TP || TpSlCondition == TpSlModes.Both)
tp = TakeProfitPerc.Percents();
if (TpSlCondition == TpSlModes.SL || TpSlCondition == TpSlModes.Both)
sl = StopLossPerc.Percents();
if (tp != default || sl != default)
StartProtection(tp, sl);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
if (values[0] is not BollingerBandsValue shortBbValue ||
shortBbValue.UpBand is not decimal shortUpper ||
shortBbValue.LowBand is not decimal shortLower ||
shortBbValue.MovingAverage is not decimal shortMiddle)
return;
if (values[1] is not BollingerBandsValue longBbValue ||
longBbValue.UpBand is not decimal longUpper ||
longBbValue.LowBand is not decimal longLower)
return;
var bbTrend = (Math.Abs(shortLower - longLower) - Math.Abs(shortUpper - longUpper)) / shortMiddle * 100m;
if (_previousBbTrend is null)
{
_previousBbTrend = bbTrend;
return;
}
var open = _previousBbTrend.Value;
var close = bbTrend;
var high = Math.Max(open, close);
var low = Math.Min(open, close);
var tr = Math.Max(Math.Max(high - low, Math.Abs(high - open)), Math.Abs(low - open));
var atr = _prevAtr is null ? tr : _prevAtr.Value + (tr - _prevAtr.Value) / SupertrendLength;
var hl2 = (high + low) / 2m;
var up = hl2 + SupertrendMultiplier * atr;
if (_prevUp is not null && !((up < _prevUp.Value) || (open > _prevUp.Value)))
up = _prevUp.Value;
var dn = hl2 - SupertrendMultiplier * atr;
if (_prevDn is not null && !((dn > _prevDn.Value) || (open < _prevDn.Value)))
dn = _prevDn.Value;
int dir;
if (_prevAtr is null)
dir = 1;
else if (_prevSt.HasValue && _prevUp.HasValue && _prevSt.Value == _prevUp.Value)
dir = close > up ? -1 : 1;
else
dir = close < dn ? 1 : -1;
var st = dir == -1 ? dn : up;
var allowLong = TradeDirection == null || TradeDirection == Sides.Buy;
var allowShort = TradeDirection == null || TradeDirection == Sides.Sell;
// Entry/exit based on direction change
if (allowLong && dir < 0 && Position <= 0)
BuyMarket();
else if (allowShort && dir > 0 && Position >= 0)
SellMarket();
_previousBbTrend = close;
_prevAtr = atr;
_prevUp = up;
_prevDn = dn;
_prevSt = st;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Sides
from StockSharp.Algo.Indicators import BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bbtrend_supertrend_decision_strategy(Strategy):
def __init__(self):
super(bbtrend_supertrend_decision_strategy, self).__init__()
self._short_bb_length = self.Param("ShortBbLength", 20) \
.SetDisplay("Short BB Length", "Short Bollinger Bands length", "BBTrend")
self._long_bb_length = self.Param("LongBbLength", 50) \
.SetDisplay("Long BB Length", "Long Bollinger Bands length", "BBTrend")
self._std_dev = self.Param("StdDev", 2.0) \
.SetDisplay("Std Dev", "Standard deviation", "BBTrend")
self._supertrend_length = self.Param("SupertrendLength", 10) \
.SetDisplay("ST Length", "SuperTrend ATR period", "SuperTrend")
self._supertrend_multiplier = self.Param("SupertrendMultiplier", 12.0) \
.SetDisplay("ST Factor", "SuperTrend multiplier", "SuperTrend")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._previous_bb_trend = None
self._prev_up = None
self._prev_dn = None
self._prev_atr = None
self._prev_st = None
@property
def short_bb_length(self):
return self._short_bb_length.Value
@property
def long_bb_length(self):
return self._long_bb_length.Value
@property
def std_dev(self):
return self._std_dev.Value
@property
def supertrend_length(self):
return self._supertrend_length.Value
@property
def supertrend_multiplier(self):
return self._supertrend_multiplier.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bbtrend_supertrend_decision_strategy, self).OnReseted()
self._previous_bb_trend = None
self._prev_up = None
self._prev_dn = None
self._prev_atr = None
self._prev_st = None
def OnStarted2(self, time):
super(bbtrend_supertrend_decision_strategy, self).OnStarted2(time)
short_bb = BollingerBands()
short_bb.Length = self.short_bb_length
short_bb.Width = self.std_dev
long_bb = BollingerBands()
long_bb.Length = self.long_bb_length
long_bb.Width = self.std_dev
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(short_bb, long_bb, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, short_bb)
self.DrawIndicator(area, long_bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, short_bb_val, long_bb_val):
if candle.State != CandleStates.Finished:
return
short_upper = short_bb_val.UpBand
short_lower = short_bb_val.LowBand
short_middle = short_bb_val.MovingAverage
if short_upper is None or short_lower is None or short_middle is None:
return
long_upper = long_bb_val.UpBand
long_lower = long_bb_val.LowBand
if long_upper is None or long_lower is None:
return
bb_trend = (abs(float(short_lower) - float(long_lower)) - abs(float(short_upper) - float(long_upper))) / float(short_middle) * 100.0
if self._previous_bb_trend is None:
self._previous_bb_trend = bb_trend
return
open_val = self._previous_bb_trend
close_val = bb_trend
high = max(open_val, close_val)
low = min(open_val, close_val)
tr = max(max(high - low, abs(high - open_val)), abs(low - open_val))
atr = tr if self._prev_atr is None else self._prev_atr + (tr - self._prev_atr) / self.supertrend_length
hl2 = (high + low) / 2.0
up = hl2 + self.supertrend_multiplier * atr
if self._prev_up is not None and not (up < self._prev_up or open_val > self._prev_up):
up = self._prev_up
dn = hl2 - self.supertrend_multiplier * atr
if self._prev_dn is not None and not (dn > self._prev_dn or open_val < self._prev_dn):
dn = self._prev_dn
if self._prev_atr is None:
direction = 1
elif self._prev_st is not None and self._prev_up is not None and self._prev_st == self._prev_up:
direction = -1 if close_val > up else 1
else:
direction = 1 if close_val < dn else -1
st = dn if direction == -1 else up
if direction < 0 and self.Position <= 0:
self.BuyMarket()
elif direction > 0 and self.Position >= 0:
self.SellMarket()
self._previous_bb_trend = close_val
self._prev_atr = atr
self._prev_up = up
self._prev_dn = dn
self._prev_st = st
def CreateClone(self):
return bbtrend_supertrend_decision_strategy()