Auf GitHub ansehen

Diff TF MA-Strategie

Übersicht

  • Diese Strategie ist ein StockSharp-Port des MetaTrader-Expertenberaters "Diff_TF_MA_EA".
  • Handelssignale entstehen durch den Vergleich eines einfachen gleitenden Durchschnitts, der auf einem höheren Zeitrahmen berechnet wird, mit einem anderen gleitenden Durchschnitt, der auf den Handelszeitrahmen umskaliert wird.
  • Der Code behält nur abgeschlossene Kerzen, spiegelt die ursprünglichen Überkreuzungsregeln wider und schließt jede entgegengesetzte Exposition, bevor eine neue Position eröffnet wird.

Parameter

Name Beschreibung
MaPeriod Länge des einfachen gleitenden Durchschnitts, der auf dem höheren Zeitrahmen berechnet wird.
CandleType Handelszeitrahmen für die Auftragsgenerierung.
HigherCandleType Höherer Zeitrahmen, der den Referenz-gleitenden Durchschnitt liefert.
ReverseSignals Kehrt die Überkreuzungsregeln um (Kauf bei bärischem Kreuz und Verkauf bei bullischem Kreuz).
Volume Strategievolumen für BuyMarket/SellMarket-Aufrufe (über die Strategy.Volume-Eigenschaft gesetzt).

Handelslogik

  1. Sowohl den Handelszeitrahmen (CandleType) als auch den höheren Zeitrahmen (HigherCandleType) abonnieren.
  2. Einen einfachen gleitenden Durchschnitt mit Länge MaPeriod auf dem höheren Zeitrahmen erstellen.
  3. Die Länge des höheren Zeitrahmens in den Handelszeitrahmen umrechnen, indem mit dem Verhältnis der Zeitrahmen-Dauern multipliziert wird, und einen weiteren gleitenden Durchschnitt auf den Handelskerzen berechnen.
  4. Die letzten zwei abgeschlossenen Werte für beide gleitende Durchschnitte speichern und bei jeder abgeschlossenen Handelskerze auf Kreuzungen prüfen.
  5. Eine Long-Position eröffnen oder umkehren, wenn der höhere Zeitrahmen-MA den Handels-MA von unten nach oben kreuzt (es sei denn, ReverseSignals ist true).
  6. Eine Short-Position eröffnen oder umkehren, wenn der höhere Zeitrahmen-MA den Handels-MA von oben nach unten kreuzt (es sei denn, ReverseSignals ist true).
  7. Positionen werden abgeflacht und gedreht, indem genug Volumen gesendet wird, um jede bestehende Exposition zu kompensieren.

Verwendungshinweise

  • Kompatible Zeitrahmen wählen: der höhere Zeitrahmen sollte in der Regel größer als der Handelszeitrahmen sein, damit die umskalierte Länge sinnvoll ist.
  • Das Standardvolumen ist 1. Strategy.Volume vor dem Start der Strategie anpassen, wenn eine andere Größe benötigt wird.
  • Stops und Take-Profits der MetaTrader-Version werden nicht reproduziert; das Risikomanagement kann bei Bedarf über StockSharp-Schutzfunktionen angehängt werden.
  • Wenn ReverseSignals aktiviert ist, werden bullische und bärische Aktionen vertauscht, während der Rest der Logik unverändert bleibt.
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>
/// Two timeframe moving average crossover strategy.
/// </summary>
public class DiffTfMaStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<DataType> _higherCandleType;
	private readonly StrategyParam<bool> _reverseSignals;

	private SimpleMovingAverage _baseMa;
	private SimpleMovingAverage _higherMa;

	private decimal? _higherMaLast;
	private decimal? _higherMaPrev;
	private decimal? _baseMaLast;
	private decimal? _baseMaPrev;

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public DataType HigherCandleType
	{
		get => _higherCandleType.Value;
		set => _higherCandleType.Value = value;
	}

	public bool ReverseSignals
	{
		get => _reverseSignals.Value;
		set => _reverseSignals.Value = value;
	}

	public DiffTfMaStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Moving average length on the higher timeframe", "General")
			
			.SetOptimize(5, 30, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Base Candle", "Trading timeframe", "General");

		_higherCandleType = Param(nameof(HigherCandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Higher Candle", "Higher timeframe for confirmation", "General");

		_reverseSignals = Param(nameof(ReverseSignals), false)
			.SetDisplay("Reverse Signals", "Invert the crossover logic", "General");

		Volume = 0.1m;
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();

		_higherMaLast = null;
		_higherMaPrev = null;
		_baseMaLast = null;
		_baseMaPrev = null;
	}

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

		if (CandleType.Arg is not TimeSpan baseSpan || baseSpan <= TimeSpan.Zero)
			throw new InvalidOperationException("CandleType must contain a positive TimeSpan argument.");

		if (HigherCandleType.Arg is not TimeSpan higherSpan || higherSpan <= TimeSpan.Zero)
			throw new InvalidOperationException("HigherCandleType must contain a positive TimeSpan argument.");

		var ratio = higherSpan.TotalMinutes / baseSpan.TotalMinutes;
		var baseLength = Math.Max(1, (int)(MaPeriod * ratio));

		_baseMa = new SimpleMovingAverage { Length = baseLength };
		_higherMa = new SimpleMovingAverage { Length = MaPeriod };

		var higherSubscription = SubscribeCandles(HigherCandleType);
		higherSubscription.Bind(_higherMa, ProcessHigher).Start();

		var baseSubscription = SubscribeCandles(CandleType);
		baseSubscription.Bind(_baseMa, ProcessBase).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, baseSubscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessHigher(ICandleMessage candle, decimal higherMaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_higherMa.IsFormed)
			return;

		// Store the last two higher timeframe MA values for crossover comparison.
		_higherMaPrev = _higherMaLast;
		_higherMaLast = higherMaValue;
	}

	private void ProcessBase(ICandleMessage candle, decimal baseMaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_baseMa.IsFormed)
			return;

		// Track the last two base timeframe MA values.
		_baseMaPrev = _baseMaLast;
		_baseMaLast = baseMaValue;

		if (_higherMaPrev is not decimal higherPrev || _higherMaLast is not decimal higherLast)
			return;

		if (_baseMaPrev is not decimal basePrev || _baseMaLast is not decimal baseLast)
			return;

		var crossUp = higherPrev < basePrev && higherLast > baseLast;
		var crossDown = higherPrev > basePrev && higherLast < baseLast;

		if (ReverseSignals)
			(crossUp, crossDown) = (crossDown, crossUp);

		// Execute orders according to the detected crossover direction.
		if (crossUp && Position <= 0)
		{
			BuyMarket(Volume + Math.Abs(Position));
		}
		else if (crossDown && Position >= 0)
		{
			SellMarket(Volume + Math.Abs(Position));
		}
	}
}