Auf GitHub ansehen

X2MA JFatl Crossover-Strategie

Diese Strategie ist eine StockSharp-Adaption des MetaTrader-Experten Exp_X2MA_JFatl. Sie kombiniert einen schnellen Simple Moving Average (SMA) mit einem langsamen Jurik Moving Average (JMA) und einem zusätzlichen Jurik-Filter zur Bestätigung der Trendrichtung. Trades werden eröffnet, wenn der schnelle Durchschnitt den langsamen kreuzt und der Preis auf derselben Seite des Filters liegt. Positionen werden geschlossen, wenn der Preis gegen den Filter läuft oder ein entgegengesetzter Crossover auftritt.

Details

  • Einstiegskriterien:
    • Long: SMA_fast kreuzt über JMA_slow und Close > JMA_filter.
    • Short: SMA_fast kreuzt unter JMA_slow und Close < JMA_filter.
  • Ausstiegskriterien:
    • Preis bewegt sich auf die entgegengesetzte Seite des Filters.
    • Entgegengesetzter Crossover der Durchschnitte.
  • Long/Short: Beide Seiten.
  • Stops: Standardmäßig nicht verwendet.
  • Standardwerte:
    • Fast MA Length = 5.
    • Slow MA Length = 12.
    • Filter Length = 20.
  • Filter:
    • Kategorie: Trendfolge
    • Richtung: Beide
    • Indikatoren: Mehrere (SMA, JMA)
    • Stops: Nein
    • Komplexität: Moderat
    • Zeitrahmen: Kurzfristig
    • Saisonalität: Nein
    • Neuronale Netze: Nein
    • Divergenz: Nein
    • Risikolevel: Mittel
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// X2MA with JFATL filter strategy.
/// Opens long when the fast SMA crosses above the slow Jurik MA and price is above the filter.
/// Opens short when the fast SMA crosses below the slow Jurik MA and price is below the filter.
/// </summary>
public class X2MaJfatlStrategy : Strategy
{
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _filterLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevDiff;
	private bool _isInitialized;
	private int _barsSinceTrade;

	/// <summary>
	/// Fast moving average length.
	/// </summary>
	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	/// <summary>
	/// Slow Jurik moving average length.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	/// <summary>
	/// Jurik filter length.
	/// </summary>
	public int FilterLength
	{
		get => _filterLength.Value;
		set => _filterLength.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public X2MaJfatlStrategy()
	{
		_fastLength = Param(nameof(FastLength), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA Length", "Length of the fast moving average", "Parameters")
			
			.SetOptimize(5, 20, 1);

		_slowLength = Param(nameof(SlowLength), 13)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA Length", "Length of the slow Jurik MA", "Parameters")
			
			.SetOptimize(10, 40, 2);

		_filterLength = Param(nameof(FilterLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Filter Length", "Length of the Jurik filter", "Parameters")
			
			.SetOptimize(10, 60, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles for calculation", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevDiff = 0m;
		_isInitialized = false;
		_barsSinceTrade = 10;
	}

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

		_prevDiff = 0m;
		_isInitialized = false;
		_barsSinceTrade = 10;

		var fastMa = new SMA { Length = FastLength };
		var slowMa = new JurikMovingAverage { Length = SlowLength };
		var filterMa = new JurikMovingAverage { Length = FilterLength };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(fastMa, slowMa, filterMa, Process)
			.Start();

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

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

		_barsSinceTrade++;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (!_isInitialized)
		{
			_prevDiff = fastValue - slowValue;
			_isInitialized = true;
			return;
		}

		var diff = fastValue - slowValue;

		// Exit if price moves against the filter
		if (Position > 0 && candle.ClosePrice < filterValue)
		{
			SellMarket();
			_barsSinceTrade = 0;
		}
		else if (Position < 0 && candle.ClosePrice > filterValue)
		{
			BuyMarket();
			_barsSinceTrade = 0;
		}

		// Crossover entries
		if (_barsSinceTrade >= 5 && _prevDiff <= 0m && diff > 0m && candle.ClosePrice > filterValue && Position <= 0)
		{
			BuyMarket();
			_barsSinceTrade = 0;
		}
		else if (_barsSinceTrade >= 5 && _prevDiff >= 0m && diff < 0m && candle.ClosePrice < filterValue && Position >= 0)
		{
			SellMarket();
			_barsSinceTrade = 0;
		}

		_prevDiff = diff;
	}
}