Auf GitHub ansehen

Explosion Range Expansion-Strategie

Überblick

Explosion Range Expansion Strategy ist ein Ausbruchssystem, das vom MetaTrader 5-Expertenberater "Explosion" konvertiert wurde. Der Algorithmus vergleicht die Spanne der aktuell abgeschlossenen Kerze mit der vorherigen Kerze und öffnet eine Marktposition in Richtung des Kerzenkörpers, wenn die Spannenexpansion ein konfigurierbares Verhältnis überschreitet. Die StockSharp-Version behält die ursprünglichen Money-Management-Funktionen bei und fügt praktische Parameter für Zeitplanung und Trailing-Stop-Verwaltung hinzu.

Handelsregeln

  • Spannenexpansion: Berechne die aktuelle Kerzenspanne (High - Low) und vergleiche sie mit der vorherigen Kerzenspanne. Wenn die aktuelle Spanne größer ist als die vorherige Spanne multipliziert mit Range Ratio, wird ein Signal erzeugt.
  • Richtungsfilter:
    • Wenn die Kerze über ihrer Eröffnung schließt und die aktuelle Position flach oder short ist, wird eine Long-Marktorder gesendet.
    • Wenn die Kerze unter ihrer Eröffnung schließt und die aktuelle Position flach oder long ist, wird eine Short-Marktorder gesendet.
  • Handelsfenster: Signale werden nur akzeptiert, wenn die Kerzenschlusszeit zwischen Start Hour und End Hour (einschließlich) liegt.
  • Tageslimit: Wenn One Trade Per Day aktiviert ist, wird nur der erste qualifizierende Einstieg des Handelstages ausgeführt.
  • Pause zwischen Trades: Nach einem Positionseinstieg wartet die Strategie Pause (sec) Sekunden, bevor ein neues Signal akzeptiert wird.
  • Maximales Exposure: Die Nettopositionsgröße darf Max Positions * Order Volume nicht überschreiten.

Ausstiege und Risikomanagement

  • Anfänglicher Schutz: Optionale Stop-Loss- und Take-Profit-Niveaus werden in Preisschritten definiert und vom Einstiegspreis berechnet.
  • Trailing Stop: Wenn aktiviert, wird der Stop-Loss nach Erreichen eines Mindestgewinnschwellenwerts (Trailing Stop + Trailing Step) näher an den Preis verschoben. Die Trailing-Logik behält dasselbe Verhalten wie im originalen EA bei.
  • Manuelles Schließen bei Zielen: Wenn die Kerzenspanne intrabar entweder das Stop-Loss- oder Take-Profit-Niveau erreicht, wird die Position mit einer Marktorder geschlossen.

Parameter

  • Candle Type – Datentyp für das Kerzenabonnement.
  • Order Volume – Größe jeder Position in Lots.
  • Range Ratio – Multiplikator auf die vorherige Kerzenspanne zum Auslösen von Einstiegen.
  • Max Positions – Maximale Anzahl gleichzeitig erlaubter Lots.
  • Pause (sec) – Mindestzeit in Sekunden zwischen Einstiegen.
  • Start Hour / End Hour – Handelsstunden-Filter (0–23).
  • One Trade Per Day – Beschränkt die Strategie auf einen Einstieg pro Kalendertag.
  • Stop Loss – Anfänglicher Stop-Loss-Abstand in Preisschritten.
  • Take Profit – Anfänglicher Take-Profit-Abstand in Preisschritten.
  • Trailing Stop – Trailing-Stop-Abstand in Preisschritten.
  • Trailing Step – Zusätzlicher Abstand, der vor einem Trailing-Update erforderlich ist.

Konvertierungshinweise

  • Die Strategie verwendet die High-Level-SubscribeCandles- und Bind-API für indikatorfreie Signalverarbeitung.
  • Trailing Stop, Handelsfenster, Pause und Tageslimit reproduzieren die ursprüngliche MQ5-Logik.
  • Money Management wird über einen einzelnen Volumenparameter ausgedrückt; risikobasiertes Lot-Sizing aus dem Originalscript wird in dieser Version nicht unterstützt.
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 ExplosionRangeExpansionStrategy : 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 ExplosionRangeExpansionStrategy()
	{
		_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;
	}
}