Estrategia de Divergencia RSI en Oro
La estrategia de Divergencia RSI en Oro opera en scalping sobre el oro identificando divergencias alcistas y bajistas entre el precio y el Índice de Fuerza Relativa (RSI). Cuando el precio marca un nuevo mínimo pero el RSI imprime un mínimo más alto, la estrategia busca comprar. Por el contrario, cuando el precio marca un nuevo máximo pero el RSI imprime un máximo más bajo, la estrategia vende. Ambas configuraciones se confirman solo si dos pivotes ocurren dentro de un rango de barras configurable.
Detalles
- Criterios de entrada:
- Largo: Precio con mínimo más bajo, RSI con mínimo más alto, RSI < 40.
- Corto: Precio con máximo más alto, RSI con máximo más bajo, RSI > 60.
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Utiliza stop loss y take profit.
- Stops: Stop loss y take profit fijos en pips.
- Valores predeterminados:
RsiLength= 60StopLossPips= 11TakeProfitPips= 33
- Filtros:
- Categoría: Divergencia
- Dirección: Ambos
- Indicadores: RSI
- Stops: Sí
- Complejidad: Moderado
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí
- Nivel de riesgo: Medio
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()