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
El algoritmo rastrea los máximos y mínimos de las velas para determinar los extremos locales.
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.
Cada línea se desplaza por Distance ticks para tener en cuenta el ruido del mercado.
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.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class mslea_strategy(Strategy):
def __init__(self):
super(mslea_strategy, self).__init__()
self._max_trades = self.Param("MaxTrades", 2) \
.SetDisplay("Max Trades", "Maximum simultaneous trades", "General")
self._level = self.Param("Level", 1) \
.SetDisplay("Level", "Number of extremes to look back", "General")
self._distance = self.Param("Distance", 4) \
.SetDisplay("Distance", "Offset from extreme in ticks", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._high_levels = []
self._low_levels = []
self._prev_high1 = None
self._prev_high2 = None
self._prev_low1 = None
self._prev_low2 = None
self._msh = None
self._msl = None
@property
def max_trades(self):
return self._max_trades.Value
@property
def level(self):
return self._level.Value
@property
def distance(self):
return self._distance.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mslea_strategy, self).OnReseted()
self._high_levels = []
self._low_levels = []
self._prev_high1 = None
self._prev_high2 = None
self._prev_low1 = None
self._prev_low2 = None
self._msh = None
self._msl = None
def OnStarted2(self, time):
super(mslea_strategy, self).OnStarted2(time)
self._high_levels = []
self._low_levels = []
self._prev_high1 = None
self._prev_high2 = None
self._prev_low1 = None
self._prev_low2 = None
self._msh = None
self._msl = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self._prev_high2 is not None and self._prev_high1 is not None:
if self._prev_high2 < self._prev_high1 and self._prev_high1 > high:
self._add_high(self._prev_high1)
if self._prev_low2 is not None and self._prev_low1 is not None:
if self._prev_low2 > self._prev_low1 and self._prev_low1 < low:
self._add_low(self._prev_low1)
self._prev_high2 = self._prev_high1
self._prev_high1 = high
self._prev_low2 = self._prev_low1
self._prev_low1 = low
if self._msh is not None and self._msl is not None:
step = 0.01
offset = step * self.distance
upper = self._msh + offset
lower = self._msl - offset
close = float(candle.ClosePrice)
if close > upper and self.Position <= 0:
self.BuyMarket()
elif close < lower and self.Position >= 0:
self.SellMarket()
def _add_high(self, high):
self._high_levels.insert(0, high)
while len(self._high_levels) > self.level:
self._high_levels.pop()
self._msh = max(self._high_levels)
def _add_low(self, low):
self._low_levels.insert(0, low)
while len(self._low_levels) > self.level:
self._low_levels.pop()
self._msl = min(self._low_levels)
def CreateClone(self):
return mslea_strategy()