Ver en GitHub

Estrategia Diff TF MA

Descripción general

  • Esta estrategia es un port a StockSharp del asesor experto de MetaTrader "Diff_TF_MA_EA".
  • Las señales de trading provienen de comparar una media móvil simple calculada en un marco temporal superior con otra media móvil reescalada al marco temporal de trading.
  • El código mantiene solo las velas terminadas, refleja las reglas de cruce originales y cierra cualquier exposición opuesta antes de abrir una nueva posición.

Parámetros

Nombre Descripción
MaPeriod Longitud de la media móvil simple calculada en el marco temporal superior.
CandleType Marco temporal de trading usado para la generación de órdenes.
HigherCandleType Marco temporal superior que proporciona la media móvil de referencia.
ReverseSignals Invierte las reglas de cruce (comprar en cruce bajista y vender en cruce alcista).
Volume Volumen de la estrategia usado por las llamadas BuyMarket/SellMarket (establecido mediante la propiedad base Strategy.Volume).

Lógica de trading

  1. Suscribirse tanto al marco temporal de trading (CandleType) como al marco temporal superior (HigherCandleType).
  2. Construir una media móvil simple con longitud MaPeriod en el marco temporal superior.
  3. Convertir la longitud del marco temporal superior en el marco temporal de trading multiplicando por la relación de duraciones del marco temporal y ejecutar otra media móvil en las velas de trading.
  4. Almacenar los dos últimos valores completados para ambas medias móviles y verificar los cruces en cada vela de trading terminada.
  5. Abrir o revertir a una posición larga cuando la MA del marco temporal superior cruza por encima de la MA de trading (a menos que ReverseSignals sea true).
  6. Abrir o revertir a una posición corta cuando la MA del marco temporal superior cruza por debajo de la MA de trading (a menos que ReverseSignals sea true).
  7. Las posiciones se aplanan y giran enviando suficiente volumen para compensar cualquier exposición existente.

Notas de uso

  • Elegir marcos temporales compatibles: el marco temporal superior generalmente debe ser más grande que el marco temporal de trading para que la longitud reescalada sea significativa.
  • El volumen predeterminado es 1. Ajustar Strategy.Volume antes de iniciar la estrategia si se requiere otro tamaño.
  • Los stops y take-profits de la versión de MetaTrader no están reproducidos; la gestión de riesgos se puede adjuntar a través de las protecciones de StockSharp si es necesario.
  • Cuando ReverseSignals está habilitado, las acciones alcistas y bajistas se intercambian mientras el resto de la lógica permanece sin cambios.
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));
		}
	}
}