Auf GitHub ansehen

iVIDyEine einfache Strategie

Überblick

Diese Strategie ist eine High-Level-StockSharp-Portierung des MetaTrader-Experten „iVIDyA Simple“. Es handelt ein einzelnes Symbol, indem es einen dynamischen variablen Indexdurchschnitt (VIDYA) verfolgt, der sich über den Chande Momentum Oscillator (CMO) an die Marktdynamik anpasst. Immer wenn die zuletzt beendete Kerze die verschobene VIDYA-Linie kreuzt, eröffnet die Strategie eine Marktposition in Richtung des Ausbruchs und fügt optional schützende Stop-Loss- und Take-Profit-Orders hinzu.

Handelslogik

  1. Kerzendaten werden aus dem konfigurierten Zeitrahmen (CandleType) gelesen.
  2. Der CMO mit der Periode CmoPeriod ist an die Kerzenserie gebunden. Sein absoluter Wert skaliert dynamisch den Glättungsfaktor von VIDYA. Der Basisfaktor entspricht 2 / (EmaPeriod + 1), genau wie bei der ursprünglichen MQL-Implementierung.
  3. Ein rollierender VIDYA-Wert wird beibehalten. Bei jeder fertigen Kerze der Algorithmus:
    • Wählt den angewendeten Preis (AppliedPrice) aus der Kerze aus (Schlusskurs, Eröffnungskurs, Mediankurs usw.).
    • Aktualisiert VIDYA mit dem adaptiven Glättungskoeffizienten.
    • Speichert historische Werte, um die Option MA shift von MetaTrader zu emulieren.
  4. Die Kerze wird mit dem verschobenen VIDYA-Wert (MaShift Balken zurück) verglichen:
    • Wenn die Kerze unterhalb von VIDYA öffnet und darüber schließt, wird ein Kaufsignal generiert.
    • Wenn die Kerze über VIDYA öffnet und darunter schließt, wird ein Verkaufssignal generiert.
  5. Vor der Eröffnung einer neuen Position glättet die Strategie jegliches gegenteilige Risiko, indem sie das gesamte für die Umkehrung erforderliche Volumen handelt.
  6. Nach jeder Eingabe werden SetStopLoss und SetTakeProfit aufgerufen, wenn die jeweiligen Abstände positiv sind.

Dies spiegelt den ursprünglichen Expertenberater wider, der Aufträge ausschließlich für neue Balken auslöste, einen VIDYA verwendete, der aus CMO und EMA-Zeiträumen berechnet wurde, und optionale Stopps in Punkten anfügte.

Parameter

Name Standard Beschreibung
Volume 1 Basishandelsvolumen, das für Aufträge verwendet wird. Bei der Umkehrung von Positionen wird das bestehende Exposure automatisch saldiert.
StopLossPoints 150 Stop-Loss-Distanz in Preisschritten. Zum Deaktivieren auf 0 setzen.
TakeProfitPoints 460 Take-Profit-Distanz in Preisschritten. Zum Deaktivieren auf 0 setzen.
CmoPeriod 15 Länge des Chande-Momentum-Oszillators, der das adaptive VIDYA-Gewicht bestimmt.
EmaPeriod 12 EMA Länge, die den Basisglättungskoeffizienten in der VIDYA-Formel definiert.
MaShift 1 Anzahl der abgeschlossenen Kerzen, die verwendet werden, um die VIDYA-Linie nach vorne zu verschieben, entsprechend der Eingabe ma_shift des Indikators MetaTrader.
AppliedPrice Close An die VIDYA-Berechnung übergebene Preisquelle (Close, Open, High, Low, Median, Typical, Weighted).
CandleType TimeSpan.FromMinutes(5) Kerzentyp und Zeitrahmen, die für alle Berechnungen und Signale verwendet werden.

Zusätzliche Hinweise

  • Schutzanordnungen werden über den integrierten High-Level-API (SetStopLoss/SetTakeProfit) verwaltet, während der ursprüngliche MQL-Code manuelle Prüfungen anhand der Einfrierstufen durchführte.
  • Die Strategie abonniert nur fertige Kerzen und repliziert die Ausführungsbeschränkung „neuer Balken“ von MetaTrader.
  • Der VIDYA-Verlauf wird automatisch gekürzt, sodass der Speicherbedarf auch dann gering bleibt, wenn MaShift groß ist.
  • Alle Kommentare im Code sind auf Englisch verfasst, um den Projektanforderungen zu entsprechen.
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "iVIDyA Simple" MetaTrader expert.
/// Computes a Variable Index Dynamic Average using CMO and EMA smoothing.
/// Trades when price crosses above/below the VIDYA line.
/// </summary>
public class IvidyaSimpleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cmoPeriod;
	private readonly StrategyParam<int> _emaPeriod;

	private ChandeMomentumOscillator _cmo;
	private decimal? _prevVidya;
	private decimal? _prevClose;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int CmoPeriod
	{
		get => _cmoPeriod.Value;
		set => _cmoPeriod.Value = value;
	}

	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	public IvidyaSimpleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle series", "General");

		_cmoPeriod = Param(nameof(CmoPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("CMO Period", "Chande Momentum Oscillator length", "Indicator");

		_emaPeriod = Param(nameof(EmaPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "Base EMA length used by VIDYA", "Indicator");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_cmo = new ChandeMomentumOscillator { Length = CmoPeriod };
		_prevVidya = null;
		_prevClose = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_cmo, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal cmoValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_cmo.IsFormed)
			return;

		var close = candle.ClosePrice;

		// VIDYA = alpha * |CMO/100| * price + (1 - alpha * |CMO/100|) * prevVidya
		var alpha = 2m / (EmaPeriod + 1m);
		var momentumFactor = Math.Abs(cmoValue) / 100m;
		var sf = alpha * momentumFactor;

		var prevVidya = _prevVidya ?? close;
		var currentVidya = sf * close + (1m - sf) * prevVidya;

		if (_prevVidya is null || _prevClose is null)
		{
			_prevVidya = currentVidya;
			_prevClose = close;
			return;
		}

		// Price crosses above VIDYA -> buy
		var crossUp = _prevClose <= _prevVidya && close > currentVidya;
		// Price crosses below VIDYA -> sell
		var crossDown = _prevClose >= _prevVidya && close < currentVidya;
		var minDistance = close * 0.001m;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		if (crossUp && Math.Abs(close - currentVidya) >= minDistance)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossDown && Math.Abs(close - currentVidya) >= minDistance)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevVidya = currentVidya;
		_prevClose = close;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_cmo = null;
		_prevVidya = null;
		_prevClose = null;

		base.OnReseted();
	}
}