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
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.
Warten auf den Kerzenabschluss. Nur abgeschlossene Bars generieren Signale und aktualisieren die Trailing/Stop-Logik.
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.
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.
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.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ilan_ima_strategy(Strategy):
def __init__(self):
super(ilan_ima_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(ilan_ima_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(ilan_ima_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return ilan_ima_strategy()