Esta estrategia porta el asesor experto de MetaTrader 5 Exp_ColorMETRO_MMRec_Duplex a StockSharp. El robot original ejecuta dos módulos independientes del indicador ColorMETRO (uno largo, uno corto) y aplica una superposición MMRec (recálculo de gestión monetaria) que reduce el tamaño de la posición después de pérdidas repetidas. La versión en C# refleja ese comportamiento usando la API de alto nivel de StockSharp para suscripciones de velas y enrutamiento de órdenes.
Lógica de trading
Dos indicadores ColorMETRO distintos operan en tipos de velas configurables. El módulo largo solo gestiona exposición larga mientras el módulo corto controla la exposición corta.
Cada indicador produce un sobre RSI escalonado rápido y lento. La estrategia imita las llamadas CopyBuffer de MQL5 almacenando valores históricos e inspeccionando la barra definida por SignalBar.
Se genera una entrada larga cuando la banda rápida cruza por debajo de la banda lenta en la barra inspeccionada mientras la barra anterior aún tenía la banda rápida por encima de la banda lenta. Cualquier posición corta abierta se aplana antes de abrir el nuevo largo.
Las salidas largas ocurren cuando la banda lenta en la barra anterior inspeccionada está por encima de la banda rápida, señalando un régimen bajista en el EA original.
Las entradas y salidas cortas replican la lógica larga (cruce por encima para entradas, línea rápida por encima de la lenta en la barra anterior para salidas).
Solo se procesan velas terminadas y el trading se bloquea hasta que el indicador reporta ambas bandas como listas, reproduciendo el período de calentamiento de MetaTrader.
Gestión monetaria (MMRec)
Strategy.Volume define el tamaño de lote de referencia. Los módulos largo y corto lo multiplican por sus respectivos coeficientes LongMm/ShortMm al dimensionar nuevas órdenes.
Después de cada operación completada, la estrategia registra si el resultado fue una pérdida (basado en precios de cierre de velas, igual que el EA que inspecciona operaciones históricas).
Si las TotalTrigger operaciones más recientes de un módulo contienen al menos LossTrigger perdedoras, el módulo cambia al multiplicador reducido (SmallMm). Una vez que el conteo de pérdidas cae por debajo del umbral, el multiplicador predeterminado se restaura automáticamente.
Las reversiones de posición primero finalizan el resultado de la operación existente (actualizando los contadores MMRec) antes de dimensionar y abrir la dirección opuesta.
Notas sobre el indicador
ColorMetroMmrecIndicator es un port fiel del indicador personalizado ColorMETRO. Alimenta los mismos sobres rápidos/lentos impulsados por un núcleo RSI con seguimiento de pasos y memoria de tendencia.
El indicador expone el RSI interno y una bandera de preparación para que la estrategia pueda ignorar valores incompletos exactamente como lo hace la implementación MQL.
Parámetros
Grupo
Nombre
Descripción
Largo
LongCandleType
Tipo de vela usado para el módulo ColorMETRO largo.
Largo
LongTotalTrigger
Número de operaciones largas completadas inspeccionadas al evaluar MMRec.
Largo
LongLossTrigger
Conteo de pérdidas que activa el multiplicador largo reducido.
Largo
LongSmallMm
Multiplicador reducido aplicado a operaciones largas tras una racha de pérdidas.
Largo
LongMm
Multiplicador predeterminado para operaciones largas.
Largo
LongEnableOpen
Habilita la apertura de posiciones largas.
Largo
LongEnableClose
Habilita el cierre de posiciones largas.
Largo
LongPeriodRsi
Longitud RSI usada dentro del indicador ColorMETRO largo.
Largo
LongStepSizeFast
Tamaño de paso del sobre rápido para el módulo largo.
Largo
LongStepSizeSlow
Tamaño de paso del sobre lento para el módulo largo.
Largo
LongSignalBar
Desplazamiento histórico (en barras cerradas) usado al leer valores del indicador.
Largo
LongMagic
Número mágico MT5 original, conservado como referencia.
Largo
LongStopLossTicks
Marcador de distancia de stop-loss del EA (no aplicado).
Largo
LongTakeProfitTicks
Marcador de distancia de take-profit del EA (no aplicado).
Largo
LongDeviationTicks
Marcador de deslizamiento permitido del EA (no aplicado).
Largo
LongMarginMode
Indicador de modo MM conservado por compatibilidad (la lógica usa multiplicadores brutos).
Corto
ShortCandleType
Tipo de vela usado para el módulo ColorMETRO corto.
Corto
ShortTotalTrigger
Número de operaciones cortas completadas inspeccionadas al evaluar MMRec.
Corto
ShortLossTrigger
Conteo de pérdidas que activa el multiplicador corto reducido.
Corto
ShortSmallMm
Multiplicador reducido aplicado a operaciones cortas tras una racha de pérdidas.
Corto
ShortMm
Multiplicador predeterminado para operaciones cortas.
Corto
ShortEnableOpen
Habilita la apertura de posiciones cortas.
Corto
ShortEnableClose
Habilita el cierre de posiciones cortas.
Corto
ShortPeriodRsi
Longitud RSI usada dentro del indicador ColorMETRO corto.
Corto
ShortStepSizeFast
Tamaño de paso del sobre rápido para el módulo corto.
Corto
ShortStepSizeSlow
Tamaño de paso del sobre lento para el módulo corto.
Corto
ShortSignalBar
Desplazamiento histórico (en barras cerradas) usado al leer valores del indicador.
Corto
ShortMagic
Número mágico MT5 original, conservado como referencia.
Corto
ShortStopLossTicks
Marcador de distancia de stop-loss del EA (no aplicado).
Corto
ShortTakeProfitTicks
Marcador de distancia de take-profit del EA (no aplicado).
Corto
ShortDeviationTicks
Marcador de deslizamiento permitido del EA (no aplicado).
Corto
ShortMarginMode
Indicador de modo MM conservado por compatibilidad (la lógica usa multiplicadores brutos).
Notas de implementación
La estrategia usa SubscribeCandles(...).BindEx(...) y evita el acceso directo a buffers, alineándose con las pautas de conversión.
Los stops protectores del EA se dejan solo como parámetros; los usuarios pueden adjuntar StartProtection o módulos de riesgo personalizados si es necesario.
Ambos módulos comparten la misma instancia de instrumento pero mantienen sus propias suscripciones de velas y contadores MMRec, coincidiendo con el diseño dúplex de MetaTrader.
Todos los comentarios en el código se proporcionan en inglés y la lógica evita usar llamadas API prohibidas como GetTrades.
Aviso
Este port reproduce la estructura lógica del EA original, pero la calidad de ejecución depende del broker conectado, el feed de datos y la configuración de StockSharp. Siempre valide el comportamiento en datos históricos y de demostración antes de operar con capital real.
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>
/// ColorMETRO MMRec Duplex strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExpColorMetroMmrecDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ExpColorMetroMmrecDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA 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");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { 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;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_color_metro_mmrec_duplex_strategy(Strategy):
def __init__(self):
super(exp_color_metro_mmrec_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA 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._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(exp_color_metro_mmrec_duplex_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_color_metro_mmrec_duplex_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_color_metro_mmrec_duplex_strategy()