Auf GitHub ansehen

Exp i-KlPrice Vol Strategie

Übersicht

Diese Strategie ist eine C#-Konvertierung des MetaTrader-Experten Exp_i-KlPrice_Vol.mq5. Sie baut den KlPrice-Oszillator neu auf, der den Abstand zwischen Preis und einem Volatilitätsband misst, multipliziert den Oszillator mit dem Kerzenvolumen und verfolgt Farbübergänge, die durch adaptive Schwellenwerte erzeugt werden. Zwei unabhängige Positions-Slots werden für jede Richtung emuliert, was das Dual-Magic-Verhalten des ursprünglichen Expert Advisors widerspiegelt.

Indikatorlogik

  • Preis wird mit dem ausgewählten AppliedPrice-Modus transformiert (Close, Open, Median, Demark usw.).
  • Der transformierte Preis wird durch die in PriceMaMethod und PriceMaLength definierte Methode des gleitenden Durchschnitts geglättet.
  • Die Kerzenspanne (High - Low) wird mit RangeMaMethod/RangeMaLength geglättet. Die Spanne fungiert als dynamische Bandbreite.
  • Der Basis-KlPrice-Oszillator wird als 100 * (Price - (MA - RangeMA)) / (2 * RangeMA) - 50 berechnet.
  • Der Oszillator wird mit der ausgewählten Volumensquelle (AppliedVolume.Tick oder AppliedVolume.Real) multipliziert.
  • Ein Jurik-Glätter der Länge SmoothingLength wird sowohl auf den Oszillator als auch auf das Rohvolumen angewendet und erzeugt zwei adaptive Reihen.
  • Adaptive Schwellenwerte werden ermittelt, indem das geglättete Volumen mit HighLevel2, HighLevel1, LowLevel1 und LowLevel2 multipliziert wird.
  • Die aktuelle Oszillatorfarbe wird durch den Vergleich des geglätteten Oszillatorwerts mit den adaptiven Schwellenwerten bestimmt:
    • 4 – über HighLevel2 * volume (extremer bullischer Druck).
    • 3 – zwischen HighLevel1 * volume und dem Extremniveau.
    • 2 – zwischen den bullischen und bärischen Schwellenwerten.
    • 1 – zwischen dem unteren Schwellenwert und der Neutrallinie.
    • 0 – unter LowLevel2 * volume (extremer bärischer Druck).

Handelsregeln

  1. Die Farbe bei SignalBar (in der Regel die vorherige abgeschlossene Kerze) und die Farbe davor auswerten.
  2. Long-Einträge:
    • Slot 1 öffnet, wenn die Farbe von 4 auf einen beliebigen Wert unter 4 wechselt und AllowLongEntry true ist.
    • Slot 2 öffnet, wenn die Farbe von 3 auf unter 3 wechselt.
  3. Short-Einträge:
    • Slot 1 öffnet, wenn die Farbe von 0 auf über 0 steigt und AllowShortEntry true ist.
    • Slot 2 öffnet, wenn die Farbe von 1 auf über 1 steigt.
  4. Long-Ausstiege erfolgen, wenn die frühere Farbe 0 oder 1 war und AllowLongExit aktiviert ist.
  5. Short-Ausstiege erfolgen, wenn die frühere Farbe 4 oder 3 war und AllowShortExit aktiviert ist.
  6. Jeder Slot verfolgt den Zeitpunkt des letzten Signals, um doppelte Orders auf derselben Kerze zu vermeiden. Schutz-Stops sind optional und werden durch StartProtection gehandhabt, wenn StopLossPoints oder TakeProfitPoints größer als null sind.

Parameter

Name Typ Standard Beschreibung
PrimaryVolume decimal 0.1 Volumen des ersten Long/Short-Slots.
SecondaryVolume decimal 0.2 Volumen des zweiten Slots.
StopLossPoints int 1000 Optionaler Schutz-Stop-Abstand in Preisschritten.
TakeProfitPoints int 2000 Optionaler Take-Profit-Abstand in Preisschritten.
AllowLongEntry bool true Öffnen von Long-Positionen aktivieren.
AllowShortEntry bool true Öffnen von Short-Positionen aktivieren.
AllowLongExit bool true Long-Positionen bei bärischen Farben schließen.
AllowShortExit bool true Short-Positionen bei bullischen Farben schließen.
CandleType DataType H8 Kerzen-Zeitrahmen für Berechnungen.
PriceMaMethod SmoothMethod Sma Typ des gleitenden Durchschnitts für den angewendeten Preis.
PriceMaLength int 100 Länge des Preis-Glätters.
PriceMaPhase int 15 Phasenparameter für Jurik-basierte Filter.
RangeMaMethod SmoothMethod Jjma Typ des gleitenden Durchschnitts für die Kerzenspanne.
RangeMaLength int 20 Länge des Spannen-Glätters.
RangeMaPhase int 100 Phasenparameter für den Spannen-Glätter.
SmoothingLength int 20 Jurik-Glättungslänge für Oszillator und Volumen.
AppliedPrice AppliedPrice Close Preisquelle für Oszillatorberechnungen.
VolumeType AppliedVolume Tick Volumensquelle, mit der der Oszillator multipliziert wird.
HighLevel2 int 150 Oberer Extremmultiplikator für den adaptiven Schwellenwert.
HighLevel1 int 20 Oberer moderater Multiplikator.
LowLevel1 int -20 Unterer moderater Multiplikator.
LowLevel2 int -150 Unterer Extremmultiplikator.
SignalBar int 1 Historischer Offset für das Lesen von Farbübergängen.

Verwendungshinweise

  • Hängen Sie die Strategie an ein Wertpapier, das sowohl Preis- als auch Volumesinformationen liefert; wenn nur Tick-Volumen verfügbar ist, wird der Tick-Zähler als Proxy verwendet.
  • Die beiden Slot-Volumina können unabhängig abgestimmt werden, um die duale Geldverwaltungseinrichtung des Original-EA zu emulieren.
  • Passen Sie SignalBar an, wenn Sie mit teilweise geformten Kerzen arbeiten oder historische Daten resynchronisieren.
  • Die Glättungsmethoden unterstützen Jurik-Filter durch Reflexion, um das Verhalten der MQL SmoothAlgorithms-Bibliothek zu replizieren.
  • Da StartProtection nur aufgerufen wird, wenn Stop- oder Target-Abstände positiv sind, lassen Sie sie auf null, um Schutzaufträge zu deaktivieren.
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>
/// KlPrice-Vol strategy using EMA crossover with volume confirmation.
/// Buys on fast EMA crossing above slow EMA with rising volume.
/// Sells on reverse crossover.
/// </summary>
public class ExpIKlPriceVolStrategy : 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 strategy parameters.
	/// </summary>
	public ExpIKlPriceVolStrategy()
	{
		_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;
	}
}