Color Ma RSI Trigger Duplex Strategie
Diese Strategie portiert den Experten-Berater Exp_ColorMaRsi-Trigger_Duplex.mq5 auf die StockSharp High-Level-API.
Sie betreibt zwei unabhängige MaRsi-Trigger-Detektoren: Der Long-Block entscheidet, wann Long-Positionen geöffnet oder geschlossen werden sollen, während der Short-Block dieselbe Aufgabe für Short-Positionen übernimmt. Jeder Detektor bewertet, ob ein benutzerdefinierter Indikator bullischen (+1), neutralen (0) oder bärischen (-1) Marktdruck meldet. Die ursprüngliche MetaTrader-Logik wird beibehalten, einschließlich der verzögerten Bestätigung, die auf zwei abgeschlossene Balken wartet, bevor sie reagiert, und der separaten Geldverwaltungseinstellungen pro Richtung.
Trading-Idee
- Berechnung zweier exponentieller gleitender Durchschnitte (schnell und langsam) und zweier RSI-Oszillatoren (schnell und langsam) auf einer wählbaren Kerzenserie für jeden Block.
- Bei jeder abgeschlossenen Kerze gibt der Indikator
+1 zurück, wenn beide schnellen Studien ihre langsamen Gegenstücke übersteigen, -1 wenn beide schwächer sind und 0 sonst. Der Rohwert wird auf den Bereich [-1, 1] begrenzt, wie im MT5-Indikator.
- Die Strategie speichert eine fortlaufende Geschichte von Indikatorwerten. Für einen konfigurierten
SignalBar-Versatz vergleicht sie den Wert der Kerze SignalBar + 1 Perioden zurück (genannt older) mit dem Wert der Kerze SignalBar Perioden zurück (genannt recent).
- Long-Logik:
- Wenn
older < 0 schließt der Long-Block alle aktiven Long-Positionen (sofern Long-Ausstiege aktiviert sind).
- Wenn
older > 0 und recent <= 0 bereitet der Long-Block einen neuen Long-Einstieg vor (sofern Long-Einstiege aktiviert sind).
- Die Short-Logik spiegelt den Long-Block:
- Wenn
older > 0 verlässt der Short-Block bestehende Short-Positionen (wenn Short-Ausstiege aktiviert sind).
- Wenn
older < 0 und recent >= 0 öffnet der Block eine neue Short-Position (wenn Short-Einstiege aktiviert sind).
- Optionale Stop-Loss- und Take-Profit-Niveaus, ausgedrückt in Instrumentenpreisschritten, schließen Positionen, wenn der Preis die konfigurierten Niveaus kreuzt.
Die beiden Blöcke können unterschiedliche Kerzen-Zeitrahmen und Preisquellen abonnieren, sodass der Benutzer das ursprüngliche Dual-Zeitrahmen-Verhalten replizieren oder alternative Kombinationen ausprobieren kann.
Parameter
| Parameter |
Beschreibung |
LongCandleType, ShortCandleType |
Von Long- und Short-Block verwendete Kerzen-Datenserien. Standardmäßig 4-Stunden-Kerzen. |
LongVolume, ShortVolume |
Gehandeltes Marktvolumen, wenn der entsprechende Block eine neue Position eröffnet. |
LongAllowOpen, ShortAllowOpen |
Öffnen neuer Positionen für jeden Block aktivieren oder deaktivieren. |
LongAllowClose, ShortAllowClose |
Schließsignale für jeden Block aktivieren oder deaktivieren. |
LongStopLossPoints, ShortStopLossPoints |
Stop-Loss-Abstand gemessen in Preisschritten. Auf 0 setzen zum Deaktivieren. |
LongTakeProfitPoints, ShortTakeProfitPoints |
Take-Profit-Abstand gemessen in Preisschritten. Auf 0 setzen zum Deaktivieren. |
LongSignalBar, ShortSignalBar |
Anzahl abgeschlossener Balken zwischen der aktuellen Kerze und der für die Entscheidungslogik verwendeten. |
LongRsiPeriod, LongRsiLongPeriod, ShortRsiPeriod, ShortRsiLongPeriod |
Längen der schnellen und langsamen RSI-Oszillatoren. |
LongMaPeriod, LongMaLongPeriod, ShortMaPeriod, ShortMaLongPeriod |
Längen der schnellen und langsamen gleitenden Durchschnitte. |
LongRsiPrice, ShortRsiPrice |
Preisquelle für den schnellen RSI (Schluss, Eröffnung, Hoch, Tief, Median, typisch oder gewichtet). |
LongRsiLongPrice, ShortRsiLongPrice |
Preisquelle für den langsamen RSI. |
LongMaPrice, ShortMaPrice |
Preisquelle für den schnellen gleitenden Durchschnitt. |
LongMaLongPrice, ShortMaLongPrice |
Preisquelle für den langsamen gleitenden Durchschnitt. |
LongMaType, ShortMaType |
Gleitender-Durchschnitt-Methode für die schnelle Linie (einfach, exponentiell, geglättet oder gewichtet). |
LongMaLongType, ShortMaLongType |
Gleitender-Durchschnitt-Methode für die langsame Linie. |
Handelsregeln
- Warten, bis die ausgewählte Kerzenserie abgeschlossene Balken produziert und alle Indikatoren vollständig aufgewärmt sind.
- Für jeden Block den MaRsi-Trigger-Wert berechnen und den Historienpuffer aktualisieren.
- Wenn die Historie mindestens
SignalBar + 2 Einträge enthält, die Long- und Short-Bedingungen aus dem Abschnitt Trading-Idee auswerten.
- Vor dem Öffnen einer Position neutralisiert die Strategie alle entgegengesetzten Exposures (wenn das entsprechende Schließ-Flag aktiviert ist). Ein neuer Long-Einstieg kauft beispielsweise genug Volumen, um eine Short-Position zu schließen, und fügt dann erst das Long-Volumen hinzu.
- Nachdem eine Position eröffnet wurde, werden optionale Stop-Loss- und Take-Profit-Niveaus bei jeder abgeschlossenen Kerze überwacht.
- Eröffnungs- und Schließorders werden als Marktorders über die High-Level-Hilfsmethoden
BuyMarket und SellMarket gesendet.
Risikomanagement
- Stops und Ziele werden mit
Security.PriceStep gemessen. Wenn das Instrument keinen Preisschritt bereitstellt, wird ein Standardwert von 1 angenommen, entsprechend dem Verhalten vieler bestehender Strategien in diesem Repository.
- Long- und Short-Blöcke behalten unabhängige Stop- und Take-Einstellungen.
- Die Strategie platziert keine zusätzlichen Schutzorders (wie Trailing Stops); das Verhalten spiegelt den MT5-Experten, der Trades nur schließt, wenn der Indikator feuert oder der harte Stop/Ziel erreicht wird.
Hinweise
- Der StockSharp-Port gibt Marktorders sofort nach Abschluss der auswertenden Kerze aus. In MetaTrader plante der Experte seine Orders für den Eröffnungszeitpunkt des nächsten Balkens über Zeitstempel-Offsets; beide Verhaltensweisen richten sich effektiv aus, da StockSharp das Signal verarbeitet, sobald die Kerze schließt.
- Der ursprüngliche EA exponierte mehrere Geldverwaltungsmodi (
LOT, BALANCE usw.). StockSharp-Strategien arbeiten mit direkten Volumenwerten, daher hält der Port das Volumen als einfachen Parameter (LongVolume/ShortVolume).
- Slippage und Magic-Number-spezifische Logik aus der MT5-Hilfsbibliothek sind in StockSharp nicht notwendig und wurden weggelassen.
- Indikatorberechnungen nutzen die eingebauten StockSharp-Implementierungen für gleitende Durchschnitte und RSI; die Ausgabe wird auf
[-1, 1] begrenzt, um dem ursprünglichen ColorMaRsi-Trigger-Indikator zu entsprechen.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Recreates the Exp_ColorMaRsi-Trigger_Duplex expert advisor.
/// Combines fast/slow MA and fast/slow RSI comparisons into a color code (+1/0/-1).
/// Trades based on color code transitions.
/// </summary>
public class ColorMaRsiTriggerDuplexStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _fastRsiPeriod;
private readonly StrategyParam<int> _slowRsiPeriod;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly List<decimal> _colorHistory = new();
private int _cooldownRemaining;
/// <summary>Candle type.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Fast MA period.</summary>
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
/// <summary>Slow MA period.</summary>
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
/// <summary>Fast RSI period.</summary>
public int FastRsiPeriod { get => _fastRsiPeriod.Value; set => _fastRsiPeriod.Value = value; }
/// <summary>Slow RSI period.</summary>
public int SlowRsiPeriod { get => _slowRsiPeriod.Value; set => _slowRsiPeriod.Value = value; }
/// <summary>Signal bar shift.</summary>
public int SignalBar { get => _signalBar.Value; set => _signalBar.Value = value; }
/// <summary>Bars to wait between reversals.</summary>
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public ColorMaRsiTriggerDuplexStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
_fastMaPeriod = Param(nameof(FastMaPeriod), 5)
.SetDisplay("Fast MA Period", "Fast moving average length", "Indicators");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 10)
.SetDisplay("Slow MA Period", "Slow moving average length", "Indicators");
_fastRsiPeriod = Param(nameof(FastRsiPeriod), 3)
.SetDisplay("Fast RSI Period", "Fast RSI length", "Indicators");
_slowRsiPeriod = Param(nameof(SlowRsiPeriod), 13)
.SetDisplay("Slow RSI Period", "Slow RSI length", "Indicators");
_signalBar = Param(nameof(SignalBar), 1)
.SetDisplay("Signal Bar", "History shift for signal evaluation", "Strategy");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 8)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Strategy");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
lock (_colorHistory)
_colorHistory.Clear();
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
lock (_colorHistory)
_colorHistory.Clear();
_cooldownRemaining = 0;
var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
var slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var fastRsi = new RelativeStrengthIndex { Length = FastRsiPeriod };
var slowRsi = new RelativeStrengthIndex { Length = SlowRsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, fastRsi, slowRsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastMaVal, decimal slowMaVal, decimal fastRsiVal, decimal slowRsiVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
// Calculate color code from MA and RSI comparisons
var score = 0m;
if (fastMaVal > slowMaVal)
score = 1m;
else if (fastMaVal < slowMaVal)
score = -1m;
if (fastRsiVal > slowRsiVal)
score += 1m;
else if (fastRsiVal < slowRsiVal)
score -= 1m;
// Clamp to [-1, 1]
if (score > 1m) score = 1m;
else if (score < -1m) score = -1m;
decimal recent;
decimal older;
lock (_colorHistory)
{
_colorHistory.Insert(0, score);
var maxHistory = Math.Max(2, SignalBar + 2);
while (_colorHistory.Count > maxHistory)
_colorHistory.RemoveAt(_colorHistory.Count - 1);
if (_colorHistory.Count <= SignalBar + 1)
return;
recent = _colorHistory[SignalBar];
older = _colorHistory[SignalBar + 1];
}
var longOpen = older == -1m && recent == 1m;
var shortOpen = older == 1m && recent == -1m;
var longExit = Position > 0 && recent < 0m;
var shortExit = Position < 0 && recent > 0m;
if (longExit)
{
SellMarket(Position);
_cooldownRemaining = SignalCooldownBars;
}
else if (shortExit)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = SignalCooldownBars;
}
else if (_cooldownRemaining == 0 && longOpen && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_cooldownRemaining = SignalCooldownBars;
}
else if (_cooldownRemaining == 0 && shortOpen && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_cooldownRemaining = SignalCooldownBars;
}
}
}
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 color_ma_rsi_trigger_duplex_strategy(Strategy):
def __init__(self):
super(color_ma_rsi_trigger_duplex_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._fast_ma_period = self.Param("FastMaPeriod", 5) \
.SetDisplay("Fast MA Period", "Fast moving average length", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 10) \
.SetDisplay("Slow MA Period", "Slow moving average length", "Indicators")
self._fast_rsi_period = self.Param("FastRsiPeriod", 3) \
.SetDisplay("Fast RSI Period", "Fast RSI length", "Indicators")
self._slow_rsi_period = self.Param("SlowRsiPeriod", 13) \
.SetDisplay("Slow RSI Period", "Slow RSI length", "Indicators")
self._signal_bar = self.Param("SignalBar", 1) \
.SetDisplay("Signal Bar", "History shift for signal evaluation", "Strategy")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 8) \
.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Strategy")
self._color_history = []
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastMaPeriod(self):
return self._fast_ma_period.Value
@property
def SlowMaPeriod(self):
return self._slow_ma_period.Value
@property
def FastRsiPeriod(self):
return self._fast_rsi_period.Value
@property
def SlowRsiPeriod(self):
return self._slow_rsi_period.Value
@property
def SignalBar(self):
return self._signal_bar.Value
@property
def SignalCooldownBars(self):
return self._signal_cooldown_bars.Value
def OnReseted(self):
super(color_ma_rsi_trigger_duplex_strategy, self).OnReseted()
self._color_history = []
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(color_ma_rsi_trigger_duplex_strategy, self).OnStarted2(time)
self._color_history = []
self._cooldown_remaining = 0
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.FastMaPeriod
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.SlowMaPeriod
fast_rsi = RelativeStrengthIndex()
fast_rsi.Length = self.FastRsiPeriod
slow_rsi = RelativeStrengthIndex()
slow_rsi.Length = self.SlowRsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ma, slow_ma, fast_rsi, slow_rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_ma_val, slow_ma_val, fast_rsi_val, slow_rsi_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
fmv = float(fast_ma_val)
smv = float(slow_ma_val)
frv = float(fast_rsi_val)
srv = float(slow_rsi_val)
score = 0.0
if fmv > smv:
score = 1.0
elif fmv < smv:
score = -1.0
if frv > srv:
score += 1.0
elif frv < srv:
score -= 1.0
if score > 1.0:
score = 1.0
elif score < -1.0:
score = -1.0
self._color_history.insert(0, score)
max_history = max(2, self.SignalBar + 2)
while len(self._color_history) > max_history:
self._color_history.pop()
if len(self._color_history) <= self.SignalBar + 1:
return
recent = self._color_history[self.SignalBar]
older = self._color_history[self.SignalBar + 1]
long_open = older == -1.0 and recent == 1.0
short_open = older == 1.0 and recent == -1.0
long_exit = self.Position > 0 and recent < 0.0
short_exit = self.Position < 0 and recent > 0.0
if long_exit:
self.SellMarket(self.Position)
self._cooldown_remaining = self.SignalCooldownBars
elif short_exit:
self.BuyMarket(abs(self.Position))
self._cooldown_remaining = self.SignalCooldownBars
elif self._cooldown_remaining == 0 and long_open and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown_remaining = self.SignalCooldownBars
elif self._cooldown_remaining == 0 and short_open and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown_remaining = self.SignalCooldownBars
def CreateClone(self):
return color_ma_rsi_trigger_duplex_strategy()