Auf GitHub ansehen

Twenty 200 Pips-Strategie

Übersicht

Die Strategie repliziert den ursprünglichen 20/200 Pips MQL5-Experten. Sie untersucht Stundenkerzen und vergleicht zwei historische Eröffnungspreise (Open[t1] und Open[t2]). Wenn die Differenz zwischen diesen Eröffnungen während einer bestimmten Stunde ein konfigurierbares Delta übersteigt, eröffnet die Strategie einen einzelnen Trade für die Sitzung und setzt auf feste Take-Profit- und Stop-Loss-Levels.

Handelslogik

  1. Stundenkerzen abonnieren (konfigurierbar) und den Eröffnungspreis in zwei Shift-Indikatoren einspeisen, um die Eröffnungen bei den erforderlichen Indizes abzurufen.
  2. Bei jeder abgeschlossenen Kerze das Flag "kann handeln" zurücksetzen, sobald die aktuelle Stunde größer als die konfigurierte Handelsstunde ist. Dies spiegelt den täglichen Reset im ursprünglichen Expertenberater wider.
  3. Wenn die Stunde mit der konfigurierten Handelsstunde übereinstimmt und keine Position offen ist, die gespeicherten Eröffnungspreise vergleichen:
    • Wenn Open[t1] > Open[t2] + delta, eine Market-Verkaufsorder einreichen.
    • Wenn Open[t1] + delta < Open[t2], eine Market-Kauforder einreichen.
  4. Nach dem Senden einer Order verbietet die Strategie neue Einstiege bis zum nächsten täglichen Reset. Schützende Take-Profit- und Stop-Loss-Orders werden über StartProtection verwaltet.

Parameter

  • TakeProfit – Abstand in Preispunkten für die Take-Profit-Order (Standard 200 Punkte).
  • StopLoss – Abstand in Preispunkten für die Stop-Loss-Order (Standard 2000 Punkte).
  • TradeHour – Stunde des Tages, zu der die Einstiegsprüfung durchgeführt wird (Standard 18).
  • FirstOffset – Index des älteren Eröffnungspreises (entspricht Open[t1] im MQL-Skript, Standard 7).
  • SecondOffset – Index des neueren Eröffnungspreises (Open[t2], Standard 2).
  • DeltaPoints – Minimale Differenz in Punkten zwischen den beiden Eröffnungen, um einen Trade auszulösen (Standard 70).
  • Volume – Ordergröße für Markteinstiege (Standard 0.1).
  • CandleType – Zeitrahmen für Berechnungen (Standard 1-Stunden-Kerzen).

Implementierungshinweise

  • Shift-Indikatoren werden manuell verarbeitet, um auf historische Eröffnungspreise zuzugreifen, ohne benutzerdefinierte Sammlungen zu pflegen.
  • Die Strategie ruft StartProtection einmal während OnStarted auf, um die im MQL-Experten definierten Stop-Loss/Take-Profit-Levels zu emulieren.
  • Englische Kommentare sind direkt im Code enthalten, um die Wartung und Überprüfung zu erleichtern.
  • Pro Tag ist nur ein Trade erlaubt, da _canTrade direkt nach dem Platzieren einer Order gelöscht wird und erst wiederhergestellt wird, nachdem die konfigurierte Handelsstunde vergangen ist.

Verwendung

  1. Die Strategie an ein Instrument anhängen und die Parameter entsprechend dem Zielinstrument konfigurieren.
  2. Sicherstellen, dass das Instrument einen gültigen PriceStep hat; er wird verwendet, um punktbasierte Parameter in absolute Preisabstände umzurechnen.
  3. Die Strategie starten. Sie wartet bis zur konfigurierten Stunde und handelt bei der nächsten abgeschlossenen Kerze, wenn die Eröffnungspreisbedingungen erfüllt sind.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Daily breakout strategy derived from the "20/200 pips" MQL5 expert.
/// Compares open prices from different bar offsets and trades the breakout.
/// </summary>
public class Twenty200PipsStrategy : Strategy
{
	private readonly StrategyParam<int> _takeProfit;
	private readonly StrategyParam<int> _stopLoss;
	private readonly StrategyParam<int> _firstOffset;
	private readonly StrategyParam<int> _secondOffset;
	private readonly StrategyParam<int> _deltaPoints;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _opens = new();
	private decimal _pointValue;

	public int TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	public int StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	public int FirstOffset
	{
		get => _firstOffset.Value;
		set => _firstOffset.Value = value;
	}

	public int SecondOffset
	{
		get => _secondOffset.Value;
		set => _secondOffset.Value = value;
	}

	public int DeltaPoints
	{
		get => _deltaPoints.Value;
		set => _deltaPoints.Value = value;
	}

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

	public Twenty200PipsStrategy()
	{
		_takeProfit = Param(nameof(TakeProfit), 200)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit (points)", "Take profit distance in points", "Risk")
			.SetOptimize(50, 500, 50);

		_stopLoss = Param(nameof(StopLoss), 2000)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss (points)", "Stop loss distance in points", "Risk")
			.SetOptimize(200, 4000, 100);

		_firstOffset = Param(nameof(FirstOffset), 7)
			.SetGreaterThanZero()
			.SetDisplay("First Offset", "Older bar index", "Signal")
			.SetOptimize(1, 12, 1);

		_secondOffset = Param(nameof(SecondOffset), 2)
			.SetGreaterThanZero()
			.SetDisplay("Second Offset", "Newer bar index", "Signal")
			.SetOptimize(1, 6, 1);

		_deltaPoints = Param(nameof(DeltaPoints), 1)
			.SetGreaterThanZero()
			.SetDisplay("Delta (points)", "Minimum difference between opens", "Signal")
			.SetOptimize(10, 200, 10);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_opens.Clear();
		_pointValue = 0m;
	}

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

		_pointValue = Security?.PriceStep ?? 1m;
		if (_pointValue <= 0m)
			_pointValue = 1m;

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

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

		StartProtection(
			takeProfit: new Unit(TakeProfit * _pointValue, UnitTypes.Absolute),
			stopLoss: new Unit(StopLoss * _pointValue, UnitTypes.Absolute),
			useMarketOrders: true);
	}

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

		_opens.Add(candle.OpenPrice);

		var maxOffset = Math.Max(FirstOffset, SecondOffset);
		if (_opens.Count <= maxOffset)
			return;

		// Keep buffer limited
		if (_opens.Count > maxOffset + 100)
			_opens.RemoveRange(0, _opens.Count - maxOffset - 50);

		if (Position != 0)
			return;

		var openFirst = _opens[_opens.Count - 1 - FirstOffset];
		var openSecond = _opens[_opens.Count - 1 - SecondOffset];
		var threshold = DeltaPoints * _pointValue;

		if (openFirst > openSecond + threshold)
		{
			SellMarket();
		}
		else if (openFirst + threshold < openSecond)
		{
			BuyMarket();
		}
	}
}