Стратегия RSI Expert
Обзор
RSI Expert — стратегия, использующая индекс относительной силы (RSI). Она ждёт пересечения значения RSI заранее заданных уровней перекупленности и перепроданности и открывает позиции в сторону пересечения.
Логика
- Для каждой свечи вычисляется RSI.
- При пробое вверх уровня перепроданности открывается длинная позиция.
- При пробое вниз уровня перекупленности открывается короткая позиция.
- Перед открытием новой позиции закрывается противоположная.
- Можно включить защиту по тейк‑профиту, стоп‑лоссу и трейлинг‑стопу.
Стратегия обрабатывает только завершённые свечи и использует высокоуровневый API StockSharp с привязкой индикаторов.
Параметры
| Имя | Описание | Значение по умолчанию |
|---|---|---|
RsiPeriod |
Период расчёта RSI | 14 |
LevelUp |
Уровень перекупленности для входа в шорт | 70 |
LevelDown |
Уровень перепроданности для входа в лонг | 30 |
TakeProfitPercent |
Процент тейк‑профита, 0 — отключено |
0 |
StopLossPercent |
Процент стоп‑лосса, 0 — отключено |
0 |
TrailingStopPercent |
Процент трейлинг‑стопа, 0 — отключено |
0 |
CandleType |
Таймфрейм свечей для расчётов | 1 минута |
Примечания
Если TrailingStopPercent больше нуля, используется встроенный механизм StartProtection, который заменяет обычный стоп‑лосс на трейлинг‑стоп и автоматически сопровождает цену.
using System;
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 strategy with overbought and oversold level cross signals.
/// Opens long when RSI crosses above oversold level, short when crosses below overbought level.
/// </summary>
public class RsiExpertStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _levelUp;
private readonly StrategyParam<decimal> _levelDown;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal LevelUp { get => _levelUp.Value; set => _levelUp.Value = value; }
public decimal LevelDown { get => _levelDown.Value; set => _levelDown.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RsiExpertStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Length of the RSI indicator", "Indicators");
_levelUp = Param(nameof(LevelUp), 70m)
.SetDisplay("RSI Overbought", "Upper RSI level triggering a short", "Indicators");
_levelDown = Param(nameof(LevelDown), 30m)
.SetDisplay("RSI Oversold", "Lower RSI level triggering a long", "Indicators");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPercent, UnitTypes.Percent),
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevRsi == 0m)
{
_prevRsi = rsiValue;
return;
}
var crossUp = _prevRsi < LevelDown && rsiValue > LevelDown;
var crossDown = _prevRsi > LevelUp && rsiValue < LevelUp;
if (crossUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (crossDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevRsi = rsiValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class rsi_expert_strategy(Strategy):
def __init__(self):
super(rsi_expert_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "Length of the RSI indicator", "Indicators")
self._level_up = self.Param("LevelUp", 70.0) \
.SetDisplay("RSI Overbought", "Upper RSI level triggering a short", "Indicators")
self._level_down = self.Param("LevelDown", 30.0) \
.SetDisplay("RSI Oversold", "Lower RSI level triggering a long", "Indicators")
self._take_profit_percent = self.Param("TakeProfitPercent", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_rsi = 0.0
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def LevelUp(self):
return self._level_up.Value
@LevelUp.setter
def LevelUp(self, value):
self._level_up.Value = value
@property
def LevelDown(self):
return self._level_down.Value
@LevelDown.setter
def LevelDown(self, value):
self._level_down.Value = value
@property
def TakeProfitPercent(self):
return self._take_profit_percent.Value
@TakeProfitPercent.setter
def TakeProfitPercent(self, value):
self._take_profit_percent.Value = value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stop_loss_percent.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(rsi_expert_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(rsi, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(float(self.TakeProfitPercent), UnitTypes.Percent),
stopLoss=Unit(float(self.StopLossPercent), UnitTypes.Percent),
useMarketOrders=True
)
def ProcessCandle(self, candle, rsi_val):
if candle.State != CandleStates.Finished:
return
rsi_f = float(rsi_val)
if self._prev_rsi == 0.0:
self._prev_rsi = rsi_f
return
level_up = float(self.LevelUp)
level_down = float(self.LevelDown)
cross_up = self._prev_rsi < level_down and rsi_f > level_down
cross_down = self._prev_rsi > level_up and rsi_f < level_up
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_rsi = rsi_f
def OnReseted(self):
super(rsi_expert_strategy, self).OnReseted()
self._prev_rsi = 0.0
def CreateClone(self):
return rsi_expert_strategy()