GitHub で見る

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.
  • Level1PipsLevel4Pips – distance from the band in pips that triggers a level 1–4 signal.
  • StrengthLevel1MultiplierStrengthLevel4Multiplier – volume multipliers for each level.
  • RiskPercent – percentage of portfolio value risked per signal.
  • CandleType – candle timeframe used for calculations.

Trading Logic

  1. Subscribe to candles of the selected timeframe and compute Bollinger Bands.
  2. 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.
  3. 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();
	}
}