Estrategia Limits RSI Momentum Bot
Resumen
Esta estrategia coloca órdenes límite basadas en los indicadores de Índice de Fuerza Relativa (RSI) y Momentum. Su objetivo es comprar con descuentos y vender con primas utilizando órdenes pendientes en lugar de ejecuciones de mercado.
Reglas de Trading
- Opera solo durante la ventana de tiempo especificada.
- En cada vela finalizada, se calculan los valores de RSI y Momentum.
- Se coloca una orden límite de compra por debajo de la apertura de la vela cuando RSI y Momentum están ambos por debajo de sus umbrales de compra.
- Se coloca una orden límite de venta por encima de la apertura de la vela cuando RSI y Momentum están ambos por encima de sus umbrales de venta.
- Cuando se abre una posición, la orden pendiente opuesta se cancela.
- El stop-loss y el take-profit se gestionan automáticamente mediante
StartProtection.
Parámetros
Volume– volumen de la orden.LimitOrderDistance– distancia en pasos de precio desde la apertura de la vela para colocar órdenes pendientes.TakeProfit– objetivo de beneficio en pasos de precio.StopLoss– límite de pérdida en pasos de precio.RsiPeriod– período para el cálculo del RSI.RsiBuyRestrict/RsiSellRestrict– umbrales de RSI que permiten entradas largas o cortas.MomentumPeriod– período para el cálculo del Momentum.MomentumBuyRestrict/MomentumSellRestrict– umbrales de Momentum para entradas largas o cortas.StartTime/EndTime– límites de la sesión de trading.CandleType– intervalo de vela utilizado para los cálculos de indicadores.
Notas
La estrategia está convertida del script MQL4 "The Limits Bot with RSI & Momentum" y utiliza la API de alto nivel de StockSharp.
using System;
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>
/// Strategy that places limit orders based on RSI and Momentum values.
/// A buy limit is set below the candle open when both indicators signal oversold.
/// A sell limit is set above the open when indicators show overbought conditions.
/// Opposite pending order is cancelled once a position is opened.
/// Stop-loss and take-profit are managed via StartProtection.
/// </summary>
public class LimitsRsiMomentumBotStrategy : Strategy
{
private readonly StrategyParam<int> _limitOrderDistance;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiBuyRestrict;
private readonly StrategyParam<decimal> _rsiSellRestrict;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<decimal> _momentumBuyRestrict;
private readonly StrategyParam<decimal> _momentumSellRestrict;
private readonly StrategyParam<TimeSpan> _startTime;
private readonly StrategyParam<TimeSpan> _endTime;
private readonly StrategyParam<DataType> _candleType;
private Order _buyOrder;
private Order _sellOrder;
/// <summary>
/// Distance from candle open to place limit orders in price steps.
/// </summary>
public int LimitOrderDistance
{
get => _limitOrderDistance.Value;
set => _limitOrderDistance.Value = value;
}
/// <summary>
/// Take profit in price steps.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss in price steps.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI threshold for long entries.
/// </summary>
public decimal RsiBuyRestrict
{
get => _rsiBuyRestrict.Value;
set => _rsiBuyRestrict.Value = value;
}
/// <summary>
/// RSI threshold for short entries.
/// </summary>
public decimal RsiSellRestrict
{
get => _rsiSellRestrict.Value;
set => _rsiSellRestrict.Value = value;
}
/// <summary>
/// Momentum period.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Momentum threshold for long entries.
/// </summary>
public decimal MomentumBuyRestrict
{
get => _momentumBuyRestrict.Value;
set => _momentumBuyRestrict.Value = value;
}
/// <summary>
/// Momentum threshold for short entries.
/// </summary>
public decimal MomentumSellRestrict
{
get => _momentumSellRestrict.Value;
set => _momentumSellRestrict.Value = value;
}
/// <summary>
/// Trading start time.
/// </summary>
public TimeSpan StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// Trading end time.
/// </summary>
public TimeSpan EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes <see cref="LimitsRsiMomentumBotStrategy"/>.
/// </summary>
public LimitsRsiMomentumBotStrategy()
{
_limitOrderDistance = Param(nameof(LimitOrderDistance), 5)
.SetGreaterThanZero()
.SetDisplay("Limit Order Distance", "Distance from candle open in price steps", "Trading")
.SetOptimize(3, 10, 1);
_takeProfit = Param(nameof(TakeProfit), 35)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Profit target in price steps", "Protection")
.SetOptimize(20, 60, 5);
_stopLoss = Param(nameof(StopLoss), 8)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in price steps", "Protection")
.SetOptimize(5, 20, 1);
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicators");
_rsiBuyRestrict = Param(nameof(RsiBuyRestrict), 30m)
.SetDisplay("RSI Buy Threshold", "Max RSI value to allow buys", "Indicators");
_rsiSellRestrict = Param(nameof(RsiSellRestrict), 70m)
.SetDisplay("RSI Sell Threshold", "Min RSI value to allow sells", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Period for Momentum calculation", "Indicators");
_momentumBuyRestrict = Param(nameof(MomentumBuyRestrict), 1m)
.SetDisplay("Momentum Buy Threshold", "Max Momentum value to allow buys", "Indicators");
_momentumSellRestrict = Param(nameof(MomentumSellRestrict), 1m)
.SetDisplay("Momentum Sell Threshold", "Min Momentum value to allow sells", "Indicators");
_startTime = Param(nameof(StartTime), TimeSpan.Zero)
.SetDisplay("Start Time", "Trading start time", "Trading");
_endTime = Param(nameof(EndTime), new TimeSpan(23, 59, 0))
.SetDisplay("End Time", "Trading end time", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for strategy", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var momentum = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, momentum, ProcessCandle)
.Start();
var step = Security.PriceStep ?? 1m;
StartProtection(
takeProfit: new Unit(TakeProfit * step, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss * step, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, momentum);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_buyOrder = null;
_sellOrder = null;
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal momentumValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!IsTradingTime(candle.OpenTime))
return;
var step = Security.PriceStep ?? 1m;
var buySignal = rsiValue < RsiBuyRestrict && momentumValue < MomentumBuyRestrict && Position <= 0;
var sellSignal = rsiValue > RsiSellRestrict && momentumValue > MomentumSellRestrict && Position >= 0;
if (buySignal)
{
if (_sellOrder != null && _sellOrder.State == OrderStates.Active)
{
CancelOrder(_sellOrder);
_sellOrder = null;
}
if (_buyOrder != null && _buyOrder.State == OrderStates.Active)
return;
var price = candle.OpenPrice - LimitOrderDistance * step;
_buyOrder = BuyLimit(price);
}
else if (_buyOrder != null && _buyOrder.State == OrderStates.Active)
{
CancelOrder(_buyOrder);
_buyOrder = null;
}
if (sellSignal)
{
if (_buyOrder != null && _buyOrder.State == OrderStates.Active)
{
CancelOrder(_buyOrder);
_buyOrder = null;
}
if (_sellOrder != null && _sellOrder.State == OrderStates.Active)
return;
var price = candle.OpenPrice + LimitOrderDistance * step;
_sellOrder = SellLimit(price);
}
else if (_sellOrder != null && _sellOrder.State == OrderStates.Active)
{
CancelOrder(_sellOrder);
_sellOrder = null;
}
}
private bool IsTradingTime(DateTimeOffset time)
{
var t = time.TimeOfDay;
return t >= StartTime && t <= EndTime;
}
/// <inheritdoc />
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
if (Position > 0)
{
if (_sellOrder != null && _sellOrder.State == OrderStates.Active)
{
CancelOrder(_sellOrder);
_sellOrder = null;
}
}
else if (Position < 0)
{
if (_buyOrder != null && _buyOrder.State == OrderStates.Active)
{
CancelOrder(_buyOrder);
_buyOrder = null;
}
}
else
{
_buyOrder = null;
_sellOrder = null;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex, Momentum
from StockSharp.Algo.Strategies import Strategy
class limits_rsi_momentum_bot_strategy(Strategy):
"""
RSI + Momentum confirmation with StartProtection for SL/TP.
"""
def __init__(self):
super(limits_rsi_momentum_bot_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14).SetDisplay("RSI Period", "RSI period", "Indicators")
self._rsi_buy = self.Param("RsiBuyRestrict", 30.0).SetDisplay("RSI Buy", "Max RSI for buy", "Indicators")
self._rsi_sell = self.Param("RsiSellRestrict", 70.0).SetDisplay("RSI Sell", "Min RSI for sell", "Indicators")
self._mom_period = self.Param("MomentumPeriod", 14).SetDisplay("Mom Period", "Momentum period", "Indicators")
self._mom_buy = self.Param("MomentumBuyRestrict", 1.0).SetDisplay("Mom Buy", "Max momentum for buy", "Indicators")
self._mom_sell = self.Param("MomentumSellRestrict", 1.0).SetDisplay("Mom Sell", "Min momentum for sell", "Indicators")
self._tp_points = self.Param("TakeProfit", 35).SetDisplay("TP", "Take profit in steps", "Risk")
self._sl_points = self.Param("StopLoss", 8).SetDisplay("SL", "Stop loss in steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Timeframe", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(limits_rsi_momentum_bot_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(limits_rsi_momentum_bot_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
mom = Momentum()
mom.Length = self._mom_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, mom, self._process_candle).Start()
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 1.0
tp = Unit(self._tp_points.Value * step, UnitTypes.Absolute)
sl = Unit(self._sl_points.Value * step, UnitTypes.Absolute)
self.StartProtection(tp, sl)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, rsi_val, mom_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
rsi = float(rsi_val)
mom = float(mom_val)
buy_signal = rsi < self._rsi_buy.Value and mom < self._mom_buy.Value and self.Position <= 0
sell_signal = rsi > self._rsi_sell.Value and mom > self._mom_sell.Value and self.Position >= 0
if buy_signal:
self.BuyMarket()
self._cooldown = 10
elif sell_signal:
self.SellMarket()
self._cooldown = 10
def CreateClone(self):
return limits_rsi_momentum_bot_strategy()