Die Risk Reward Ratio-Strategie ist ein High-Level-StockSharp-Port des MetaTrader-Experten "Risk Reward Ratio". Die Strategie kombiniert mehrere Momentum- und Trendbestätigungsfilter mit einem disziplinierten Risikomanagementmodul. Einstiege werden aus einer Kombination von stochastischen Oszillatoren, einem LWMA-Crossover (Linear Weighted Moving Average), einem 14-Perioden-RSI-Filter und einer MACD-Trendprüfung generiert. Die Risikosteuerung erfolgt durch einen pip-basierten Stop-Loss, einen automatischen Reward-Ratio-Take-Profit, optionale Trailing Stops und Break-Even-Logik sowie einen Notfallschalter, der die Position sofort liquidiert.
Die Konvertierung behält den ursprünglichen Charakter des MetaTrader-Experten bei und verwendet StockSharp's Kerzen-Subscriptions und Indikator-Binding-APIs. Die gesamte Indikatorverarbeitung erfolgt auf abgeschlossenen Kerzen und vermeidet den direkten Zugriff auf Indikatorbuffer, wodurch das Streaming-Paradigma der Engine erhalten bleibt.
Trading-Logik
Stochastische Konfluenz
Ein schneller Stochastik (5, 2, 2) liefert das primäre Momentumsignal über die %K-Linie.
Ein langsamer Stochastik (21, 10, 4) liefert den Richtungsbias über seine geglättete %D-Linie.
Long-Setups erfordern, dass der schnelle %K über dem langsamen %D liegt, Short-Setups das Gegenteil.
RSI-Bestätigung
Ein 14-Perioden-RSI muss für Long-Trades über 50 und für Short-Trades unter 50 liegen, um sicherzustellen, dass der Markt mit der vorgeschlagenen Richtung übereinstimmt.
Trendfilter via LWMAs
Zwei linear gewichtete gleitende Durchschnitte (Längen 6 und 85) müssen die Richtung bestätigen: schneller LWMA über dem langsamen für Longs und darunter für Shorts.
MACD-Trendqualifikator
Das MACD-Histogramm (12, 26, 9) muss mit der Signalrichtung übereinstimmen. Die Hauptlinie muss die Signallinie anführen und auf der entsprechenden Seite der Nulllinie bleiben.
Momentum-Abweichungsfilter
Ein 14-Perioden-Momentum-Indikator misst die Distanz von 100. Mindestens eine der letzten drei Momentum-Readings muss den konfigurierbaren Schwellenwert überschreiten, um zu beweisen, dass der Preis genug beschleunigt, um einen Trade zu rechtfertigen.
Positionslimits
Das Netto-Exposure wird durch MaxPositions * TradeVolume begrenzt, sodass die Strategie nicht über die ursprüngliche EA-Beschränkung hinaus pyramidieren kann.
Aufträge werden als Market-Ausführungen mit BuyMarket und SellMarket gesendet. Die Strategie ignoriert unfertige Kerzen und hält den gesamten Zustand in Klassenfeldern, um die ereignisgesteuerte Architektur von StockSharp zu respektieren.
Risikomanagement
Stop-Loss in Pips – Jeder Einstieg installiert einen Schutz-Stop bei StopLossPips * PriceStep vom Fill-Preis entfernt.
Reward-Ratio-Take-Profit – Die Take-Profit-Distanz entspricht der Stop-Distanz multipliziert mit RewardRatio, um ein festes Gewinn-Risiko-Verhältnis zu erhalten.
Trailing Stop – Wenn aktiviert, bewegt sich der Stop hinter dem Preis um TrailingStopPips, sobald der Markt mindestens diese Distanz vom Einstieg vorgerückt ist.
Break-Even-Verschiebung – Nach BreakEvenTriggerPips günstiger Bewegung wird der Stop zum Einstieg plus einem zusätzlichen BreakEvenOffsetPips-Puffer verschoben (oder minus für Shorts), was Gewinne sichert.
Notfallschalter – Das Setzen von ExitSwitch auf true schließt die aktuelle Position bei der nächsten abgeschlossenen Kerze und deaktiviert die weitere Verarbeitung, bis die Flagge zurückgesetzt wird.
Parameter
Name
Standard
Beschreibung
TradeVolume
0.1
Volumen jeder Market-Order.
CandleType
15m Zeitrahmen
Primäre Kerzenserie.
FastMaPeriod
6
Periode des schnellen LWMA.
SlowMaPeriod
85
Periode des langsamen LWMA.
MomentumThreshold
0.3
Minimaler absoluter Abstand des Momentum-Indikators von 100, der für Einstiege erforderlich ist.
RewardRatio
2
Take-Profit-Vielfaches relativ zum Stop-Loss.
StopLossPips
20
Stop-Loss-Distanz in Pips (PriceStep-Vielfache).
MaxPositions
10
Maximale Anzahl gleichzeitig erlaubter Volumeneinheiten (TradeVolume).
EnableTrailing
true
Aktiviert pip-basierte Trailing-Stop-Updates.
TrailingStopPips
40
Trailing-Stop-Distanz in Pips.
EnableBreakEven
true
Aktiviert das Break-Even-Stop-Management.
BreakEvenTriggerPips
30
Gewinn (in Pips) erforderlich, bevor der Stop auf Break-Even verschoben wird.
BreakEvenOffsetPips
30
Zusätzlicher Pip-Offset beim Verschieben des Stops auf Break-Even.
ExitSwitch
false
Zwingt die Strategie, das gesamte Exposure bei der nächsten abgeschlossenen Kerze zu schließen.
Arbeitsablauf
Konfigurieren Sie das gewünschte Instrument und die Kerzenserie, dann setzen Sie die Risikoparameter.
Starten Sie die Strategie. Sie abonniert Kerzen, bindet Indikatoren und beginnt mit der Verarbeitung abgeschlossener Bars.
Wenn die Einstiegsbedingungen übereinstimmen, sendet die Engine eine Market-Order und speichert Stop/Ziel-Niveaus.
Bei jeder abgeschlossenen Kerze bewertet der Risikoblock Trailing-, Break-Even- und Notfallausgangsregeln.
Ausstiege werden durch Erreichen von Stop/Take-Profit-Niveaus, Trailing-Updates, Break-Even-Anpassungen oder den Notfallschalter ausgelöst.
Alle Berechnungen basieren auf dem PriceStep des Instruments. Wenn der Schritt null oder fehlend ist, werden Risikoabstände deaktiviert, um ungültige Preisniveaus zu vermeiden.
Die Strategie modifiziert keine ausstehenden Orders; sie sendet einfach Market-Orders zum Öffnen/Schließen von Positionen, was die Art widerspiegelt, wie der ursprüngliche EA Exposure schloss, wenn Schwellenwerte erreicht wurden.
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;
public class RiskRewardRatioStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public RiskRewardRatioStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA 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");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { 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;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class risk_reward_ratio_strategy(Strategy):
def __init__(self):
super(risk_reward_ratio_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow 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._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(risk_reward_ratio_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(risk_reward_ratio_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
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 = 100
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 = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return risk_reward_ratio_strategy()