Diese Strategie portiert den MetaTrader 5 Expert Advisor TDSGlobal aus MQL/23255/TDSGlobal.mq5 auf die StockSharp High-Level-API. Sie bewertet den Momentum auf Vier-Stunden-Kerzen durch die MACD-Linie, das MACD-Histogramm (OsMA) und den Force Index. Wenn die Indikator-Kombination eine potenzielle Umkehr signalisiert, sendet die Strategie ausstehende Limit-Orders rund um die Extrempunkte der vorherigen Kerze und verwaltet die resultierende Position mit optionaler Stop-Loss-, Take-Profit- und Trailing-Stop-Logik.
Die Implementierung reproduziert den ursprünglichen Workflow und passt ihn an idiomatische StockSharp-Konstrukte wie StrategyParam<T>, Kerzen-Abonnements über SubscribeCandles und asynchrone Orderverarbeitung durch die Strategie-Lifecycle-Events an.
Handelslogik
Indikatorberechnungen
MACD(12, 26, 9) liefert sowohl die MACD-Linie als auch das Histogramm (OsMA).
ForceIndex(24) misst die Kraft der letzten abgeschlossenen Kerze.
Jeder Indikator wird beim Schließen des gewählten Kerzentyps aktualisiert (Standard: 4 Stunden).
Signalerkennung
Der Algorithmus wartet, bis zwei historische MACD- und OsMA-Werte verfügbar sind, um deren Steigung zu bestimmen.
Ein Verkaufs-Setup erfordert, dass OsMA steigt (osma[1] > osma[2]), während der Force Index der vorherigen Kerze negativ ist.
Ein Kauf-Setup erfordert, dass OsMA fällt (osma[1] < osma[2]), während der vorherige Force Index positiv ist.
Order-Platzierung
Sell-Limit-Orders werden leicht oberhalb des vorherigen Kerzenhochs platziert; Buy-Limit-Orders leicht unterhalb des vorherigen Kerzenunterteils.
Wenn der Preis nicht weit genug vom aktuellen Bid/Ask entfernt ist, wird der Order-Preis zum konfigurierten Offset-Puffer gezogen (EntryOffsetPips, Standard 16 Pips).
Die Strategie prüft, ob der Abstand zwischen dem Order-Preis und dem aktuellen Bid/Ask die Broker-Sicherheitsniveau-Approximation überschreitet (MinDistancePips oder der dynamische Spread-basierte Wert).
Risikokontrollen
Optionale Stop-Loss- und Take-Profit-Niveaus werden aus dem Order-Preis berechnet.
Wenn eine Position aktiv ist, kann ein Trailing Stop um den konfigurierten Schritt vorrücken, sobald der Preis den anfänglichen Trailing-Abstand überschreitet.
Wenn der Preis innerhalb einer Kerze die Schutzniveaus erreicht, wird die Position mit einer Marktorder geschlossen, um das MetaTrader-Verhalten nachzuahmen.
Order-Wartung
Ausstehende Orders werden storniert, wenn die OsMA-Steigung gegen das ursprüngliche Setup dreht, entsprechend der Bereinigungsroutine des Quell-EAs.
Das Füllen einer Seite storniert automatisch die entgegengesetzte ausstehende Order, um widersprüchliche Engagements zu vermeiden.
Geldverwaltung
Zwei Positionsgrößenansätze sind verfügbar:
Festes Volumen (Standard OrderVolume = 1) — verwendet das Basis-Strategy.Volume ohne Anpassungen.
Risikobasierte Größenbestimmung — wenn UseRiskSizing aktiviert ist, schätzt die Strategie das Portfolio-Eigenkapital, wandelt den konfigurierten Risikoanteil in Währungsrisiko um und dividiert es durch den Stop-Loss-Abstand, um das Order-Volumen abzuleiten. Volumina werden am Volumen-Schritt des Instruments ausgerichtet, um ungültige Order-Größen zu vermeiden.
Parameter
Name
Beschreibung
Standard
OrderVolume
Feste Order-Größe wenn Risiko-Sizing deaktiviert ist.
1
UseRiskSizing
Geldverwaltung basierend auf RiskPercent aktivieren.
true
RiskPercent
Prozentsatz des Portfolio-Eigenkapitals pro Trade riskiert.
3
MacdFastPeriod
Schnelle EMA-Länge für die MACD-Linie.
12
MacdSlowPeriod
Langsame EMA-Länge für die MACD-Linie.
26
MacdSignalPeriod
Signal-EMA-Länge für das MACD-Histogramm.
9
ForceLength
EMA-Glättungslänge für den Force Index.
24
StopLossPips
Stop-Loss-Abstand in Pips (0 deaktiviert).
50
TakeProfitPips
Take-Profit-Abstand in Pips (0 deaktiviert).
50
TrailingStopPips
Trailing-Stop-Abstand in Pips (0 deaktiviert).
5
TrailingStepPips
Mindestschritt für Trailing-Updates.
5
EntryOffsetPips
Puffer um vorherige Hochs/Tiefs für ausstehende Orders.
16
MinDistancePips
Mindestzulässiger Abstand zwischen Preis und Schutzniveaus.
3
PipSize
Pip-Größe für Pip-zu-Preis-Konvertierungen.
0.0001
CandleType
Von der Strategie verarbeiteter Kerzentyp.
4-Stunden-Kerzen
Verwendungshinweise
Füge die Datei CS/TdsGlobalPendingStrategy.cs zu deinem StockSharp-Projekt hinzu oder lade sie dynamisch über die Backtester-Umgebung.
Weise das gewünschte Wertpapier und Portfolio zu, bevor du die Strategie startest. Wenn UseRiskSizing aktiviert ist, stelle sicher, dass das Portfolio aktuelle Eigenkapitalwerte bereitstellt.
Die Strategie benötigt mindestens zwei abgeschlossene Kerzen, um MACD/OsMA-Steigungen zu initialisieren. Es ist eine kurze Aufwärmphase zu erwarten.
Überwache die Logs auf detaillierte Order- und Positionsereignisse. Die Implementierung protokolliert Schlüsselaktionen (Order-Einreichung, Stornierung, Trailing-Updates) zur Überprüfung gegen das ursprüngliche EA-Verhalten.
Unterschiede zur MQL-Version
Die High-Level-API verwaltet asynchrone Order-Events, sodass Limit-Order-Füllungen über OnOwnTradeReceived statt über synchrone OrderSend-Ergebnisse verarbeitet werden.
Broker-"Freeze"- und "Stops"-Niveaus werden mit dem konfigurierten Mindestabstand und einer Spread-basierten Heuristik approximiert, da StockSharp keine MetaTrader-spezifischen Handelslimits exponiert.
Schutzende Exits werden über Marktorders ausgeführt, wenn die Kerze einen Bruch zeigt. Dies repliziert die manuelle Stop-Modifikationslogik des EAs ohne Abhängigkeit von MT5-Handelsserver-Einschränkungen.
Diese Anpassungen halten die Handelslogik treu, während sie sicherstellen, dass die Strategie reibungslos in das StockSharp-Framework integriert wird.
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 TdsGlobalPendingStrategy : 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 TdsGlobalPendingStrategy()
{
_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 tds_global_pending_strategy(Strategy):
def __init__(self):
super(tds_global_pending_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(tds_global_pending_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(tds_global_pending_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 tds_global_pending_strategy()