RSI Trader-Strategie
Übersicht
Diese Strategie repliziert den MetaTrader-Expertenberater "RSI trader v0.15" in der StockSharp High-Level API. Sie richtet die Trendrichtung zwischen Preisaktion und einem geglätteten Relativen Stärke-Index (RSI) aus. Der Handel wird standardmäßig auf einem einzigen Instrument mit Ein-Stunden-Kerzen durchgeführt, aber der Zeitrahmen ist über den Parameter CandleType konfigurierbar.
Handelslogik
- Berechnung eines Standard-RSI mit einem konfigurierbaren Zeitraum.
- Glättung des RSI mit zwei einfachen gleitenden Durchschnitten (SMA): ein schneller Signal-Durchschnitt und ein langsamerer Bestätigungs-Durchschnitt.
- Verfolgung von zwei gleitenden Durchschnitten des Schlusskurses: ein kurzer einfacher gleitender Durchschnitt und ein langer gewichteter gleitender Durchschnitt, um das ursprüngliche MQL SMA/LWMA-Paar anzunähern.
- Generierung von Trendzuständen bei jeder abgeschlossenen Kerze:
- Bullische Ausrichtung: kurzer Preis-SMA über dem langen und schneller RSI-SMA über dem langsamen.
- Bärische Ausrichtung: kurzer Preis-SMA unter dem langen und schneller RSI-SMA unter dem langsamen.
- Seitwärts / Uneinigkeit: gleitende Durchschnitte zeigen in entgegengesetzte Richtungen, was keinen klaren Trend signalisiert.
- Reaktion auf den erkannten Zustand:
- Eröffnen einer Long-Position, wenn bullische Ausrichtung erscheint und derzeit keine Position offen ist.
- Eröffnen einer Short-Position, wenn bärische Ausrichtung erscheint und derzeit keine Position offen ist.
- Sofortiges Schließen jeder offenen Position, wenn der Seitwärtszustand erkannt wird, was den Schutzausstieg der MQL-Version spiegelt.
- Der optionale Umkehrmodus dreht alle Einstiegsrichtungen um und ermöglicht es dem Benutzer, gegen den Trend der erkannten Signale zu handeln.
Die Strategie respektiert die integrierte Schutzbehandlung von StockSharp und erfordert abgeschlossene Kerzen vor einer Aktion.
Parameter
| Parameter |
Beschreibung |
Standard |
RsiPeriod |
Rückblickzeitraum für die RSI-Berechnung. |
14 |
ShortRsiMaPeriod |
Länge des schnellen SMA, angewendet auf RSI-Werte. |
9 |
LongRsiMaPeriod |
Länge des langsamen SMA, angewendet auf RSI-Werte. |
45 |
ShortPriceMaPeriod |
Länge des kurzen SMA, angewendet auf Schlusskurse. |
9 |
LongPriceMaPeriod |
Länge des langen gewichteten gleitenden Durchschnitts auf Preise. |
45 |
Reverse |
Wenn true, werden Kauf- und Verkaufsorders vertauscht (spiegelt den ursprünglichen "Reverse"-Eingang). |
false |
CandleType |
Datentyp für Preiskerzen. Standard ist ein Ein-Stunden-Zeitrahmen. |
1h |
Alle ganzzahligen Parameter setzen Optimierungsbereiche frei, die die Flexibilität der MetaTrader-Experten-Eingabeeinstellungen widerspiegeln.
Risikomanagement
- Positionen werden sofort geschlossen, wenn Preis- und RSI-Trends nicht übereinstimmen (Seitwärtszustand), was das sofortige Ausstiegsverhalten des EA reproduziert.
StartProtection() wird beim Start aktiviert, um mit der Schutzinfrastruktur von StockSharp zusammenzuarbeiten.
Hinweise
- Die Strategie nutzt die Basis-
Volume-Eigenschaft von Strategy zur Definition der Handelsgröße.
- Nur abgeschlossene Kerzen werden verarbeitet; partielle Aktualisierungen werden ignoriert, um vorzeitige Signale zu vermeiden.
- Der gewichtete gleitende Durchschnitt wird verwendet, um dem ursprünglichen langen LWMA auf Preisschlüssen zu entsprechen.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// RSI trader strategy aligning price and RSI moving-average trends.
/// </summary>
public class RsiTraderStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _shortRsiMaPeriod;
private readonly StrategyParam<int> _longRsiMaPeriod;
private readonly StrategyParam<int> _shortPriceMaPeriod;
private readonly StrategyParam<int> _longPriceMaPeriod;
private readonly StrategyParam<bool> _reverse;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _closes = new();
private readonly List<decimal> _rsiValues = new();
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int ShortRsiMaPeriod { get => _shortRsiMaPeriod.Value; set => _shortRsiMaPeriod.Value = value; }
public int LongRsiMaPeriod { get => _longRsiMaPeriod.Value; set => _longRsiMaPeriod.Value = value; }
public int ShortPriceMaPeriod { get => _shortPriceMaPeriod.Value; set => _shortPriceMaPeriod.Value = value; }
public int LongPriceMaPeriod { get => _longPriceMaPeriod.Value; set => _longPriceMaPeriod.Value = value; }
public bool Reverse { get => _reverse.Value; set => _reverse.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RsiTraderStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI calculation length", "RSI").SetGreaterThanZero();
_shortRsiMaPeriod = Param(nameof(ShortRsiMaPeriod), 12).SetDisplay("Short RSI MA", "Short moving average on RSI", "RSI").SetGreaterThanZero();
_longRsiMaPeriod = Param(nameof(LongRsiMaPeriod), 60).SetDisplay("Long RSI MA", "Long moving average on RSI", "RSI").SetGreaterThanZero();
_shortPriceMaPeriod = Param(nameof(ShortPriceMaPeriod), 12).SetDisplay("Short Price MA", "Short simple moving average", "Price").SetGreaterThanZero();
_longPriceMaPeriod = Param(nameof(LongPriceMaPeriod), 60).SetDisplay("Long Price MA", "Long weighted moving average", "Price").SetGreaterThanZero();
_reverse = Param(nameof(Reverse), false).SetDisplay("Reverse", "Flip buy/sell signals", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Primary candle type", "Data");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closes.Clear();
_rsiValues.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_closes.Add(candle.ClosePrice);
var maxCache = Math.Max(LongPriceMaPeriod, Math.Max(LongRsiMaPeriod + RsiPeriod, 300));
if (_closes.Count > maxCache)
_closes.RemoveAt(0);
var rsi = CalculateRsi();
if (rsi is null)
return;
_rsiValues.Add(rsi.Value);
if (_rsiValues.Count > maxCache)
_rsiValues.RemoveAt(0);
if (_rsiValues.Count < LongRsiMaPeriod || _closes.Count < LongPriceMaPeriod)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var shortRsi = AverageLast(_rsiValues, ShortRsiMaPeriod);
var longRsi = AverageLast(_rsiValues, LongRsiMaPeriod);
var shortPrice = AverageLast(_closes, ShortPriceMaPeriod);
var longPrice = WeightedAverageLast(_closes, LongPriceMaPeriod);
var goLong = shortPrice > longPrice && shortRsi > longRsi;
var goShort = shortPrice < longPrice && shortRsi < longRsi;
var sideways = !goLong && !goShort;
if (sideways && Position != 0)
{
if (Position > 0)
SellMarket(Position);
else
BuyMarket(Math.Abs(Position));
return;
}
if (Position != 0)
return;
if (goLong)
{
if (Reverse)
SellMarket();
else
BuyMarket();
}
else if (goShort)
{
if (Reverse)
BuyMarket();
else
SellMarket();
}
}
private decimal? CalculateRsi()
{
if (_closes.Count <= RsiPeriod)
return null;
decimal gainSum = 0m;
decimal lossSum = 0m;
var start = _closes.Count - RsiPeriod;
for (var i = start; i < _closes.Count; i++)
{
var change = _closes[i] - _closes[i - 1];
if (change > 0m)
gainSum += change;
else
lossSum -= change;
}
var averageGain = gainSum / RsiPeriod;
var averageLoss = lossSum / RsiPeriod;
if (averageLoss == 0m)
return 100m;
var rs = averageGain / averageLoss;
return 100m - 100m / (1m + rs);
}
private static decimal AverageLast(IReadOnlyList<decimal> values, int length)
{
decimal sum = 0m;
var start = values.Count - length;
for (var i = start; i < values.Count; i++)
sum += values[i];
return sum / length;
}
private static decimal WeightedAverageLast(IReadOnlyList<decimal> values, int length)
{
decimal weightedSum = 0m;
decimal weightSum = 0m;
var start = values.Count - length;
var weight = 1m;
for (var i = start; i < values.Count; i++)
{
weightedSum += values[i] * weight;
weightSum += weight;
weight += 1m;
}
return weightSum > 0m ? weightedSum / weightSum : values[^1];
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class rsi_trader_strategy(Strategy):
def __init__(self):
super(rsi_trader_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14)
self._short_rsi_ma_period = self.Param("ShortRsiMaPeriod", 12)
self._long_rsi_ma_period = self.Param("LongRsiMaPeriod", 60)
self._short_price_ma_period = self.Param("ShortPriceMaPeriod", 12)
self._long_price_ma_period = self.Param("LongPriceMaPeriod", 60)
self._reverse = self.Param("Reverse", False)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._closes = []
self._rsi_values = []
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def ShortRsiMaPeriod(self):
return self._short_rsi_ma_period.Value
@ShortRsiMaPeriod.setter
def ShortRsiMaPeriod(self, value):
self._short_rsi_ma_period.Value = value
@property
def LongRsiMaPeriod(self):
return self._long_rsi_ma_period.Value
@LongRsiMaPeriod.setter
def LongRsiMaPeriod(self, value):
self._long_rsi_ma_period.Value = value
@property
def ShortPriceMaPeriod(self):
return self._short_price_ma_period.Value
@ShortPriceMaPeriod.setter
def ShortPriceMaPeriod(self, value):
self._short_price_ma_period.Value = value
@property
def LongPriceMaPeriod(self):
return self._long_price_ma_period.Value
@LongPriceMaPeriod.setter
def LongPriceMaPeriod(self, value):
self._long_price_ma_period.Value = value
@property
def Reverse(self):
return self._reverse.Value
@Reverse.setter
def Reverse(self, value):
self._reverse.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(rsi_trader_strategy, self).OnStarted2(time)
self._closes = []
self._rsi_values = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
self._closes.append(close)
max_cache = max(int(self.LongPriceMaPeriod), max(int(self.LongRsiMaPeriod) + int(self.RsiPeriod), 300))
while len(self._closes) > max_cache:
self._closes.pop(0)
rsi = self._calculate_rsi()
if rsi is None:
return
self._rsi_values.append(rsi)
while len(self._rsi_values) > max_cache:
self._rsi_values.pop(0)
if len(self._rsi_values) < int(self.LongRsiMaPeriod) or len(self._closes) < int(self.LongPriceMaPeriod):
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
short_rsi = self._average_last(self._rsi_values, int(self.ShortRsiMaPeriod))
long_rsi = self._average_last(self._rsi_values, int(self.LongRsiMaPeriod))
short_price = self._average_last(self._closes, int(self.ShortPriceMaPeriod))
long_price = self._weighted_average_last(self._closes, int(self.LongPriceMaPeriod))
go_long = short_price > long_price and short_rsi > long_rsi
go_short = short_price < long_price and short_rsi < long_rsi
sideways = not go_long and not go_short
if sideways and self.Position != 0:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
return
if self.Position != 0:
return
if go_long:
if self.Reverse:
self.SellMarket()
else:
self.BuyMarket()
elif go_short:
if self.Reverse:
self.BuyMarket()
else:
self.SellMarket()
def _calculate_rsi(self):
rsi_period = int(self.RsiPeriod)
if len(self._closes) <= rsi_period:
return None
gain_sum = 0.0
loss_sum = 0.0
start = len(self._closes) - rsi_period
for i in range(start, len(self._closes)):
change = self._closes[i] - self._closes[i - 1]
if change > 0.0:
gain_sum += change
else:
loss_sum -= change
average_gain = gain_sum / rsi_period
average_loss = loss_sum / rsi_period
if average_loss == 0.0:
return 100.0
rs = average_gain / average_loss
return 100.0 - 100.0 / (1.0 + rs)
def _average_last(self, values, length):
total = 0.0
start = len(values) - length
for i in range(start, len(values)):
total += values[i]
return total / length
def _weighted_average_last(self, values, length):
weighted_sum = 0.0
weight_sum = 0.0
start = len(values) - length
weight = 1.0
for i in range(start, len(values)):
weighted_sum += values[i] * weight
weight_sum += weight
weight += 1.0
if weight_sum > 0.0:
return weighted_sum / weight_sum
return values[-1]
def OnReseted(self):
super(rsi_trader_strategy, self).OnReseted()
self._closes = []
self._rsi_values = []
def CreateClone(self):
return rsi_trader_strategy()