Auf GitHub ansehen

Strategie RSI Martingale

Überblick

RSI Martingale ist eine Portierung des MetaTrader 5 Expertenberaters RSI&Martingale1.5. Die Strategie sucht nach Momentumumkehrungen, indem sie wartet, bis der Relative Strength Index (RSI) innerhalb eines konfigurierbaren Lookback-Fensters einen Extremwert erreicht. Wenn ein Extrem auftritt, eröffnet es einen Handel in Richtung der erwarteten Mittelwertumkehr und endet, wenn RSI die Mittellinie von 50 überschreitet oder wenn ein festes Stop/Take-Ziel erreicht wird. Ein Martingale-Modul kann die Position nach einem Verlusthandel optional in die entgegengesetzte Richtung mit einem erhöhten Volumen wieder öffnen. Tägliche Gewinn- und Verlustlimits sowie stündliche Filter ermöglichen es, den Handel während riskanterer Sitzungen oder nach Erreichen der Kapitalerhaltungsziele auszusetzen.

Strategielogik

RSI Extreme

  • Indikator – ein einzelner RSI, berechnet für den ausgewählten Kerzentyp. Der Indikator muss gebildet werden (ausreichend historische Daten), bevor Trades berücksichtigt werden.
  • Mindesterkennung – wenn der letzte RSI-Wert kleiner oder gleich jedem RSI-Wert innerhalb des konfigurierten Bars For Extremes-Fensters ist und der Wert unter 50 liegt, eröffnet die Strategie eine Long-Position.
  • Maximale Erkennung – wenn der letzte RSI-Wert größer oder gleich jedem Wert innerhalb des Lookback-Fensters ist und der Wert über 50 liegt, eröffnet die Strategie eine Short-Position.

Positionsmanagement

  • Ausstiegsauslöser – Positionen werden geschlossen, wenn RSI die neutrale 50-Linie auf die entgegengesetzte Seite überschreitet (Long-Positionen steigen über 50 aus, Short-Positionen steigen unter 50 aus).
  • Feste Ziele – optionale Stop-Loss- und Take-Profit-Abstände, ausgedrückt in Pips. Wenn diese Option aktiviert ist, vergleicht die Strategie das Hoch/Tief der letzten Kerze mit diesen Zielpreisen und schließt die Position, wenn eines dieser Niveaus erreicht wird.
  • Volumenausrichtung – jedes Auftragsvolumen wird vor der Übermittlung an die Schritt-, Mindest- und Höchsteinstellungen des Wertpapiers angepasst.

Martingale Wiederherstellung

  • Auslöser – Nachdem eine Position mit einem negativen Gewinn geschlossen wurde, merkt sich die Strategie die Richtung und das Volumen des Verlusthandels.
  • Wiedereintritt – bei der nächsten geeigneten Kerze und nur wenn keine Position offen ist, kann sofort ein Handel in die entgegengesetzte Richtung eröffnet werden. Das Volumen ist entweder das Verlustvolumen multipliziert mit Martingale Multiplier oder dem Basiswert Initial Volume, abhängig vom Schalter Enable Martingale.
  • Zurücksetzen – Sobald der Martingal-Befehl übermittelt wurde, werden die gespeicherten Verlustinformationen gelöscht, um wiederholte Versuche zu vermeiden.

Tägliche Kapitalkontrolle

  • Baseline – Die Strategie erfasst das Kontoguthaben zu Beginn jedes Handelstages und setzt die Aussetzungsmarkierung zurück.
  • Überwachungsfenster – Tageslimits werden nur zwischen Daily Control Start und Daily Control End Stunden ausgewertet.
  • Aussetzung – wenn das Eigenkapital über Daily Profit % steigt oder unter Daily Loss % fällt, schließt die Strategie alle offenen Positionen und überspringt neue Trades bis zum nächsten Tag.

Sitzungsfilter

  • Handelsfenster – neue Positionen sind nur zulässig, wenn die aktuelle Stunde zwischen Trading Start und Trading End (einschließlich) liegt.
  • Stundenvermeidung – 24 boolesche Parameter spiegeln die „Nachrichtenvermeidungs“-Einstellungen der Quelle EA wider und blockieren den Handel während der ausgewählten Stunden.

