Diese Strategie ist eine direkte Portierung des MetaTrader Expert Advisors EA_MARSI_1-02. Es handelt sich um Crossovers zwischen zwei Kopien von
Der benutzerdefinierte EMA_RSI_VA-Indikator von Integer, ein volatilitätsadaptiver gleitender Durchschnitt, der vom Relative Strength Index (RSI) gesteuert wird.
Immer wenn die langsame Linie die schnelle Linie kreuzt, kehrt der Motor die Nettoposition um und reproduziert so den ursprünglichen „Flip-on-Crossover“.
Verhalten unter Beachtung der Best Practices für die Auftragsabwicklung von StockSharp.
Anzeigemechanik
Das Originalpaket MQL wird mit einem benutzerdefinierten Indikator namens EMA_RSI_VA geliefert. Es berechnet einen preisgeglätteten EMA, dessen Effektivwert
Die Länge wird durch den Abstand von RSI von seinem neutralen Wert moduliert. Der StockSharp-Port führt das ein
EmaRsiVolatilityAdaptiveIndicator-Klasse, die die Formel genau repliziert:
Berechnen Sie RSI für die ausgewählte AppliedPrice-Quelle mit dem Zeitraum RSIPeriod.
Messen Sie den RSI-Abstand von 50 (|RSI - 50| + 1), der als Volatilitäts-Proxy dient.
Leiten Sie einen adaptiven Multiplikator ab
multi = (5 + 100 / RSIPeriod) / (0.06 + 0.92 * dist + 0.02 * dist^2).
Multiplizieren Sie den konfigurierten Zeitraum EMA mit diesem Multiplikator, um eine dynamische Länge pdsx zu erhalten.
Wenden Sie die Standardrekursion EMA mit dem Glättungsfaktor 2 / (pdsx + 1) an und verwenden Sie dabei den angewendeten Preis der Kerze als Eingabe.
Große RSI-Auslenkungen verkürzen das Glättungsfenster und sorgen dafür, dass die Leitung schneller reagiert. Ein flacher RSI verlängert das Fenster und dämpft
Lärm. Sowohl die langsame als auch die schnelle Zeile stellen den gesamten Satz der von StockSharp.Messages.AppliedPrice unterstützten Preismodi bereit.
Die Strategie analysiert nur fertige Kerzen aus der konfigurierten Kerzenserie.
Wenn ein Signal auftritt, wird eine Marktorder übermittelt, die sowohl das bestehende Engagement schließt als auch die neue Richtung eröffnet.
Umtauschlimits werden durch Security.MinVolume, Security.VolumeStep und Security.MaxVolume eingehalten.
Umbuchungen
Aufträge werden saldiert, sodass ein einzelner SellMarket- oder BuyMarket-Aufruf die Position über der Nulllinie annimmt und mit der übereinstimmt
MQL-Verhalten, bei dem ein entgegengesetztes Signal den Handel sofort umkehrt.
Risikomanagement
TakeProfitPoints und StopLossPoints replizieren die TP/SL-Felder des Expert Advisors (ausgedrückt in Preispunkten). Wenn entweder
Der Wert ist ungleich Null. Die Strategie startet den Schutzmanager von StockSharp mit absoluten Preisversätzen und useMarketOrders = true.
um die ursprüngliche OrderSend Stopp-/Limit-Änderungsschleife widerzuspiegeln.
UseBalanceMultiplier implementiert den use_Multpl-Schalter. Bei Aktivierung wird das effektive Auftragsvolumen erhöht
Volume * PortfolioEquity / MaxDrawdown mit einer defensiven Klammer, um Einschränkungen auszutauschen.
Der Aufruf der Basisklasse StartProtection() wird weiterhin ausgeführt, sodass externe Risikomodule Trailing- oder Break-Even-Anhänge anhängen können
Logik, falls erforderlich.
Parameter
Parameter
Standard
Beschreibung
Volume
0.1
Basis-Market-Order-Größe vor Anwendung eines Saldomultiplikators.
TakeProfitPoints
0
Take-Profit-Distanz in Instrumentenpunkten; 0 deaktiviert die Take-Profit-Komponente.
StopLossPoints
0
Stop-Loss-Distanz in Instrumentenpunkten; 0 deaktiviert den Schutzstopp.
UseBalanceMultiplier
false
Ermöglicht die Balance-proportionale Positionsgröße, identisch mit use_Multpl im EA.
MaxDrawdown
10000
Nenner für den Saldomultiplikator; entspricht dem Max_drawdown von EA.
SlowRsiPeriod
310
RSI-Lookback für die langsame EMA_RSI_VA-Zeile.
SlowEmaPeriod
40
Basislänge von EMA für die langsame Zeile vor der Adaption von RSI.
SlowAppliedPrice
Close
Der Preismodus wird an den langsamen Indikator weitergeleitet.
FastRsiPeriod
200
RSI-Lookback für die schnelle EMA_RSI_VA-Zeile.
FastEmaPeriod
50
Basislänge EMA für die schnelle Linie vor der RSI-Anpassung.
FastAppliedPrice
Close
Preismodus wird an den Schnellindikator weitergeleitet.
CandleType
TimeFrame(1m)
Für Berechnungen verwendete Kerzenreihe.
Hinweise zur Implementierung
Der Port wird mit StockSharps High-Level-API (SubscribeCandles().Bind(...)) geschrieben, um manuelle Indikatorschleifen zu vermeiden.
Es werden nur abgeschlossene Kerzen verarbeitet, die mit CopyBuffer(..., 1, 2, ...)-Aufrufen in der MQL-Quelle übereinstimmen.
Die Volumennormalisierung verwendet Security.MinVolume, Security.VolumeStep und Security.MaxVolume und verhindert so ungültige Bestellungen
echter Austausch.
Auf eine Python-Version wird wie gewünscht bewusst verzichtet; Das Verzeichnis enthält nur die C#-Implementierung und Dokumentation.
Das resultierende Verhalten spiegelt die Quelle EA wider und legt gleichzeitig StockSharp-freundliche Parameter und geeignete Risikokontrollen offen
Designer, Runner oder jeder benutzerdefinierte Host, der auf StockSharp API basiert.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// EMA RSI VA Crossover: Fast/slow EMA crossover with RSI volatility filter.
/// </summary>
public class EmaRsiVaCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public EmaRsiVaCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 40)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (fastVal < slowVal && _prevFast >= _prevSlow))
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (fastVal > slowVal && _prevFast <= _prevSlow))
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 40 && rsiVal < 70)
{
_entryPrice = close;
BuyMarket();
}
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal > 30 && rsiVal < 60)
{
_entryPrice = close;
SellMarket();
}
}
_prevFast = fastVal;
_prevSlow = slowVal;
}
}
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, RelativeStrengthIndex, AverageTrueRange
class ema_rsi_va_cross_strategy(Strategy):
def __init__(self):
super(ema_rsi_va_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 40) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
def OnStarted2(self, time):
super(ema_rsi_va_cross_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = 14
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_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
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 2.0 or (fv < sv and self._prev_fast >= self._prev_slow):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 2.0 or (fv > sv and self._prev_fast <= self._prev_slow):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 40 and rv < 70:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv > 30 and rv < 60:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(ema_rsi_va_cross_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return ema_rsi_va_cross_strategy()