Auf GitHub ansehen

Extreme EA (StockSharp-Konvertierung)

Die Extreme EA Strategie ist ein Trendfolge-Experte, der ursprünglich für MetaTrader geschrieben wurde. Sie kombiniert zwei gleitende Durchschnitte mit einem Commodity Channel Index (CCI) Filter und einem adaptiven Geldverwaltungsmodul. Dieser Port hält die Handelslogik intakt, während alle wichtigen Parameter über die High-Level-API von StockSharp zugänglich gemacht werden. Die Strategie operiert nur auf abgeschlossenen Kerzen und ist mit mehreren Zeitrahmen kompatibel, indem die gleitenden Durchschnitte und der CCI auf unabhängigen Kerzenabonnements ausgeführt werden.

Strategie-Überblick

  1. Trendfilter: Zwei gleitende Durchschnitte werden auf dem konfigurierbaren MaCandleType berechnet. Der schnelle Durchschnitt verfolgt kurzfristiges Momentum, während der langsame Durchschnitt die dominante Trendsteigung definiert. Die Strategie prüft die Steigung des langsamen Durchschnitts mit den zwei vorherigen Werten, um die ursprünglichen CopyBuffer-Array-Offsets aus dem MQL-Code nachzuahmen.
  2. Momentum-Filter: Der CCI wird auf seinem eigenen Zeitrahmen (CciCandleType) und seiner Preisquelle ausgewertet. Der letzte abgeschlossene Wert wird zwischengespeichert und wiederverwendet, bis eine neue CCI-Kerze erscheint, was dem Verhalten der MetaTrader-Puffer entspricht.
  3. Einstiegsregeln:
    • Long eintreten, wenn der langsame MA steigt, der schnelle MA steigt und der CCI unter das untere Niveau fällt.
    • Short eintreten, wenn der langsame MA fällt, der schnelle MA fällt und der CCI über das obere Niveau steigt.
  4. Ausstiegsregeln:
    • Alle Longs schließen, wenn der langsame MA aufhört zu steigen.
    • Alle Shorts schließen, wenn der langsame MA aufhört zu fallen.

Risikomanagement

  • MaximumRisk steuert die Zielpositionsgröße basierend auf dem aktuellen Portfolio-Eigenkapital und dem letzten Preis. Wenn das berechnete Volumen null ist oder die Portfolio-Werte nicht verfügbar sind, greift die Strategie auf das konfigurierte Volume oder das Börsenminimum zurück.
  • DecreaseFactor reduziert das berechnete Volumen nach zwei oder mehr aufeinanderfolgenden Verlust-Trades. Die Reduktion spiegelt die ursprüngliche Formel lot = lot - lot * losses / DecreaseFactor wider.
  • HistoryDays begrenzt, wie lange eine Verluststrähne erinnert wird. Wenn ein Schließungshandel nach der angegebenen Anzahl von Tagen stattfindet, wird die Strähne vor der Anwendung der Reduktion zurückgesetzt.
  • MaxPositions begrenzt die Pyramidisierung, indem die Nettoexposition pro Richtung begrenzt wird. Wenn das Limit erreicht wird, werden neue Einstiege unterdrückt, bis die Exposition sinkt.

Parameter

Name Typ Standard Beschreibung
MaximumRisk decimal 0.05 Eigenkapitalanteil für die Dimensionierung jedes neuen Trades.
DecreaseFactor decimal 6 Verluststrähnen-Reduktionsfaktor. Auf 0 setzen, um zu deaktivieren.
HistoryDays int 60 Anzahl der Tage, die beim Zählen aufeinanderfolgender Verluste aufbewahrt werden.
MaxPositions int 3 Maximale gleichzeitige Einstiege pro Richtung.
FastMaPeriod int 15 Periode für den schnellen gleitenden Durchschnitt.
SlowMaPeriod int 75 Periode für den langsamen gleitenden Durchschnitt.
CciPeriod int 12 Lookback-Länge für den CCI.
CciUpperLevel decimal 50 Oberer CCI-Schwellenwert für Shorts.
CciLowerLevel decimal -50 Unterer CCI-Schwellenwert für Longs.
MaCandleType DataType 15m Zeitrahmen für beide gleitenden Durchschnitte und Ausführung.
CciCandleType DataType 30m Zeitrahmen für den CCI-Filter.
MaMethod MaMethod Exponential Glättungsmethode (Simple, Exponential, Smoothed, LinearWeighted).
MaPriceMode AppliedPriceMode Median Preiseingabe für die gleitenden Durchschnitte.
CciPriceMode AppliedPriceMode Typical Preiseingabe für den CCI.

