Delta-RSIオシレーター戦略
この戦略はDelta-RSIオシレーターを使用します。これはEMAで平滑化されたRSIの変化量として定義されます。デルタがゼロを越えたとき、シグナルラインを越えたとき、または方向が変わったときにシグナルが発生します。エグジットは選択した条件を反映します。
詳細
- エントリー条件: Delta-RSIの
BuyCondition(ゼロ交差、シグナルライン交差、または方向転換)に基づく。 - ロング/ショート: 両方、
UseLongおよびUseShortで制御。 - エグジット条件: Delta-RSIの
ExitConditionに基づく。 - ストップ: なし。
- デフォルト値:
RsiLength= 21SignalLength= 9CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: Momentum
- 方向: 両方
- インジケーター: RSI, EMA
- ストップ: いいえ
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Delta-RSI oscillator strategy.
/// Delta-RSI is the bar-to-bar change of RSI and its signal line is an EMA of it.
/// Entries follow BuyCondition: Delta-RSI crossing zero, crossing its signal line or changing direction;
/// a bullish event opens a long, a bearish event a short (each side enabled by UseLong and UseShort), reversing an opposite position.
/// Otherwise positions are closed by the opposite event of ExitCondition.
/// </summary>
public class DeltaRsiOscillatorStrategy : Strategy
{
/// <summary>
/// Delta-RSI events that can trigger entries or exits.
/// </summary>
public enum DeltaRsiConditions
{
/// <summary>
/// Delta-RSI crosses zero.
/// </summary>
ZeroCrossing,
/// <summary>
/// Delta-RSI crosses its signal line.
/// </summary>
SignalLineCrossing,
/// <summary>
/// Delta-RSI changes direction.
/// </summary>
DirectionChange,
}
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<DeltaRsiConditions> _buyCondition;
private readonly StrategyParam<DeltaRsiConditions> _exitCondition;
private readonly StrategyParam<bool> _useLong;
private readonly StrategyParam<bool> _useShort;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _signal;
private decimal? _prevRsi;
private decimal? _prevDelta;
private decimal? _prevPrevDelta;
private decimal? _prevSignal;
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
/// <summary>
/// EMA length of the signal line.
/// </summary>
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
/// <summary>
/// Event that opens positions.
/// </summary>
public DeltaRsiConditions BuyCondition { get => _buyCondition.Value; set => _buyCondition.Value = value; }
/// <summary>
/// Event that closes positions.
/// </summary>
public DeltaRsiConditions ExitCondition { get => _exitCondition.Value; set => _exitCondition.Value = value; }
/// <summary>
/// Allow long trades.
/// </summary>
public bool UseLong { get => _useLong.Value; set => _useLong.Value = value; }
/// <summary>
/// Allow short trades.
/// </summary>
public bool UseShort { get => _useShort.Value; set => _useShort.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public DeltaRsiOscillatorStrategy()
{
_rsiLength = Param(nameof(RsiLength), 21)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Indicators");
_signalLength = Param(nameof(SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Length", "EMA length of the signal line", "Indicators");
_buyCondition = Param(nameof(BuyCondition), DeltaRsiConditions.ZeroCrossing)
.SetDisplay("Entry Condition", "Event that opens positions", "Signals");
_exitCondition = Param(nameof(ExitCondition), DeltaRsiConditions.ZeroCrossing)
.SetDisplay("Exit Condition", "Event that closes positions", "Signals");
_useLong = Param(nameof(UseLong), true)
.SetDisplay("Use Long", "Allow long trades", "Signals");
_useShort = Param(nameof(UseShort), true)
.SetDisplay("Use Short", "Allow short trades", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
ResetState();
}
private void ResetState()
{
_prevRsi = null;
_prevDelta = null;
_prevPrevDelta = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsi = new RelativeStrengthIndex { Length = RsiLength };
_signal = new ExponentialMovingAverage { Length = SignalLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!rsiValue.IsFormed)
return;
var rsi = rsiValue.GetValue<decimal>();
var prevRsi = _prevRsi;
_prevRsi = rsi;
if (prevRsi is not decimal pr)
return;
var delta = rsi - pr;
var signalValue = _signal.Process(delta, candle.ServerTime, true);
var signal = signalValue.GetValue<decimal>();
var prevDelta = _prevDelta;
var prevPrevDelta = _prevPrevDelta;
var prevSignal = _prevSignal;
_prevPrevDelta = _prevDelta;
_prevDelta = delta;
_prevSignal = signalValue.IsFormed ? signal : null;
if (!signalValue.IsFormed || prevDelta is not decimal pd || prevPrevDelta is not decimal ppd || prevSignal is not decimal ps)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
bool IsBullish(DeltaRsiConditions condition) => condition switch
{
DeltaRsiConditions.ZeroCrossing => pd <= 0m && delta > 0m,
DeltaRsiConditions.SignalLineCrossing => pd <= ps && delta > signal,
_ => pd <= ppd && delta > pd,
};
bool IsBearish(DeltaRsiConditions condition) => condition switch
{
DeltaRsiConditions.ZeroCrossing => pd >= 0m && delta < 0m,
DeltaRsiConditions.SignalLineCrossing => pd >= ps && delta < signal,
_ => pd >= ppd && delta < pd,
};
if (UseLong && IsBullish(BuyCondition) && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (UseShort && IsBearish(BuyCondition) && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && IsBearish(ExitCondition))
SellMarket(Position);
else if (Position < 0 && IsBullish(ExitCondition))
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
# Delta-RSI events, matching the C# DeltaRsiConditions enum.
ZERO_CROSSING = 0
SIGNAL_LINE_CROSSING = 1
DIRECTION_CHANGE = 2
class delta_rsi_oscillator_strategy(Strategy):
"""
Delta-RSI oscillator strategy.
Delta-RSI is the bar-to-bar change of RSI and its signal line is an EMA of it.
Entries follow BuyCondition: Delta-RSI crossing zero, crossing its signal line or changing direction;
a bullish event opens a long, a bearish event a short (each side enabled by UseLong and UseShort), reversing an opposite position.
Otherwise positions are closed by the opposite event of ExitCondition.
"""
def __init__(self):
super(delta_rsi_oscillator_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 21).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Indicators")
self._signal_length = self.Param("SignalLength", 9).SetGreaterThanZero().SetDisplay("Signal Length", "EMA length of the signal line", "Indicators")
self._buy_condition = self.Param("BuyCondition", ZERO_CROSSING).SetDisplay("Entry Condition", "Event that opens positions: 0 zero crossing, 1 signal line crossing, 2 direction change", "Signals")
self._exit_condition = self.Param("ExitCondition", ZERO_CROSSING).SetDisplay("Exit Condition", "Event that closes positions: 0 zero crossing, 1 signal line crossing, 2 direction change", "Signals")
self._use_long = self.Param("UseLong", True).SetDisplay("Use Long", "Allow long trades", "Signals")
self._use_short = self.Param("UseShort", True).SetDisplay("Use Short", "Allow short trades", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._signal = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def _reset_state(self):
self._prev_rsi = None
self._prev_delta = None
self._prev_prev_delta = None
self._prev_signal = None
def OnReseted(self):
super(delta_rsi_oscillator_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(delta_rsi_oscillator_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
self._signal = ExponentialMovingAverage()
self._signal.Length = self._signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if not rsi_value.IsFormed:
return
rsi = float(rsi_value.GetValue[Decimal](None))
prev_rsi = self._prev_rsi
self._prev_rsi = rsi
if prev_rsi is None:
return
delta = rsi - prev_rsi
signal_value = process_float(self._signal, delta, candle.ServerTime, True)
signal = float(signal_value.GetValue[Decimal](None))
pd = self._prev_delta
ppd = self._prev_prev_delta
ps = self._prev_signal
self._prev_prev_delta = self._prev_delta
self._prev_delta = delta
self._prev_signal = signal if signal_value.IsFormed else None
if not signal_value.IsFormed or pd is None or ppd is None or ps is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
def is_bullish(condition):
if condition == ZERO_CROSSING:
return pd <= 0 and delta > 0
if condition == SIGNAL_LINE_CROSSING:
return pd <= ps and delta > signal
return pd <= ppd and delta > pd
def is_bearish(condition):
if condition == ZERO_CROSSING:
return pd >= 0 and delta < 0
if condition == SIGNAL_LINE_CROSSING:
return pd >= ps and delta < signal
return pd >= ppd and delta < pd
buy_condition = int(self._buy_condition.Value)
exit_condition = int(self._exit_condition.Value)
if self._use_long.Value and is_bullish(buy_condition) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self._use_short.Value and is_bearish(buy_condition) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and is_bearish(exit_condition):
self.SellMarket(self.Position)
elif self.Position < 0 and is_bullish(exit_condition):
self.BuyMarket(-self.Position)
def CreateClone(self):
return delta_rsi_oscillator_strategy()