Auf GitHub ansehen

Ilan iMA-Strategie

Überblick

Die Ilan iMA-Strategie ist ein StockSharp-Port des MetaTrader 5 Expert Advisors Ilan iMA.mq5. Der Advisor kombiniert einen verschobenen gleitenden Durchschnitt als Trendfilter mit einem Martingal-Mittelungsraster. Die StockSharp-Version implementiert dieselben Ideen mit der High-Level-API: Wenn der gewichtete gleitende Durchschnitt einen Trend bestätigt, öffnet die Strategie eine Marktorder und fügt jedes Mal neue Trades hinzu, wenn der Preis um einen konfigurierbaren Schritt gegen die Position läuft. Der gesamte Korb wird geschlossen, wenn ein Gewinnziel, Trailing-Stop oder expliziter Stop-Loss erreicht wird, was das Geldmanagement-Modell des ursprünglichen EA reproduziert.

Trading-Logik

  1. Abonnieren des ausgewählten Zeitrahmens (CandleType) und Fütterung eines konfigurierbaren gleitenden Durchschnitts (MaMethod, MaPeriod, PriceMode). Ein positiver MaShift verschiebt den Indikator vorwärts, sodass die Strategie historische Werte auswertet, um das MT5-Verhalten nachzubilden.
  2. Warten auf den Kerzenabschluss. Nur abgeschlossene Bars generieren Signale und aktualisieren die Trailing/Stop-Logik.
  3. Trend durch Vergleich von vier aufeinanderfolgenden gleitenden Durchschnittswerten, verschoben um MaShift Bars, erkennen:
    • strikt abnehmende Werte signalisieren einen Abwärtstrend;
    • strikt zunehmende Werte signalisieren einen Aufwärtstrend.
  4. Wenn kein Korb offen ist:
    • im Abwärtstrend, wenn der Schlusskurs über dem gleitenden Durchschnittswert liegt, Short mit StartVolume eröffnen;
    • im Aufwärtstrend, wenn der Schlusskurs unter dem gleitenden Durchschnittswert liegt, Long mit StartVolume eröffnen.
  5. Wenn ein Korb existiert:
    • wenn der Preis sich mindestens GridStepPips gegen die Position bewegt, eine weitere Order öffnen, deren Größe mit LotExponent wächst, aber durch LotMaximum und die Börsenvolumenlimits begrenzt wird;
    • der durchschnittliche Einstandspreis, der niedrigste Kaufpreis und der höchste Verkaufspreis werden intern verfolgt, um das Verhalten nah an der MT5-Logik zu halten.
  6. Schließbedingungen:
    • sobald der schwebende Gewinn eines Korbs mit mehr als einem Trade ProfitMinimum (in Kontowährung) erreicht, alle Orders in dieser Richtung schließen;
    • wenn der schwebende Gewinn TakeProfitPips erreicht oder der Verlust StopLossPips trifft, den Korb schließen;
    • der Trailing-Schutz wird nach TrailingStopPips + TrailingStepPips Punkten günstiger Bewegung aktiv und bewegt sich in Schritten von TrailingStepPips.

Risikomanagement und Größenanpassung

  • StartVolume repliziert den MT5-Parameter StartLots. Jede zusätzliche Order multipliziert die vorherige Größe mit LotExponent unter Einhaltung von LotMaximum und den Börsengrenzwerten (Security.MinVolume, Security.VolumeStep, Security.MaxVolume).
  • ProfitMinimum bewahrt das "Sperrfreigabe"-Verhalten der MT5-Version: Sobald das Raster von einer Absicherung erholt hat und den angeforderten Gewinn druckt, werden alle Trades in dieser Richtung geschlossen.
  • Stop-Loss- und Take-Profit-Abstände werden in Pips gemessen (StopLossPips, TakeProfitPips). Die Hilfsmethode konvertiert Pips in Exchange-Preisschritte mit Security.PriceStep.
  • Der Trailing-Block emuliert die MT5-Implementierung: Trailing beginnt erst, wenn der Preis TrailingStopPips + TrailingStepPips überschreitet, und wird in diskreten Schritten aktualisiert, um vorzeitige Stop-Anpassungen zu vermeiden.

Parameter

Name Typ Standard MT5-Entsprechung Beschreibung
MaPeriod int 15 Inp_MA_ma_period Periode des Trendfilter-gleitenden Durchschnitts.
MaShift int 5 Inp_MA_ma_shift Vorwärtsverschiebung der gleitenden Durchschnittslinie in Bars.
MaMethod MovingAverageMethod Weighted Inp_MA_ma_method Glättungsalgorithmus (SMA, EMA, SMMA, LWMA).
PriceMode CandlePrice Weighted Inp_MA_applied_price In den Indikator eingespeister Kerzenpreis.
StartVolume decimal 1 InpStartLots Basisordervolumen für den ersten Trade in einem Korb.
GridStepPips decimal 30 InpStep Abstand (in Pips) zwischen Mittelungseinträgen.
LotExponent decimal 1.6 InpLotExponent Auf die vorherige Ordergröße angewendeter Multiplikator.
LotMaximum decimal 15 InpLotMaximum Harte Obergrenze für ein einzelnes Ordervolumen.
ProfitMinimum decimal 15 InpProfitMinimum Mindest-schwebender Gewinn zum Schließen eines Korbs mit mehreren Trades.
StopLossPips decimal 0 InpStopLoss Stop-Loss-Abstand in Pips (0 deaktiviert den Stop).
TakeProfitPips decimal 100 InpTakeProfit Take-Profit-Abstand in Pips.
TrailingStopPips decimal 15 InpTrailingStop Gewinnschwelle, die den Trailing-Stop aktiviert.
TrailingStepPips decimal 5 InpTrailingStep Minimaler zusätzlicher Gewinn, bevor sich der Trailing-Stop wieder bewegt.
CandleType DataType 15-Minuten-Zeitrahmen Chartperiode Zeitrahmen für die Signalberechnung.

Unterschiede zum ursprünglichen EA

  • StockSharp arbeitet in einer Netting-Umgebung, daher existiert nur eine Nettoposition pro Richtung. Die Strategie führt eine interne Liste von Einstiegspreisen und Volumen, um die MT5-Korbabrechnung zu emulieren.
  • Börsenspezifische Volumenlimits werden beim Runden von Volumen immer respektiert, während der MT5-Code auf manuelle Prüfungen angewiesen war. Dies verhindert Orders, die vom Broker-Connector abgelehnt würden.
  • Stop-Loss-, Take-Profit- und Trailing-Logik werden durch Marktausstiege statt durch Modifikation bestehender MT5-Positionen ausgedrückt. Das funktionale Verhalten bleibt dasselbe, aber das Ordermanagement wird von StockSharp übernommen.

Verwendungshinweise

  • Stellen Sie sicher, dass die Instrument-Metadaten (PriceStep, StepPrice, MinVolume, VolumeStep, MaxVolume) im Connector ausgefüllt sind, damit Pip-zu-Preis-Konvertierungen und Volumenrundungen korrekt funktionieren.
  • Der Trailing-Block geht davon aus, dass die Pip-Größe dem Exchange-Preisschritt entspricht. Passen Sie GridStepPips, StopLossPips und TrailingStopPips für Instrumente mit ungewöhnlichen Tick-Größen an.
  • Martingal-Raster sind von Natur aus riskant. Testen Sie die Strategie auf historischen Daten und verwenden Sie realistische Kommissions-/Slippage-Einstellungen, bevor Sie in der Produktion einsetzen.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class IlanImaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public IlanImaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}