Diese Strategie ist ein C#-Port des MetaTrader 5-Experten Exp_SR-RateIndicator. Sie reproduziert die ursprüngliche Handelslogik mit der High-Level-API von StockSharp und einer benutzerdefinierten Implementierung des SR Rate Oszillators. Der Indikator misst, wie weit der gewichtete Kerzenpris innerhalb eines geglätteten Unterstützungs-/Widerstands-Kanals liegt, und malt einen Farbcode, der extreme Lesungen hervorhebt.
Der Algorithmus verarbeitet abgeschlossene Kerzen aus einem konfigurierbaren Zeitrahmen. Wenn die Oszillatorfarbe zum bullischen oder bearischen Extrem springt, schließt die Strategie jede entgegengesetzte Position und eröffnet einen neuen Trade in Signalrichtung. Schutzende Stop-Loss- und Take-Profit-Niveaus werden mit denselben Punktabständen angewendet, die in der MetaTrader-Version verwendet werden.
SR Rate Oszillator
Der Indikator berechnet ein Gaußsches geglättetes Band um den Preis unter Verwendung einer konfigurierbaren Fensterlänge:
Für jede Kerze werden das Hoch, Tief und der gewichtete Schlusskurs mit einseitigen Gaußschen Gewichten der Länge sechs geglättet.
Das höchste geglättete Hoch und das niedrigste geglättete Tief über dem Fenster definieren einen dynamischen Bereich.
Der aktuelle geglättete gewichtete Schlusskurs wird innerhalb dieses Bereichs normiert und auf das Intervall [-100, 100] abgebildet.
Der finale Oszillatorwert wird in fünf Farbzustände konvertiert: 0 (stark bearisch), 1 (leicht bearisch), 2 (neutral), 3 (leicht bullisch) und 4 (stark bullisch).
Eine stark bullische Farbe (4) zeigt an, dass der Preis das obere Extrem des Bereichs erreicht hat, während eine stark bearische Farbe (0) einen Besuch am unteren Extrem signalisiert.
Handelsregeln
Kerzen des konfigurierten Typs abonnieren und den SR Rate Oszillator auf jeder abgeschlossenen Kerze berechnen.
Die Signalauswertung um SignalBar geschlossene Kerzen verschieben (Standard: eine Kerze zurück), um das Expert-Advisor-Verhalten nachzuahmen.
Wenn die verschobene Farbe 4 wird und die vorherige Farbe unter 4 liegt:
Jede bestehende Short-Position schließen, wenn Long-Exits aktiviert sind.
Eine neue Long-Position eröffnen, wenn Long-Einträge aktiviert sind und keine andere Position aktiv ist.
Wenn die verschobene Farbe 0 wird und die vorherige Farbe über 0 liegt:
Jede bestehende Long-Position schließen, wenn Short-Exits aktiviert sind.
Eine neue Short-Position eröffnen, wenn Short-Einträge aktiviert sind und keine andere Position aktiv ist.
Nur eine Position kann jederzeit geöffnet sein. Neue Signale werden ignoriert, bis der vorherige Trade geschlossen ist.
Optionale Stop-Loss- und Take-Profit-Niveaus werden in Preispunkten ausgedrückt und automatisch unter Verwendung des Instrument-Kursschritts in absolute Preise umgerechnet.
Parameter
Name
Beschreibung
OrderVolume
Trade-Volumen für jede Marktorder.
EnableLongEntries
Öffnen von Long-Positionen aktivieren/deaktivieren.
EnableShortEntries
Öffnen von Short-Positionen aktivieren/deaktivieren.
EnableLongExits
Long-Positionen schließen wenn eine stark bearische Farbe erscheint.
EnableShortExits
Short-Positionen schließen wenn eine stark bullische Farbe erscheint.
StopLossPoints
Stop-Loss-Abstand in Instrument-Punkten (konvertiert mit dem Kursschritt).
TakeProfitPoints
Take-Profit-Abstand in Instrument-Punkten (konvertiert mit dem Kursschritt).
SlippagePoints
Maximal tolerierter Slippage beim Schließen von Positionen. Für Kompatibilität beibehalten; kein explizites Slippage-Control wird von der High-Level-API angewendet.
CandleType
Kerzentyp und Zeitrahmen zur Berechnung des Indikators.
SignalBar
Anzahl der übersprungenen geschlossenen Kerzen vor der Histogrammanalyse (Standard 1).
WindowSize
Länge des rollenden Fensters für die SR Rate Normierung.
HighLevel
Oszillatorlevel der das bullische Extrem definiert (Standard +20).
LowLevel
Oszillatorlevel der das bearische Extrem definiert (Standard -20).
Hinweise
Die Strategie funktioniert mit jedem Instrument, das Standard-OHLC-Kerzen liefert.
Signale werden nur auf abgeschlossenen Kerzen verarbeitet; Intrabar-Neuberechnungen werden ignoriert, genauso wie in der MetaTrader-Implementierung.
Die Slippage-Behandlung im ursprünglichen Experten hing von Ausführungseinstellungen ab. StockSharp-Marktorders respektieren bereits Exchange-Regeln, daher wird der SlippagePoints-Parameter nur für Dokumentationszwecke beibehalten.
Der Indikator speichert nur die minimale Menge an Verlauf, die zur Auswertung des Fensters benötigt wird, um unnötigen Speicherverbrauch zu vermeiden.
Die Python-Version wird gemäß den Projektrichtlinien absichtlich weggelassen.
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 SrRateIndicatorStrategy : 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 SrRateIndicatorStrategy()
{
_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 sr_rate_indicator_strategy(Strategy):
def __init__(self):
super(sr_rate_indicator_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(sr_rate_indicator_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(sr_rate_indicator_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 sr_rate_indicator_strategy()