Esta estratégia porta o consultor especialista MetaTrader 5 Exp_ColorMETRO_MMRec_Duplex para o StockSharp. O robô original executa dois módulos independentes do indicador ColorMETRO (um longo, um curto) e aplica uma sobreposição MMRec (recálculo de gestão de dinheiro) que reduz o tamanho da posição após perdas repetidas. A versão em C# replica esse comportamento usando a API de alto nível do StockSharp para assinaturas de velas e roteamento de ordens.
Lógica de Negociação
Dois indicadores ColorMETRO distintos operam em tipos de velas configuráveis. O módulo longo gerencia apenas exposição comprada, enquanto o módulo curto controla a exposição vendida.
Cada indicador produz um envelope RSI escalonado rápido e lento. A estratégia imita as chamadas CopyBuffer do MQL5 armazenando valores históricos e inspecionando a barra definida por SignalBar.
Uma entrada longa é gerada quando a banda rápida cruza abaixo da banda lenta na barra inspecionada, enquanto a barra anterior ainda tinha a banda rápida acima da banda lenta. Qualquer posição vendida aberta é achatada antes de abrir o novo comprado.
Saídas longas ocorrem quando a banda lenta na barra anterior inspecionada fica acima da banda rápida, sinalizando um regime baixista no EA original.
Entradas e saídas curtas replicam a lógica longa (cruzamento acima para entradas, linha rápida acima da lenta na barra anterior para saídas).
Apenas velas terminadas são processadas e a negociação é bloqueada até que o indicador reporte ambas as bandas como prontas, reproduzindo o período de aquecimento do MetaTrader.
Gestão de Dinheiro (MMRec)
Strategy.Volume define o tamanho do lote de referência. Os módulos longo e curto multiplicam-no por seus respectivos coeficientes LongMm/ShortMm ao dimensionar novas ordens.
Após cada operação concluída, a estratégia registra se o resultado foi uma perda (baseado em preços de fechamento de velas, assim como o EA que inspeciona negociações históricas).
Se as TotalTrigger operações mais recentes de um módulo contiverem pelo menos LossTrigger perdedoras, o módulo muda para o multiplicador reduzido (SmallMm). Uma vez que a contagem de perdas cai abaixo do limiar, o multiplicador padrão é restaurado automaticamente.
Reversões de posição primeiro finalizam o resultado da operação existente (atualizando os contadores MMRec) antes de dimensionar e abrir a direção oposta.
Notas sobre o Indicador
ColorMetroMmrecIndicator é um port fiel do indicador personalizado ColorMETRO. Ele alimenta os mesmos envelopes rápidos/lentos impulsionados por um núcleo RSI com rastreamento de passos e memória de tendência.
O indicador expõe o RSI interno e uma flag de prontidão para que a estratégia possa ignorar valores incompletos exatamente como a implementação MQL faz.
Parâmetros
Grupo
Nome
Descrição
Comprado
LongCandleType
Tipo de vela usado para o módulo ColorMETRO comprado.
Comprado
LongTotalTrigger
Número de operações longas concluídas inspecionadas ao avaliar MMRec.
Comprado
LongLossTrigger
Contagem de perdas que ativa o multiplicador longo reduzido.
Comprado
LongSmallMm
Multiplicador reduzido aplicado a operações longas após uma sequência de perdas.
Comprado
LongMm
Multiplicador padrão para operações longas.
Comprado
LongEnableOpen
Habilita a abertura de posições longas.
Comprado
LongEnableClose
Habilita o fechamento de posições longas.
Comprado
LongPeriodRsi
Comprimento RSI usado dentro do indicador ColorMETRO longo.
Comprado
LongStepSizeFast
Tamanho do passo do envelope rápido para o módulo longo.
Comprado
LongStepSizeSlow
Tamanho do passo do envelope lento para o módulo longo.
Comprado
LongSignalBar
Deslocamento histórico (em barras fechadas) usado ao ler valores do indicador.
Comprado
LongMagic
Número mágico MT5 original, mantido como referência.
Comprado
LongStopLossTicks
Marcador de distância de stop-loss do EA (não aplicado).
Comprado
LongTakeProfitTicks
Marcador de distância de take-profit do EA (não aplicado).
Comprado
LongDeviationTicks
Marcador de slippage permitido do EA (não aplicado).
Comprado
LongMarginMode
Flag de modo MM mantido para compatibilidade (lógica usa multiplicadores brutos).
Vendido
ShortCandleType
Tipo de vela usado para o módulo ColorMETRO vendido.
Vendido
ShortTotalTrigger
Número de operações curtas concluídas inspecionadas ao avaliar MMRec.
Vendido
ShortLossTrigger
Contagem de perdas que ativa o multiplicador curto reduzido.
Vendido
ShortSmallMm
Multiplicador reduzido aplicado a operações curtas após uma sequência de perdas.
Vendido
ShortMm
Multiplicador padrão para operações curtas.
Vendido
ShortEnableOpen
Habilita a abertura de posições curtas.
Vendido
ShortEnableClose
Habilita o fechamento de posições curtas.
Vendido
ShortPeriodRsi
Comprimento RSI usado dentro do indicador ColorMETRO curto.
Vendido
ShortStepSizeFast
Tamanho do passo do envelope rápido para o módulo curto.
Vendido
ShortStepSizeSlow
Tamanho do passo do envelope lento para o módulo curto.
Vendido
ShortSignalBar
Deslocamento histórico (em barras fechadas) usado ao ler valores do indicador.
Vendido
ShortMagic
Número mágico MT5 original, mantido como referência.
Vendido
ShortStopLossTicks
Marcador de distância de stop-loss do EA (não aplicado).
Vendido
ShortTakeProfitTicks
Marcador de distância de take-profit do EA (não aplicado).
Vendido
ShortDeviationTicks
Marcador de slippage permitido do EA (não aplicado).
Vendido
ShortMarginMode
Flag de modo MM mantido para compatibilidade (lógica usa multiplicadores brutos).
Notas de Implementação
A estratégia depende de SubscribeCandles(...).BindEx(...) e evita acesso direto a buffers, alinhando-se com as diretrizes de conversão.
Stops protetores do EA são deixados apenas como parâmetros; os usuários podem anexar StartProtection ou módulos de risco personalizados se necessário.
Ambos os módulos compartilham a mesma instância de instrumento, mas mantêm suas próprias assinaturas de velas e contadores MMRec, correspondendo ao layout duplex do MetaTrader.
Todos os comentários no código são fornecidos em inglês e a lógica evita usar chamadas de API proibidas como GetTrades.
Aviso
Este port reproduz a estrutura lógica do EA original, mas a qualidade de execução depende do broker conectado, feed de dados e configuração do StockSharp. Sempre valide o comportamento em dados históricos e de demonstração antes de negociar com 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()