Стратегия JBrainUltraRSI
Пример стратегии объединяет индикатор относительной силы (RSI) и стохастический осциллятор для генерации торговых сигналов. Идея взята из оригинального советника MetaTrader, в котором использовались индикаторы JBrainTrendSig1 и UltraRSI. В данной адаптации стохастик выполняет роль фильтра тренда, а RSI подает сигналы входа.
Как это работает
- Индикаторы
- RSI — измеряет импульс, сравнивая недавние прибыли и убытки. Пересечение уровня 50 снизу вверх указывает на бычий импульс, сверху вниз — на медвежий.
- Стохастический осциллятор — оценивает положение цены закрытия относительно диапазона. Пересечения линий %K и %D подтверждают направление тренда.
- Режимы
- JBrainSig1Filter — сигналы формирует RSI, стохастик подтверждает направление.
- UltraRsiFilter — сигналы формирует стохастик, RSI выступает фильтром.
- Composition — сделки открываются только при совпадении сигналов обоих индикаторов.
- Правила торговли
- Длинная позиция открывается при появлении сигнала на покупку и отсутствии короткой позиции.
- Короткая позиция открывается при сигнале на продажу и отсутствии длинной позиции.
- Обратные сигналы закрывают текущие позиции при соответствующем разрешении.
Параметры
| Параметр | Описание |
|---|---|
RsiPeriod |
Период расчёта RSI. |
StochLength |
Период %K стохастика. |
SignalLength |
Период %D стохастика. |
Mode |
Режим комбинирования индикаторов. |
AllowLongEntry / AllowShortEntry |
Разрешения на открытие длинных/коротких позиций. |
AllowLongExit / AllowShortExit |
Разрешения на закрытие длинных/коротких позиций. |
CandleType |
Таймфрейм свечей для работы. |
Примечания
- Стратегия использует высокоуровневый API StockSharp с
Bind/BindExдля обработки индикаторов. - Защитные уровни задаются методом
StartProtection(). - При наличии области графика стратегия отображает свечи, индикаторы и собственные сделки.
using System;
using System.Linq;
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 combining RSI and Stochastic oscillator signals.
/// </summary>
public class JBrainUltraRsiStrategy : Strategy
{
/// <summary>
/// Combination modes for indicators.
/// </summary>
public enum AlgorithmModes
{
JBrainSig1Filter,
UltraRsiFilter,
Composition
}
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<AlgorithmModes> _mode;
private readonly StrategyParam<bool> _allowLongEntry;
private readonly StrategyParam<bool> _allowShortEntry;
private readonly StrategyParam<bool> _allowLongExit;
private readonly StrategyParam<bool> _allowShortExit;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private StochasticOscillator _stochastic;
private decimal? _prevRsi;
private decimal? _prevK;
private decimal? _prevD;
/// <summary>
/// RSI calculation period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Stochastic %K period.
/// </summary>
public int StochLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// Stochastic %D period.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// Mode combining indicators.
/// </summary>
public AlgorithmModes Mode
{
get => _mode.Value;
set => _mode.Value = value;
}
/// <summary>
/// Permission to open long positions.
/// </summary>
public bool AllowLongEntry
{
get => _allowLongEntry.Value;
set => _allowLongEntry.Value = value;
}
/// <summary>
/// Permission to open short positions.
/// </summary>
public bool AllowShortEntry
{
get => _allowShortEntry.Value;
set => _allowShortEntry.Value = value;
}
/// <summary>
/// Permission to close long positions.
/// </summary>
public bool AllowLongExit
{
get => _allowLongExit.Value;
set => _allowLongExit.Value = value;
}
/// <summary>
/// Permission to close short positions.
/// </summary>
public bool AllowShortExit
{
get => _allowShortExit.Value;
set => _allowShortExit.Value = value;
}
/// <summary>
/// Candle type to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="JBrainUltraRsiStrategy"/>.
/// </summary>
public JBrainUltraRsiStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
;
_stochLength = Param(nameof(StochLength), 9)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Period for %K line", "Indicators")
;
_signalLength = Param(nameof(SignalLength), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Period for %D line", "Indicators")
;
_mode = Param(nameof(Mode), AlgorithmModes.Composition)
.SetDisplay("Mode", "Algorithm to enter the market", "General");
_allowLongEntry = Param(nameof(AllowLongEntry), true)
.SetDisplay("Allow Long Entry", "Permission to open long positions", "Trading");
_allowShortEntry = Param(nameof(AllowShortEntry), true)
.SetDisplay("Allow Short Entry", "Permission to open short positions", "Trading");
_allowLongExit = Param(nameof(AllowLongExit), true)
.SetDisplay("Allow Long Exit", "Permission to close long positions", "Trading");
_allowShortExit = Param(nameof(AllowShortExit), true)
.SetDisplay("Allow Short Exit", "Permission to close short positions", "Trading");
_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();
_rsi = default;
_stochastic = default;
_prevRsi = default;
_prevK = default;
_prevD = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_stochastic = new StochasticOscillator
{
K = { Length = StochLength },
D = { Length = SignalLength },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawIndicator(area, _stochastic);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
// process RSI manually
var rsiResult = _rsi.Process(candle.ClosePrice, candle.OpenTime, true);
if (!rsiResult.IsFormed)
return;
var rsi = rsiResult.ToDecimal();
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal k || stoch.D is not decimal d)
{
_prevRsi = rsi;
return;
}
var rsiUp = _prevRsi is decimal pr && pr <= 50m && rsi > 50m;
var rsiDown = _prevRsi is decimal pr2 && pr2 >= 50m && rsi < 50m;
var stochUp = _prevK is decimal pk && _prevD is decimal pd && pk <= pd && k > d;
var stochDown = _prevK is decimal pk2 && _prevD is decimal pd2 && pk2 >= pd2 && k < d;
var buySignal = false;
var sellSignal = false;
switch (Mode)
{
case AlgorithmModes.JBrainSig1Filter:
buySignal = rsiUp && k > d;
sellSignal = rsiDown && k < d;
break;
case AlgorithmModes.UltraRsiFilter:
buySignal = stochUp && rsi > 50m;
sellSignal = stochDown && rsi < 50m;
break;
case AlgorithmModes.Composition:
buySignal = rsiUp && stochUp;
sellSignal = rsiDown && stochDown;
break;
}
if (buySignal)
{
if (Position < 0 && AllowShortExit)
BuyMarket();
if (AllowLongEntry && Position <= 0)
BuyMarket();
}
else if (sellSignal)
{
if (Position > 0 && AllowLongExit)
SellMarket();
if (AllowShortEntry && Position >= 0)
SellMarket();
}
_prevRsi = rsi;
_prevK = k;
_prevD = d;
}
}
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, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class j_brain_ultra_rsi_strategy(Strategy):
# Algorithm modes
JBRAIN_SIG1_FILTER = 0
ULTRA_RSI_FILTER = 1
COMPOSITION = 2
def __init__(self):
super(j_brain_ultra_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 13) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._stoch_length = self.Param("StochLength", 9) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %K", "Period for %K line", "Indicators")
self._signal_length = self.Param("SignalLength", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %D", "Period for %D line", "Indicators")
self._mode = self.Param("Mode", 2) \
.SetDisplay("Mode", "Algorithm to enter the market", "General")
self._allow_long_entry = self.Param("AllowLongEntry", True) \
.SetDisplay("Allow Long Entry", "Permission to open long positions", "Trading")
self._allow_short_entry = self.Param("AllowShortEntry", True) \
.SetDisplay("Allow Short Entry", "Permission to open short positions", "Trading")
self._allow_long_exit = self.Param("AllowLongExit", True) \
.SetDisplay("Allow Long Exit", "Permission to close long positions", "Trading")
self._allow_short_exit = self.Param("AllowShortExit", True) \
.SetDisplay("Allow Short Exit", "Permission to close short positions", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._rsi = None
self._prev_rsi = None
self._prev_k = None
self._prev_d = None
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def stoch_length(self):
return self._stoch_length.Value
@property
def signal_length(self):
return self._signal_length.Value
@property
def mode(self):
return self._mode.Value
@property
def allow_long_entry(self):
return self._allow_long_entry.Value
@property
def allow_short_entry(self):
return self._allow_short_entry.Value
@property
def allow_long_exit(self):
return self._allow_long_exit.Value
@property
def allow_short_exit(self):
return self._allow_short_exit.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(j_brain_ultra_rsi_strategy, self).OnReseted()
self._rsi = None
self._prev_rsi = None
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(j_brain_ultra_rsi_strategy, self).OnStarted2(time)
self.StartProtection(None, None)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
stochastic = StochasticOscillator()
stochastic.K.Length = self.stoch_length
stochastic.D.Length = self.signal_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stochastic, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._rsi)
self.DrawIndicator(area, stochastic)
self.DrawOwnTrades(area)
def process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
# process RSI manually
rsi_result = process_float(self._rsi, candle.ClosePrice, candle.OpenTime, True)
if not rsi_result.IsFormed:
return
rsi = float(rsi_result)
k_val = stoch_value.K
d_val = stoch_value.D
if k_val is None or d_val is None:
self._prev_rsi = rsi
return
k = float(k_val)
d = float(d_val)
rsi_up = self._prev_rsi is not None and self._prev_rsi <= 50.0 and rsi > 50.0
rsi_down = self._prev_rsi is not None and self._prev_rsi >= 50.0 and rsi < 50.0
stoch_up = self._prev_k is not None and self._prev_d is not None and self._prev_k <= self._prev_d and k > d
stoch_down = self._prev_k is not None and self._prev_d is not None and self._prev_k >= self._prev_d and k < d
buy_signal = False
sell_signal = False
m = self.mode
if m == self.JBRAIN_SIG1_FILTER:
buy_signal = rsi_up and k > d
sell_signal = rsi_down and k < d
elif m == self.ULTRA_RSI_FILTER:
buy_signal = stoch_up and rsi > 50.0
sell_signal = stoch_down and rsi < 50.0
elif m == self.COMPOSITION:
buy_signal = rsi_up and stoch_up
sell_signal = rsi_down and stoch_down
if buy_signal:
if self.Position < 0 and self.allow_short_exit:
self.BuyMarket()
if self.allow_long_entry and self.Position <= 0:
self.BuyMarket()
elif sell_signal:
if self.Position > 0 and self.allow_long_exit:
self.SellMarket()
if self.allow_short_entry and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi
self._prev_k = k
self._prev_d = d
def CreateClone(self):
return j_brain_ultra_rsi_strategy()