Auf GitHub ansehen

MA-nach-MA-Kreuzungs-Strategie

Diese Strategie implementiert einen doppelt geglätteten gleitenden Durchschnitt-Crossover. Die Preisreihe wird durch einen schnellen exponentiellen gleitenden Durchschnitt (EMA) geglättet. Das Ergebnis des schnellen EMA wird dann erneut durch einen langsameren EMA geglättet. Die beiden Reihen werden verglichen, um Signale zu generieren:

  • Eine Long-Position wird eröffnet, wenn der schnelle EMA den langsamen EMA nach oben kreuzt.
  • Eine Short-Position wird eröffnet, wenn der schnelle EMA den langsamen EMA nach unten kreuzt. Jede bestehende entgegengesetzte Position wird beim Crossover geschlossen.

Die Strategie funktioniert auf jedem Zeitrahmen von Kerzen.

Parameter

  • FastLength – Periode des schnellen EMA.
  • SlowLength – Periode des langsamen EMA, der auf den Ausgang des schnellen EMA angewendet wird.
  • EnableLong – Long-Positionen erlauben.
  • EnableShort – Short-Positionen erlauben.
  • CandleType – Kerzentyp für Berechnungen.

Details

  • Einstiegskriterien:
    • Long: schneller EMA kreuzt langsamen EMA nach oben.
    • Short: schneller EMA kreuzt langsamen EMA nach unten.
  • Long/Short: Beide Richtungen unterstützt.
  • Ausstiegskriterien:
    • Entgegengesetzter Crossover schließt eine bestehende Position.
  • Stops: Kein expliziter Stop-Loss oder Take-Profit wird verwendet.
  • Standardwerte:
    • FastLength = 7
    • SlowLength = 7
    • EnableLong = true
    • EnableShort = true
    • CandleType = 12-Stunden-Zeitrahmen
  • Filter:
    • Kategorie: Trendfolge
    • Richtung: Beide
    • Indikatoren: Moving averages
    • Stops: Nein
    • Komplexität: Grundlegend
    • Zeitrahmen: Beliebig
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>
/// Double smoothed moving average crossover strategy.
/// </summary>
public class MaByMaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _minSpreadPercent;
	private readonly StrategyParam<int> _cooldownBars;

	private bool _isInitialized;
	private bool _wasFastBelowSlow;
	private int _cooldownRemaining;

	public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
	public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public decimal MinSpreadPercent { get => _minSpreadPercent.Value; set => _minSpreadPercent.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public MaByMaStrategy()
	{
		_fastLength = Param(nameof(FastLength), 7)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA Length", "Period for fast EMA", "Indicator");

		_slowLength = Param(nameof(SlowLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA Length", "Period for slow EMA", "Indicator");

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

		_minSpreadPercent = Param(nameof(MinSpreadPercent), 0.003m)
			.SetDisplay("Minimum Spread %", "Minimum normalized spread between fast and slow EMA values", "Filters");

		_cooldownBars = Param(nameof(CooldownBars), 6)
			.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_isInitialized = false;
		_wasFastBelowSlow = false;
		_cooldownRemaining = 0;
	}

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

		var fastMa = new ExponentialMovingAverage { Length = FastLength };
		var slowMa = new ExponentialMovingAverage { Length = SlowLength };

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

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

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		var spreadPercent = slowValue != 0m ? Math.Abs(fastValue - slowValue) / slowValue : 0m;
		if (!_isInitialized)
		{
			_wasFastBelowSlow = fastValue < slowValue;
			_isInitialized = true;
			return;
		}

		var isFastBelowSlow = fastValue < slowValue;
		if (_cooldownRemaining == 0 && spreadPercent >= MinSpreadPercent)
		{
			if (_wasFastBelowSlow && !isFastBelowSlow && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();

				BuyMarket();
				_cooldownRemaining = CooldownBars;
			}
			else if (!_wasFastBelowSlow && isFastBelowSlow && Position >= 0)
			{
				if (Position > 0)
					SellMarket();

				SellMarket();
				_cooldownRemaining = CooldownBars;
			}
		}

		_wasFastBelowSlow = isFastBelowSlow;
	}
}