Gold RSI Divergence 策略
Gold RSI Divergence 策略通过识别价格与相对强弱指数 (RSI) 之间的背离来对黄金进行短线交易。 当价格创出新低而 RSI 创出更高低点时,策略做多; 当价格创出新高而 RSI 创出更低高点时,策略做空。 两次枢轴之间的间隔必须落在设定的范围内才被视为有效信号。
细节
- 入场条件:
- 多头:价格更低低点、RSI 更高低点、RSI < 40。
- 空头:价格更高高点、RSI 更低高点、RSI > 60。
- 方向:双向。
- 出场条件:
- 使用止损和止盈。
- 止损:以 pips 定义的固定止损和止盈。
- 默认值:
RsiLength= 60StopLossPips= 11TakeProfitPips= 33
- 过滤器:
- 分类:背离
- 方向:双向
- 指标:RSI
- 止损:是
- 复杂度:中等
- 时间框架:日内
- 季节性:否
- 神经网络:否
- 背离:是
- 风险等级:中等
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>
/// Gold RSI Divergence strategy.
/// RSI pivots are confirmed LookbackLeft bars before and LookbackRight bars after them. A pivot low whose RSI is higher than the
/// previous pivot low while price made a lower low, within RangeLower..RangeUpper bars of it and with RSI below 40, buys; the mirrored
/// bearish divergence with RSI above 60 sells. Positions are closed by a fixed stop loss and take profit in pips.
/// </summary>
public class GoldRsiDivergenceStrategy : Strategy
{
private const decimal _longRsiLimit = 40m;
private const decimal _shortRsiLimit = 60m;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _lookbackLeft;
private readonly StrategyParam<int> _lookbackRight;
private readonly StrategyParam<int> _rangeLower;
private readonly StrategyParam<int> _rangeUpper;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<DataType> _candleType;
private readonly List<(decimal rsi, decimal low, decimal high)> _window = [];
private int _barIndex;
private (decimal rsi, decimal low, int bar)? _lastPivotLow;
private (decimal rsi, decimal high, int bar)? _lastPivotHigh;
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Bars to the left of a pivot.
/// </summary>
public int LookbackLeft
{
get => _lookbackLeft.Value;
set => _lookbackLeft.Value = value;
}
/// <summary>
/// Bars to the right of a pivot.
/// </summary>
public int LookbackRight
{
get => _lookbackRight.Value;
set => _lookbackRight.Value = value;
}
/// <summary>
/// Minimum bars between two pivots.
/// </summary>
public int RangeLower
{
get => _rangeLower.Value;
set => _rangeLower.Value = value;
}
/// <summary>
/// Maximum bars between two pivots.
/// </summary>
public int RangeUpper
{
get => _rangeUpper.Value;
set => _rangeUpper.Value = value;
}
/// <summary>
/// Stop loss in pips (price steps).
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit in pips (price steps).
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GoldRsiDivergenceStrategy()
{
_rsiLength = Param(nameof(RsiLength), 60)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_lookbackLeft = Param(nameof(LookbackLeft), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback Left", "Bars to the left of a pivot", "Divergence");
_lookbackRight = Param(nameof(LookbackRight), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback Right", "Bars to the right of a pivot", "Divergence");
_rangeLower = Param(nameof(RangeLower), 5)
.SetNotNegative()
.SetDisplay("Range Lower", "Minimum bars between two pivots", "Divergence");
_rangeUpper = Param(nameof(RangeUpper), 60)
.SetGreaterThanZero()
.SetDisplay("Range Upper", "Maximum bars between two pivots", "Divergence");
_stopLossPips = Param(nameof(StopLossPips), 11m)
.SetNotNegative()
.SetDisplay("Stop Loss Pips", "Stop loss in pips (price steps)", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 33m)
.SetNotNegative()
.SetDisplay("Take Profit Pips", "Take profit in pips (price steps)", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, ProcessCandle)
.Start();
var step = Security.PriceStep ?? 1m;
StartProtection(new Unit(TakeProfitPips * step, UnitTypes.Absolute), new Unit(StopLossPips * step, UnitTypes.Absolute), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ResetState()
{
_window.Clear();
_barIndex = 0;
_lastPivotLow = null;
_lastPivotHigh = null;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!rsiValue.IsFormed)
return;
var rsi = rsiValue.ToDecimal();
_barIndex++;
_window.Add((rsi, candle.LowPrice, candle.HighPrice));
var size = LookbackLeft + LookbackRight + 1;
if (_window.Count > size)
_window.RemoveAt(0);
if (_window.Count < size)
return;
var pivot = _window[LookbackLeft];
var pivotBar = _barIndex - LookbackRight;
var isPivotLow = true;
var isPivotHigh = true;
for (var i = 0; i < size; i++)
{
if (i == LookbackLeft)
continue;
if (_window[i].rsi <= pivot.rsi)
isPivotLow = false;
if (_window[i].rsi >= pivot.rsi)
isPivotHigh = false;
}
var canTrade = IsFormedAndOnlineAndAllowTrading();
if (isPivotLow)
{
var bullish = _lastPivotLow is { } prev
&& InRange(pivotBar - prev.bar)
&& pivot.rsi > prev.rsi
&& pivot.low < prev.low;
if (canTrade && bullish && rsi < _longRsiLimit && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
_lastPivotLow = (pivot.rsi, pivot.low, pivotBar);
}
if (isPivotHigh)
{
var bearish = _lastPivotHigh is { } prev
&& InRange(pivotBar - prev.bar)
&& pivot.rsi < prev.rsi
&& pivot.high > prev.high;
if (canTrade && bearish && rsi > _shortRsiLimit && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
_lastPivotHigh = (pivot.rsi, pivot.high, pivotBar);
}
}
private bool InRange(int bars) => bars >= RangeLower && bars <= RangeUpper;
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
LONG_RSI_LIMIT = 40
SHORT_RSI_LIMIT = 60
class gold_rsi_divergence_strategy(Strategy):
"""
Gold RSI Divergence strategy.
RSI pivots are confirmed LookbackLeft bars before and LookbackRight bars after them. A pivot low whose RSI is higher than the
previous pivot low while price made a lower low, within RangeLower..RangeUpper bars of it and with RSI below 40, buys; the mirrored
bearish divergence with RSI above 60 sells. Positions are closed by a fixed stop loss and take profit in pips.
"""
def __init__(self):
super(gold_rsi_divergence_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 60).SetGreaterThanZero().SetDisplay("RSI Length", "RSI period", "RSI")
self._lookback_left = self.Param("LookbackLeft", 5).SetGreaterThanZero().SetDisplay("Lookback Left", "Bars to the left of a pivot", "Divergence")
self._lookback_right = self.Param("LookbackRight", 5).SetGreaterThanZero().SetDisplay("Lookback Right", "Bars to the right of a pivot", "Divergence")
self._range_lower = self.Param("RangeLower", 5).SetNotNegative().SetDisplay("Range Lower", "Minimum bars between two pivots", "Divergence")
self._range_upper = self.Param("RangeUpper", 60).SetGreaterThanZero().SetDisplay("Range Upper", "Maximum bars between two pivots", "Divergence")
self._stop_loss_pips = self.Param("StopLossPips", 11.0).SetNotNegative().SetDisplay("Stop Loss Pips", "Stop loss in pips (price steps)", "Risk")
self._take_profit_pips = self.Param("TakeProfitPips", 33.0).SetNotNegative().SetDisplay("Take Profit Pips", "Take profit in pips (price steps)", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._window = []
self._bar_index = 0
self._last_pivot_low = None
self._last_pivot_high = None
def OnReseted(self):
super(gold_rsi_divergence_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(gold_rsi_divergence_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi, self._process_candle).Start()
step = self.Security.PriceStep if self.Security.PriceStep is not None else Decimal(1)
take = Decimal(self._take_profit_pips.Value) * step
stop = Decimal(self._stop_loss_pips.Value) * step
self.StartProtection(Unit(take, UnitTypes.Absolute), Unit(stop, UnitTypes.Absolute), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _in_range(self, bars):
return self._range_lower.Value <= bars <= self._range_upper.Value
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if not rsi_value.IsFormed:
return
rsi = rsi_value.GetValue[Decimal](None)
self._bar_index += 1
left = self._lookback_left.Value
right = self._lookback_right.Value
size = left + right + 1
self._window.append((rsi, candle.LowPrice, candle.HighPrice))
if len(self._window) > size:
self._window.pop(0)
if len(self._window) < size:
return
pivot_rsi, pivot_low, pivot_high = self._window[left]
pivot_bar = self._bar_index - right
is_pivot_low = True
is_pivot_high = True
for i in range(size):
if i == left:
continue
if self._window[i][0] <= pivot_rsi:
is_pivot_low = False
if self._window[i][0] >= pivot_rsi:
is_pivot_high = False
can_trade = self.IsFormedAndOnlineAndAllowTrading()
if is_pivot_low:
prev = self._last_pivot_low
bullish = (prev is not None and self._in_range(pivot_bar - prev[2])
and pivot_rsi > prev[0] and pivot_low < prev[1])
if can_trade and bullish and rsi < LONG_RSI_LIMIT and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._last_pivot_low = (pivot_rsi, pivot_low, pivot_bar)
if is_pivot_high:
prev = self._last_pivot_high
bearish = (prev is not None and self._in_range(pivot_bar - prev[2])
and pivot_rsi < prev[0] and pivot_high > prev[1])
if can_trade and bearish and rsi > SHORT_RSI_LIMIT and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._last_pivot_high = (pivot_rsi, pivot_high, pivot_bar)
def CreateClone(self):
return gold_rsi_divergence_strategy()