Ver no GitHub

MelBar Take325 Strategy

The MelBar Take325 strategy is a direct conversion of the Expert Advisor Studio system "MelBar™Take325%™ 5.5Y NZD-USD". It trades both directions on NZD/USD using a combination of tick volume breakouts, a swing filter based on a 12-period simple moving average, and a 14-period RSI exit filter. The StockSharp port keeps the original risk parameters of a 16-pip stop loss and a 45-pip take profit, expressed in pip distances from the entry price.

The strategy starts by waiting for an increase in tick volume, defined as a breakout above the configured volume threshold. When volume expands, it checks whether the simple moving average formed a local turning point two bars earlier. A local maximum in the SMA opens a long trade, while a local minimum opens a short trade. Only one direction can be taken at a time, and conflicting signals are ignored to avoid flip-flopping on the same bar.

Open positions are actively managed. Stop-loss and take-profit levels are enforced every time a candle closes, making the behaviour similar to the MetaTrader version. Additionally, the 14-period RSI is used to force exits: long trades close when RSI crosses down through the configured level (default 80), and short trades close when RSI crosses up through the symmetric level (default 20). The high/low of the processed candle is compared with the entry price to trigger stop-loss and take-profit exits.

Details

  • Entry Criteria:
    • Volume filter: tick volume two bars ago must be below the threshold while the previous bar exceeds it.
    • Long: SMA (length 12) has a local peak two bars ago (SMA[t-3] < SMA[t-2] and SMA[t-2] > SMA[t-1]).
    • Short: SMA has a local trough (SMA[t-3] > SMA[t-2] and SMA[t-2] < SMA[t-1]).
  • Exit Criteria:
    • Stop-loss: 16 pips from entry, evaluated on candle closes.
    • Take-profit: 45 pips from entry, evaluated on candle closes.
    • Long RSI exit: RSI crosses downward through 80 (RSI[t-3] > 80 and RSI[t-2] < 80).
    • Short RSI exit: RSI crosses upward through 20 (RSI[t-3] < 20 and RSI[t-2] > 20).
  • Default Parameters:
    • Entry volume = 0.1 lots.
    • Volume threshold = 1000 tick volume units.
    • SMA period = 12.
    • RSI period = 14.
    • RSI level = 80 (short exit uses 100 - level).
    • Candle timeframe = 30 minutes.
  • Market: Designed for NZD/USD but can be applied to other FX pairs.
  • Style: Momentum breakout with mean-reversion exits.
  • Stops: Fixed stop-loss and take-profit; no trailing stop in the original code.
  • Complexity: Moderate; combines multiple filters but no position scaling.
  • Risk: Medium, as the stop is tighter than the take-profit but both are fixed distances.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// MelBar Take325 strategy: SMA reversal with RSI exit filter.
/// Enters on SMA direction change, exits when RSI reaches extreme levels.
/// </summary>
public class MelBarTake325Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiExitLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevSma;
	private decimal _prevPrevSma;
	private bool _hasPrev2;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal RsiExitLevel { get => _rsiExitLevel.Value; set => _rsiExitLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public MelBarTake325Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_smaPeriod = Param(nameof(SmaPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period", "Indicators");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_rsiExitLevel = Param(nameof(RsiExitLevel), 75m)
			.SetDisplay("RSI Exit Level", "RSI level to close long; 100-level closes short", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 8)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevSma = 0m;
		_prevPrevSma = 0m;
		_hasPrev2 = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev2 = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var sma = new SimpleMovingAverage { Length = SmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(sma, rsi, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal sma, decimal rsi)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev2)
		{
			// Exit on RSI extremes
			if (Position > 0 && rsi > RsiExitLevel)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
			else if (Position < 0 && rsi < (100 - RsiExitLevel))
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}

			// Entry on SMA reversal (peak/trough)
			if (Position == 0 && _candlesSinceTrade >= SignalCooldownCandles && _prevPrevSma > _prevSma && _prevSma < sma)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (Position == 0 && _candlesSinceTrade >= SignalCooldownCandles && _prevPrevSma < _prevSma && _prevSma > sma)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevPrevSma = _prevSma;
		_prevSma = sma;
		_hasPrev2 = true;
	}
}