Auf GitHub ansehen

Exp i-KlPrice Vol Direkt-Strategie

Übersicht

Die Exp i-KlPrice Vol Direkt-Strategie ist eine StockSharp-Adaptation des MetaTrader 5 Expert Advisors Exp_i-KlPrice_Vol_Direct. Das Originalsystem multipliziert einen benutzerdefinierten KlPrice-Oszillator mit dem Volumen, glättet ihn mit mehreren gleitenden Durchschnittsstufen und reagiert auf Änderungen in der Steigung der resultierenden Linie. Der Port behält die mehrstufige Verarbeitungskette bei, exponiert die gleichen konfigurierbaren Parameter und führt Trades durch StockSharp's High-Level-API auf abgeschlossenen Kerzen aus.

Aus der MQL5-Version bewahrte Schlüsselideen:

  • Zweistufige Glättung von Preis und Spanne – Preisdaten werden durch einen konfigurierbaren gleitenden Durchschnitt gefiltert, die Hoch-Tief-Spanne wird separat geglättet.
  • Volumengewichtung – die Oszillatorausgabe wird vor einem abschließenden Jurik-Filter mit dem ausgewählten Volumenstrom multipliziert.
  • Direktionale Farbkarte – die Strategie überwacht das Vorzeichen der geglätteten Oszillatorsteigung.
  • SignalverzögerungSignalBar ermöglicht dem Benutzer, zusätzliche geschlossene Kerzen zu fordern.

Verarbeitungs-Pipeline

  1. Angewendete Preisauswahl – Wählen aus den gleichen zwölf angewendeten Preisformeln wie der MQL-Indikator.
  2. Primäre GlättungPriceMethod über PriceLength Bars mit optionalem PricePhase anwenden.
  3. Spannenglättung – dasselbe Verfahren für die Kerzenspanne (High - Low) mit RangeMethod, RangeLength und RangePhase wiederholen.
  4. Oszillatorkonstruktion(Price - (PriceMA - RangeMA)) / (2 * RangeMA) * 100 - 50 berechnen, identisch zur MQL- Formel, und mit dem ausgewählten Volumenstrom (VolumeSource) multiplizieren.
  5. Abschließender Jurik-Filter – der volumengewichtete Oszillator und der Rohvolumenstrom werden beide durch Jurik- gleitende Durchschnitte mit Periode ResultLength geleitet.
  6. Farberkennung – den neuesten geglätteten Oszillatorwert mit dem vorherigen vergleichen. Steigende Werte färben die Bar bullisch (0), fallende Werte bearisch (1), gleiche Werte erben die vorherige Farbe.

Handelslogik

Long-Seite

  • Eintritt: Wenn die Farbe bei der Signalbar (SignalBar) bullisch (0) ist und die unmittelbar ältere Farbe bearisch (1) ist, Long-Position öffnen wenn AllowLongEntries = true und die aktuelle Nettoposition nicht positiv ist.
  • Ausstieg: Wenn die Signalbar-Farbe bullisch ist und AllowShortExits = true, offene Short-Positionen schließen.

Short-Seite

  • Eintritt: Wenn die Signalbar-Farbe nach bullisch (0) bearisch (1) wird, Short-Position öffnen wenn AllowShortEntries = true und die aktuelle Nettoposition nicht negativ ist.
  • Ausstieg: Wenn die Signalbar-Farbe bearisch ist und AllowLongExits = true, bestehende Long-Exposition schließen.

Parameter-Referenz

Parameter Beschreibung Standard
CandleType Zeitrahmen der analysierten Kerzen. H4
VolumeSource Volumenstrom für die Gewichtung (Tick oder Real). Tick
PriceMethod / PriceLength / PricePhase Primärer Glättungsalgorithmus, Periode und Jurik-Phase für den angewendeten Preis. Sma, 100, 15
RangeMethod / RangeLength / RangePhase Glättungsalgorithmus, Periode und Phase für die Kerzenspanne. Jjma, 20, 100
ResultLength Jurik-Periode für den volumengewichteten Oszillator und den Volumenstrom. 20
PriceMode Angewendete Preisformel (Close, Open, Median, Demark, TrendFollow0/1, etc.). Close
HighLevel2, HighLevel1, LowLevel1, LowLevel2 Niveaumultiplikatoren für visuelle Diagnose; ändern keine Signale. 0, 0, 0, 0
SignalBar Anzahl vollständig geschlossener Kerzen, die vor der Bewertung des Farbwechsels übersprungen werden. 1
AllowLongEntries / AllowShortEntries Berechtigungsflags für das Öffnen von Long/Short-Trades. true
AllowLongExits / AllowShortExits Berechtigungsflags für das Schließen bestehender Positionen bei entgegengesetzter Farbe. true
StopLossPoints / TakeProfitPoints Schutzoffsets in Preispunkten, die an StartProtection übergeben werden. 1000, 2000

Risikomanagement

  • Stop-Loss- und Take-Profit-Niveaus werden in UnitTypes.Point-Offsets übersetzt und von StartProtection verwaltet. Einen Wert auf 0 setzen, um den jeweiligen Schutz zu deaktivieren.
  • Die Positionsgröße wird vollständig durch Strategy.Volume gesteuert.
  • Farben werden nur ausgewertet, wenn die Strategie geformt, online und der Handel erlaubt ist.

Einschränkungen und Unterschiede vs. MQL5

  • Exotischere Glättungsannäherungen können leicht von der MT5-Ausgabe abweichen.
  • StockSharp-Kerzen exponieren nur das Gesamtvolumen.
  • Geldverwaltungsmodi des Original-EA sind nicht portiert.
  • Orders werden unmittelbar nach dem Schließen der Signalkerze gesendet.
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>
/// Exp i-KlPrice Vol Direct strategy using EMA crossover with volume-weighted confirmation.
/// Buys when fast EMA crosses above slow EMA. Sells on reverse crossover.
/// </summary>
public class ExpIKlPriceVolDirectStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="ExpIKlPriceVolDirectStrategy"/> class.
	/// </summary>
	public ExpIKlPriceVolDirectStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fast.IsFormed || !_slow.IsFormed)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// EMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 60;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}