Ver en GitHub

Estrategia Milestone Trend

Esta estrategia es un port en StockSharp del asesor experto Milestone 22.5. Opera retrocesos dentro de una tendencia combinando dos medias móviles suavizadas con un filtro de volatilidad y de picos. Cuando una vela rompe el extremo de la barra anterior y la media rápida confirma el movimiento, se abre una posición en la dirección de la tendencia dominante. El ATR evita operar en mercados tranquilos y los cuerpos de velas grandes se tratan como picos.

Las pruebas retrospectivas de la versión MQL original muestran buen rendimiento en los principales pares de divisas. La traducción en C# se enfoca en la claridad y utiliza solo órdenes de mercado para entradas y salidas.

Detalles

  • Criterios de entrada:
    • Fuerza de tendencia entre MinTrend y MaxTrend.
    • La vela rompe el máximo o mínimo anterior y la SMA rápida confirma.
    • ATR por encima de MinRange y cuerpo de vela por debajo de CandleSpike.
  • Largo/Corto: Ambas direcciones.
  • Criterios de salida: La señal opuesta cierra la posición.
  • Stops: No implementados; la señal opuesta actúa como stop.
  • Valores predeterminados:
    • SlowMaPeriod = 120
    • FastMaPeriod = 30
    • AtrPeriod = 14
    • MinTrend = 10
    • MaxTrend = 100
    • MinRange = 5
    • CandleSpike = 10
    • CandleType = TimeSpan.FromMinutes(1)
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: SMA, ATR
    • Stops: No
    • Complejidad: Intermedio
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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>
/// Milestone trend strategy using EMA crossover with trend confirmation.
/// </summary>
public class MilestoneTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	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 MilestoneTrendStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "General");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "General");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

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

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

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

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

		if (!_hasPrev)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevFast <= _prevSlow && fastVal > slowVal;
		var crossDown = _prevFast >= _prevSlow && fastVal < slowVal;

		if (crossUp && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}