Ver no GitHub

Exp ColorMETRO MMRec Duplex Strategy

Overview

This strategy ports the MetaTrader 5 expert advisor Exp_ColorMETRO_MMRec_Duplex to StockSharp. The original robot runs two independent ColorMETRO indicator modules (one long, one short) and applies an MMRec (money management recalculation) overlay that shrinks the position size after repeated losses. The C# version mirrors that behaviour while using StockSharp's high-level API for candle subscriptions and order routing.

Trading Logic

  • Two distinct ColorMETRO indicators operate on configurable candle types. The long module only manages long exposure while the short module controls short exposure.
  • Each indicator produces a fast and a slow stepped RSI envelope. The strategy mimics the MQL5 CopyBuffer calls by storing historical values and inspecting the bar defined by SignalBar.
  • A long entry is generated when the fast band crosses below the slow band on the inspected bar while the previous bar still had the fast band above the slow band. Any open short position is flattened before opening the new long.
  • Long exits occur when the slow band on the previous inspected bar sits above the fast band, signalling a bearish regime in the original EA.
  • Short entries and exits mirror the long logic (crossing above for entries, fast line above the slow line on the previous bar for exits).
  • Only finished candles are processed and trading is blocked until the indicator reports both bands as ready, reproducing the MetaTrader warm-up period.

Money Management (MMRec)

  • Strategy.Volume defines the reference lot size. The long and short modules multiply it by their respective LongMm/ShortMm coefficients when sizing new orders.
  • After every completed trade the strategy records whether the result was a loss (based on candle close prices, just like the EA that inspects historical deals).
  • If the most recent TotalTrigger trades for a module contain at least LossTrigger losers, the module switches to the reduced multiplier (SmallMm). Once the loss count drops below the threshold the default multiplier is restored automatically.
  • Position reversals first finalise the existing trade's result (updating the MMRec counters) before sizing and opening the opposite direction.

Indicator Notes

  • ColorMetroMmrecIndicator is a faithful port of the ColorMETRO custom indicator. It feeds the same fast/slow envelopes driven by an RSI core with step tracking and trend memory.
  • The indicator exposes the internal RSI and a readiness flag so that the strategy can ignore incomplete values exactly as the MQL implementation does.

Parameters

Group Name Description
Long LongCandleType Candle type used for the long ColorMETRO module.
Long LongTotalTrigger Number of completed long trades inspected when evaluating MMRec.
Long LongLossTrigger Loss count that activates the reduced long multiplier.
Long LongSmallMm Reduced multiplier applied to long trades after a loss streak.
Long LongMm Default multiplier for long trades.
Long LongEnableOpen Enables opening long positions.
Long LongEnableClose Enables closing long positions.
Long LongPeriodRsi RSI length used inside the long ColorMETRO indicator.
Long LongStepSizeFast Fast envelope step size for the long module.
Long LongStepSizeSlow Slow envelope step size for the long module.
Long LongSignalBar Historical shift (in closed bars) used when reading indicator values.
Long LongMagic Original MT5 magic number, kept for reference.
Long LongStopLossTicks Stop-loss distance placeholder from the EA (not enforced).
Long LongTakeProfitTicks Take-profit distance placeholder from the EA (not enforced).
Long LongDeviationTicks Allowed slippage placeholder from the EA (not enforced).
Long LongMarginMode MM mode flag retained for compatibility (logic uses raw multipliers).
Short ShortCandleType Candle type used for the short ColorMETRO module.
Short ShortTotalTrigger Number of completed short trades inspected when evaluating MMRec.
Short ShortLossTrigger Loss count that activates the reduced short multiplier.
Short ShortSmallMm Reduced multiplier applied to short trades after a loss streak.
Short ShortMm Default multiplier for short trades.
Short ShortEnableOpen Enables opening short positions.
Short ShortEnableClose Enables closing short positions.
Short ShortPeriodRsi RSI length used inside the short ColorMETRO indicator.
Short ShortStepSizeFast Fast envelope step size for the short module.
Short ShortStepSizeSlow Slow envelope step size for the short module.
Short ShortSignalBar Historical shift (in closed bars) used when reading indicator values.
Short ShortMagic Original MT5 magic number, kept for reference.
Short ShortStopLossTicks Stop-loss distance placeholder from the EA (not enforced).
Short ShortTakeProfitTicks Take-profit distance placeholder from the EA (not enforced).
Short ShortDeviationTicks Allowed slippage placeholder from the EA (not enforced).
Short ShortMarginMode MM mode flag retained for compatibility (logic uses raw multipliers).

Implementation Notes

  • The strategy relies on SubscribeCandles(...).BindEx(...) and avoids direct buffer access, aligning with the conversion guidelines.
  • Protective stops from the EA are left as parameters only; users can attach StartProtection or custom risk modules if needed.
  • Both modules share the same security instance but keep their own candle subscriptions and MMRec counters, matching the duplex layout from MetaTrader.
  • All in-code comments are provided in English and the logic refrains from using prohibited API calls such as GetTrades.

Disclaimer

This port reproduces the logical structure of the original EA, but execution quality depends on the connected broker, data feed, and StockSharp configuration. Always validate behaviour on historical and demo data before trading live capital.

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;
	}
}