Auf GitHub ansehen

Für Max V2

Überblick

Für Max V2 handelt es sich um eine Portierung des MetaTrader 4 Expert Advisors for_max_v2.mq4. Die Strategie wartet auf spezifische Zwei-Kerzen-Verschlingungsmuster und platziert dann ein symmetrisches Paar von Kauf-Stopp- und Verkaufs-Stopp-Aufträgen um die jüngste Kerze herum. Sobald eine Breakout-Order ausgeführt wird, wird die entgegengesetzte Pending-Order entfernt und die Position wird mit festen Stopps, optionalen Take-Profit-Levels und einer Trailing-Routine verwaltet, die zunächst einen kleinen Gewinn bei Break-Even sichert und dann dem Preis folgt.

Strategielogik

Erkennung von Verschlingungsmustern

Der ursprüngliche Fachberater legt zwei Einstiegsblöcke offen und beide bleiben erhalten:

  • Setup Typ 1 – scannt die vorherigen Max Search-Kerzen (überspringt den aktuellen Balken) und wartet darauf, dass das niedrigste Tief innerhalb dieses Bereichs vor zwei Balken auftritt oder darauf, dass das höchste Hoch vor zwei Balken auftritt. Wenn das passiert, muss die zwei Balken zurückliegende Kerze die vorherige Kerze (höheres Hoch und niedrigeres Tief) überdecken. Das Setup stellt einen Straddle um die zuletzt fertige Kerze herum her.
  • Setup vom Typ 2 – scannt auch die vorherigen Max Search-Kerzen, sucht aber nach dem Extrem, das einen Balken zurückliegt. Darüber hinaus muss die Kerze vor einem Balken die Kerze vor zwei Balken umhüllen. Anschließend wird ein Straddle um die jüngste Kerze gelegt. Beide Setups können koexistieren; Jeder verwaltet seine eigenen ausstehenden Aufträge und seine Ablaufuhr.

Ausstehende Auftragserteilung

  • Einstiegspreise – Kauf-Stopp-Aufträge werden beim vorherigen Kerzenhoch plus Gap Points platziert, Verkaufsstopp-Aufträge beim vorherigen Kerzentief minus Gap Points.
  • Stop-Loss – für Typ 1 ist der Long-Stop auf dem Tiefpunkt der Kerze zwei Balken zurück (abzüglich der Lücke) und der Short-Stop auf dem Hoch dieser Kerze (plus der Lücke) verankert. Typ 2 verwendet die vorherige Kerze für beide Seiten.
  • Take-Profit – optional. Long-Ziele addieren Gap Points + Buy Take Profit Points zum vorherigen Hoch und Short-Ziele subtrahieren Gap Points + Sell Take Profit Points vom vorherigen Tief. Wenn Sie die Take-Profit-Eingaben auf 0 setzen, werden die entsprechenden Ziele deaktiviert.
  • Ablauf – jeder Straddle trägt einen Gültigkeitszeitstempel, der als Order Expiry (bars) multipliziert mit dem konfigurierten Kerzenzeitrahmen berechnet wird. Wenn die ausstehenden Bestellungen bei Erreichen des Zeitstempels noch funktionieren, werden beide Seiten storniert.

Positionsmanagement

  • Sobald ein Buy-Stop erfüllt ist, werden alle verbleibenden Sell-Stop-Orders aus beiden Setups storniert; Nach einem kurzen Einstieg gilt die Symmetrieregel.
  • Stopps und Ziele werden bei abgeschlossenen Kerzen überwacht. Wenn das Tief einer Kerze den Long-Stop erreicht (oder das Hoch den Short-Stop erreicht), wird die Position mit einer Marktorder geschlossen. Der gleiche Ansatz wird für die Take-Profit-Ebenen verwendet.
  • Die Break-Even-Routine (Break-even Trigger und Break-even Offset) verschiebt den Stop auf den Einstiegspreis plus/minus dem konfigurierten Offset, sobald die Position um den Triggerbetrag steigt.
  • Der Trailing-Block hält den Stop Long/Short Trailing Buffer Punkte von der besten Exkursion entfernt, aber erst, nachdem der Preis weit genug gestiegen ist (und optional erst, nachdem der Handel bereits profitabel ist). Trailing Step verhindert zu häufige Anpassungen, indem eine Mindestverbesserung erforderlich ist, bevor der Anschlag wieder festgezogen wird.

