Стратегия ScalpWiz Bollinger
Обзор
ScalpWiz Bollinger — контртрендовая стратегия, использующая полосы Боллинджера для поиска перекупленных и перепроданных состояний. Когда цена закрытия значительно удаляется от верхней или нижней полосы, открывается позиция в противоположную сторону в ожидании возврата цены.
Проверяются четыре уровня отклонения. Каждый уровень соответствует определённой силе сигнала и увеличивает объём сделки. Размер позиции также масштабируется в зависимости от процентного риска от текущего портфеля.
Параметры
BandsPeriod— количество свечей для расчёта полос Боллинджера.BandsDeviation— коэффициент стандартного отклонения.Level1Pips…Level4Pips— расстояние от полосы в пунктах для уровней 1–4.StrengthLevel1Multiplier…StrengthLevel4Multiplier— множители объёма для каждого уровня.RiskPercent— доля портфеля, рискуемая на сигнал.CandleType— таймфрейм используемых свечей.
Логика торговли
- Подписка на свечи выбранного таймфрейма и расчёт полос Боллинджера.
- На каждой завершённой свече:
- если цена закрытия выше верхней полосы на заданное расстояние — открывается короткая позиция;
- если цена закрытия ниже нижней полосы на заданное расстояние — открывается длинная позиция.
- Объём рассчитывается на основе процента риска и множителя силы сигнала.
Стратегия основана на оригинальном MQL-скрипте mcb.scalpwiz.9001.mq4.
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-based reverse scalping strategy.
/// Places counter-trend orders when price deviates from the bands.
/// Position size scales with signal strength and risk percentage.
/// </summary>
public class ScalpWizBollingerStrategy : Strategy
{
private readonly StrategyParam<int> _bandsPeriod;
private readonly StrategyParam<decimal> _bandsDeviation;
private readonly StrategyParam<decimal> _level1Pips;
private readonly StrategyParam<decimal> _level2Pips;
private readonly StrategyParam<decimal> _level3Pips;
private readonly StrategyParam<decimal> _level4Pips;
private readonly StrategyParam<int> _multiplier1;
private readonly StrategyParam<int> _multiplier2;
private readonly StrategyParam<int> _multiplier3;
private readonly StrategyParam<int> _multiplier4;
private readonly StrategyParam<int> _riskPercent;
private readonly StrategyParam<DataType> _candleType;
public int BandsPeriod { get => _bandsPeriod.Value; set => _bandsPeriod.Value = value; }
public decimal BandsDeviation { get => _bandsDeviation.Value; set => _bandsDeviation.Value = value; }
public decimal Level1Pips { get => _level1Pips.Value; set => _level1Pips.Value = value; }
public decimal Level2Pips { get => _level2Pips.Value; set => _level2Pips.Value = value; }
public decimal Level3Pips { get => _level3Pips.Value; set => _level3Pips.Value = value; }
public decimal Level4Pips { get => _level4Pips.Value; set => _level4Pips.Value = value; }
public int StrengthLevel1Multiplier { get => _multiplier1.Value; set => _multiplier1.Value = value; }
public int StrengthLevel2Multiplier { get => _multiplier2.Value; set => _multiplier2.Value = value; }
public int StrengthLevel3Multiplier { get => _multiplier3.Value; set => _multiplier3.Value = value; }
public int StrengthLevel4Multiplier { get => _multiplier4.Value; set => _multiplier4.Value = value; }
public int RiskPercent { get => _riskPercent.Value; set => _riskPercent.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public ScalpWizBollingerStrategy()
{
_bandsPeriod = Param(nameof(BandsPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Bands Period", "Number of candles for Bollinger calculation", "Bollinger")
.SetOptimize(20, 40, 5);
_bandsDeviation = Param(nameof(BandsDeviation), 2m)
.SetGreaterThanZero()
.SetDisplay("Bands Deviation", "Standard deviation multiplier", "Bollinger")
.SetOptimize(1m, 3m, 0.5m);
_level1Pips = Param(nameof(Level1Pips), 1m)
.SetGreaterThanZero()
.SetDisplay("Level1 Pips", "Deviation from band for weakest signal", "Levels");
_level2Pips = Param(nameof(Level2Pips), 5m)
.SetGreaterThanZero()
.SetDisplay("Level2 Pips", "Deviation from band for level 2", "Levels");
_level3Pips = Param(nameof(Level3Pips), 10m)
.SetGreaterThanZero()
.SetDisplay("Level3 Pips", "Deviation from band for level 3", "Levels");
_level4Pips = Param(nameof(Level4Pips), 20m)
.SetGreaterThanZero()
.SetDisplay("Level4 Pips", "Deviation from band for strongest signal", "Levels");
_multiplier1 = Param(nameof(StrengthLevel1Multiplier), 1)
.SetGreaterThanZero()
.SetDisplay("Strength 1 Multiplier", "Volume multiplier for level 1", "Strength");
_multiplier2 = Param(nameof(StrengthLevel2Multiplier), 2)
.SetGreaterThanZero()
.SetDisplay("Strength 2 Multiplier", "Volume multiplier for level 2", "Strength");
_multiplier3 = Param(nameof(StrengthLevel3Multiplier), 3)
.SetGreaterThanZero()
.SetDisplay("Strength 3 Multiplier", "Volume multiplier for level 3", "Strength");
_multiplier4 = Param(nameof(StrengthLevel4Multiplier), 4)
.SetGreaterThanZero()
.SetDisplay("Strength 4 Multiplier", "Volume multiplier for level 4", "Strength");
_riskPercent = Param(nameof(RiskPercent), 2)
.SetGreaterThanZero()
.SetDisplay("Risk %", "Risk percentage per trade", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BandsPeriod,
Width = BandsDeviation
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bb = (BollingerBandsValue)value;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
return;
var step = Security?.PriceStep ?? 0.0001m;
var close = candle.ClosePrice;
if (close - upper > Level4Pips * step)
{
SellByStrength(StrengthLevel4Multiplier, close);
}
else if (close - upper > Level3Pips * step)
{
SellByStrength(StrengthLevel3Multiplier, close);
}
else if (close - upper > Level2Pips * step)
{
SellByStrength(StrengthLevel2Multiplier, close);
}
else if (close - upper > Level1Pips * step)
{
SellByStrength(StrengthLevel1Multiplier, close);
}
else if (lower - close > Level4Pips * step)
{
BuyByStrength(StrengthLevel4Multiplier, close);
}
else if (lower - close > Level3Pips * step)
{
BuyByStrength(StrengthLevel3Multiplier, close);
}
else if (lower - close > Level2Pips * step)
{
BuyByStrength(StrengthLevel2Multiplier, close);
}
else if (lower - close > Level1Pips * step)
{
BuyByStrength(StrengthLevel1Multiplier, close);
}
}
private void BuyByStrength(int strength, decimal price)
{
BuyMarket();
}
private void SellByStrength(int strength, decimal price)
{
SellMarket();
}
}
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
from StockSharp.Algo.Strategies import Strategy
class scalp_wiz_bollinger_strategy(Strategy):
def __init__(self):
super(scalp_wiz_bollinger_strategy, self).__init__()
self._bands_period = self.Param("BandsPeriod", 30) \
.SetDisplay("Bands Period", "Number of candles for Bollinger calculation", "Bollinger")
self._bands_deviation = self.Param("BandsDeviation", 2.0) \
.SetDisplay("Bands Deviation", "Standard deviation multiplier", "Bollinger")
self._level1_pips = self.Param("Level1Pips", 1.0) \
.SetDisplay("Level1 Pips", "Deviation from band for weakest signal", "Levels")
self._level2_pips = self.Param("Level2Pips", 5.0) \
.SetDisplay("Level2 Pips", "Deviation from band for level 2", "Levels")
self._level3_pips = self.Param("Level3Pips", 10.0) \
.SetDisplay("Level3 Pips", "Deviation from band for level 3", "Levels")
self._level4_pips = self.Param("Level4Pips", 20.0) \
.SetDisplay("Level4 Pips", "Deviation from band for strongest signal", "Levels")
self._multiplier1 = self.Param("StrengthLevel1Multiplier", 1) \
.SetDisplay("Strength 1 Multiplier", "Volume multiplier for level 1", "Strength")
self._multiplier2 = self.Param("StrengthLevel2Multiplier", 2) \
.SetDisplay("Strength 2 Multiplier", "Volume multiplier for level 2", "Strength")
self._multiplier3 = self.Param("StrengthLevel3Multiplier", 3) \
.SetDisplay("Strength 3 Multiplier", "Volume multiplier for level 3", "Strength")
self._multiplier4 = self.Param("StrengthLevel4Multiplier", 4) \
.SetDisplay("Strength 4 Multiplier", "Volume multiplier for level 4", "Strength")
self._risk_percent = self.Param("RiskPercent", 2) \
.SetDisplay("Risk %", "Risk percentage per trade", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def bands_period(self):
return self._bands_period.Value
@property
def bands_deviation(self):
return self._bands_deviation.Value
@property
def level1_pips(self):
return self._level1_pips.Value
@property
def level2_pips(self):
return self._level2_pips.Value
@property
def level3_pips(self):
return self._level3_pips.Value
@property
def level4_pips(self):
return self._level4_pips.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(scalp_wiz_bollinger_strategy, self).OnReseted()
def OnStarted2(self, time):
super(scalp_wiz_bollinger_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.bands_period
bollinger.Width = self.bands_deviation
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, self.process_candle).Start()
def process_candle(self, candle, value):
if candle.State != CandleStates.Finished:
return
upper = value.UpBand
lower = value.LowBand
if upper is None or lower is None:
return
upper = float(upper)
lower = float(lower)
step = 0.0001
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
close = float(candle.ClosePrice)
l1 = float(self.level1_pips) * step
l2 = float(self.level2_pips) * step
l3 = float(self.level3_pips) * step
l4 = float(self.level4_pips) * step
if close - upper > l4:
self.SellMarket()
elif close - upper > l3:
self.SellMarket()
elif close - upper > l2:
self.SellMarket()
elif close - upper > l1:
self.SellMarket()
elif lower - close > l4:
self.BuyMarket()
elif lower - close > l3:
self.BuyMarket()
elif lower - close > l2:
self.BuyMarket()
elif lower - close > l1:
self.BuyMarket()
def CreateClone(self):
return scalp_wiz_bollinger_strategy()