Открыть на GitHub

Стратегия ColorMaRsi Trigger MMRec Duplex

Обзор

Стратегия переносит советник MetaTrader Exp_ColorMaRsi-Trigger_MMRec_Duplex.mq5 на высокий уровень API StockSharp. Запускаютс я два независимых блока MaRsi-Trigger: первый отвечает за длинные сигналы, второй – за короткие. Каждый блок сравнивает быструю и медленную скользящие средние, а также быстрый и медленный RSI. Итоговый счёт цвета ограничивается диапазоном [-1, 1], что полностью повторяет работу оригинального индикатора: +1 – бычье состояние, -1 – медвежье, 0 – смешанная картина.

Модуль управления капиталом «MMRec» отслеживает результаты последних сделок в каждом направлении. Если в заданном окне накопилос ь требуемое число убыточных сделок, следующая позиция открывается уменьшенным объёмом, пока серия не закончится. Тем самым реал изовано адаптивное изменение размера позиции из библиотеки TradeAlgorithms.mqh.

Торговая логика

  1. Индикаторная цепочка (для каждого блока):

    • Считаем быструю и медленную скользящие средние (MA_fast/MA_slow) по выбранной цене и таймфрейму.
    • Считаем быстрый и медленный RSI (RSI_fast/RSI_slow) с отдельными источниками цены.
    • Формируем цветовой счёт: стартуем с 0, добавляем +1, если MA_fast > MA_slow, иначе -1; аналогично учитываем пару RSI и ограничиваем результат диапазоном [-1, 1].
    • Сохраняем историю значений и берём её сдвигом SignalBar, как это делает оригинальный советник.
  2. Длинный блок:

    • Вход: разрешён только при отсутствии открытой длинной позиции (шорты закрываются заранее). Требуется, чтобы предыдущее зн ачение (SignalBar + 1) равнялось +1, а текущее (SignalBar) стало ≤ 0, то есть бычий цвет перешёл в нейтральный.
    • Выход: при появлении отрицательного цвета (-1) на предыдущем баре, если закрытия разрешены параметрами.
  3. Короткий блок:

    • Вход: возможен лишь при отсутствии открытого шорта (лонги закрываются сначала). Предыдущее значение должно быть -1, теку щее – ≥ 0, что говорит о смене медвежьего цвета на нейтральный.
    • Выход: когда предыдущий цвет становится положительным и разрешено закрытие коротких позиций.
  4. Стопы и цели: стоп-лосс и тейк-профит задаются в шагах цены инструмента и проверяются на каждой завершённой свече. Пробой лю бого уровня мгновенно закрывает позицию.

  5. Управление капиталом: стратегия сохраняет результат каждой завершённой сделки по направлениям. Если среди последних History Depth сделок число убытков достигает LossTrigger, следующий вход выполняется сокращённым объёмом, иначе используется базовый объём.

Параметры

Группа Имя Описание Значение по умолчанию
Длинный блок LongCandleType Таймфрейм, на котором работает длинный MaRsi-Trigger. H4
LongAllowOpen / LongAllowClose Разрешение на открытие / закрытие длинных позиций. true
LongStopLossPoints / LongTakeProfitPoints Дистанции стоп-лосса и тейк-профита в шагах цены (0 отключает уровень). 1000 / 2000
LongSignalBar Сдвиг по закрытым барам при чтении индикатора. 1
LongRsiPeriod / LongRsiLongPeriod Периоды быстрого и медленного RSI. 3 / 13
LongMaPeriod / LongMaLongPeriod Периоды быстрой и медленной скользящих средних. 5 / 10
LongRsiPrice / LongRsiLongPrice Источник цены для быстрого / медленного RSI. Weighted / Median
LongMaPrice / LongMaLongPrice Источник цены для быстрой / медленной MA. Close / Close
LongMaType / LongMaLongType Алгоритмы усреднения (Simple, Exponential, Smoothed, Weighted). Exponential / Exponential
Управление капиталом LongNormalVolume / LongReducedVolume Базовый и пониженный объём длинных сделок. 0.1 / 0.01
LongHistoryDepth Количество последних длинных сделок в окне анализа. 5
LongLossTrigger Минимальное число убытков в окне для перехода на пониженный объём. 3
Группа Имя Описание Значение по умолчанию
Короткий блок ShortCandleType Таймфрейм для короткого блока MaRsi-Trigger. H4
ShortAllowOpen / ShortAllowClose Разрешение на открытие / закрытие коротких позиций. true
ShortStopLossPoints / ShortTakeProfitPoints Дистанции стоп-лосса и тейк-профита в шагах цены (0 отключает уровень). 1000 / 2000
ShortSignalBar Сдвиг по закрытым барам при чтении индикатора. 1
ShortRsiPeriod / ShortRsiLongPeriod Периоды быстрого и медленного RSI. 3 / 13
ShortMaPeriod / ShortMaLongPeriod Периоды быстрой и медленной скользящих средних. 5 / 10
ShortRsiPrice / ShortRsiLongPrice Источник цены для быстрого / медленного RSI. Weighted / Median
ShortMaPrice / ShortMaLongPrice Источник цены для быстрой / медленной MA. Close / Close
ShortMaType / ShortMaLongType Алгоритмы усреднения. Exponential / Exponential
Управление капиталом ShortNormalVolume / ShortReducedVolume Базовый и пониженный объём коротких сделок. 0.1 / 0.01
ShortHistoryDepth Количество последних коротких сделок в окне анализа. 5
ShortLossTrigger Минимальное число убытков для перехода на пониженный объём. 3

Примечания

  • Типы цен полностью соответствуют обозначениям MetaTrader. Например, Weighted вычисляется как (High + Low + 2 * Close) / 4, а Typical – как (High + Low + Close) / 3.
  • Если оба блока используют один и тот же таймфрейм (значение по умолчанию), создаётся одна подписка на свечи, питающая оба расчё та.
  • Установка LossTrigger в 0 моментально включает пониженный объём, что повторяет поведение оригинального модуля MMRec.
  • Заявки регистрируются по рынку, поэтому параметр Deviation из MetaTrader не требуется.
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;

public class ColorMaRsiTriggerMmRecDuplexStrategy : 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 ColorMaRsiTriggerMmRecDuplexStrategy()
	{
		_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;
	}
}