ScalpWiz Bollinger Strategy
Overview
The ScalpWiz Bollinger Strategy is a counter-trend system that uses Bollinger Bands to detect stretched prices. When the close price moves far above the upper band or below the lower band, the strategy opens a position in the opposite direction expecting a reversion.
Four distance levels are checked. Each level corresponds to a different signal strength and multiplies the trade volume. Position size is also scaled by a risk percentage of the current portfolio value.
Parameters
BandsPeriod– number of candles used to calculate Bollinger Bands.BandsDeviation– standard deviation multiplier for the bands.Level1Pips…Level4Pips– distance from the band in pips that triggers a level 1–4 signal.StrengthLevel1Multiplier…StrengthLevel4Multiplier– volume multipliers for each level.RiskPercent– percentage of portfolio value risked per signal.CandleType– candle timeframe used for calculations.
Trading Logic
- Subscribe to candles of the selected timeframe and compute Bollinger Bands.
- On every finished candle:
- If the close is above the upper band by a configured level distance, open a short position.
- If the close is below the lower band by a configured level distance, open a long position.
- Volume is calculated from the risk percentage and signal strength multiplier.
The strategy was inspired by the original MQL mcb.scalpwiz.9001.mq4 script.
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()