Órdenes Pendientes Automáticas por RSI
Esta estrategia coloca órdenes límite pendientes después de que el Índice de Fuerza Relativa (RSI) permanece en zonas extremas durante varias velas consecutivas.
Cuando el RSI permanece por debajo del nivel de sobreventa durante MatchCount velas, se registra una orden de compra límite por debajo del cierre de la vela en PendingOffset puntos de precio. Cuando el RSI permanece por encima del nivel de sobrecompra durante el mismo número de velas, se coloca una orden de venta límite por encima del cierre con el mismo desplazamiento.
Parámetros
RsiPeriod– período de cálculo del RSI.RsiOverbought– nivel que define la zona de sobrecompra.RsiOversold– nivel que define la zona de sobreventa.PendingOffset– distancia desde el precio de cierre para colocar las órdenes pendientes (puntos de precio).MatchCount– número de velas consecutivas requeridas antes de colocar las órdenes.CandleType– marco temporal de velas utilizado para el análisis.
Los valores predeterminados emulan el script MQL original y utilizan velas de 4 horas.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Enters after RSI stays in extreme zones for several consecutive candles.
/// Buys after sustained oversold, sells after sustained overbought.
/// </summary>
public class AutoPendingByRsiStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<int> _matchCount;
private readonly StrategyParam<DataType> _candleType;
private int _overboughtCount;
private int _oversoldCount;
/// <summary>RSI calculation period.</summary>
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
/// <summary>RSI overbought level.</summary>
public decimal RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }
/// <summary>RSI oversold level.</summary>
public decimal RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }
/// <summary>Number of consecutive candles before placing orders.</summary>
public int MatchCount { get => _matchCount.Value; set => _matchCount.Value = value; }
/// <summary>Candle type for calculations.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public AutoPendingByRsiStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
.SetOptimize(7, 21, 7);
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "Overbought level", "Indicators")
.SetOptimize(60m, 80m, 5m);
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "Oversold level", "Indicators")
.SetOptimize(20m, 40m, 5m);
_matchCount = Param(nameof(MatchCount), 3)
.SetDisplay("Match Count", "Consecutive candles before entry", "General")
.SetOptimize(1, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_overboughtCount = 0;
_oversoldCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, Process)
.Start();
StartProtection(null, null);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void Process(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (rsi < RsiOversold)
{
_oversoldCount++;
_overboughtCount = 0;
}
else if (rsi > RsiOverbought)
{
_overboughtCount++;
_oversoldCount = 0;
}
else
{
_overboughtCount = 0;
_oversoldCount = 0;
}
if (_oversoldCount >= MatchCount && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_oversoldCount = 0;
}
if (_overboughtCount >= MatchCount && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_overboughtCount = 0;
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class auto_pending_by_rsi_strategy(Strategy):
def __init__(self):
super(auto_pending_by_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators") \
.SetOptimize(7, 21, 7)
self._rsi_overbought = self.Param("RsiOverbought", 70.0) \
.SetDisplay("RSI Overbought", "Overbought level", "Indicators") \
.SetOptimize(60.0, 80.0, 5.0)
self._rsi_oversold = self.Param("RsiOversold", 30.0) \
.SetDisplay("RSI Oversold", "Oversold level", "Indicators") \
.SetOptimize(20.0, 40.0, 5.0)
self._match_count = self.Param("MatchCount", 3) \
.SetDisplay("Match Count", "Consecutive candles before entry", "General") \
.SetOptimize(1, 10, 1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Time frame for analysis", "General")
self._overbought_count = 0
self._oversold_count = 0
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def rsi_overbought(self):
return self._rsi_overbought.Value
@property
def rsi_oversold(self):
return self._rsi_oversold.Value
@property
def match_count(self):
return self._match_count.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(auto_pending_by_rsi_strategy, self).OnReseted()
self._overbought_count = 0
self._oversold_count = 0
def OnStarted2(self, time):
super(auto_pending_by_rsi_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.process).Start()
self.StartProtection(None, None)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process(self, candle, rsi):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi)
if rsi_val < float(self.rsi_oversold):
self._oversold_count += 1
self._overbought_count = 0
elif rsi_val > float(self.rsi_overbought):
self._overbought_count += 1
self._oversold_count = 0
else:
self._overbought_count = 0
self._oversold_count = 0
if self._oversold_count >= self.match_count and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._oversold_count = 0
if self._overbought_count >= self.match_count and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._overbought_count = 0
def CreateClone(self):
return auto_pending_by_rsi_strategy()