Die Strategie recreiert das Verhalten des MetaTrader 5-Expertenberaters Exp_RJTX_Matches_Smoothed_Duplex.mq5. Zwei unabhängige RJTX-Signalblöcke analysieren geglättete Eröffnungs- und Schlusskurse auf ihren jeweiligen Zeitrahmen. Jeder Block klassifiziert jede abgeschlossene Kerze als bullish oder bearish, je nachdem ob der geglättete Schluss über die geglättete Eröffnung von vor Period Bars steigt. Bullische "Matches" lösen Einstiege für das Long-Modul aus, während bearische Matches das Short-Modul verwalten.
Signalerzeugung
Glättung – beide Blöcke speisen Kerzen-Eröffnungen und -Schlusskurse in den ausgewählten Glättungsalgorithmus ein. Dieselbe Methode wird auf Eröffnungs- und Schlussströme angewendet, aber separate Instanzen werden verwendet, um die internen Puffer unabhängig zu halten.
Vergleich – sobald genug Historie verfügbar ist, wird der aktuelle geglättete Schluss mit der geglätteten Eröffnung verglichen, die Period Bars früher aufgezeichnet wurde.
Match-Erkennung – wenn der Schluss größer ist, erhält die Kerze ein bullisches Match; andernfalls wird sie bearisch. Signale werden nach einer Verschiebung um SignalBar geschlossene Kerzen ausgewertet, genau wie der MT5-Pufferzugriff.
Positionsmanagement
Der Long-Block öffnet eine Long-Position (schließt dabei ggf. ein bestehendes Short) wenn ein bullisches Match das Auswertungsfenster erreicht. Ein bearisches Match schließt die Long-Position, wenn Long-Exits aktiviert sind.
Der Short-Block spiegelt diese Logik: ein bearisches Match öffnet einen Short-Trade (schließt Long-Exposure wenn erlaubt) und ein bullisches Match schließt den Short.
StockSharp-Strategien sind genettert. Daher schließen entgegengesetzte Module die aktuelle Position, bevor sie eine neue öffnen, anstatt wie die MT5-Version zwei unabhängige gehedgte Positionen aufrechtzuerhalten. Deaktivieren Sie den entsprechenden Allow ... Close-Parameter, um die automatische Abdeckung zu verbieten.
Risikomanagement
Stops und Gewinnziele werden in Preisschritten ausgedrückt (PriceStep × points). Für jede abgeschlossene Kerze prüft die Strategie, ob der Barrange den aktiven Stop-Loss- oder Take-Profit-Level berührt und schließt die entsprechende Position sofort. Dies emuliert das Verhalten von MT5-Schutzorders ohne auf broker-verwaltete Orders zurückzugreifen.
Parameter
Abschnitt
Parameter
Standard
Beschreibung
Long
LongCandleType
H4
Zeitrahmen des Long-RJTX-Blocks.
Long
LongVolume
0.1
Volumen bei Ausführung eines Long-Signals.
Long
LongAllowOpen
true
Long-Positionen öffnen erlauben.
Long
LongAllowClose
true
Long-Positionen bei bearischen Matches schließen erlauben.
Long
LongStopLossPoints
1000
Stop-Loss-Abstand für Long-Trades in Preisschritten (0 deaktiviert die Prüfung).
Long
LongTakeProfitPoints
2000
Take-Profit-Abstand für Long-Trades in Preisschritten (0 deaktiviert die Prüfung).
Long
LongSignalBar
1
Verschiebung beim Lesen von RJTX-Puffern (0 = aktuelle geschlossene Kerze).
Long
LongPeriod
10
Anzahl Bars zwischen aktuellem geglättetem Schluss und historischer geglätteter Eröffnung.
Long
LongMethod
Sma
Glättungsalgorithmus für den Long-Block (Sma, Ema, Smma, Lwma, Jjma, Jurx, Parma, T3, Vidya, Ama).
Long
LongLength
12
Länge des Glättungsfilters für Eröffnungs-/Schlussreihen.
Long
LongPhase
15
Phasenparameter für Jurik-Stil-Filter (für Kompatibilität beibehalten).
Short
ShortCandleType
H4
Zeitrahmen des Short-RJTX-Blocks.
Short
ShortVolume
0.1
Volumen bei Ausführung eines Short-Signals.
Short
ShortAllowOpen
true
Short-Positionen öffnen erlauben.
Short
ShortAllowClose
true
Short-Positionen bei bullischen Matches schließen erlauben.
Short
ShortStopLossPoints
1000
Stop-Loss-Abstand für Short-Trades in Preisschritten (0 deaktiviert die Prüfung).
Short
ShortTakeProfitPoints
2000
Take-Profit-Abstand für Short-Trades in Preisschritten (0 deaktiviert die Prüfung).
Short
ShortSignalBar
1
Verschiebung beim Lesen von RJTX-Puffern für den Short-Block.
Short
ShortPeriod
10
Anzahl Bars zwischen aktuellem geglättetem Schluss und historischer geglätteter Eröffnung.
Short
ShortMethod
Sma
Glättungsalgorithmus für den Short-Block.
Short
ShortLength
12
Länge des Glättungsfilters für Short-Signale.
Short
ShortPhase
15
Phasenparameter für Jurik-Stil-Filter im Short-Block.
Hinweise
Jjma entspricht dem Jurik Moving Average. Jurx, Parma und Vidya werden mit Zero-Lag EMA, Arnaud Legoux MA bzw. EMA approximiert, da StockSharp keine identischen Filter aus der SmoothAlgorithms-Bibliothek bereitstellt.
Die Stop-Loss/Take-Profit-Logik wird anhand der Kerzenextrema ausgewertet. Intrabar-Spikes kürzer als Hoch/Tief der Kerze lösen keine Exits aus.
Signale werden nur bei abgeschlossenen Kerzen verarbeitet; Intrabar-Matches werden entsprechend dem MT5-IsNewBar-Verhalten ignoriert.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Exp RJTX Matches Smoothed Duplex strategy using SmoothedMA crossover.
/// Buys when fast SmoothedMA crosses above slow SmoothedMA.
/// Sells on reverse crossover.
/// </summary>
public class ExpRjtxMatchesSmoothedDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SmoothedMovingAverage _fast;
private SmoothedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast smoothed MA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow smoothed MA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpRjtxMatchesSmoothedDuplexStrategy"/> class.
/// </summary>
public ExpRjtxMatchesSmoothedDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast smoothed MA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow smoothed MA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SmoothedMovingAverage { Length = FastPeriod };
_slow = new SmoothedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SmoothedMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_rjtx_matches_smoothed_duplex_strategy(Strategy):
"""
SmoothedMA crossover strategy with SL/TP and cooldown.
Buys when fast SmoothedMA crosses above slow, sells on reverse.
"""
def __init__(self):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast smoothed MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow smoothed MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).OnStarted2(time)
fast = SmoothedMovingAverage()
fast.Length = self._fast_period.Value
slow = SmoothedMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_rjtx_matches_smoothed_duplex_strategy()