The MelBar EuroSwiss strategy reproduces the logic of the "MelBar EuroSwiss M30 500 1.85x 2Y" expert advisor. It combines Bollinger Band breakout entries with an exit filter based on the Relative Vigor Index (RVI). The default template is tuned for the EUR/CHF pair on the M30 timeframe, but the parameters can be optimized for other symbols.
At the start of each finished candle the strategy reads the Bollinger Bands and the RVI values calculated on closing prices. New positions are opened when the current bar opens beyond the envelope while the previous bar opened back inside the channel. This behaviour imitates the gap-style breakout logic of the original MQL5 robot. Long trades use the lower band as the trigger, while short trades react to the upper band. Existing positions are closed when the delayed RVI crosses above or below an absolute level, indicating momentum exhaustion in the direction of the trade. Optional protective orders are set using fixed pip distances.
The default volume is 0.2 lots, but the TradeVolume parameter allows fine control over the position size. Both stop loss and take profit are expressed in pips and converted to price offsets through the configurable PipSize parameter. The same pip size is reused to arm the protection module at start-up. All calculations rely on finished candles to avoid look-ahead bias.
Details
Entry Criteria:
Long: Current candle open < previous lower Bollinger band AND previous candle open > lower band from two candles ago.
Short: Current candle open > previous upper Bollinger band AND previous candle open < upper band from two candles ago.
Exit Criteria:
Long: Close when historic RVI value exceeds +RviLevel.
Short: Close when historic RVI value falls below -RviLevel.
Stops: Optional fixed stop loss and take profit distances in pips.
Indicators: Bollinger Bands (period BollingerPeriod, deviation BollingerDeviation) and Relative Vigor Index (RviPeriod).
Default Values:
TradeVolume = 0.2 lots
BollingerPeriod = 18
BollingerDeviation = 2.75
RviPeriod = 15
RviLevel = 0.30
StopLossPips = 13
TakeProfitPips = 61
PipSize = 0.0001
CandleType = TimeSpan.FromMinutes(30)
Other Notes:
Category: Breakout reversal
Direction: Both long and short
Timeframe: Intraday (M30 by default)
Risk Level: Medium due to fixed pip-based risk controls
Trailing stop: Not enabled by default (can be implemented externally)
The provided parameters mirror the original configuration and serve as a solid starting point for walk-forward tests or optimization runs in StockSharp.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MelBar EuroSwiss strategy: Bollinger Bands breakout with RSI confirmation.
/// Buys when close breaks below lower band with oversold RSI.
/// Sells when close breaks above upper band with overbought RSI.
/// </summary>
public class MelBarEuroSwissStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public MelBarEuroSwissStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_bbPeriod = Param(nameof(BbPeriod), 18)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 12)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candlesSinceTrade = SignalCooldownCandles;
var sma = new SimpleMovingAverage { Length = BbPeriod };
var atr = new AverageTrueRange { Length = BbPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, atr, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma, decimal atr, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var close = candle.ClosePrice;
var bandDistance = atr * 4m;
if (_candlesSinceTrade >= SignalCooldownCandles && close <= sma - bandDistance && rsi < 25 && Position <= 0)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_candlesSinceTrade >= SignalCooldownCandles && close >= sma + bandDistance && rsi > 75 && Position >= 0)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
}
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 SimpleMovingAverage, AverageTrueRange, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class mel_bar_euro_swiss_strategy(Strategy):
def __init__(self):
super(mel_bar_euro_swiss_strategy, self).__init__()
self._bb_period = self.Param("BbPeriod", 18) \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._signal_cooldown = self.Param("SignalCooldownCandles", 12) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._sma = None
self._atr = None
self._rsi = None
self._candles_since_trade = 0
@property
def bb_period(self):
return self._bb_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(mel_bar_euro_swiss_strategy, self).OnReseted()
self._sma = None
self._atr = None
self._rsi = None
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(mel_bar_euro_swiss_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.bb_period
self._atr = AverageTrueRange()
self._atr.Length = self.bb_period
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(60)))
subscription.Bind(self._sma, self._atr, self._rsi, self._process_candle)
subscription.Start()
def _process_candle(self, candle, sma_value, atr_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._sma.IsFormed or not self._atr.IsFormed or not self._rsi.IsFormed:
return
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
close = float(candle.ClosePrice)
sma_val = float(sma_value)
atr_val = float(atr_value)
rsi_val = float(rsi_value)
band_distance = atr_val * 4.0
if self._candles_since_trade >= self.signal_cooldown and close <= sma_val - band_distance and rsi_val < 25.0 and self.Position <= 0:
self.BuyMarket()
self._candles_since_trade = 0
elif self._candles_since_trade >= self.signal_cooldown and close >= sma_val + band_distance and rsi_val > 75.0 and self.Position >= 0:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return mel_bar_euro_swiss_strategy()