RVI Diff Umkehr-Strategie
Die Strategie handelt auf Basis der geglätteten Differenz zwischen dem Relative Vigor Index (RVI) und seiner Signallinie. Sie erkennt Punkte, an denen diese Differenz aufhört zu fallen und beginnt zu steigen, um Long-Positionen einzugehen, und umgekehrt für Short-Positionen.
Details
- Einstiegskriterien: Steigungsumkehr der geglätteten RVI-Differenz
- Long/Short: Beide
- Ausstiegskriterien: Entgegengesetztes Signal
- Stops: Nein
- Standardwerte:
RviLength= 12SmoothingLength= 13CandleType= 6-Stunden-Kerzen
- Filter:
- Kategorie: Oszillator
- Richtung: Beide
- Indikatoren: RVI, SMA, EMA
- Stops: Nein
- Komplexität: Grundlegend
- Zeitrahmen: 6H
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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 the smoothed difference between RVI average and signal.
/// Buy when smoothed diff turns up, sell when it turns down.
/// </summary>
public class RviDiffReversalStrategy : Strategy
{
private readonly StrategyParam<int> _rviLength;
private readonly StrategyParam<int> _smoothingLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevDiff;
private decimal? _prevPrevDiff;
public int RviLength { get => _rviLength.Value; set => _rviLength.Value = value; }
public int SmoothingLength { get => _smoothingLength.Value; set => _smoothingLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RviDiffReversalStrategy()
{
_rviLength = Param(nameof(RviLength), 12)
.SetGreaterThanZero()
.SetDisplay("RVI Length", "Length of RVI", "General");
_smoothingLength = Param(nameof(SmoothingLength), 13)
.SetGreaterThanZero()
.SetDisplay("Smoothing Length", "Length of EMA smoothing", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevDiff = null;
_prevPrevDiff = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevDiff = null;
_prevPrevDiff = null;
var rvi = new RelativeVigorIndex();
rvi.Average.Length = RviLength;
rvi.Signal.Length = SmoothingLength;
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(rvi, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rvi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rviVal)
{
if (candle.State != CandleStates.Finished)
return;
if (rviVal is not IRelativeVigorIndexValue rviTyped)
return;
if (rviTyped.Average is not decimal avg || rviTyped.Signal is not decimal sig)
return;
var current = avg - sig;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevPrevDiff = _prevDiff;
_prevDiff = current;
return;
}
if (_prevDiff.HasValue && _prevPrevDiff.HasValue)
{
var wasFalling = _prevPrevDiff > _prevDiff;
var wasRising = _prevPrevDiff < _prevDiff;
if (wasFalling && current > _prevDiff && Position <= 0)
BuyMarket();
else if (wasRising && current < _prevDiff && Position >= 0)
SellMarket();
}
_prevPrevDiff = _prevDiff;
_prevDiff = current;
}
}
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 RelativeVigorIndex
from StockSharp.Algo.Strategies import Strategy
class rvi_diff_reversal_strategy(Strategy):
def __init__(self):
super(rvi_diff_reversal_strategy, self).__init__()
self._rvi_length = self.Param("RviLength", 12) \
.SetDisplay("RVI Length", "Length of RVI", "General")
self._smoothing_length = self.Param("SmoothingLength", 13) \
.SetDisplay("Smoothing Length", "Length of EMA smoothing", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_diff = None
self._prev_prev_diff = None
@property
def rvi_length(self):
return self._rvi_length.Value
@property
def smoothing_length(self):
return self._smoothing_length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(rvi_diff_reversal_strategy, self).OnReseted()
self._prev_diff = None
self._prev_prev_diff = None
def OnStarted2(self, time):
super(rvi_diff_reversal_strategy, self).OnStarted2(time)
self._prev_diff = None
self._prev_prev_diff = None
rvi = RelativeVigorIndex()
rvi.Average.Length = int(self.rvi_length)
rvi.Signal.Length = int(self.smoothing_length)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rvi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rvi)
self.DrawOwnTrades(area)
def process_candle(self, candle, rvi_val):
if candle.State != CandleStates.Finished:
return
if not rvi_val.IsFormed:
return
avg = rvi_val.Average
sig = rvi_val.Signal
if avg is None or sig is None:
return
avg = float(avg)
sig = float(sig)
current = avg - sig
if self._prev_diff is not None and self._prev_prev_diff is not None:
was_falling = self._prev_prev_diff > self._prev_diff
was_rising = self._prev_prev_diff < self._prev_diff
if was_falling and current > self._prev_diff and self.Position <= 0:
self.BuyMarket()
elif was_rising and current < self._prev_diff and self.Position >= 0:
self.SellMarket()
self._prev_prev_diff = self._prev_diff
self._prev_diff = current
def CreateClone(self):
return rvi_diff_reversal_strategy()