Auf GitHub ansehen

Exp XFisher org v1-Strategie

Überblick

Die Strategie reproduziert den MetaTrader 5-Experten Exp_XFisher_org_v1. Sie handelt Umkehrungen, die an der Fisher-Transformation des Preises erkannt werden, der zusätzlich mit einem konfigurierbaren gleitenden Durchschnitt geglättet wird. Der StockSharp-Port behält die Gegentrend-Natur des ursprünglichen Roboters bei: wenn die Fisher-Kurve nach einem Aufwärtsschwung abwärts dreht, wird eine Long-Position eröffnet, und wenn die Kurve nach einem Abwärtsschwung aufwärts dreht, wird eine Short-Position eröffnet. Bestehende Positionen werden geschlossen, sobald der Indikator in die entgegengesetzte Richtung umkehrt.

Der Hilfsindikator XFisherOrgIndicator in CS/ExpXFisherOrgV1Strategy.cs folgt der MT5-Logik:

  1. Das höchste Hoch und niedrigste Tief über Length abgeschlossene Kerzen nehmen.
  2. Die ausgewählte Preisquelle (siehe Applied Price unten) anhand dieser Extreme in den Bereich 0–1 umrechnen.
  3. Den rekursiven Filter value = (wpr - 0.5) + 0.67 * value[prev] gefolgt von der Fisher-Transformation anwenden fish = 0.5 * ln((1 + value) / (1 - value)) + 0.5 * fish[prev].
  4. Das Ergebnis mit einem der unterstützten gleitenden Durchschnitte glätten. Der geglättete Fisher-Wert bildet die Hauptlinie; die Signallinie ist einfach der Wert der vorherigen Bar, genau wie in der MQL-Version, wo Puffer #1 eine Einbar-Verschiebung speichert.

Die Konvertierung behält die ursprünglichen Standardwerte (Length = 7, Jurik-Glättung der Länge 5, Phase 15, H4-Kerzen) bei und stellt dieselben Enable/Disable-Schalter für das Öffnen und Schließen von Long/Short-Trades bereit.

Handelsregeln

  • Long-Einstieg – wenn der Fisher-Wert von SignalBar + 1 Bars vor stieg (Fisher[SignalBar+1] > Fisher[SignalBar+2]) aber der Wert bei SignalBar unter seine verzögerte Kopie kreuzt oder diese berührt (Fisher[SignalBar] <= Fisher[SignalBar+1]).
  • Short-Einstieg – wenn der Fisher-Wert von SignalBar + 1 Bars vor fiel, aber der Wert bei SignalBar über seine verzögerte Kopie kreuzt.
  • Positions-Exit – die entgegengesetzte Umkehr schließt eine bestehende Position, bevor ein neuer Trade in Betracht gezogen wird. Ein Long-Exit wird durch dieselbe Bedingung ausgelöst, die einen Short öffnet, und umgekehrt.
  • Volumen – wird durch OrderVolume gesteuert. Wenn ein Flip von Short zu Long (oder Long zu Short) erforderlich ist, sendet die Strategie eine einzelne Marktorder mit ausreichendem Volumen, um die alte Position zu schließen und die neue in derselben Transaktion zu öffnen.

Alle Berechnungen verwenden ausschließlich abgeschlossene Kerzen. Wenn SignalBar null ist, wird die aktuelle geschlossene Kerze zur Signalauswertung verwendet; positive Werte verschieben das Signal zeitlich zurück, genau wie der MT5-SignalBar-Input.

Parameter

Name Beschreibung Standard
OrderVolume Volumen jeder Marktorder. 1
BuyOpenAllowed / SellOpenAllowed Öffnen von Long/Short-Trades erlauben. true
BuyCloseAllowed / SellCloseAllowed Schließen bestehender Long/Short-Trades erlauben. true
SignalBar Verschiebung (in geschlossenen Kerzen) zum Lesen der Fisher-Puffer. 1
Length Lookback für höchste/niedrigste Preisextrema. 7
SmoothingLength Periode des Glättungsdurchschnitts. 5
Phase Jurik-Phase (von anderen Methoden ignoriert). 15
SmoothingMethod Gleitender Durchschnitt, der auf den Fisher-Output angewendet wird. Jjma
PriceType Applied Price, die an den Indikator weitergeleitet wird (Schluss, Eröffnung, Median, etc.). Close
CandleType Kerzenserie für die Berechnung (Standard: 4-Stunden-Kerzen). H4

Glättungsmethoden-Mapping

Der ursprüngliche Indikator stellt eine große Auswahl an Glättungskernen bereit. Der StockSharp-Port ordnet sie zuverlässigen eingebauten Implementierungen zu:

  • Jjma, Jurx, T3JurikMovingAverage (Phasenparameter wird angewendet, wenn die Eigenschaft verfügbar ist).
  • Sma, Ema, Smma, Lwma → jeweilige StockSharp-Durchschnitte.
  • Parabolic → durch ExponentialMovingAverage approximiert (nächstes Verhalten unter StockSharp).
  • Vidya, AmaKaufmanAdaptiveMovingAverage (adaptives VIDYA-Verhalten wird mit Kaufman AMA modelliert).

Dieses Mapping spiegelt den in anderen Kositsin-Konvertierungen im Repository verwendeten Ansatz und hält die Reaktion der geglätteten Fisher-Linie vergleichbar mit der MT5-Implementierung.

Unterschiede zum MT5-Experten

  • Geldmanagement – StockSharp-Strategien arbeiten mit expliziten Volumen. Die MM/MarginMode-Inputs von MT5 werden durch einen einzelnen OrderVolume-Parameter ersetzt, damit der Trader die Lotgröße direkt definieren kann.
  • Ausführungsmodell – Trades werden einmal pro abgeschlossener Kerze über die High-Level-Subscription-API generiert, anstatt bei jedem Tick. Dies vermeidet doppelte Orders und macht den ursprünglichen IsNewBar-Helper überflüssig.
  • Applied Price-Optionen – alle Preismodi aus SmoothAlgorithms.mqh sind unterstützt, einschließlich TrendFollow- und Demark-Varianten.
  • Charting – die Strategie zeichnet Kerzen, die geglättete Fisher-Transformation und die ausgeführten Trades im Standardchartbereich.

Dateien

  • CS/ExpXFisherOrgV1Strategy.cs – Strategieklasse, Indikatorimplementierung und Wertecontainer.
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 XFisher org v1 strategy using EMA crossover as trend filter.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExpXFisherOrgV1Strategy : 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="ExpXFisherOrgV1Strategy"/> class.
	/// </summary>
	public ExpXFisherOrgV1Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 50)
			.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 = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

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

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

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

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

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

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}