Parameter

  • Volumen – Ordervolumen für jede ausstehende Stop-Order.
  • Buy Take Profit (Punkte) – Distanz in Punkten, die zur Berechnung des Long Take Profit verwendet wird (zum Deaktivieren auf 0 setzen).
  • Verkaufs-Take-Profit (Punkte) – Distanz in Punkten, die zur Berechnung des Short-Take-Profits verwendet wird (zum Deaktivieren auf 0 setzen).
  • Gap (Punkte) – Puffer, der den Hochs/Tiefs hinzugefügt wird, bevor Stop-Einträge platziert werden, und in die Take-Profit-Distanz gefaltet wird.
  • Suchtiefe – Anzahl der fertigen Kerzen, die bei der Überprüfung auf Engulfing-Setups vom Typ 1 und Typ 2 gescannt wurden.
  • Auftragsablauf (Balken) – Anzahl der Kerzenlängen, die ein ausstehender Straddle aktiv bleibt, bevor beide Seiten storniert werden.
  • Break-Even-Trigger (Punkte) – Gewinnschwelle, die die Break-Even-Stopp-Anpassung aktiviert.
  • Break-Even-Offset (Punkte) – zusätzlicher Puffer, der dem Einstiegspreis hinzugefügt wird, wenn der Break-Even-Stopp platziert wird.
  • Long Trailing Buffer (Punkte) – Nachlaufdistanz für Long-Positionen, sobald die Gewinnschwelle erreicht ist.
  • Short Trailing Buffer (Punkte) – Nachlaufdistanz für Short-Positionen, sobald die Gewinnschwelle erreicht ist.
  • Trailing Step (Punkte) – minimale Verbesserung der Stoppposition erforderlich, bevor der Trailing Stop erneut aktualisiert wird.
  • Trail Only After Profit – wenn aktiviert, wartet das Trailing, bis sich die Position über den Puffer hinaus bewegt hat, bevor es aktiviert wird.
  • Kerzentyp – Zeitrahmen der Kerzen, die zur Mustererkennung, Auftragsablauf und Ausstiegsverarbeitung verwendet werden.

Zusätzliche Hinweise

  • In „Punkten“ ausgedrückte Preisversätze basieren auf dem PriceStep des Wertpapiers. Symbole mit fünf (oder drei) Dezimalstellen werden automatisch in gebrochene Pip-Größen umgewandelt, genau wie in MetaTrader.
  • Stop-Losses und Take-Profits werden über Marktaufträge innerhalb der Strategie ausgeführt, um das Verhalten von EA bei der Verwaltung von Levels bei geschlossenen Kerzen widerzuspiegeln.
  • Die Strategie implementiert nicht die ungenutzte vhod_3-Funktion aus der Originalquelle; Es wurden nur die beiden aktiven Eintrittsblöcke portiert.
  • Dieses Paket enthält nur die C#-Implementierung; Es wird keine Python-Version bereitgestellt.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// For Max V2: N-bar engulfing breakout with EMA filter and ATR stops.
/// </summary>
public class ForMaxV2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<int> _lookback;

	private decimal _entryPrice;
	private decimal _prevHigh;
	private decimal _prevLow;
	private readonly decimal[] _highs = new decimal[10];
	private readonly decimal[] _lows = new decimal[10];
	private int _barCount;

	public ForMaxV2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 30)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");

		_lookback = Param(nameof(Lookback), 10)
			.SetDisplay("Lookback", "N-bar channel lookback.", "Indicators");
	}

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

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public int Lookback
	{
		get => _lookback.Value;
		set => _lookback.Value = value;
	}

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

		_entryPrice = 0;
		_barCount = 0;
		_prevHigh = 0;
		_prevLow = 0;
		Array.Clear(_highs, 0, _highs.Length);
		Array.Clear(_lows, 0, _lows.Length);
	}

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

		_entryPrice = 0;
		_barCount = 0;
		_prevHigh = 0;
		_prevLow = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		var len = Math.Min(Lookback, _highs.Length);
		var idx = _barCount % len;
		_highs[idx] = candle.HighPrice;
		_lows[idx] = candle.LowPrice;
		_barCount++;

		if (_barCount < len || atrVal <= 0)
			return;

		var high = decimal.MinValue;
		var low = decimal.MaxValue;
		for (var i = 0; i < len; i++)
		{
			if (_highs[i] > high) high = _highs[i];
			if (_lows[i] < low) low = _lows[i];
		}

		var close = candle.ClosePrice;

		if (_prevHigh == 0 || _prevLow == 0)
		{
			_prevHigh = high;
			_prevLow = low;
			return;
		}

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close > _prevHigh && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < _prevLow && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevHigh = high;
		_prevLow = low;
	}
}