Ver en GitHub

Estrategia MSL EA

Descripción general

MSL EA es una estrategia de ruptura que construye líneas dinámicas de soporte y resistencia a partir de extremos locales recientes. La estrategia detecta máximos y mínimos fractales de corto plazo, los ajusta por una distancia especificada en ticks, y abre posiciones cuando el precio cierra más allá de estos niveles. Fue convertida desde la implementación original en MQL4.

Cómo funciona

  1. El algoritmo rastrea los máximos y mínimos de las velas para determinar los extremos locales.
  2. El máximo más alto y el mínimo más bajo entre los últimos Level extremos detectados se almacenan como líneas de resistencia y soporte.
  3. Cada línea se desplaza por Distance ticks para tener en cuenta el ruido del mercado.
  4. Cuando el precio de cierre rompe por encima de la línea superior, se abre una posición larga; cuando rompe por debajo de la línea inferior, se abre una posición corta.
  5. El número de operaciones simultáneas está limitado por Max Trades.

Parámetros

  • Max Trades – máximo de posiciones abiertas permitidas.
  • Level – número de extremos locales utilizados para construir los niveles.
  • Distance – desplazamiento desde el extremo en ticks al colocar las líneas.
  • Candle Type – marco temporal de las velas procesadas por la estrategia.

Notas

Esta versión en C# utiliza la API de alto nivel de StockSharp e incluye comentarios en inglés. Las funciones de gestión de riesgo de la librería auxiliar original de MQL4 se simplifican a comprobaciones básicas de posición.

using System;
using System.Linq;
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>
/// MSL EA strategy.
/// Builds support and resistance lines from local extremes and trades breakouts.
/// </summary>
public class MsleaStrategy : Strategy
{
	private readonly StrategyParam<int> _maxTrades;
	private readonly StrategyParam<int> _level;
	private readonly StrategyParam<int> _distance;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _highLevels = new();
	private readonly List<decimal> _lowLevels = new();

	private decimal? _prevHigh1;
	private decimal? _prevHigh2;
	private decimal? _prevLow1;
	private decimal? _prevLow2;

	private decimal? _msh;
	private decimal? _msl;

	/// <summary>
	/// Maximum allowed open trades.
	/// </summary>
	public int MaxTrades
	{
		get => _maxTrades.Value;
		set => _maxTrades.Value = value;
	}

	/// <summary>
	/// Number of local extremes used to build levels.
	/// </summary>
	public int Level
	{
		get => _level.Value;
		set => _level.Value = value;
	}

	/// <summary>
	/// Distance from extremes in ticks.
	/// </summary>
	public int Distance
	{
		get => _distance.Value;
		set => _distance.Value = value;
	}

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

	/// <summary>
	/// Initialize Mslea strategy.
	/// </summary>
	public MsleaStrategy()
	{
		_maxTrades = Param(nameof(MaxTrades), 2)
			.SetGreaterThanZero()
			.SetDisplay("Max Trades", "Maximum simultaneous trades", "General");

		_level = Param(nameof(Level), 1)
			.SetGreaterThanZero()
			.SetDisplay("Level", "Number of extremes to look back", "General");

		_distance = Param(nameof(Distance), 4)
			.SetGreaterThanZero()
			.SetDisplay("Distance", "Offset from extreme in ticks", "General");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_highLevels.Clear();
		_lowLevels.Clear();
		_prevHigh1 = _prevHigh2 = _prevLow1 = _prevLow2 = null;
		_msh = _msl = null;
	}

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

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

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

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

		if (_prevHigh2 is decimal h2 && _prevHigh1 is decimal h1 && h2 < h1 && h1 > candle.HighPrice)
			AddHigh(h1);

		if (_prevLow2 is decimal l2 && _prevLow1 is decimal l1 && l2 > l1 && l1 < candle.LowPrice)
			AddLow(l1);

		_prevHigh2 = _prevHigh1;
		_prevHigh1 = candle.HighPrice;
		_prevLow2 = _prevLow1;
		_prevLow1 = candle.LowPrice;

		if (_msh is decimal top && _msl is decimal bottom)
		{
			var step = Security.PriceStep ?? 0.01m;
			var offset = step * Distance;
			var upper = top + offset;
			var lower = bottom - offset;

			if (candle.ClosePrice > upper && Position <= 0)
				BuyMarket();
			else if (candle.ClosePrice < lower && Position >= 0)
				SellMarket();
		}
	}

	private void AddHigh(decimal high)
	{
		_highLevels.Insert(0, high);
		TrimList(_highLevels);
		_msh = GetMax(_highLevels);
	}

	private void AddLow(decimal low)
	{
		_lowLevels.Insert(0, low);
		TrimList(_lowLevels);
		_msl = GetMin(_lowLevels);
	}

	private void TrimList(List<decimal> list)
	{
		while (list.Count > Level)
			list.RemoveAt(list.Count - 1);
	}

	private static decimal GetMax(List<decimal> list)
	{
		var max = list[0];
		for (var i = 1; i < list.Count; i++)
		{
			var v = list[i];
			if (v > max)
				max = v;
		}
		return max;
	}

	private static decimal GetMin(List<decimal> list)
	{
		var min = list[0];
		for (var i = 1; i < list.Count; i++)
		{
			var v = list[i];
			if (v < min)
				min = v;
		}
		return min;
	}
}