Стратегия cm RSI
Обзор
Стратегия является портом эксперта MetaTrader 4 "cm_RSI". Она использует индикатор относительной силы (RSI) для поиска разворотов импульса.
Алгоритм вычисляет значения RSI по ценам открытия свечей. Длинная позиция открывается, когда RSI поднимается выше настраиваемого уровня покупки после нахождения ниже него. Короткая позиция открывается, когда RSI опускается ниже уровня продажи после нахождения выше. Каждая сделка защищена фиксированными take profit и stop loss в пунктах цены.
Логика стратегии
- Рассчитать RSI с указанным периодом по ценам открытия свечей.
- Если предыдущее значение RSI было ниже уровня покупки, а текущее пересекло его снизу вверх, открыть длинную позицию по рынку.
- Если предыдущее значение RSI было выше уровня продажи, а текущее пересекло его сверху вниз, открыть короткую позицию по рынку.
- Для каждой сделки используется заданный объем, а также стоп‑лосс и тейк‑профит.
Параметры
| Имя |
Описание |
RsiPeriod |
Период расчета RSI. |
BuyLevel |
Уровень RSI для открытия длинных позиций. |
SellLevel |
Уровень RSI для открытия коротких позиций. |
TakeProfit |
Тейк‑профит в абсолютных пунктах. |
StopLoss |
Стоп‑лосс в абсолютных пунктах. |
OrderVolume |
Объем каждой сделки. |
CandleType |
Тип свечей для расчетов. |
Примечания
- Обрабатываются только завершенные свечи.
- В любой момент времени открыта только одна позиция.
- Метод
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>
/// Strategy based on RSI cross signals from the original cm_RSI expert.
/// </summary>
public class CmRsiStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _buyLevel;
private readonly StrategyParam<decimal> _sellLevel;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
private bool _isFirst = true;
/// <summary>
/// RSI calculation period.
/// </summary>
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
/// <summary>
/// RSI level to trigger long entries.
/// </summary>
public decimal BuyLevel { get => _buyLevel.Value; set => _buyLevel.Value = value; }
/// <summary>
/// RSI level to trigger short entries.
/// </summary>
public decimal SellLevel { get => _sellLevel.Value; set => _sellLevel.Value = value; }
/// <summary>
/// Take profit in price points.
/// </summary>
public int TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Stop loss in price points.
/// </summary>
public int StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
/// <summary>
/// Volume applied to each trade.
/// </summary>
public decimal OrderVolume { get => _orderVolume.Value; set => _orderVolume.Value = value; }
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize <see cref="CmRsiStrategy"/>.
/// </summary>
public CmRsiStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
.SetOptimize(7, 21, 1);
_buyLevel = Param(nameof(BuyLevel), 30m)
.SetDisplay("Buy Level", "RSI level to enter long", "Indicators")
.SetOptimize(10m, 40m, 5m);
_sellLevel = Param(nameof(SellLevel), 70m)
.SetDisplay("Sell Level", "RSI level to enter short", "Indicators")
.SetOptimize(60m, 90m, 5m);
_takeProfit = Param(nameof(TakeProfit), 200)
.SetDisplay("Take Profit", "Take profit in price points", "Risk Management")
.SetOptimize(100, 400, 50);
_stopLoss = Param(nameof(StopLoss), 100)
.SetDisplay("Stop Loss", "Stop loss in price points", "Risk Management")
.SetOptimize(50, 200, 50);
_orderVolume = Param(nameof(OrderVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Volume of each trade", "General")
.SetOptimize(0.1m, 1m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0;
_isFirst = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_isFirst = true;
_prevRsi = 0;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
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 (!IsFormedAndOnlineAndAllowTrading())
{
_prevRsi = rsiValue;
_isFirst = false;
return;
}
if (_isFirst)
{
_prevRsi = rsiValue;
_isFirst = false;
return;
}
// Open long when RSI crosses above buy level
if (_prevRsi < BuyLevel && rsiValue > BuyLevel && Position <= 0)
BuyMarket();
// Open short when RSI crosses below sell level
if (_prevRsi > SellLevel && rsiValue < SellLevel && Position >= 0)
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class cm_rsi_strategy(Strategy):
def __init__(self):
super(cm_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._buy_level = self.Param("BuyLevel", 30.0) \
.SetDisplay("Buy Level", "RSI level to enter long", "Indicators")
self._sell_level = self.Param("SellLevel", 70.0) \
.SetDisplay("Sell Level", "RSI level to enter short", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_rsi = 0.0
self._is_first = True
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def buy_level(self):
return self._buy_level.Value
@property
def sell_level(self):
return self._sell_level.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cm_rsi_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._is_first = True
def OnStarted2(self, time):
super(cm_rsi_strategy, self).OnStarted2(time)
self._is_first = True
self._prev_rsi = 0.0
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_value = float(rsi_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rsi_value
self._is_first = False
return
if self._is_first:
self._prev_rsi = rsi_value
self._is_first = False
return
buy_level = float(self.buy_level)
sell_level = float(self.sell_level)
if self._prev_rsi < buy_level and rsi_value > buy_level and self.Position <= 0:
self.BuyMarket()
if self._prev_rsi > sell_level and rsi_value < sell_level and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi_value
def CreateClone(self):
return cm_rsi_strategy()