View on GitHub

MelBar EuroSwiss Strategy

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;
		}
	}
}