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
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.
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.
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.
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;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class elli_strategy(Strategy):
def __init__(self):
super(elli_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_length = self.Param("FastLength", 19) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 60) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period for momentum", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_atr = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastLength(self):
return self._fast_length.Value
@property
def SlowLength(self):
return self._slow_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(elli_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_atr = 0.0
self._entry_price = 0.0
self._fast = ExponentialMovingAverage()
self._fast.Length = self.FastLength
self._slow = ExponentialMovingAverage()
self._slow.Length = self.SlowLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast, self._slow, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
self._prev_atr = av
return
close = float(candle.ClosePrice)
# Exit: stop or take based on ATR
if self.Position > 0:
if close <= self._entry_price - av * 2.0 or close >= self._entry_price + av * 3.0:
self.SellMarket()
self._entry_price = 0.0
elif fv < sv:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close >= self._entry_price + av * 2.0 or close <= self._entry_price - av * 3.0:
self.BuyMarket()
self._entry_price = 0.0
elif fv > sv:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = fv
self._prev_slow = sv
self._prev_atr = av
return
# Entry: EMA crossover with ATR expansion
if self.Position == 0:
atr_rising = av > self._prev_atr
if self._prev_fast <= self._prev_slow and fv > sv and atr_rising:
self._entry_price = close
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and atr_rising:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
self._prev_atr = av
def OnReseted(self):
super(elli_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_atr = 0.0
self._entry_price = 0.0
def CreateClone(self):
return elli_strategy()