Стратегия Scalp RSI
Скальпинговая стратегия на основе резких изменений индикатора RSI. Конвертирована из MetaTrader скрипта scalpen_rsi.mq4.
Стратегия открывает позиции при резком падении или росте RSI и использует фиксированные уровни тейк-профита и стоп-лосса.
Детали
- Условия входа:
- Покупка: значение RSI
buy_periodбаров назад минус текущий RSI ≥BuyMovement, RSI предыдущего бара минус текущий RSI >BuyBreakdown, текущий RSI <BuyRsiValue. - Продажа: текущий RSI минус RSI
sell_periodбаров назад ≥SellMovement, текущий RSI минус RSI предыдущего бара >SellBreakdown, текущий RSI >SellRsiValue.
- Покупка: значение RSI
- Длинные/Короткие: Оба варианта.
- Условия выхода: фиксированные тейк-профит и стоп-лосс в тиках.
- Стопы: Да, через
BuyStopLoss,BuyTakeProfit,SellStopLoss,SellTakeProfit. - Фильтры:
- Минимальная задержка между сделками (
TradeDelaySeconds). - Максимум одновременно открытых сделок (
MaxOpenTrades).
- Минимальная задержка между сделками (
using System;
using System.Linq;
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>
/// RSI-based scalping strategy converted from MetaTrader "scalpen_rsi.mq4".
/// </summary>
public class ScalpRsiStrategy : Strategy
{
private readonly StrategyParam<decimal> _buyMovement;
private readonly StrategyParam<int> _buyPeriod;
private readonly StrategyParam<decimal> _buyBreakdown;
private readonly StrategyParam<decimal> _buyRsiValue;
private readonly StrategyParam<decimal> _sellMovement;
private readonly StrategyParam<int> _sellPeriod;
private readonly StrategyParam<decimal> _sellBreakdown;
private readonly StrategyParam<decimal> _sellRsiValue;
private readonly StrategyParam<int> _buyStopLoss;
private readonly StrategyParam<int> _buyTakeProfit;
private readonly StrategyParam<int> _sellStopLoss;
private readonly StrategyParam<int> _sellTakeProfit;
private readonly StrategyParam<int> _buyMaLength;
private readonly StrategyParam<int> _sellMaLength;
private readonly StrategyParam<bool> _enableBuy;
private readonly StrategyParam<bool> _enableSell;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _tradeDelaySeconds;
private readonly StrategyParam<int> _maxOpenTrades;
private readonly List<decimal> _buyRsiValues = new();
private readonly List<decimal> _sellRsiValues = new();
private DateTimeOffset _lastTradeTime;
private decimal _entryPrice;
private int _openTrades;
public decimal BuyMovement { get => _buyMovement.Value; set => _buyMovement.Value = value; }
public int BuyPeriod { get => _buyPeriod.Value; set => _buyPeriod.Value = value; }
public decimal BuyBreakdown { get => _buyBreakdown.Value; set => _buyBreakdown.Value = value; }
public decimal BuyRsiValue { get => _buyRsiValue.Value; set => _buyRsiValue.Value = value; }
public decimal SellMovement { get => _sellMovement.Value; set => _sellMovement.Value = value; }
public int SellPeriod { get => _sellPeriod.Value; set => _sellPeriod.Value = value; }
public decimal SellBreakdown { get => _sellBreakdown.Value; set => _sellBreakdown.Value = value; }
public decimal SellRsiValue { get => _sellRsiValue.Value; set => _sellRsiValue.Value = value; }
public int BuyStopLoss { get => _buyStopLoss.Value; set => _buyStopLoss.Value = value; }
public int BuyTakeProfit { get => _buyTakeProfit.Value; set => _buyTakeProfit.Value = value; }
public int SellStopLoss { get => _sellStopLoss.Value; set => _sellStopLoss.Value = value; }
public int SellTakeProfit { get => _sellTakeProfit.Value; set => _sellTakeProfit.Value = value; }
public int BuyMaLength { get => _buyMaLength.Value; set => _buyMaLength.Value = value; }
public int SellMaLength { get => _sellMaLength.Value; set => _sellMaLength.Value = value; }
public bool EnableBuy { get => _enableBuy.Value; set => _enableBuy.Value = value; }
public bool EnableSell { get => _enableSell.Value; set => _enableSell.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int TradeDelaySeconds { get => _tradeDelaySeconds.Value; set => _tradeDelaySeconds.Value = value; }
public int MaxOpenTrades { get => _maxOpenTrades.Value; set => _maxOpenTrades.Value = value; }
public ScalpRsiStrategy()
{
_buyMovement = Param(nameof(BuyMovement), 10m)
.SetDisplay("Buy Movement", "RSI drop vs earlier period", "Buy");
_buyPeriod = Param(nameof(BuyPeriod), 2)
.SetDisplay("Buy Period", "Bars back for comparison", "Buy");
_buyBreakdown = Param(nameof(BuyBreakdown), 5m)
.SetDisplay("Buy Breakdown", "RSI drop vs previous bar", "Buy");
_buyRsiValue = Param(nameof(BuyRsiValue), 30m)
.SetDisplay("Buy RSI", "RSI value threshold", "Buy");
_sellMovement = Param(nameof(SellMovement), 0.0040m)
.SetDisplay("Sell Movement", "RSI rise vs earlier period", "Sell");
_sellPeriod = Param(nameof(SellPeriod), 2)
.SetDisplay("Sell Period", "Bars back for comparison", "Sell");
_sellBreakdown = Param(nameof(SellBreakdown), 0.0030m)
.SetDisplay("Sell Breakdown", "RSI rise vs previous bar", "Sell");
_sellRsiValue = Param(nameof(SellRsiValue), 30m)
.SetDisplay("Sell RSI", "RSI value threshold", "Sell");
_buyStopLoss = Param(nameof(BuyStopLoss), 60)
.SetDisplay("Buy Stop Loss", "Ticks for stop loss", "Buy");
_buyTakeProfit = Param(nameof(BuyTakeProfit), 3)
.SetDisplay("Buy Take Profit", "Ticks for take profit", "Buy");
_sellStopLoss = Param(nameof(SellStopLoss), 60)
.SetDisplay("Sell Stop Loss", "Ticks for stop loss", "Sell");
_sellTakeProfit = Param(nameof(SellTakeProfit), 3)
.SetDisplay("Sell Take Profit", "Ticks for take profit", "Sell");
_buyMaLength = Param(nameof(BuyMaLength), 14)
.SetDisplay("Buy RSI Length", "RSI period for buy", "Buy");
_sellMaLength = Param(nameof(SellMaLength), 14)
.SetDisplay("Sell RSI Length", "RSI period for sell", "Sell");
_enableBuy = Param(nameof(EnableBuy), true)
.SetDisplay("Enable Buy", "Allow buy trades", "General");
_enableSell = Param(nameof(EnableSell), true)
.SetDisplay("Enable Sell", "Allow sell trades", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle", "Candle type", "General");
_tradeDelaySeconds = Param(nameof(TradeDelaySeconds), 360)
.SetDisplay("Trade Delay", "Seconds between trades", "General");
_maxOpenTrades = Param(nameof(MaxOpenTrades), 3)
.SetDisplay("Max Trades", "Maximum open trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_buyRsiValues.Clear();
_sellRsiValues.Clear();
_openTrades = 0;
_entryPrice = 0;
_lastTradeTime = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var buyRsi = new RelativeStrengthIndex { Length = BuyMaLength };
var sellRsi = new RelativeStrengthIndex { Length = SellMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(buyRsi, sellRsi, ProcessCandle)
.Start();
}
// Process candle and generate trading signals
private void ProcessCandle(ICandleMessage candle, decimal buyRsi, decimal sellRsi)
{
if (candle.State != CandleStates.Finished)
return;
UpdateList(_buyRsiValues, buyRsi, Math.Max(BuyPeriod, 1) + 1);
UpdateList(_sellRsiValues, sellRsi, Math.Max(SellPeriod, 1) + 1);
var step = Security?.PriceStep ?? 1m;
var now = candle.CloseTime;
// Check buy conditions
var buySignal = EnableBuy && _buyRsiValues.Count > BuyPeriod
&& _buyRsiValues.Count >= 2
&& _buyRsiValues[_buyRsiValues.Count - 1 - BuyPeriod] - _buyRsiValues[^1] >= BuyMovement
&& _buyRsiValues[^2] - _buyRsiValues[^1] > BuyBreakdown
&& _buyRsiValues[^1] < BuyRsiValue;
// Check sell conditions
var sellSignal = EnableSell && _sellRsiValues.Count > SellPeriod
&& _sellRsiValues.Count >= 2
&& _sellRsiValues[^1] - _sellRsiValues[_sellRsiValues.Count - 1 - SellPeriod] >= SellMovement
&& _sellRsiValues[^1] - _sellRsiValues[^2] > SellBreakdown
&& _sellRsiValues[^1] > SellRsiValue;
var canTrade = (now - _lastTradeTime).TotalSeconds > TradeDelaySeconds && _openTrades < MaxOpenTrades;
if (buySignal && canTrade)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_lastTradeTime = now;
_openTrades++;
}
else if (sellSignal && canTrade)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_lastTradeTime = now;
_openTrades++;
}
if (Position > 0)
{
var stop = _entryPrice - BuyStopLoss * step;
var take = _entryPrice + BuyTakeProfit * step;
if (candle.ClosePrice <= stop || candle.ClosePrice >= take)
{
SellMarket();
_openTrades = Math.Max(0, _openTrades - 1);
}
}
else if (Position < 0)
{
var stop = _entryPrice + SellStopLoss * step;
var take = _entryPrice - SellTakeProfit * step;
if (candle.ClosePrice >= stop || candle.ClosePrice <= take)
{
BuyMarket();
_openTrades = Math.Max(0, _openTrades - 1);
}
}
if (Position == 0)
_openTrades = 0;
}
// Maintain limited history of indicator values
private static void UpdateList(List<decimal> list, decimal value, int max)
{
list.Add(value);
if (list.Count > max)
list.RemoveAt(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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class scalp_rsi_strategy(Strategy):
def __init__(self):
super(scalp_rsi_strategy, self).__init__()
self._buy_movement = self.Param("BuyMovement", 10.0).SetDisplay("Buy Movement", "RSI drop vs earlier period", "Buy")
self._buy_period = self.Param("BuyPeriod", 2).SetDisplay("Buy Period", "Bars back for comparison", "Buy")
self._buy_breakdown = self.Param("BuyBreakdown", 5.0).SetDisplay("Buy Breakdown", "RSI drop vs previous bar", "Buy")
self._buy_rsi_value = self.Param("BuyRsiValue", 30.0).SetDisplay("Buy RSI", "RSI value threshold", "Buy")
self._sell_movement = self.Param("SellMovement", 0.004).SetDisplay("Sell Movement", "RSI rise vs earlier period", "Sell")
self._sell_period = self.Param("SellPeriod", 2).SetDisplay("Sell Period", "Bars back for comparison", "Sell")
self._sell_breakdown = self.Param("SellBreakdown", 0.003).SetDisplay("Sell Breakdown", "RSI rise vs previous bar", "Sell")
self._sell_rsi_value = self.Param("SellRsiValue", 30.0).SetDisplay("Sell RSI", "RSI value threshold", "Sell")
self._buy_sl = self.Param("BuyStopLoss", 60).SetDisplay("Buy Stop Loss", "Ticks for stop loss", "Buy")
self._buy_tp = self.Param("BuyTakeProfit", 3).SetDisplay("Buy Take Profit", "Ticks for take profit", "Buy")
self._sell_sl = self.Param("SellStopLoss", 60).SetDisplay("Sell Stop Loss", "Ticks for stop loss", "Sell")
self._sell_tp = self.Param("SellTakeProfit", 3).SetDisplay("Sell Take Profit", "Ticks for take profit", "Sell")
self._buy_ma_length = self.Param("BuyMaLength", 14).SetDisplay("Buy RSI Length", "RSI period for buy", "Buy")
self._sell_ma_length = self.Param("SellMaLength", 14).SetDisplay("Sell RSI Length", "RSI period for sell", "Sell")
self._enable_buy = self.Param("EnableBuy", True).SetDisplay("Enable Buy", "Allow buy trades", "General")
self._enable_sell = self.Param("EnableSell", True).SetDisplay("Enable Sell", "Allow sell trades", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle", "Candle type", "General")
self._trade_delay_seconds = self.Param("TradeDelaySeconds", 360).SetDisplay("Trade Delay", "Seconds between trades", "General")
self._max_open_trades = self.Param("MaxOpenTrades", 3).SetDisplay("Max Trades", "Maximum open trades", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(scalp_rsi_strategy, self).OnReseted()
self._buy_rsi_history = []
self._sell_rsi_history = []
self._open_trades = 0
self._entry_price = 0.0
self._last_trade_time = None
def OnStarted2(self, time):
super(scalp_rsi_strategy, self).OnStarted2(time)
self._buy_rsi_history = []
self._sell_rsi_history = []
self._open_trades = 0
self._entry_price = 0.0
self._last_trade_time = None
self._step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
self._step = float(self.Security.PriceStep)
buy_rsi = RelativeStrengthIndex()
buy_rsi.Length = self._buy_ma_length.Value
sell_rsi = RelativeStrengthIndex()
sell_rsi.Length = self._sell_ma_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(buy_rsi, sell_rsi, self.OnProcess).Start()
def OnProcess(self, candle, buy_rsi_val, sell_rsi_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
buy_max = max(self._buy_period.Value, 1) + 1
self._buy_rsi_history.append(buy_rsi_val)
if len(self._buy_rsi_history) > buy_max:
self._buy_rsi_history.pop(0)
sell_max = max(self._sell_period.Value, 1) + 1
self._sell_rsi_history.append(sell_rsi_val)
if len(self._sell_rsi_history) > sell_max:
self._sell_rsi_history.pop(0)
step = self._step
bh = self._buy_rsi_history
sh = self._sell_rsi_history
bp = self._buy_period.Value
sp = self._sell_period.Value
now = candle.CloseTime
buy_signal = (self._enable_buy.Value
and len(bh) > bp
and len(bh) >= 2
and bh[len(bh) - 1 - bp] - bh[-1] >= self._buy_movement.Value
and bh[-2] - bh[-1] > self._buy_breakdown.Value
and bh[-1] < self._buy_rsi_value.Value)
sell_signal = (self._enable_sell.Value
and len(sh) > sp
and len(sh) >= 2
and sh[-1] - sh[len(sh) - 1 - sp] >= self._sell_movement.Value
and sh[-1] - sh[-2] > self._sell_breakdown.Value
and sh[-1] > self._sell_rsi_value.Value)
can_trade = (self._open_trades < self._max_open_trades.Value
and (self._last_trade_time is None or (now - self._last_trade_time).TotalSeconds > self._trade_delay_seconds.Value))
if buy_signal and can_trade:
self.BuyMarket()
self._entry_price = close
self._last_trade_time = now
self._open_trades += 1
elif sell_signal and can_trade:
self.SellMarket()
self._entry_price = close
self._last_trade_time = now
self._open_trades += 1
if self.Position > 0:
sl = self._entry_price - self._buy_sl.Value * step
tp = self._entry_price + self._buy_tp.Value * step
if close <= sl or close >= tp:
self.SellMarket()
self._open_trades = max(0, self._open_trades - 1)
elif self.Position < 0:
sl = self._entry_price + self._sell_sl.Value * step
tp = self._entry_price - self._sell_tp.Value * step
if close >= sl or close <= tp:
self.BuyMarket()
self._open_trades = max(0, self._open_trades - 1)
if self.Position == 0:
self._open_trades = 0
def CreateClone(self):
return scalp_rsi_strategy()