Стратегия Limits RSI Momentum Bot
Описание
Стратегия выставляет отложенные лимитные заявки на основе индикаторов RSI и Momentum. Цель — покупать по более выгодной цене и продавать по завышенной, используя отложенные ордера вместо рыночных.
Правила торговли
- Работает только в заданном временном интервале.
- На каждой закрытой свече вычисляются значения RSI и Momentum.
- Покупка: лимитная заявка ниже открытия свечи, если оба индикатора ниже порога на покупку.
- Продажа: лимитная заявка выше открытия свечи, если оба индикатора выше порога на продажу.
- При открытии позиции противоположная отложенная заявка отменяется.
- Стоп‑лосс и тейк‑профит устанавливаются автоматически через
StartProtection.
Параметры
Volume– объём заявки.LimitOrderDistance– расстояние в шагах цены от открытия свечи для установки ордера.TakeProfit– цель по прибыли в шагах цены.StopLoss– предел убытка в шагах цены.RsiPeriod– период расчёта RSI.RsiBuyRestrict/RsiSellRestrict– пороги RSI для входа в длинные или короткие позиции.MomentumPeriod– период расчёта Momentum.MomentumBuyRestrict/MomentumSellRestrict– пороги Momentum для длинных или коротких позиций.StartTime/EndTime– границы торговой сессии.CandleType– интервал свечи для расчётов индикаторов.
Примечания
Стратегия конвертирована из MQL4‑скрипта «The Limits Bot with RSI & Momentum» и использует высокоуровневый API 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()