Auf GitHub ansehen

Three EMA-Strategie

Überblick

Die Strategie reproduziert den MetaTrader-Expertenberater "ThreeEMA" durch das Stapeln von drei exponentiellen gleitenden Durchschnitten (EMAs). Sie sucht nach direktionaler Ausrichtung zwischen einem schnellen, mittleren und langsamen EMA im selben Zeitrahmen. Wenn die Durchschnitte streng aufsteigend geordnet sind (schnell über mittel über langsam), öffnet oder hält die Strategie eine Long-Position. Wenn die Reihenfolge umkippt (schnell unter mittel unter langsam), öffnet oder hält sie eine Short-Position. Schützende Stop-Loss- und Take-Profit-Abstände spiegeln die ursprünglichen MQL-Parameter wider und werden in Preispunkten relativ zur Tick-Größe des Instruments ausgedrückt.

Ursprüngliches MQL-Verhalten

Die MQL-Version instanziierte drei EMA-Indikatoren (FastPeriod, MediumPeriod, SlowPeriod) und erzeugte Handelssignale basierend auf ihrer relativen Reihenfolge auf dem zuletzt geschlossenen Balken:

  • Long eröffnen / Short schließen wenn FastEMA > MediumEMA > SlowEMA.
  • Short eröffnen / Long schließen wenn FastEMA < MediumEMA < SlowEMA.
  • Stop-Loss und Take-Profit wurden als feste Abstände in Punkten vom Einstiegspreis angewendet.

Orders wurden mit Marktausführung eingereicht und der Money-Management-Block verwendete eine feste Lot-Größe. Das Trailing-Modul war deaktiviert.

StockSharp-Implementierungsdetails

  • Verwendet die High-Level-Kerzenabonnement-API. Drei ExponentialMovingAverage-Indikatoren sind an das Hauptzeitrahmen-Abonnement gebunden, sodass jede fertige Kerze alle EMA-Werte gleichzeitig liefert.
  • Handelsentscheidungen werden nur auf vollständig geformten Kerzen bewertet, um Intrabar-Rauschen zu vermeiden.
  • Wenn ein direktionaler Stack erscheint, storniert die Strategie alle laufenden Orders, schließt das entgegengesetzte Exposure falls nötig und öffnet eine neue Marktposition in der erforderlichen Richtung.
  • StartProtection konvertiert die konfigurierten punktbasierten Stop-Loss- und Take-Profit-Abstände in tatsächliche Preisoffsets unter Verwendung des PriceStep des Instruments. Dies spiegelt das Schutzverhalten des ursprünglichen EA wider.
  • Die Chart-Integration zeichnet Kerzen und alle drei EMAs, wenn ein Chartbereich verfügbar ist, was die visuelle Validierung von Signalen erleichtert.

Parameter

Name Standard Beschreibung
CandleType 1-Minuten-Zeitrahmen Zeitrahmen des Kerzenabonnements für EMAs.
FastPeriod 5 Länge des schnellen EMA. Muss kleiner als MediumPeriod sein.
MediumPeriod 12 Länge des mittleren EMA. Muss zwischen dem schnellen und langsamen Zeitraum liegen.
SlowPeriod 24 Länge des langsamen EMA. Muss der höchste Periodenwert sein.
StopLossPoints 400 Schützender Stop-Loss-Abstand in Instrumentenpunkten (in Preis umgerechnet mit PriceStep). Null zum Deaktivieren.
TakeProfitPoints 900 Take-Profit-Abstand in Instrumentenpunkten (in Preis umgerechnet mit PriceStep). Null zum Deaktivieren.

Verwendungshinweise

  1. Konfigurieren Sie Volume vor dem Starten der Strategie, um die gewünschte Order-Größe widerzuspiegeln (das originale EA verwendete feste Lots).
  2. Stellen Sie sicher, dass die EMA-Perioden streng steigend bleiben; andernfalls wird während OnStarted eine Ausnahme ausgelöst, die der Validierung im MQL-Quellcode entspricht.
  3. Da die Logik Positionen immer umkehrt, wenn der EMA-Stack sich umkehrt, ist die Strategie kontinuierlich dem Markt ausgesetzt, wenn die Bedingungen zwischen bullischen und bearischen Ausrichtungen wechseln.
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 ThreeEmaStrategy : 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 ThreeEmaStrategy()
	{
		_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;
	}
}