La Estrategia Lego EA es un puerto directo del asesor experto "Lego EA" de MetaTrader. Utiliza una combinación configurable de filtros técnicos—Commodity Channel Index, dobles medias móviles, oscilador estocástico, Accelerator Oscillator, DeMarker y Awesome Oscillator—para validar entradas y salidas. Cada filtro puede activarse o desactivarse independientemente para entradas y salidas, lo que permite reconstruir el "Lego" original bloque por bloque o experimentar con configuraciones personalizadas.
Parámetros
Volume – volumen de trading base usado cuando la operación anterior fue rentable.
LotMultiplier – multiplicador aplicado al último volumen ejecutado después de una operación perdedora (recuperación tipo martingale).
StopLossPips – stop de protección expresado en pips (convertido internamente usando el tamaño de tick del símbolo).
TakeProfitPips – objetivo de ganancia en pips.
UseCciForEntry / UseCciForExit – activar el filtro CCI al abrir o cerrar posiciones.
UseMaForEntry / UseMaForExit – usar el cruce de medias móviles rápida/lenta para confirmaciones.
UseStochasticForEntry / UseStochasticForExit – requerir alineación del estocástico %K/%D dentro de los umbrales configurados.
UseAcceleratorForEntry / UseAcceleratorForExit – requerir patrones de aceleración del Accelerator Oscillator.
UseDemarkerForEntry / UseDemarkerForExit – aplicar comprobaciones de nivel DeMarker.
UseAwesomeForEntry / UseAwesomeForExit – incluir confirmación de momentum del Awesome Oscillator.
CciPeriod – período del Commodity Channel Index.
MaFastPeriod / MaSlowPeriod – longitudes de lookback para las medias móviles rápida y lenta.
MaShift – número de barras completadas para desplazar los valores de media móvil hacia atrás en el tiempo, reproduciendo el parámetro de desplazamiento horizontal de MT5.
MaMethod – método de suavizado (simple, exponencial, suavizado o ponderado).
MaPrice – fuente de precio de la vela suministrada a ambas medias móviles.
StochasticKPeriod, StochasticDPeriod, StochasticSlow – configuración del oscilador estocástico.
StochasticLevelUp / StochasticLevelDown – umbrales de sobrecompra/sobreventa usados para señales.
DemarkerPeriod, DemarkerLevelUp, DemarkerLevelDown – configuración del oscilador DeMarker.
CandleType – marco temporal de la serie de velas usada por todos los indicadores.
Flujo de trabajo de trading
En cada vela completada, la estrategia recopila valores de indicadores de los filtros seleccionados.
Cada filtro calcula la disponibilidad de compra/venta basándose en la barra anterior completamente formada (coincidiendo con el desplazamiento iGetArray(..., 1) del EA original).
Solo se permite una entrada larga cuando todos los filtros de entrada habilitados acuerdan una señal alcista. Del mismo modo, una entrada corta requiere confirmación bajista unánime.
Si la cuenta está plana y aparece una señal de entrada válida, se envía una orden de mercado usando el Volume base o el último volumen de operación perdedora multiplicado por LotMultiplier.
Cuando ya hay una posición, los filtros de salida habilitados se evalúan de la misma manera. La posición se cierra solo cuando todos los filtros de salida acuerdan una señal opuesta.
La protección de stop-loss y take-profit se instala automáticamente usando StartProtection, convirtiendo las entradas en pips a distancias de precio absolutas basadas en el tamaño de tick del símbolo.
Gestión del dinero
Después de una operación ganadora, la siguiente orden vuelve al Volume base.
Después de una operación perdedora, el volumen se multiplica por LotMultiplier, emulando la lógica de escalada de lotes del EA original.
Los límites de volumen impuestos por el exchange (paso, mín. y máx.) se aplican antes de cada orden.
Notas y diferencias respecto a la versión de MetaTrader
Las fuentes de precio del indicador se mapean a equivalentes de StockSharp. CCI usa el precio típico internamente y las medias móviles usan la fuente MaPrice seleccionada.
Todos los cálculos de indicadores dependen de velas completamente cerradas. Esto evita datos parcialmente formados y emula el procesamiento de "nueva barra" del EA.
Las comprobaciones del nivel de freeze y la colocación manual de precio de SL/TP son manejadas por el servicio StartProtection de StockSharp.
Las salidas parciales de posición actualizan el estado de seguimiento de pérdidas solo cuando toda la posición es flat, coincidiendo con la lógica DEAL_ENTRY_OUT del EA.
Consejos de uso
Comience con la configuración original (filtro MA habilitado, otros filtros deshabilitados) para reproducir el comportamiento base, luego habilite filtros adicionales para mejorar la calidad de la señal.
Monitoree la exposición de la cuenta cuando use valores altos de LotMultiplier; el riesgo crece rápidamente durante rachas de pérdidas.
Combine la estrategia con el Backtester para confirmar si su mezcla de filtros elegida se alinea con los instrumentos que planea operar.
Esta estrategia actualmente no tiene versión en Python.
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>
/// Lego EA multi-indicator trend following strategy using SMA crossover.
/// Buys when fast SMA crosses above slow SMA, sells on reverse.
/// </summary>
public class LegoEaStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SimpleMovingAverage _fast;
private SimpleMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="LegoEaStrategy"/> class.
/// </summary>
public LegoEaStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 67)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SimpleMovingAverage { Length = FastPeriod };
_slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class lego_ea_strategy(Strategy):
"""
Lego EA: SMA crossover with SL/TP in price steps and cooldown.
"""
def __init__(self):
super(lego_ea_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 67) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(lego_ea_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(lego_ea_strategy, self).OnStarted2(time)
fast = SimpleMovingAverage()
fast.Length = self._fast_period.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast
self._prev_slow = slow
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl = self._stop_loss_points.Value
tp = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl > 0 and close <= self._entry_price - sl * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast
self._prev_slow = slow
return
if tp > 0 and close >= self._entry_price + tp * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and self._entry_price > 0:
if sl > 0 and close >= self._entry_price + sl * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast
self._prev_slow = slow
return
if tp > 0 and close <= self._entry_price - tp * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return lego_ea_strategy()