Gold-RSI-Divergenz-Strategie
Die Gold-RSI-Divergenz-Strategie scalpiert Gold, indem sie bullische und bärische Divergenzen zwischen dem Preis und dem Relative Strength Index (RSI) identifiziert. Wenn der Preis ein neues Tief markiert, aber der RSI ein höheres Tief druckt, sucht die Strategie nach einem Kauf. Umgekehrt, wenn der Preis ein neues Hoch markiert, aber der RSI ein niedrigeres Hoch druckt, verkauft die Strategie. Beide Setups werden nur bestätigt, wenn zwei Pivots innerhalb eines konfigurierbaren Balkenbands auftreten.
Details
- Einstiegskriterien:
- Long: Preis tieferes Tief, RSI höheres Tief, RSI < 40.
- Short: Preis höheres Hoch, RSI niedrigeres Hoch, RSI > 60.
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Verwendet Stop-Loss und Take-Profit.
- Stops: Fester Stop-Loss und Take-Profit in Pips.
- Standardwerte:
RsiLength= 60StopLossPips= 11TakeProfitPips= 33
- Filter:
- Kategorie: Divergenz
- Richtung: Beide
- Indikatoren: RSI
- Stops: Ja
- Komplexität: Moderat
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Ja
- Risikolevel: Mittel
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>
/// RSI Divergence strategy.
/// Looks for price/RSI divergence for entries.
/// </summary>
public class GoldRsiDivergenceStrategy : Strategy
{
private readonly StrategyParam<int> _lookbackLeft;
private readonly StrategyParam<int> _lookbackRight;
private readonly StrategyParam<int> _rangeLower;
private readonly StrategyParam<int> _rangeUpper;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal[] _rsiBuffer = Array.Empty<decimal>();
private decimal[] _lowBuffer = Array.Empty<decimal>();
private decimal[] _highBuffer = Array.Empty<decimal>();
private int _bufferCount;
private int _barIndex;
private decimal? _lastRsiLow;
private decimal? _lastPriceLow;
private int _lastPivotLowIndex = -1;
private decimal? _lastRsiHigh;
private decimal? _lastPriceHigh;
private int _lastPivotHighIndex = -1;
private int _cooldownRemaining;
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int LookbackLeft { get => _lookbackLeft.Value; set => _lookbackLeft.Value = value; }
public int LookbackRight { get => _lookbackRight.Value; set => _lookbackRight.Value = value; }
public int RangeLower { get => _rangeLower.Value; set => _rangeLower.Value = value; }
public int RangeUpper { get => _rangeUpper.Value; set => _rangeUpper.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public GoldRsiDivergenceStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI calculation length", "RSI");
_lookbackLeft = Param(nameof(LookbackLeft), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback Left", "Bars to the left of pivot", "Divergence");
_lookbackRight = Param(nameof(LookbackRight), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback Right", "Bars to the right of pivot", "Divergence");
_rangeLower = Param(nameof(RangeLower), 5)
.SetGreaterThanZero()
.SetDisplay("Range Lower", "Minimum bars between pivots", "Divergence");
_rangeUpper = Param(nameof(RangeUpper), 60)
.SetGreaterThanZero()
.SetDisplay("Range Upper", "Maximum bars between pivots", "Divergence");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
InitializeBuffers();
_barIndex = 0;
_lastRsiLow = null;
_lastPriceLow = null;
_lastPivotLowIndex = -1;
_lastRsiHigh = null;
_lastPriceHigh = null;
_lastPivotHighIndex = -1;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
InitializeBuffers();
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void InitializeBuffers()
{
var length = Math.Max(1, LookbackLeft + LookbackRight + 1);
_rsiBuffer = new decimal[length];
_lowBuffer = new decimal[length];
_highBuffer = new decimal[length];
_bufferCount = 0;
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_barIndex++;
AddToBuffer(rsiValue, candle.LowPrice, candle.HighPrice);
if (_bufferCount < _rsiBuffer.Length)
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
CheckPivots(rsiValue, candle);
return;
}
var pivotIndex = LookbackRight;
var candidateRsi = _rsiBuffer[pivotIndex];
var candidateLow = _lowBuffer[pivotIndex];
var candidateHigh = _highBuffer[pivotIndex];
var candidateBar = _barIndex - LookbackRight;
var isPivotLow = IsPivotLow(candidateRsi);
var isPivotHigh = IsPivotHigh(candidateRsi);
if (isPivotLow)
{
var inRange = _lastPivotLowIndex >= 0 &&
candidateBar - _lastPivotLowIndex >= RangeLower &&
candidateBar - _lastPivotLowIndex <= RangeUpper;
var bullishDiv = inRange &&
_lastRsiLow is decimal prevRsiLow &&
_lastPriceLow is decimal prevPriceLow &&
candidateRsi > prevRsiLow &&
candidateLow < prevPriceLow;
if (bullishDiv && rsiValue < 40m && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
_lastRsiLow = candidateRsi;
_lastPriceLow = candidateLow;
_lastPivotLowIndex = candidateBar;
}
if (isPivotHigh)
{
var inRange = _lastPivotHighIndex >= 0 &&
candidateBar - _lastPivotHighIndex >= RangeLower &&
candidateBar - _lastPivotHighIndex <= RangeUpper;
var bearishDiv = inRange &&
_lastRsiHigh is decimal prevRsiHigh &&
_lastPriceHigh is decimal prevPriceHigh &&
candidateRsi < prevRsiHigh &&
candidateHigh > prevPriceHigh;
if (bearishDiv && rsiValue > 60m && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
_lastRsiHigh = candidateRsi;
_lastPriceHigh = candidateHigh;
_lastPivotHighIndex = candidateBar;
}
}
private void CheckPivots(decimal rsiValue, ICandleMessage candle)
{
// Still track pivots during cooldown
var pivotIndex = LookbackRight;
var candidateRsi = _rsiBuffer[pivotIndex];
var candidateBar = _barIndex - LookbackRight;
if (IsPivotLow(candidateRsi))
{
_lastRsiLow = candidateRsi;
_lastPriceLow = _lowBuffer[pivotIndex];
_lastPivotLowIndex = candidateBar;
}
if (IsPivotHigh(candidateRsi))
{
_lastRsiHigh = candidateRsi;
_lastPriceHigh = _highBuffer[pivotIndex];
_lastPivotHighIndex = candidateBar;
}
}
private void AddToBuffer(decimal rsi, decimal low, decimal high)
{
if (_bufferCount < _rsiBuffer.Length)
{
_rsiBuffer[_bufferCount] = rsi;
_lowBuffer[_bufferCount] = low;
_highBuffer[_bufferCount] = high;
_bufferCount++;
}
else
{
Array.Copy(_rsiBuffer, 1, _rsiBuffer, 0, _rsiBuffer.Length - 1);
Array.Copy(_lowBuffer, 1, _lowBuffer, 0, _lowBuffer.Length - 1);
Array.Copy(_highBuffer, 1, _highBuffer, 0, _highBuffer.Length - 1);
_rsiBuffer[^1] = rsi;
_lowBuffer[^1] = low;
_highBuffer[^1] = high;
}
}
private bool IsPivotLow(decimal value)
{
for (var i = 0; i < _rsiBuffer.Length; i++)
{
if (i == LookbackRight)
continue;
if (_rsiBuffer[i] <= value)
return false;
}
return true;
}
private bool IsPivotHigh(decimal value)
{
for (var i = 0; i < _rsiBuffer.Length; i++)
{
if (i == LookbackRight)
continue;
if (_rsiBuffer[i] >= value)
return false;
}
return true;
}
}
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class gold_rsi_divergence_strategy(Strategy):
def __init__(self):
super(gold_rsi_divergence_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI calculation length", "RSI")
self._lookback_left = self.Param("LookbackLeft", 5) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Left", "Bars to the left of pivot", "Divergence")
self._lookback_right = self.Param("LookbackRight", 5) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Right", "Bars to the right of pivot", "Divergence")
self._range_lower = self.Param("RangeLower", 5) \
.SetGreaterThanZero() \
.SetDisplay("Range Lower", "Minimum bars between pivots", "Divergence")
self._range_upper = self.Param("RangeUpper", 60) \
.SetGreaterThanZero() \
.SetDisplay("Range Upper", "Maximum bars between pivots", "Divergence")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._rsi_buffer = []
self._low_buffer = []
self._high_buffer = []
self._buffer_count = 0
self._bar_index = 0
self._last_rsi_low = None
self._last_price_low = None
self._last_pivot_low_index = -1
self._last_rsi_high = None
self._last_price_high = None
self._last_pivot_high_index = -1
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(gold_rsi_divergence_strategy, self).OnReseted()
self._initialize_buffers()
self._bar_index = 0
self._last_rsi_low = None
self._last_price_low = None
self._last_pivot_low_index = -1
self._last_rsi_high = None
self._last_price_high = None
self._last_pivot_high_index = -1
self._cooldown_remaining = 0
def _initialize_buffers(self):
length = max(1, int(self._lookback_left.Value) + int(self._lookback_right.Value) + 1)
self._rsi_buffer = [0.0] * length
self._low_buffer = [0.0] * length
self._high_buffer = [0.0] * length
self._buffer_count = 0
def OnStarted2(self, time):
super(gold_rsi_divergence_strategy, self).OnStarted2(time)
self._initialize_buffers()
rsi = RelativeStrengthIndex()
rsi.Length = int(self._rsi_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _add_to_buffer(self, rsi, low, high):
buf_len = len(self._rsi_buffer)
if self._buffer_count < buf_len:
self._rsi_buffer[self._buffer_count] = rsi
self._low_buffer[self._buffer_count] = low
self._high_buffer[self._buffer_count] = high
self._buffer_count += 1
else:
self._rsi_buffer = self._rsi_buffer[1:] + [rsi]
self._low_buffer = self._low_buffer[1:] + [low]
self._high_buffer = self._high_buffer[1:] + [high]
def _is_pivot_low(self, value):
lr = int(self._lookback_right.Value)
for i in range(len(self._rsi_buffer)):
if i == lr:
continue
if self._rsi_buffer[i] <= value:
return False
return True
def _is_pivot_high(self, value):
lr = int(self._lookback_right.Value)
for i in range(len(self._rsi_buffer)):
if i == lr:
continue
if self._rsi_buffer[i] >= value:
return False
return True
def _check_pivots(self, rsi_value, candle):
lr = int(self._lookback_right.Value)
candidate_rsi = self._rsi_buffer[lr]
candidate_bar = self._bar_index - lr
if self._is_pivot_low(candidate_rsi):
self._last_rsi_low = candidate_rsi
self._last_price_low = self._low_buffer[lr]
self._last_pivot_low_index = candidate_bar
if self._is_pivot_high(candidate_rsi):
self._last_rsi_high = candidate_rsi
self._last_price_high = self._high_buffer[lr]
self._last_pivot_high_index = candidate_bar
def _on_process(self, candle, rsi_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi_value = float(rsi_val)
self._bar_index += 1
self._add_to_buffer(rsi_value, float(candle.LowPrice), float(candle.HighPrice))
if self._buffer_count < len(self._rsi_buffer):
return
cooldown = int(self._cooldown_bars.Value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._check_pivots(rsi_value, candle)
return
lr = int(self._lookback_right.Value)
candidate_rsi = self._rsi_buffer[lr]
candidate_low = self._low_buffer[lr]
candidate_high = self._high_buffer[lr]
candidate_bar = self._bar_index - lr
range_lo = int(self._range_lower.Value)
range_up = int(self._range_upper.Value)
is_pivot_low = self._is_pivot_low(candidate_rsi)
is_pivot_high = self._is_pivot_high(candidate_rsi)
if is_pivot_low:
in_range = (self._last_pivot_low_index >= 0 and
candidate_bar - self._last_pivot_low_index >= range_lo and
candidate_bar - self._last_pivot_low_index <= range_up)
bullish_div = (in_range and
self._last_rsi_low is not None and
self._last_price_low is not None and
candidate_rsi > self._last_rsi_low and
candidate_low < self._last_price_low)
if bullish_div and rsi_value < 40.0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
self._last_rsi_low = candidate_rsi
self._last_price_low = candidate_low
self._last_pivot_low_index = candidate_bar
if is_pivot_high:
in_range = (self._last_pivot_high_index >= 0 and
candidate_bar - self._last_pivot_high_index >= range_lo and
candidate_bar - self._last_pivot_high_index <= range_up)
bearish_div = (in_range and
self._last_rsi_high is not None and
self._last_price_high is not None and
candidate_rsi < self._last_rsi_high and
candidate_high > self._last_price_high)
if bearish_div and rsi_value > 60.0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
self._last_rsi_high = candidate_rsi
self._last_price_high = candidate_high
self._last_pivot_high_index = candidate_bar
def CreateClone(self):
return gold_rsi_divergence_strategy()