Открыть на GitHub

Стратегия Exp ColorMETRO MMRec Duplex

Обзор

Стратегия переносит на StockSharp советника MetaTrader 5 Exp_ColorMETRO_MMRec_Duplex. Оригинальный робот использует две независимые подсистемы индикатора ColorMETRO (для длин и для коротких позиций) и накладывает модуль MMRec, уменьшающий объём сделок после серии убыточных результатов. Перенос воспроизводит эту структуру и задействует высокоуровневый API StockSharp для подписки на свечи и выставления заявок.

Логика торговли

  • Для длинной и короткой стороны создаются отдельные экземпляры ColorMETRO, каждый работает на собственном типе свечей и управляет только своей стороной рынка.
  • Индикатор формирует пару ступенчатых RSI-каналов (быстрый и медленный). В коде хранится история значений и анализируется бар, задаваемый параметром SignalBar, что повторяет вызовы CopyBuffer из MQL5.
  • Лонг открывается, когда быстрый канал на выбранном баре пересекает медленный сверху вниз, а на предыдущем баре быстрый канал был выше медленного. Перед открытием новая позиция закрывает существующий шорт.
  • Закрытие длинной позиции происходит, если на предыдущем анализируемом баре медленный канал находился выше быстрого, что в оригинале считается сигналом к выходу.
  • Для шортов применяется зеркальная логика (пересечение снизу вверх для входа и быстрый канал выше медленного на предыдущем баре для выхода).
  • Обрабатываются только завершённые свечи; торговля начинается после того, как индикатор сообщит о готовности обоих каналов, тем самым воспроизводится прогрев из MetaTrader.

Управление капиталом (MMRec)

  • Свойство Strategy.Volume задаёт базовый объём. Модули умножают его на коэффициенты LongMm/ShortMm при расчёте заявок.
  • После каждой закрытой сделки стратегия фиксирует, была ли она убыточной (по ценам закрытия свечей, как и в MQL-версии, анализирующей историю сделок).
  • Если среди последних TotalTrigger сделок выбранного модуля накопилось не меньше LossTrigger убыточных, включается пониженный множитель SmallMm. При уменьшении числа убыточных сделок модуль автоматически возвращается к основному множителю.
  • При развороте позиции сначала фиксируется результат предыдущей сделки (обновляется счётчик MMRec), затем рассчитывается объём и открывается позиция в противоположную сторону.

Особенности индикатора

  • ColorMetroMmrecIndicator — это порт кастомного индикатора ColorMETRO с теми же ступенчатыми каналами и памятью тренда на основе RSI.
  • Индикатор публикует внутреннее значение RSI и флаг готовности, что позволяет стратегии игнорировать неполные данные точно так же, как делает оригинальный код.

Параметры

Группа Имя Описание
Long LongCandleType Тип свечей для длинного модуля ColorMETRO.
Long LongTotalTrigger Количество последних длинных сделок, анализируемых MMRec.
Long LongLossTrigger Число убыточных сделок, при котором включается пониженный множитель для лонгов.
Long LongSmallMm Уменьшенный множитель объёма после серии убыточных длинных сделок.
Long LongMm Базовый множитель объёма для длинных сделок.
Long LongEnableOpen Разрешение на открытие длинных позиций.
Long LongEnableClose Разрешение на закрытие длинных позиций.
Long LongPeriodRsi Период RSI внутри длинного ColorMETRO.
Long LongStepSizeFast Шаг быстрого канала длинного модуля.
Long LongStepSizeSlow Шаг медленного канала длинного модуля.
Long LongSignalBar Смещение по истории (в барах) при чтении значений индикатора.
Long LongMagic Исходный MT5 magic number (для совместимости).
Long LongStopLossTicks Заглушка дистанции стоп-лосса из EA (не используется).
Long LongTakeProfitTicks Заглушка дистанции тейк-профита из EA (не используется).
Long LongDeviationTicks Заглушка максимально допустимого проскальзывания (не используется).
Long LongMarginMode Режим MM из оригинала, сохранён в информационных целях.
Short ShortCandleType Тип свечей для короткого модуля ColorMETRO.
Short ShortTotalTrigger Количество последних коротких сделок, анализируемых MMRec.
Short ShortLossTrigger Число убыточных сделок, при котором включается пониженный множитель для шортов.
Short ShortSmallMm Уменьшенный множитель объёма после серии убыточных коротких сделок.
Short ShortMm Базовый множитель объёма для коротких сделок.
Short ShortEnableOpen Разрешение на открытие коротких позиций.
Short ShortEnableClose Разрешение на закрытие коротких позиций.
Short ShortPeriodRsi Период RSI внутри короткого ColorMETRO.
Short ShortStepSizeFast Шаг быстрого канала короткого модуля.
Short ShortStepSizeSlow Шаг медленного канала короткого модуля.
Short ShortSignalBar Смещение по истории при чтении значений индикатора.
Short ShortMagic Исходный MT5 magic number (для совместимости).
Short ShortStopLossTicks Заглушка дистанции стоп-лосса из EA (не используется).
Short ShortTakeProfitTicks Заглушка дистанции тейк-профита из EA (не используется).
Short ShortDeviationTicks Заглушка максимально допустимого проскальзывания (не используется).
Short ShortMarginMode Режим MM из оригинала, сохранён в информационных целях.

Особенности реализации

  • Используются подписки SubscribeCandles(...).BindEx(...), прямого доступа к буферам индикатора нет.
  • Параметры стоп-лосса/тейк-профита оставлены для совместимости; фактическая защита может быть реализована через StartProtection или пользовательские блоки.
  • Оба модуля работают с одним инструментом, но ведут собственные подписки на свечи и независимые счётчики MMRec, что повторяет структуру «дуplex» из MetaTrader.
  • Все комментарии в коде написаны на английском языке, а запрещённые API (GetTrades и т.п.) не используются.

Дисклеймер

Перенос воспроизводит логику оригинального советника, однако результаты зависят от провайдера данных, брокера и настроек StockSharp. Перед реальной торговлей протестируйте стратегию на истории и демо-счёте.

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