Auf GitHub ansehen

EMA Pullback-Strategie

Überblick

Die EMA Pullback-Strategie ist eine High-Level-Portierung des MetaTrader „Ema“-Expertenberaters. Es beobachtet ein Paar exponentieller gleitender Durchschnitte (EMA), wobei die Perioden 5 und 10 anhand der mittleren Kerzenpreise berechnet werden. Wenn ein bullischer oder bärischer Crossover auftritt, wartet die Strategie darauf, dass der Preis in Richtung des Extrems der vorherigen Kerze zurückkehrt, bevor sie in die Richtung des Crossovers eintritt. Feste Take-Profit- und Stop-Loss-Werte, gemessen in Preispunkten, steuern das Risiko, sobald die Position geöffnet ist.

Handelslogik

  1. Abonnieren Sie die konfigurierte Kerzenserie (Standard: 5-Minuten-Zeitrahmen) und berechnen Sie zwei EMAs zum Medianpreis (high + low) / 2.
  2. Erkennen Sie einen bullischen Crossover, wenn der schnelle EMA den langsamen EMA kreuzt, oder einen bärischen Crossover, wenn der schnelle EMA den langsamen EMA kreuzt.
  3. Aktivieren Sie einen Pullback-Einstieg, nachdem der Crossover erfolgt ist:
    • Warten Sie bei einem Long-Setup, bis der Schlusskurs auf das vorherige Kerzenhoch abzüglich des MoveBackPoints-Versatzes zurückgeht, während der schnelle EMA um mindestens zwei Preispunkte über dem langsamen EMA bleibt.
    • Warten Sie bei einem kurzen Setup, bis der Schlusskurs zum vorherigen Kerzentief zuzüglich des MoveBackPoints-Versatzes zurückkehrt, während der langsame EMA um mindestens zwei Preispunkte über dem schnellen EMA bleibt.
  4. Wenn die Pullback-Bedingung erfüllt ist, senden Sie eine Marktorder mit dem konfigurierten Handelsvolumen.
  5. Berechnen Sie beim Einstieg die statischen Take-Profit- und Stop-Loss-Werte mithilfe der Einstellungen TakeProfitPoints und StopLossPoints, umgewandelt in absolute Preisabweichungen vom Einstiegspreis.
  6. Beobachten Sie jede fertige Kerze und schließen Sie die Position, sobald entweder das Take-Profit- oder Stop-Loss-Niveau durch das Hoch/Tief der Kerze berührt wird.

Parameter

Name Standard Beschreibung
TradeVolume 0.1 Für jede Marktorder verwendetes Volumen.
FastLength 5 Periode des schnellen EMA, angewendet auf Durchschnittspreise.
SlowLength 10 Zeitraum der langsamen EMA angewendet auf mittlere Preise.
MoveBackPoints 3 Rückzugsdistanz in Preispunkten, gemessen vom Extrem der vorherigen Kerze.
TakeProfitPoints 5 Take-Profit-Distanz, in Preispunkten.
StopLossPoints 20 Stop-Loss-Distanz, in Preispunkten.
CandleType 5m Zeitrahmen für Kerzenabonnements und Indikatorberechnungen.

Notizen

  • Es werden nur vollständig ausgebildete Kerzen verarbeitet, um vorzeitige Signale zu vermeiden.
  • Die Strategie gleicht die Eigenschaft Strategy.Volume beim Start automatisch mit dem Parameter TradeVolume aus.
  • Alle Berechnungen basieren auf dem Instrument PriceStep, um punktbasierte Entfernungen in absolute Preise umzurechnen.
  • Die Strategie eröffnet jeweils höchstens eine Position und erfordert einen neuen EMA-Crossover, bevor ein weiterer Trade vorbereitet wird.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA Pullback strategy - fast/slow EMA crossover with pullback entry.
/// After a bullish crossover, waits for a pullback to fast EMA to enter long.
/// After a bearish crossover, waits for a pullback to fast EMA to enter short.
/// </summary>
public class EmaPullbackStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevClose;
	private bool _hasPrev;
	private bool _bullishCross;
	private bool _bearishCross;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public EmaPullbackStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 8)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _prevClose = 0m; _hasPrev = false; _bullishCross = false; _bearishCross = false; }

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

		_hasPrev = false;
		_bullishCross = false;
		_bearishCross = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

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

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_prevClose = close;
			_hasPrev = true;
			return;
		}

		// Detect crossovers
		if (_prevFast <= _prevSlow && fast > slow)
		{
			_bullishCross = true;
			_bearishCross = false;
		}
		else if (_prevFast >= _prevSlow && fast < slow)
		{
			_bearishCross = true;
			_bullishCross = false;
		}

		// Pullback entry: after bullish cross, wait for close to touch fast EMA
		if (_bullishCross && fast > slow && _prevClose > _prevFast && close <= fast && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
			_bullishCross = false;
		}
		// Pullback entry: after bearish cross, wait for close to touch fast EMA
		else if (_bearishCross && fast < slow && _prevClose < _prevFast && close >= fast && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
			_bearishCross = false;
		}
		// Exit on opposite crossover
		else if (Position > 0 && fast < slow)
		{
			SellMarket();
		}
		else if (Position < 0 && fast > slow)
		{
			BuyMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
		_prevClose = close;
	}
}