Auf GitHub ansehen

Elli-Strategie

Überblick

Die Elli-Strategie portiert den MetaTrader 4-Expertenberater „Elli“ auf das StockSharp-Hochniveau API. Der ursprüngliche Roboter kombinierte die Ichimoku-Kinko-Hyo-Struktur im H1-Zeitrahmen mit einem Filter für den niedrigeren Zeitrahmen ADX und strengen Risikoparametern. Die Konvertierung behält die gleiche Richtungslogik bei, ersetzt die manuelle Auftragsverwaltung durch StartProtection und stellt jeden Einstellknopf als optimierbaren StrategyParam<T> bereit, sodass das Verhalten an verschiedene Märkte angepasst werden kann.

Handelslogik

  1. Ichimoku Trendstruktur
    • Die Strategie abonniert den durch CandleType definierten Zeitrahmen (standardmäßig H1) und berechnet Tenkan-sen-, Kijun-sen- und Senkou-Spannen unter Verwendung der ursprünglichen Zeiträume (19, 60, 120).
    • Ein bullisches Setup erfordert Tenkan > Kijun > Senkou Span A > Senkou Span B, wobei die Kerze über Kijun schließt. Bärische Setups spiegeln diesen Zustand wider.
    • Die absolute Entfernung zwischen Tenkan und Kijun muss mehr als TenkanKijunGapPips Pips betragen, um flache oder weitläufige Wolken zu vermeiden.
  2. Bestätigung der Richtungsbewegung
    • Ein zweites Kerzenabonnement führt den Average Directional Index in dem durch AdxCandleType angegebenen Zeitrahmen aus (standardmäßig M1).
    • Lange Signale sind nur zulässig, wenn der vorherige +DI-Wert unter ConvertLow liegt und der aktuelle +DI über ConvertHigh liegt. Shorts erfordern die gleiche Beziehung für die −DI-Komponente und replizieren den im MT4-Code vorhandenen Beschleunigungsfilter.
  3. Eintrittsausführung
    • Wenn alle Filter übereinstimmen, gibt die Strategie eine Marktorder mit dem Volumen OrderVolume + |Position| aus. Dadurch wird jedes entgegengesetzte Exposure automatisch geschlossen, bevor Sie sich dem Trend anschließen.
    • Es wird jeweils nur eine Richtungsbelichtung beibehalten, die dem ursprünglichen OrdersTotal() < 1-Schutz folgt.
  4. Risikomanagement
    • StartProtection fügt symmetrische Stop-Loss- und Take-Profit-Orders hinzu, die aus Pip-Abständen unter Verwendung der Pip-Größe des Instruments konvertiert werden.
    • Die Position wird ansonsten passiv verwaltet, sodass die Schutzaufträge genau wie der MT4-Expertenberater Exits abwickeln können.

Indikatoren und Datenabonnements

  • Primäre Kerzen: CandleType (Standard-1-Stunden-Kerzen) für die Ichimoku-Verarbeitung.
  • ADX Kerzen: AdxCandleType (Standardkerzen von 1 Minute) für DI-Beschleunigungsprüfungen.
  • Indikatoren: Ichimoku (Tenkan, Kijun, Senkou Span B) und AverageDirectionalIndex (liefert +DI/−DI).
  • Beide Abonnements unterstützen das Rendern von Diagrammen über DrawCandles, DrawIndicator und DrawOwnTrades, sofern ein Diagrammbereich verfügbar ist.

Parameter

Name Standard Beschreibung
OrderVolume 1 Basis-Market-Order-Volumen.
TakeProfitPips 60 Take-Profit-Distanz, ausgedrückt in Pips.
StopLossPips 30 Stop-Loss-Distanz, ausgedrückt in Pips.
TenkanPeriod 19 Tenkan-sen-Periode für den Ichimoku-Indikator.
KijunPeriod 60 Kijun-Sen-Periode für den Ichimoku-Indikator.
SenkouSpanBPeriod 120 Senkou Span B-Periode für die Ichimoku-Wolke.
TenkanKijunGapPips 20 Mindestabstand zwischen Tenkan und Kijun (in Pips), der vor dem Handel erforderlich ist.
ConvertHigh 13 DI-Schwellenwert, den der aktuelle Wert überschreiten muss, um den Impuls zu bestätigen.
ConvertLow 6 DI-Schwellenwert, unter dem der vorherige Wert bleiben muss, bevor ein neuer Handel erfolgen kann.
AdxPeriod 10 Zeitraum, der für die ADX-Berechnung verwendet wird.
CandleType H1 Zeitrahmen, der die Ichimoku-Berechnung steuert.
AdxCandleType M1 Zeitrahmen für ADX und DI-Überwachung.

Alle Parameter werden mit StrategyParam<T>-Helfern implementiert, was Optimierungen und Laufzeitanpassungen innerhalb von StockSharp Designer ermöglicht.

Implementierungshinweise

  • Die Pip-Umrechnung folgt der Standard-Forex-Konvention (0,0001 für 5-stellige Kurse und 0,01 für 3-stellige Instrumente), um die ursprünglichen Pip-basierten Schwellenwerte beizubehalten.
  • ADX-Werte werden in _latestPlusDi, _previousPlusDi, _latestMinusDi und _previousMinusDi zwischengespeichert, um sicherzustellen, dass die DI-Beschleunigungsprüfung mit den MQL iADX-Aufrufen mit den Schichten 0 und 1 übereinstimmt.
  • IsFormedAndOnlineAndAllowTrading() blockiert Signale, bis die Strategie, Indikatoren und Datenfeeds bereit sind, und verhindert so vorzeitige Trades während der Aufwärmphase.
  • Markteintritte basieren auf Volume + Math.Abs(Position), sodass Richtungsänderungen bestehende Trades sofort abflachen und das Einzelpositionsverhalten des MT4-Skripts nachahmen.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Elli: EMA crossover with ATR momentum confirmation.
/// Fast EMA above slow EMA = bullish, below = bearish.
/// Entry when ATR expansion confirms trend strength.
/// </summary>
public class ElliStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevAtr;
	private decimal _entryPrice;

	public ElliStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 19)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 60)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for momentum.", "Indicators");
	}

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

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0;
		_prevSlow = 0;
		_prevAtr = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = 0;
		_prevSlow = 0;
		_prevAtr = 0;
		_entryPrice = 0;

		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, atr, ProcessCandle)
			.Start();

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			_prevAtr = atrVal;
			return;
		}

		var close = candle.ClosePrice;

		// Exit: stop or take based on ATR
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (fastVal < slowVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (fastVal > slowVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: EMA crossover with ATR expansion
		if (Position == 0)
		{
			var atrRising = atrVal > _prevAtr;

			if (_prevFast <= _prevSlow && fastVal > slowVal && atrRising)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevFast >= _prevSlow && fastVal < slowVal && atrRising)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
		_prevAtr = atrVal;
	}
}