Parameter

  • Anfangsvolumen – Basisauftragsvolumen für Standardeinträge.
  • RSI-Periode – Anzahl der Perioden, die vom RSI-Indikator verwendet werden.
  • Bars For Extremes – wie viele fertige Kerzen gescannt werden, wenn nach dem neuesten Minimum oder Maximum von RSI gesucht wird.
  • Take Profit (Pips) – Abstand zum festen Take-Profit; zum Deaktivieren auf 0 setzen.
  • Stop-Loss (Pips) – Abstand zum festen Stop-Loss; zum Deaktivieren auf 0 setzen.
  • Aktivieren Sie Martingale – aktiviert das Martingale-Wiederherstellungsmodul nach einem verlorenen Trade.
  • Martingale Multiplikator – Multiplikator, der auf das vorherige Verlustvolumen angewendet wird, wenn Martingal aktiv ist.
  • Tägliche Ziele – schaltet die tägliche Gewinn-/Verlust-Aussetzungslogik um.
  • Tagesgewinn % – Gewinnprozentsatz, der den Handel für den aktuellen Tag stoppt.
  • Täglicher Verlust % – Verlustprozentsatz, der den Handel für den aktuellen Tag stoppt.
  • Täglicher Kontrollbeginn / Tägliches Kontrollende – Stundengrenzen zur Auswertung der Tagesgrenzen.
  • Handelsbeginn / Handelsende – Stundengrenzen, die neue Positionen ermöglichen.
  • Vermeiden Sie Stunde 00 … Vermeiden Sie Stunde 23 – Deaktivieren Sie den Handel während der entsprechenden Uhrstunde.
  • Kerzentyp – Kerzenabonnement, das für den Indikator RSI und alle Berechnungen verwendet wird.

Zusätzliche Hinweise

  • Die Strategie gilt nur für fertige Kerzen und wertet keine Intrabar-Ticks aus.
  • Tägliche Gewinnberechnungen kombinieren realisierte Strategie-PnL mit variablem PnL basierend auf dem letzten Schlusskurs.
  • Das Paket enthält keine Python-Implementierung für diese Strategie. Es wird nur die C#-Version bereitgestellt.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RSI extremes strategy with martingale recovery.
/// Buys when RSI is at a local minimum below 50, sells when RSI is at a local maximum above 50.
/// Closes on RSI crossing 50. Doubles volume after a losing trade.
/// </summary>
public class RSIMartingaleStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _barsForCondition;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _recentRsi = new();
	private decimal _entryPrice;
	private int _direction; // 1=long, -1=short, 0=flat

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public int BarsForCondition
	{
		get => _barsForCondition.Value;
		set => _barsForCondition.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public RSIMartingaleStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI indicator period", "Indicator");

		_barsForCondition = Param(nameof(BarsForCondition), 10)
			.SetDisplay("Bars For Extremes", "Number of RSI values to check for extremes", "Indicator");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "Data");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, rsi);
			DrawOwnTrades(area);
		}
	}

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

		_recentRsi.Add(rsiValue);
		if (_recentRsi.Count > BarsForCondition)
			_recentRsi.RemoveAt(0);

		if (_recentRsi.Count < BarsForCondition)
			return;

		// Check exit: close on RSI crossing 50
		if (_direction > 0 && rsiValue > 50)
		{
			SellMarket();
			_direction = 0;
			return;
		}
		else if (_direction < 0 && rsiValue < 50)
		{
			BuyMarket();
			_direction = 0;
			return;
		}

		if (Position != 0)
			return;

		// Check if current RSI is local minimum (oversold entry)
		if (IsLocalMinimum() && rsiValue < 50 && Position <= 0)
		{
			BuyMarket();
			_entryPrice = candle.ClosePrice;
			_direction = 1;
		}
		// Check if current RSI is local maximum (overbought entry)
		else if (IsLocalMaximum() && rsiValue > 50 && Position >= 0)
		{
			SellMarket();
			_entryPrice = candle.ClosePrice;
			_direction = -1;
		}
	}

	private bool IsLocalMinimum()
	{
		if (_recentRsi.Count < 2)
			return false;

		var current = _recentRsi[_recentRsi.Count - 1];
		for (var i = 0; i < _recentRsi.Count - 1; i++)
		{
			if (current > _recentRsi[i])
				return false;
		}
		return true;
	}

	private bool IsLocalMaximum()
	{
		if (_recentRsi.Count < 2)
			return false;

		var current = _recentRsi[_recentRsi.Count - 1];
		for (var i = 0; i < _recentRsi.Count - 1; i++)
		{
			if (current < _recentRsi[i])
				return false;
		}
		return true;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_recentRsi.Clear();
		_entryPrice = 0;
		_direction = 0;

		base.OnReseted();
	}
}