Implementierungshinweise

  • Die Strategie abonniert den Zeitrahmen der gleitenden Durchschnitte einmal und optional ein zweites Abonnement für den CCI. Wenn beide Zeitrahmen übereinstimmen, speist ein einzelnes Abonnement beide Komponenten und reproduziert den ursprünglichen Einzel-Chart-Workflow.
  • Vorherige Indikatorwerte werden in privaten Feldern zwischengespeichert, um die Vergleiche ma_slow_array[1], ma_slow_array[2] und ma_fast_array[0] ohne manuelle Indikatorpuffer zu emulieren.
  • Die Positionsgrößenbestimmung wird gegen den Instrument-Volumenschritt, das Minimum und Maximum normalisiert, um abgelehnte Orders zu vermeiden.
  • Das Risikomodul zeichnet Einstiegs- und Ausstiegspreise auf, um den realisierten PnL pro abgeschlossener Position zu schätzen, was die in MetaTrader verwendete HistoryDealGet-Schleife ersetzt.

Unterschiede zur MQL-Version

  • MetaTrader-spezifische Funktionen wie FreeMarginCheck, MarginCheck und HistorySelect werden mit StockSharp-Portfolio-Metriken und dem internen Verluststrähnen-Tracker approximiert.
  • Der StockSharp-Port operiert auf Nettopositionen. Schließungsorders glätten die gesamte Exposition in der relevanten Richtung, was mit dem konsolidierten Positionsmodell übereinstimmt.
  • Protokollierungsroutinen aus dem Original-EA wurden zugunsten der integrierten Diagnose von StockSharp weggelassen.
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;

/// <summary>
/// Extreme EA strategy using fast/slow EMA crossover with CCI filter.
/// Buys when both EMAs rising and CCI below lower level (oversold bounce).
/// Sells when both EMAs falling and CCI above upper level (overbought reversal).
/// </summary>
public class ExtremeEaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _cciUpperLevel;
	private readonly StrategyParam<decimal> _cciLowerLevel;

	private ExponentialMovingAverage _fastMa;
	private ExponentialMovingAverage _slowMa;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevFast2;
	private decimal _prevSlow2;
	private bool _hasPrev;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// CCI indicator length.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Upper CCI threshold for sell entries.
	/// </summary>
	public decimal CciUpperLevel
	{
		get => _cciUpperLevel.Value;
		set => _cciUpperLevel.Value = value;
	}

	/// <summary>
	/// Lower CCI threshold for buy entries.
	/// </summary>
	public decimal CciLowerLevel
	{
		get => _cciLowerLevel.Value;
		set => _cciLowerLevel.Value = value;
	}

	/// <summary>
	/// Initialize strategy parameters.
	/// </summary>
	public ExtremeEaStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast EMA period", "Indicator");

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow EMA period", "Indicator");

		_cciPeriod = Param(nameof(CciPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI lookback period", "Indicator");

		_cciUpperLevel = Param(nameof(CciUpperLevel), 50m)
			.SetDisplay("CCI Upper", "Upper CCI threshold for sell", "Levels");

		_cciLowerLevel = Param(nameof(CciLowerLevel), -50m)
			.SetDisplay("CCI Lower", "Lower CCI threshold for buy", "Levels");
	}

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

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

		_fastMa = null;
		_slowMa = null;
		_prevFast = 0;
		_prevSlow = 0;
		_prevFast2 = 0;
		_prevSlow2 = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
		_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fastMa, _slowMa, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fastMa.IsFormed || !_slowMa.IsFormed)
		{
			_prevFast2 = _prevFast;
			_prevSlow2 = _prevSlow;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		if (!_hasPrev)
		{
			_prevFast2 = _prevFast;
			_prevSlow2 = _prevSlow;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		var slowIsRising = _prevSlow > _prevSlow2;
		var slowIsFalling = _prevSlow < _prevSlow2;
		var fastIsRising = fastValue > _prevFast;
		var fastIsFalling = fastValue < _prevFast;

		// Buy: fast crosses above slow
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
		}
		// Sell: fast crosses below slow
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
		}

		_prevFast2 = _prevFast;
		_prevSlow2 = _prevSlow;
		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}