ColorMetroDuplexStrategy é uma conversão em C# do expert do MetaTrader 5 Exp_ColorMETRO_Duplex. O robô original usa duas instâncias independentes do indicador ColorMETRO para gerenciar os módulos de trading comprado e vendido. Cada módulo opera com sua própria assinatura de candles, avalia dois envelopes RSI escalonados produzidos pelo indicador ColorMETRO e, opcionalmente, abre ou fecha posições quando os envelopes rápido e lento se cruzam.
A versão do StockSharp mantém ambos os módulos e reproduz as mesmas regras de avaliação de sinais, usando a API de alto nível para assinaturas de candles, gerenciamento de ordens e vinculação de indicadores. Um ColorMetroIndicator personalizado está incluído para imitar a implementação iCustom do MT5, expondo as bandas rápida e lenta do ColorMETRO junto com o valor RSI interno.
Como funciona
Duas instâncias de SignalModule são criadas — Long e Short — cada uma com sua própria série de candles, configurações de ColorMETRO e opções de gerenciamento de operações.
Quando a estratégia inicia, cada módulo assina seu período configurado e vincula o ColorMetroIndicator por meio de SubscribeCandles(...).BindEx(...).
Para cada candle finalizado o indicador produz:
A banda rápida do ColorMETRO (envelope RSI rápido).
A banda lenta do ColorMETRO (envelope RSI lento).
O valor RSI subjacente (usado apenas como referência).
O módulo armazena o histórico do indicador e avalia os últimos dois valores usando o deslocamento SignalBar configurado (correspondendo à lógica CopyBuffer do MT5).
Regras de trading:
Módulo comprado
Abrir: a banda rápida estava acima da banda lenta na barra anterior e agora está abaixo ou igual.
Fechar: a banda lenta estava acima da banda rápida na barra anterior.
Módulo vendido
Abrir: a banda rápida estava abaixo da banda lenta na barra anterior e agora está acima ou igual.
Fechar: a banda lenta estava abaixo da banda rápida na barra anterior.
As ordens são roteadas via BuyMarket / SellMarket. A posição líquida atual é respeitada — operações opostas encerram a exposição existente antes de abrir uma nova.
Parâmetros
Cada módulo expõe um grupo de parâmetros dedicado. Os valores padrão espelham o expert de MT5.
Parâmetros de mercado compartilhados
Long_Volume, Short_Volume — tamanho da operação (lotes) usado para novas entradas.
Long_OpenAllowed, Short_OpenAllowed — habilitar ou desabilitar a abertura de operações para o módulo.
Long_CloseAllowed, Short_CloseAllowed — habilitar ou desabilitar saídas automáticas.
Long_MarginMode, Short_MarginMode — modo de gerenciamento de dinheiro mantido para compatibilidade (sem efeito neste port).
Long_StopLoss, Long_TakeProfit, Long_Deviation, Short_StopLoss, Short_TakeProfit, Short_Deviation — reservados para documentação; stops e controle de slippage não estão automatizados nesta versão.
Long_Magic, Short_Magic — números mágicos originais do MT5 preservados como referência.
Parâmetros do indicador
Long_CandleType, Short_CandleType — período para cada módulo ColorMETRO.
Long_PeriodRSI, Short_PeriodRSI — comprimento RSI usado dentro do algoritmo ColorMETRO.
Long_StepSizeFast, Short_StepSizeFast — passo (em pontos RSI) para o envelope rápido.
Long_StepSizeSlow, Short_StepSizeSlow — passo para o envelope lento.
Long_SignalBar, Short_SignalBar — deslocamento de barra usado ao ler os buffers do indicador (idêntico à entrada SignalBar do MT5).
Long_AppliedPrice, Short_AppliedPrice — fonte de preço para o cálculo RSI (preço de fechamento por padrão).
Diferenças em relação ao MT5
Modelo de posição — as estratégias do StockSharp trabalham com a posição líquida. O expert original armazenava posições separadas via números mágicos; o port encerra a exposição atual antes de abrir o lado oposto.
Gerenciamento de dinheiro — modos de margem e configurações de desvio são preservados como parâmetros, mas não aplicados automaticamente. Use as entradas de Volume para controlar o tamanho.
Stop-loss / take-profit — o expert MT5 colocava stops de proteção com cada ordem. A versão do StockSharp mantém as distâncias como parâmetros de referência, mas ordens de stop reais devem ser implementadas separadamente se necessário.
Controle de nível de tempo — o código MT5 usava variáveis globais para garantir apenas uma operação por tempo de sinal. No StockSharp processamos cada candle finalizado uma vez e dependemos da verificação de posição líquida para prevenir entradas duplicadas.
Notas
O ColorMetroIndicator personalizado reproduz a lógica do MT5, incluindo os envelopes RSI escalonados e a memória de tendência. Ele expõe as bandas rápida/lenta e o RSI interno para gráficos ou depuração.
Os comentários dentro do código são intencionalmente detalhados para esclarecer as decisões de portagem e auxiliar na personalização futura.
Para habilitar a automação de stop-loss ou take-profit, estenda SignalModule.ProcessModule para colocar ordens de proteção usando os controles de risco do StockSharp.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Conversion of the MT5 expert "Exp_ColorMETRO_Duplex".
/// Uses RSI with step-based envelopes (fast/slow) to generate long/short signals.
/// </summary>
public class ColorMetroDuplexStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _fastStep;
private readonly StrategyParam<int> _slowStep;
private readonly StrategyParam<int> _signalCooldownBars;
// fast envelope state
private decimal? _fastMin, _fastMax;
private int _fastTrend;
private decimal? _prevFastBand;
// slow envelope state
private decimal? _slowMin, _slowMax;
private int _slowTrend;
private decimal? _prevSlowBand;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int FastStep { get => _fastStep.Value; set => _fastStep.Value = value; }
public int SlowStep { get => _slowStep.Value; set => _slowStep.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public ColorMetroDuplexStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI lookback", "Indicator");
_fastStep = Param(nameof(FastStep), 8)
.SetGreaterThanZero()
.SetDisplay("Fast Step", "Step size for fast envelope", "Indicator");
_slowStep = Param(nameof(SlowStep), 24)
.SetGreaterThanZero()
.SetDisplay("Slow Step", "Step size for slow envelope", "Indicator");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastMin = _fastMax = null;
_slowMin = _slowMax = null;
_fastTrend = _slowTrend = 0;
_prevFastBand = _prevSlowBand = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastMin = _fastMax = null;
_slowMin = _slowMax = null;
_fastTrend = _slowTrend = 0;
_prevFastBand = _prevSlowBand = null;
_cooldownRemaining = 0;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var fStep = (decimal)FastStep;
var sStep = (decimal)SlowStep;
// fast envelope
var fastMinCand = rsiVal - 2m * fStep;
var fastMaxCand = rsiVal + 2m * fStep;
if (_fastMin == null || _fastMax == null)
{
_fastMin = fastMinCand;
_fastMax = fastMaxCand;
_fastTrend = 0;
_slowMin = rsiVal - 2m * sStep;
_slowMax = rsiVal + 2m * sStep;
_slowTrend = 0;
return;
}
// fast trend
if (rsiVal > _fastMax) _fastTrend = 1;
else if (rsiVal < _fastMin) _fastTrend = -1;
if (_fastTrend > 0 && fastMinCand < _fastMin) fastMinCand = _fastMin.Value;
else if (_fastTrend < 0 && fastMaxCand > _fastMax) fastMaxCand = _fastMax.Value;
// slow envelope
var slowMinCand = rsiVal - 2m * sStep;
var slowMaxCand = rsiVal + 2m * sStep;
if (rsiVal > _slowMax) _slowTrend = 1;
else if (rsiVal < _slowMin) _slowTrend = -1;
if (_slowTrend > 0 && slowMinCand < _slowMin) slowMinCand = _slowMin.Value;
else if (_slowTrend < 0 && slowMaxCand > _slowMax) slowMaxCand = _slowMax.Value;
// compute band values
decimal? fastBand = null;
if (_fastTrend > 0) fastBand = fastMinCand + fStep;
else if (_fastTrend < 0) fastBand = fastMaxCand - fStep;
decimal? slowBand = null;
if (_slowTrend > 0) slowBand = slowMinCand + sStep;
else if (_slowTrend < 0) slowBand = slowMaxCand - sStep;
_fastMin = fastMinCand;
_fastMax = fastMaxCand;
_slowMin = slowMinCand;
_slowMax = slowMaxCand;
if (fastBand == null || slowBand == null)
{
_prevFastBand = fastBand;
_prevSlowBand = slowBand;
return;
}
if (_prevFastBand == null || _prevSlowBand == null)
{
_prevFastBand = fastBand;
_prevSlowBand = slowBand;
return;
}
var up = fastBand.Value;
var down = slowBand.Value;
var prevUp = _prevFastBand.Value;
var prevDown = _prevSlowBand.Value;
_prevFastBand = fastBand;
_prevSlowBand = slowBand;
// Long signal: fast crosses below slow (up crosses down downward)
var longOpen = prevUp > prevDown && up <= down;
// Short signal: fast crosses above slow (up crosses down upward)
var shortOpen = prevUp < prevDown && up >= down;
if (_cooldownRemaining == 0 && longOpen && Position == 0)
{
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
else if (_cooldownRemaining == 0 && shortOpen && Position == 0)
{
SellMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
}