Auf GitHub ansehen

MelBar Take325-Strategie

Die MelBar Take325-Strategie ist eine direkte Umsetzung des Expert Advisor Studio-Systems „MelBar™Take325%™ 5.5Y NZD-USD“. Der Handel mit NZD/USD in beide Richtungen erfolgt mithilfe einer Kombination aus Ausbrüchen des Tick-Volumens, einem Swing-Filter basierend auf einem einfachen gleitenden Durchschnitt über 12 Perioden und einem Exit-Filter über 14 Perioden RSI. Der StockSharp-Port behält die ursprünglichen Risikoparameter eines 16-Pip-Stop-Loss und eines 45-Pip-Take-Profit bei, ausgedrückt in Pip-Abständen vom Einstiegspreis.

Die Strategie beginnt damit, auf einen Anstieg des Tick-Volumens zu warten, das als Ausbruch über den konfigurierten Volumenschwellenwert definiert ist. Wenn das Volumen zunimmt, wird geprüft, ob der einfache gleitende Durchschnitt zwei Balken zuvor einen lokalen Wendepunkt gebildet hat. Ein lokales Maximum im SMA eröffnet einen Long-Trade, während ein lokales Minimum einen Short-Trade eröffnet. Es kann jeweils nur eine Richtung eingeschlagen werden und widersprüchliche Signale werden ignoriert, um ein Umkippen auf derselben Leiste zu vermeiden.

Offene Positionen werden aktiv verwaltet. Stop-Loss- und Take-Profit-Level werden jedes Mal erzwungen, wenn eine Kerze schließt, wodurch das Verhalten der MetaTrader-Version ähnelt. Darüber hinaus wird der 14-Perioden-RSI verwendet, um Ausstiege zu erzwingen: Long-Trades werden geschlossen, wenn RSI das konfigurierte Niveau nach unten durchläuft (Standard 80), und Short-Trades werden geschlossen, wenn RSI das symmetrische Niveau nach oben kreuzt (Standard 20). Das Hoch/Tief der verarbeiteten Kerze wird mit dem Einstiegspreis verglichen, um Stop-Loss- und Take-Profit-Ausstiege auszulösen.

Einzelheiten

  • Eintrittskriterien:
    • Volumenfilter: Das Tick-Volumen vor zwei Balken muss unter dem Schwellenwert liegen, während der vorherige Balken ihn überschreitet.
    • Lang: SMA (Länge 12) hat einen lokalen Höhepunkt vor zwei Balken (SMA[t-3] < SMA[t-2] und SMA[t-2] > SMA[t-1]).
    • Kurzfassung: SMA hat einen lokalen Tiefpunkt (SMA[t-3] > SMA[t-2] und SMA[t-2] < SMA[t-1]).
  • Ausstiegskriterien:
    • Stop-Loss: 16 Pips ab Einstieg, bewertet bei Kerzenschluss.
    • Take-Profit: 45 Pips ab Einstieg, bewertet bei Kerzenschluss.
    • Langer RSI-Ausgang: RSI kreuzt nach unten durch 80 (RSI[t-3] > 80 und RSI[t-2] < 80).
    • Kurzer RSI-Ausgang: RSI kreuzt nach oben durch 20 (RSI[t-3] < 20 und RSI[t-2] > 20).
  • Standardparameter:
    • Eintrittsvolumen = 0,1 Lots.
    • Volumenschwelle = 1000 Tick-Volumeneinheiten.
    • SMA Zeitraum = 12.
    • RSI Zeitraum = 14.
    • RSI Level = 80 (kurzer Exit verwendet 100 - Level).
    • Kerzenzeitrahmen = 30 Minuten.
  • Markt: Konzipiert für NZD/USD, kann aber auch auf andere FX-Paare angewendet werden.
  • Stil: Momentum-Ausbruch mit Mean-Reversion-Ausstiegen.
  • Stops: Fester Stop-Loss und Take-Profit; kein Trailing Stop im Originalcode.
  • Komplexität: Moderat; kombiniert mehrere Filter, aber keine Positionsskalierung.
  • Risiko: Mittel, da der Stop enger als der Take-Profit ist, aber beide feste Distanzen haben.
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;
	}
}