Auf GitHub ansehen

Simple-Pivot-Strategie

Diese Strategie reproduziert den MetaTrader 5-Expertenberater "SimplePivot". Sie bewertet kontinuierlich die Beziehung zwischen dem Eröffnungskurs der aktuellen Bar und dem Pivot-Level der vorherigen Bar und hält dabei immer eine einzige direktionale Position. Wenn sich die Richtung ändert, schließt die Strategie die bestehende Position und eröffnet sofort eine in der entgegengesetzten Richtung.

Übersicht

  • Marktregime: Always-in-the-Market Swing-Trading.
  • Instrumente: Jedes Instrument, das Kerzendaten für den ausgewählten Zeitrahmen bereitstellt.
  • Zeitrahmen: Konfigurierbar über den Parameter Candle Type (standardmäßig 1-Stunden-Kerzen).
  • Orders: Marktorders dimensioniert durch den Parameter Volume.

Funktionsweise

Pivot-Berechnung

  1. Warten auf mindestens eine abgeschlossene Kerze zur Initialisierung der Berechnung.
  2. Berechnung des Pivots der vorherigen Kerze als arithmetisches Mittel ihrer Hoch- und Tiefpreise.
  3. Beibehaltung des vorherigen Hochs und Tiefs, damit der Pivot für die nächste Bar sofort produziert werden kann, wenn eine neue Kerze abgeschlossen ist.

Direktionale Entscheidung

  1. Die Standardausrichtung ist Long (Kauf).
  2. Wenn die aktuelle Kerze unterhalb des vorherigen Hochs eröffnet, aber über dem Pivot bleibt, wechselt die Ausrichtung zu Short (Verkauf).
  3. Wenn die gewünschte Richtung gegenüber dem letzten ausgeführten Trade unverändert ist, wird die bestehende Position beibehalten und keine neuen Orders werden gesendet.

Positionsmanagement

  1. Wenn die gewünschte Richtung vom aktuellen Trade abweicht, wird die laufende Position durch eine entgegengesetzte Marktorder flachgestellt.
  2. Nach dem Flachstellen etabliert eine Marktorder in Höhe von Volume das neue direktionale Engagement.
  3. Der Prozess wiederholt sich bei jeder abgeschlossenen Kerze und stellt sicher, dass die Strategie immer entweder Long oder Short ist.

Parameter

  • Volume: Handelsgröße für jeden Einstieg. Bestimmt auch die Größe der Schließorder, wenn die Strategie die Richtung wechselt.
  • Candle Type: Datentyp der Kerzen, die für Pivot- und Einstiegsberechnungen verwendet werden. Der Standard ist ein 1-Stunden-Zeitrahmen, aber jeder verfügbare Zeitrahmen kann ausgewählt werden.

Zusätzliche Hinweise

  • Die Logik reagiert auf vollständig abgeschlossene Kerzen (CandleStates.Finished), um wiederholte Signale zu vermeiden, während eine Kerze noch gebildet wird.
  • Es sind keine Stops oder Gewinnziele definiert; Ausstiege erfolgen nur, wenn die Pivot-Regel einen Richtungswechsel anfordert.
  • Da die Strategie immer im Markt ist, sollten Risikokontrollen wie Überwachung des maximalen Drawdowns oder Session-Filter extern gehandhabt werden, sofern erforderlich.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simple pivot strategy that flips position based on the previous bar pivot.
/// </summary>
public class SimplePivotStrategy : Strategy
{
	private enum TradeDirections
	{
		None,
		Long,
		Short,
	}

	private readonly StrategyParam<DataType> _candleType;

	private TradeDirections _lastDirection = TradeDirections.None;
	private decimal _previousHigh;
	private decimal _previousLow;
	private bool _hasPreviousCandle;

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

	public SimplePivotStrategy()
	{

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "Data");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	protected override void OnReseted()
	{
		base.OnReseted();

		_lastDirection = TradeDirections.None;
		_previousHigh = 0m;
		_previousLow = 0m;
		_hasPreviousCandle = false;
	}

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

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ProcessCandle).Start();
	}

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

		if (!_hasPreviousCandle)
		{
			// Collect the very first completed candle as the seed for the pivot calculation.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			_hasPreviousCandle = true;
			return;
		}

		var pivot = (_previousHigh + _previousLow) / 2m;
		var desiredDirection = TradeDirections.Long;

		// When the new open sits between the previous high and pivot we switch to a short bias.
		if (candle.OpenPrice < _previousHigh && candle.OpenPrice > pivot)
			desiredDirection = TradeDirections.Short;

		if (desiredDirection == _lastDirection && _lastDirection != TradeDirections.None)
		{
			// Keep the existing position when direction has not changed.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		CloseExistingPosition();

		if (desiredDirection == TradeDirections.Long)
		{
			// Enter a long position when the open is below the pivot.
			BuyMarket(Volume);
		}
		else
		{
			// Enter a short position when the open is above the pivot zone.
			SellMarket(Volume);
		}

		_lastDirection = desiredDirection;
		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
	}

	private void CloseExistingPosition()
	{
		if (Position > 0)
		{
			// Flip from long to flat before opening the opposite direction.
			SellMarket(Math.Abs(Position));
			_lastDirection = TradeDirections.None;
		}
		else if (Position < 0)
		{
			// Flip from short to flat before opening the opposite direction.
			BuyMarket(Math.Abs(Position));
			_lastDirection = TradeDirections.None;
		}
	}
}