Double RSI
Double RSI использует два индекса относительной силы: один на рабочем таймфрейме, другой на более высоком. Сделки открываются только когда оба RSI указывают в одну сторону, что согласует короткие входы с долгосрочным импульсом.
На основном таймфрейме отслеживается выход RSI из зон перекупленности или перепроданности. Если RSI старшего таймфрейма подтверждает движение, стратегия входит в позицию. Опциональный тейк‑профит фиксирует прибыль после заданного смещения.
Подробности
- Данные: свечи на двух таймфреймах.
- Условия входа:
- Лонг: RSI младшего ТФ выходит из перепроданности и RSI старшего ТФ бычий.
- Шорт: RSI младшего ТФ выходит из перекупленности и RSI старшего ТФ медвежий.
- Условия выхода: противоположный сигнал RSI или тейк‑профит при
UseTP. - Стопы: отсутствуют по умолчанию.
- Параметры по умолчанию:
CandleType= tf(5)RSILength= 14MTFTimeframe= tf(15)UseTP= False
- Фильтры:
- Категория: моментум
- Направление: лонг и шорт
- Индикаторы: RSI (мульти‑таймфрейм)
- Сложность: средняя
- Уровень риска: средний
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>
/// Double RSI Strategy.
/// Uses a short RSI and a long RSI for confirmation.
/// Buys when both RSIs are oversold and sells when both are overbought.
/// </summary>
public class DoubleRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _rsiShortLength;
private readonly StrategyParam<int> _rsiLongLength;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _cooldownBars;
private RelativeStrengthIndex _rsiShort;
private RelativeStrengthIndex _rsiLong;
private int _cooldownRemaining;
public DoubleRsiStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_rsiShortLength = Param(nameof(RSIShortLength), 7)
.SetGreaterThanZero()
.SetDisplay("Short RSI", "Short RSI period", "RSI");
_rsiLongLength = Param(nameof(RSILongLength), 21)
.SetGreaterThanZero()
.SetDisplay("Long RSI", "Long RSI period", "RSI");
_oversold = Param(nameof(Oversold), 35m)
.SetDisplay("Oversold", "RSI oversold level", "RSI");
_overbought = Param(nameof(Overbought), 65m)
.SetDisplay("Overbought", "RSI overbought level", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int RSIShortLength
{
get => _rsiShortLength.Value;
set => _rsiShortLength.Value = value;
}
public int RSILongLength
{
get => _rsiLongLength.Value;
set => _rsiLongLength.Value = value;
}
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsiShort = null;
_rsiLong = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsiShort = new RelativeStrengthIndex { Length = RSIShortLength };
_rsiLong = new RelativeStrengthIndex { Length = RSILongLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsiShort, _rsiLong, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal rsiShort, decimal rsiLong)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsiShort.IsFormed || !_rsiLong.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Buy: both RSIs oversold
if (rsiShort < Oversold && rsiLong < Oversold && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: both RSIs overbought
else if (rsiShort > Overbought && rsiLong > Overbought && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: short RSI overbought
else if (Position > 0 && rsiShort > Overbought)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: short RSI oversold
else if (Position < 0 && rsiShort < Oversold)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
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 double_rsi_strategy(Strategy):
"""Double RSI Strategy. Uses short and long RSI for entry/exit signals."""
def __init__(self):
super(double_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._rsi_short_length = self.Param("RSIShortLength", 7) \
.SetDisplay("Short RSI", "Short RSI period", "RSI")
self._rsi_long_length = self.Param("RSILongLength", 21) \
.SetDisplay("Long RSI", "Long RSI period", "RSI")
self._oversold = self.Param("Oversold", 35.0) \
.SetDisplay("Oversold", "RSI oversold level", "RSI")
self._overbought = self.Param("Overbought", 65.0) \
.SetDisplay("Overbought", "RSI overbought level", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._rsi_short = None
self._rsi_long = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(double_rsi_strategy, self).OnReseted()
self._rsi_short = None
self._rsi_long = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(double_rsi_strategy, self).OnStarted2(time)
self._rsi_short = RelativeStrengthIndex()
self._rsi_short.Length = int(self._rsi_short_length.Value)
self._rsi_long = RelativeStrengthIndex()
self._rsi_long.Length = int(self._rsi_long_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._rsi_short, self._rsi_long, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_short_val, rsi_long_val):
if candle.State != CandleStates.Finished:
return
if not self._rsi_short.IsFormed or not self._rsi_long.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
rs = float(rsi_short_val)
rl = float(rsi_long_val)
oversold = float(self._oversold.Value)
overbought = float(self._overbought.Value)
cooldown = int(self._cooldown_bars.Value)
if rs < oversold and rl < oversold and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif rs > overbought and rl > overbought and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and rs > overbought:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and rs < oversold:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return double_rsi_strategy()