Estratégia Limits RSI Momentum Bot
Resumo
Esta estratégia coloca ordens limitadas com base nos indicadores de Índice de Força Relativa (RSI) e Momentum. Seu objetivo é comprar com descontos e vender com prêmios usando ordens pendentes em vez de execuções a mercado.
Regras de Trading
- Opera apenas durante a janela de tempo especificada.
- Em cada vela finalizada, os valores de RSI e Momentum são calculados.
- Uma ordem limitada de compra é colocada abaixo da abertura da vela quando RSI e Momentum estão ambos abaixo de seus limiares de compra.
- Uma ordem limitada de venda é colocada acima da abertura da vela quando RSI e Momentum estão ambos acima de seus limiares de venda.
- Quando uma posição é aberta, a ordem pendente oposta é cancelada.
- Stop-loss e take-profit são gerenciados automaticamente via
StartProtection.
Parâmetros
Volume– volume da ordem.LimitOrderDistance– distância em passos de preço da abertura da vela para colocar ordens pendentes.TakeProfit– objetivo de lucro em passos de preço.StopLoss– limite de perda em passos de preço.RsiPeriod– período para cálculo do RSI.RsiBuyRestrict/RsiSellRestrict– limiares de RSI que permitem entradas compradas ou vendidas.MomentumPeriod– período para cálculo do Momentum.MomentumBuyRestrict/MomentumSellRestrict– limiares de Momentum para entradas compradas ou vendidas.StartTime/EndTime– limites da sessão de trading.CandleType– intervalo de vela usado para cálculos de indicadores.
Observações
A estratégia é convertida do script MQL4 "The Limits Bot with RSI & Momentum" e usa a API de alto nível do 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()