Diese Strategie ist eine originalgetreue C#-Konvertierung des MetaTrader 4 Expert Advisors „STO_m5xm15xm30“. Es verwendet drei stochastische Oszillatoren, die auf den Zeitrahmen M5, M15 und M30 berechnet wurden, um synchronisierte Impulsverschiebungen zu identifizieren. Die StockSharp-Implementierung behält die ursprüngliche Ein-/Ausstiegsstruktur bei, ersetzt die manuelle Auftragsverwaltung durch die übergeordnete API und stellt jede Schlüsselkonstante als konfigurierbare StrategyParam bereit.
Handelslogik
Bestätigung mehrerer Zeitrahmen
Die primäre Stochastik (Standard M5) muss einen bullischen Crossover aufweisen (%K kreuzt über %D).
Die mittleren (Standard M15) und langsamen (Standard M30) stochastischen Werte müssen bereits bullisch sein (%K über %D).
Ein bärisches Setup erfordert die gespiegelten Bedingungen (%K unter %D).
Filter verschieben
Die primäre Stochastik überprüft auch den Status ShiftBars Kerzen früher. Für ein Kaufsignal muss der historische %K unter %D liegen, um einen erneuten Crossover sicherzustellen. Verkaufssignale erfordern das Gegenteil.
Preismomentum-Filter
Der letzte Schlusskurs muss höher (für Käufe) oder niedriger (für Verkäufe) sein als der zuvor abgeschlossene Kerzenschluss. Dies spiegelt die Close[0] > Close[1]-Regel aus dem MT4-Skript wider.
Eintrittsregeln
Wenn keine Position offen ist und die bullischen Kriterien erfüllt sind, eröffnet die Strategie eine Long-Market-Order mit dem konfigurierten TradeVolume.
Wenn beim Eintreffen eines bullischen Signals eine Short-Position besteht, wird diese zunächst abgeflacht und anschließend eine Long-Position eröffnet. Das Umgekehrte gilt für bärische Signale.
Ausgangsregeln
Eine dedizierte M5-Stochastik mit der Periode ExitKPeriod prüft die vorherige Kerze (shift = 1). Eine Long-Position wird geschlossen, wenn %K unter %D fällt; Ein Short wird geschlossen, wenn %K über %D steigt.
Nachdem ein Ausstieg ausgelöst wurde, überspringt die Strategie den sofortigen Wiedereintritt bei derselben Kerze und reproduziert so das Verhalten der MT4-Orderschleife.
Indikatoren und Datenabonnements
Primärkerzen: Standardzeitrahmen von 5 Minuten (CandleType).
Mittlere Bestätigungskerzen: Standardzeitrahmen von 15 Minuten (MiddleCandleType).
Langsame Bestätigungskerzen: Standardzeitrahmen von 30 Minuten (SlowCandleType).
Stochastic-Oszillatoren: Alle verwenden %K-Glättung = 3 und %D-Glättung = 3, entsprechend den ursprünglichen Parametern.
Parameter
Name
Standard
Beschreibung
CandleType
5-Minuten-Kerzen
Arbeitszeitrahmen für Ein- und Ausstiege.
MiddleCandleType
15-Minuten-Kerzen
Bestätigungszeitraum Nr. 1.
SlowCandleType
30-Minuten-Kerzen
Bestätigungszeitraum Nr. 2.
FastKPeriod
5
%K-Periode für die primäre Stochastik.
MiddleKPeriod
5
%K-Periode für die mittlere Stochastik.
SlowKPeriod
5
%K-Periode für die langsame Stochastik.
ExitKPeriod
5
%K-Periode für den Exit-Stochastic, der auf dem vorherigen Balken wirkt.
ShiftBars
3
Anzahl der Balken zwischen dem Referenz-Crossover und dem aktuellen Balken.
TakeProfitPoints
30
Schutz-Take-Profit-Distanz (Punkte).
StopLossPoints
10
Schutz-Stop-Loss-Distanz (Punkte).
TradeVolume
0,1
Bestellvolumen, das für Neuzugänge verwendet wird.
Alle Parameter werden über StrategyParam<T> verfügbar gemacht, sodass sie für die Optimierung im StockSharp-Designer verfügbar sind.
Risikomanagement
StartProtection() übersetzt die MT4-Eingaben TP und SL in StockSharp Schutzanordnungen. Beide können deaktiviert werden, indem der entsprechende Parameter auf Null gesetzt wird.
Implementierungshinweise
Indikatorwerte werden ausschließlich über SubscribeCandles(...).BindEx(...) ermittelt, wobei die übergeordneten API-Richtlinien eingehalten und manuelle Indikatorerfassungen vermieden werden.
Der StochasticShiftBuffer-Helfer ahmt das MT4-Argument shift nach, ohne GetValue aufzurufen, und behält nur den erforderlichen Balkenverlauf bei.
Die Eingabeverarbeitung erfolgt einmal pro abgeschlossener Kerze. Die Exit-Bewertung erfolgt vor der Eingangslogik und entspricht der Verarbeitungsreihenfolge des ursprünglichen EA.
Inline-Kommentare erläutern jeden Verarbeitungsschritt und verdeutlichen, wie die MQL-Logik dem StockSharp-Code zugeordnet wird.
Nutzung
Fügen Sie die Strategie einem StockSharp-Schema oder Designerprojekt hinzu.
Konfigurieren Sie das gewünschte Symbol und stellen Sie sicher, dass historische Daten für M5-, M15- und M30-Kerzen verfügbar sind.
Passen Sie die Parameter an den Zielmarkt oder das Optimierungsszenario an.
Starten Sie die Strategie; Für jede Position werden automatisch schützende Stop-Loss-/Take-Profit-Level registriert.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// STO M5xM15xM30: RSI momentum with dual EMA confirmation.
/// Uses RSI crossover of 50 level with fast/slow EMA alignment.
/// </summary>
public class StoM5xM15xM30Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public StoM5xM15xM30Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_fastLength = Param(nameof(FastLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowLength = Param(nameof(SlowLength), 30)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.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();
_prevRsi = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var fast = new ExponentialMovingAverage { Length = FastLength };
var slow = new ExponentialMovingAverage { Length = SlowLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, 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 rsiVal, decimal fastVal, decimal slowVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevRsi == 0 || atrVal <= 0)
{
_prevRsi = rsiVal;
return;
}
var close = candle.ClosePrice;
// Exit management
if (Position > 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
{
SellMarket();
_entryPrice = 0;
}
else if (rsiVal < 40 && fastVal < slowVal)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
{
BuyMarket();
_entryPrice = 0;
}
else if (rsiVal > 60 && fastVal > slowVal)
{
BuyMarket();
_entryPrice = 0;
}
}
// Entry: RSI crosses 50 with EMA alignment
if (Position == 0)
{
if (_prevRsi <= 50 && rsiVal > 50 && fastVal > slowVal)
{
_entryPrice = close;
BuyMarket();
}
else if (_prevRsi >= 50 && rsiVal < 50 && fastVal < slowVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevRsi = rsiVal;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class sto_m5x_m15x_m30_strategy(Strategy):
def __init__(self):
super(sto_m5x_m15x_m30_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._fast_length = self.Param("FastLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period for stops", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.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(sto_m5x_m15x_m30_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
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._rsi, self._fast, self._slow, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
fv = float(fast_val)
sv = float(slow_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
# Exit management
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 rv < 40 and 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 rv > 60 and fv > sv:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
# Entry: RSI crosses 50 with EMA alignment
if self.Position == 0:
if self._prev_rsi <= 50 and rv > 50 and fv > sv:
self._entry_price = close
self.BuyMarket()
elif self._prev_rsi >= 50 and rv < 50 and fv < sv:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(sto_m5x_m15x_m30_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
def CreateClone(self):
return sto_m5x_m15x_m30_strategy()