Bollinger Band breakout scalping strategy translated from the MQL4 "fxscalper" expert.
The strategy subscribes to candle data and Bollinger Bands. When the closing price breaks above the upper band it opens a long position; when the closing price breaks below the lower band it opens a short position. Positions are protected by stop-loss and take-profit levels.
Details
Entry Criteria:
Long: Close > Upper Band
Short: Close < Lower Band
Long/Short: Both
Exit Criteria: Opposite signal or protective stops
Stops: Stop loss and take profit
Default Values:
CandleType = TimeSpan.FromMinutes(5).TimeFrame()
BollingerPeriod = 20
BollingerDeviation = 2
StopLoss = 200m
TakeProfit = 150m
Filters:
Category: Bollinger Bands
Direction: Both
Indicators: Bollinger Bands
Stops: Yes
Complexity: Basic
Timeframe: Intraday
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bollinger Band breakout scalping strategy.
/// </summary>
public class FxscalperStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public decimal BollingerDeviation { get => _bollingerDeviation.Value; set => _bollingerDeviation.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FxscalperStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands length", "Indicators");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Width", "Bollinger Bands width", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle Type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(bollinger, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
var bb = (BollingerBandsValue)bbValue;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
if (candle.ClosePrice > upper && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (candle.ClosePrice < lower && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
// Mean reversion exit: close when price returns to middle band
if (Position > 0 && candle.ClosePrice <= middle)
SellMarket();
else if (Position < 0 && candle.ClosePrice >= middle)
BuyMarket();
}
}
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 fxscalper_strategy(Strategy):
def __init__(self):
super(fxscalper_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetDisplay("BB Period", "Bollinger Bands length", "Indicators")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetDisplay("BB Width", "Bollinger Bands width", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle Type", "General")
self._entry_price = 0.0
@property
def bollinger_period(self):
return self._bollinger_period.Value
@property
def bollinger_deviation(self):
return self._bollinger_deviation.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fxscalper_strategy, self).OnReseted()
self._entry_price = 0.0
def OnStarted2(self, time):
super(fxscalper_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.bollinger_period
bollinger.Width = self.bollinger_deviation
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
def on_process(self, candle, value):
if candle.State != CandleStates.Finished:
return
upper = float(value.UpBand)
lower = float(value.LowBand)
middle = float(value.MovingAverage)
if upper == 0 or lower == 0:
return
close = float(candle.ClosePrice)
if close > upper and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
elif close < lower and self.Position >= 0:
self.SellMarket()
self._entry_price = close
if self.Position > 0 and close <= middle:
self.SellMarket()
elif self.Position < 0 and close >= middle:
self.BuyMarket()
def CreateClone(self):
return fxscalper_strategy()