Auf GitHub ansehen

Reverse Day Fractal-Strategie

Überblick

Reverse Day Fractal ist eine Preisaktionsstrategie, die nach scharfen Umkehrungen nach einem Intraday-Ausbruch sucht. Der Algorithmus analysiert die letzten drei fertigen Kerzen. Wenn der aktuelle Balken ein neues Extrem jenseits der vorherigen zwei Kerzen bildet und zurück in die entgegengesetzte Richtung schließt, behandelt er dies als gescheiterten Ausbruch und tritt in einen Umkehrtrade ein. Schutzorders werden durch konfigurierbare Take-Profit-, Stop-Loss- und Trailing-Stop-Abstände in Preisschritten verwaltet.

Handelslogik

  • Bullisches Setup:
    • Die aktuelle fertige Kerze bildet ein tieferes Tief als jede der zwei vorherigen Kerzen.
    • Die Kerze schließt über ihrem Eröffnungspreis, was eine bullische Ablehnung des neuen Tiefs anzeigt.
    • Wenn diese Bedingungen erfüllt sind und die Strategie handeln darf, öffnet sie eine Long-Position. Optional kann zunächst ein bestehender Short geschlossen werden.
  • Bärisches Setup:
    • Die aktuelle fertige Kerze bildet ein höheres Hoch als jede der zwei vorherigen Kerzen.
    • Die Kerze schließt unter ihrem Eröffnungspreis, was eine bärische Ablehnung des neuen Hochs anzeigt.
    • Wenn diese Bedingungen erfüllt sind, öffnet sie eine Short-Position, optional mit vorherigem Schließen eines bestehenden Longs.
  • Positionsverwaltung: Die Strategie kann so konfiguriert werden, dass nur eine offene Position gleichzeitig erlaubt ist (Standardverhalten). Wenn deaktiviert, kehrt sie eine bestehende Position um, indem das erforderliche Volumen zum Richtungswechsel hinzugefügt wird.
  • Risikokontrollen: Beim Start ruft die Strategie StartProtection auf, um Take-Profit-, Stop-Loss- und Trailing-Stop-Schutz mit den konfigurierten Punktabständen anzuwenden. Wenn ein Trailing Stop aktiviert ist, folgt der Schutzstop dem Preis in diskreten Schritten.

Parameter

  • Trade Volume – Ordervolumen für neue Einstiege.
  • Take Profit – Abstand zum Gewinnziel in Preisschritten. Null zum Deaktivieren.
  • Stop Loss – Abstand zum Schutzstop in Preisschritten. Null zum Deaktivieren.
  • Trailing Stop – Trailing-Stop-Abstand in Preisschritten. Null zum Deaktivieren.
  • Trailing Step – Mindestbewegung (in Schritten), bevor der Trailing Stop angepasst wird.
  • Only One Position – Wenn aktiviert, ignoriert die Strategie neue Signale, während eine Position offen ist.
  • Candle Type – Für Berechnungen verwendeter Kerzendatentyp (Standard: 1-Stunden-Zeitrahmen).

Hinweise

  • Signale werden nur auf fertigen Kerzen erzeugt, die durch das konfigurierte Abonnement geliefert werden.
  • Die Strategie hält die zwei jüngsten Kerzenextrema im Speicher; daher benötigt sie mindestens zwei abgeschlossene Kerzen nach dem Start, bevor sie ein Signal erzeugen kann.
  • Standard-Parameterwerte replizieren den ursprünglichen MQL4-Expertenberater: 0.01 Lot Volumen, 20-Punkt Stop-Loss, 10-Punkt Take-Profit, 25-Punkt Trailing Stop und 5-Punkt Trailing Step.
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;

public class ReverseDayFractalStrategy : 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;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ReverseDayFractalStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).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");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	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;

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

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

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}