Diese Strategie portiert den Expertenberater „Divergence + ema + rsi close buy only“ von MetaTrader auf den hochrangigen API von StockSharp. Es reagiert auf 5-Minuten-Kerzen und zieht gleichzeitig stündliche und tägliche Daten heran, um die Trendausrichtung und überverkaufte Bedingungen zu bestätigen. Bestellungen sind nur auf lange Sicht möglich. Einträge erfordern eine bullische MACD-Histogrammdivergenz, die durch einen stündlichen stochastischen Crossover innerhalb eines engen überverkauften Bandes und durch eine steigende tägliche EMA-Struktur bestätigt wird. Exits basieren auf einer festen RSI-Überschreitung in Kombination mit einem optionalen Stop-Loss- und Take-Profit-Schutz, der vom Framework verwaltet wird.
Handelslogik
Trendfilter für längere Zeiträume
Täglich muss EMA(9) über EMA(20) liegen, um einen vorherrschenden Aufwärtstrend sicherzustellen.
Der letzte 5-Minuten-Schlusskurs muss unter dem täglichen EMA(9) bleiben, damit bei Pullbacks Long-Einstiege versucht werden.
Stündliche stochastische Bestätigung
Der letzte abgeschlossene stündliche stochastische %K-Wert muss zwischen StochasticLowerBound (Standard 0) und StochasticUpperBound (Standard 40) liegen.
%K muss auf dem letzten Stundenbalken %D überschritten haben (aktueller %K > %D, während der vorherige %K ≤ vorheriger %D).
MACD Divergenzauslöser (5 Minuten)
Das MACD-Histogramm (MACD-Linie minus Signallinie) muss sich um mindestens MacdThreshold verbessern, während der 5-Minuten-Schluss ein niedrigeres Tief im Vergleich zur vorherigen Kerze setzt. Dies entspricht in etwa der bullischen Divergenz, die vom ursprünglichen EA verwendet wurde.
Eintrittsausführung
Wenn alle Filter übereinstimmen und keine Long-Position offen ist, sendet die Strategie einen Marktkauf. Wenn eine unerwartete Short-Position besteht, wird das angeforderte Volumen erhöht, um die Position zu neutralisieren, bevor eine Long-Position eingegangen wird.
Ausgangsregeln
Ein schützender RSI-Ausgang schließt den Long, wenn der 5-Minuten-RSI RsiExitLevel überschreitet (Standard 77).
StartProtection aktiviert sowohl Stop-Loss- als auch Take-Profit-Level, die von Pips in Preisabstände umgewandelt werden, wenn die entsprechenden Parameter positiv sind.
Auftragsverwaltung
Alle aktiven Aufträge werden storniert, bevor ein neuer Marktkaufauftrag gesendet wird, um doppelte Ausführungen zu vermeiden.
Die Lautstärke ist standardmäßig auf den Parameter TradeVolume eingestellt und kann zur Optimierung angepasst werden.
Parameter
Name
Beschreibung
Standard
CandleType
Primärer Zeitrahmen für MACD, RSI und Ausführung.
5-Minuten-Kerzen
HourTimeFrame
Stündlicher Zeitrahmen, der vom stochastischen Filter verwendet wird.
Die Strategie abonniert drei Datenströme: den konfigurierten primären Zeitrahmen, eine stündliche Serie und eine tägliche Serie. Jeder Stream steuert seinen eigenen Indikatorsatz über Bind/BindEx, um die Implementierung prägnant und ereignisgesteuert zu halten.
Indikatorwerte werden nur bei fertigen Kerzen verarbeitet, um die ursprünglichen Verschiebungsparameter von EA widerzuspiegeln.
Die MACD-Divergenzerkennung verwendet den Schluss- und Histogrammwert des vorherigen Balkens als einfache, aber robuste Annäherung an die vom Builder generierte Logik aus der Quelldatei MQL.
Stop-Loss und Take-Profit werden von StartProtection verwaltet, um mit der Ausführung durch den Broker synchronisiert zu bleiben und Backtesting oder Live-Handel ohne manuelle Auftragsreplikation zu unterstützen.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Divergence EMA RSI Close Buy Only" MetaTrader expert.
/// Long-only strategy: buys when price pulls back below fast EMA with RSI oversold,
/// exits when RSI reaches overbought level.
/// </summary>
public class DivergenceEmaRsiCloseBuyOnlyStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiEntry;
private readonly StrategyParam<decimal> _rsiExit;
private ExponentialMovingAverage _ema;
private RelativeStrengthIndex _rsi;
private decimal? _prevRsi;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal RsiEntry
{
get => _rsiEntry.Value;
set => _rsiEntry.Value = value;
}
public decimal RsiExit
{
get => _rsiExit.Value;
set => _rsiExit.Value = value;
}
public DivergenceEmaRsiCloseBuyOnlyStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signals", "General");
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for trend filter", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period for entry/exit", "Indicators");
_rsiEntry = Param(nameof(RsiEntry), 35m)
.SetDisplay("RSI Entry", "RSI level to enter long", "Signals");
_rsiExit = Param(nameof(RsiExit), 65m)
.SetDisplay("RSI Exit", "RSI level to exit long", "Signals");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_prevRsi = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, _rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_rsi.IsFormed)
{
_prevRsi = rsiValue;
return;
}
if (_prevRsi is null)
{
_prevRsi = rsiValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var close = candle.ClosePrice;
// Exit: RSI crosses above exit level
if (Position > 0 && _prevRsi.Value < RsiExit && rsiValue >= RsiExit)
{
SellMarket(Position);
}
// Entry: RSI crosses below entry level and price is near/below EMA (buy the dip)
if (Position <= 0 && _prevRsi.Value > RsiEntry && rsiValue <= RsiEntry && close <= emaValue * 1.005m)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(volume);
}
_prevRsi = rsiValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_ema = null;
_rsi = null;
_prevRsi = null;
base.OnReseted();
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class divergence_ema_rsi_close_buy_only_strategy(Strategy):
def __init__(self):
super(divergence_ema_rsi_close_buy_only_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._ema_period = self.Param("EmaPeriod", 20)
self._rsi_period = self.Param("RsiPeriod", 7)
self._rsi_entry = self.Param("RsiEntry", 35.0)
self._rsi_exit = self.Param("RsiExit", 65.0)
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def RsiEntry(self):
return self._rsi_entry.Value
@RsiEntry.setter
def RsiEntry(self, value):
self._rsi_entry.Value = value
@property
def RsiExit(self):
return self._rsi_exit.Value
@RsiExit.setter
def RsiExit(self, value):
self._rsi_exit.Value = value
def OnReseted(self):
super(divergence_ema_rsi_close_buy_only_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(divergence_ema_rsi_close_buy_only_strategy, self).OnStarted2(time)
self._prev_rsi = None
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._process_candle).Start()
def _process_candle(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
rsi_val = float(rsi_value)
close = float(candle.ClosePrice)
if self._prev_rsi is None:
self._prev_rsi = rsi_val
return
rsi_entry = float(self.RsiEntry)
rsi_exit = float(self.RsiExit)
# Exit: RSI crosses above exit level
if self.Position > 0 and self._prev_rsi < rsi_exit and rsi_val >= rsi_exit:
self.SellMarket()
# Entry: RSI crosses below entry level and price is near/below EMA
if self.Position <= 0 and self._prev_rsi > rsi_entry and rsi_val <= rsi_entry and close <= ema_val * 1.005:
self.BuyMarket()
self._prev_rsi = rsi_val
def CreateClone(self):
return divergence_ema_rsi_close_buy_only_strategy()