Die MA RSI EA-Strategie reproduziert die Logik des ursprünglichen MetaTrader-Expertenberaters, der einen schnellen gleitenden Durchschnitt mit einem RSI-Filter kurzer Periode kombiniert. Die Strategie handelt auf der ausgewählten Kerzen-Serie, bewertet neue Orders nur bei fertigen Balken und verwendet dynamisches Positionsgrößen-Sizing basierend auf Kontostand oder Eigenkapital. Wenn der schwebende Gewinn aller offenen Positionen positiv wird, wird jede Position sofort geschlossen, um den Gewinn zu sichern.
Indikatoren
Moving Average – konfigurierbarer Methode (einfach, exponentiell, geglättet, linear gewichtet) mit Preisquellen-Auswahl und optionalem Shift.
Relative Strength Index (RSI) – kurzfristiger Oszillator, der aus derselben Kerzenpreisfamilie wie in der MQL-Version liest.
Handelslogik
Für jede abgeschlossene Kerze berechnet die Strategie die gleitenden Durchschnitts- und RSI-Werte unter Verwendung der konfigurierten Preisquellen.
Der aktuellste gleitende Durchschnittswert kann um eine benutzerdefinierte Anzahl von Balken verschoben werden, um das MQL-Verhalten zu entsprechen.
Es wertet das schwebende PnL der aktuellen Netto-Position aus:
Wenn das schwebende Ergebnis aller offenen Positionen größer als null ist, schließt die Strategie die gesamte Position, um den Gewinn zu realisieren.
Wenn das schwebende Ergebnis negativ ist, wird die Seite mit dem kleineren Verlust (Kauf-Seite vs. Verkauf-Seite) durch Eröffnen eines zusätzlichen Trades in dieser Richtung verstärkt.
Wenn kein Mittelungssignal vorhanden ist, wird der RSI + MA-Filter angewendet:
Short-Einstieg – RSI ≥ RsiOverbought und der Kerzen-Eröffnungspreis liegt unter dem verschobenen gleitenden Durchschnitt.
Long-Einstieg – RSI ≤ RsiOversold und der Kerzen-Eröffnungspreis liegt über dem verschobenen gleitenden Durchschnitt.
Ausstiegslogik
Positives schwebendes PnL löst CloseAllPositions aus und flattert die Strategie sofort.
Manuelle Umkehrsignale aus der Mittelungslogik schließen das entgegengesetzte Exposure, da StockSharp mit Netto-Positionen arbeitet.
Positionsgrößen
LotSizingModes spiegelt die OptLot-Auswahl aus dem EA:
Fixed – sendet immer LotSize-Volumen.
Balance – konvertiert PercentOfBalance des Portfoliowerts in Volumen unter Verwendung des Kerzen-Schlusskurses.
Equity – konvertiert PercentOfEquity des aktuellen Portfolio-Eigenkapitals in Volumen.
Das berechnete Volumen wird auf das nächste Security.VolumeStep gerundet (wenn verfügbar), damit Orders der Lot-Größe des Instruments entsprechen.
Shift auf das Ergebnis des gleitenden Durchschnitts.
0
FastMaMethod
Berechnungsmethode des gleitenden Durchschnitts (Simple, Exponential, Smoothed, LinearWeighted).
LinearWeighted
FastMaPrice
Kerzenpreisquelle für den gleitenden Durchschnitt.
Open
RsiPeriod
RSI-Länge.
4
RsiPrice
Kerzenpreisquelle für den RSI.
Open
RsiOverbought
RSI-Level, der einen überkauften Markt definiert.
80
RsiOversold
RSI-Level, der einen überverkauften Markt definiert.
20
CandleType
Von der Strategie verwendete Kerzen-Serie.
15-Minuten-Zeitrahmen
Kerzenpreisquellen
CandlePriceSources repliziert die MQL-Applied-Price-Liste:
Open, High, Low, Close
Median = (High + Low) / 2
Typical = (High + Low + Close) / 3
Weighted = (High + Low + Close + Close) / 4
Hinweise
Orders werden nur generiert, wenn die Strategie online ist und die Kerze beendet ist, was dem ursprünglichen EA entspricht, der bei neuen Balken auslöst.
Da StockSharp eine Netto-Position hält, reduzieren oder drehen Mittelungssignale automatisch das aktuelle Exposure, anstatt Hedge-Positionen zu erstellen.
Python-Implementierung wird auf Anfrage absichtlich weggelassen.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MA + RSI strategy. Uses EMA trend direction with RSI momentum confirmation.
/// </summary>
public class MaRsiEaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private decimal? _prevRsi;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
public MaRsiEaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "EMA period for trend", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "Indicators");
_rsiOverbought = Param(nameof(RsiOverbought), 65m)
.SetDisplay("Overbought", "RSI overbought level", "Levels");
_rsiOversold = Param(nameof(RsiOversold), 35m)
.SetDisplay("Oversold", "RSI oversold level", "Levels");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
var ema = new ExponentialMovingAverage { Length = MaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
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 (!IsFormedAndOnlineAndAllowTrading())
{
_prevRsi = rsiValue;
return;
}
var close = candle.ClosePrice;
if (_prevRsi == null)
{
_prevRsi = rsiValue;
return;
}
// Buy: price above EMA and RSI crosses above oversold
if (close > emaValue && _prevRsi.Value <= RsiOversold && rsiValue > RsiOversold && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell: price below EMA and RSI crosses below overbought
else if (close < emaValue && _prevRsi.Value >= RsiOverbought && rsiValue < RsiOverbought && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevRsi = rsiValue;
}
}
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 ma_rsi_ea_strategy(Strategy):
def __init__(self):
super(ma_rsi_ea_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ma_period = self.Param("MaPeriod", 20) \
.SetDisplay("MA Period", "EMA period for trend", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 65.0) \
.SetDisplay("Overbought", "RSI overbought level", "Levels")
self._rsi_oversold = self.Param("RsiOversold", 35.0) \
.SetDisplay("Oversold", "RSI oversold level", "Levels")
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaPeriod(self):
return self._ma_period.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def RsiOverbought(self):
return self._rsi_overbought.Value
@property
def RsiOversold(self):
return self._rsi_oversold.Value
def OnReseted(self):
super(ma_rsi_ea_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(ma_rsi_ea_strategy, self).OnStarted2(time)
self._prev_rsi = None
ema = ExponentialMovingAverage()
ema.Length = self.MaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
ev = float(ema_value)
if self._prev_rsi is None:
self._prev_rsi = rv
return
ob = float(self.RsiOverbought)
os_level = float(self.RsiOversold)
# Buy: price above EMA and RSI crosses above oversold
if close > ev and self._prev_rsi <= os_level and rv > os_level and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Sell: price below EMA and RSI crosses below overbought
elif close < ev and self._prev_rsi >= ob and rv < ob and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return ma_rsi_ea_strategy()