Auf GitHub ansehen

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 = 12
    • SmoothingLength = 13
    • CandleType = 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;
	}
}