Открыть на GitHub

Стратегия Multi Strategy Combo

Общее описание

Multi Strategy Combo Strategy — порт на C# оригинального советника MetaTrader 4 «Multi-Strategy iFSF». Стратегия объединяет сигналы индикаторов MA, RSI, MACD, Stochastic и Parabolic SAR, а также дополняет их фильтрами тренда, диапазона по Боллинджеру и шумовым фильтром. В StockSharp логика реализована через высокоуровневые подписки SubscribeCandles().Bind(...), что исключает ручную работу с буферами индикаторов. Сделки совершаются только тогда, когда все задействованные индикаторы единогласно подают сигнал BUY или SELL, после чего применяются дополнительные фильтры-комбинации.

Основная логика

  • Консенсус индикаторов — каждый включённый модуль (MA, RSI, MACD, Stochastic, SAR) отдаёт дискретный сигнал. Совпадение всех сигналов вверх/вниз формирует общий тренд.
  • Фактор комбинации (1–3) — соответствует параметру Combo_Trader_Factor из EA и определяет, как совмещаются консенсус, ADX и фильтр Боллинджера:
    • Фактор 1 ориентирован на тренд. В фазе боковика при включённом фильтре используются развороты от полос Боллинджера.
    • Фактор 2 предъявляет жёсткие требования — тренд и диапазонный фильтр должны подтверждать консенсус.
    • Фактор 3 — самый строгий вариант, требующий одновременного совпадения всех активных модулей.
  • Трендовый фильтр — ADX на выбранном таймфрейме определяет состояния «тренд вверх/вниз» или «диапазон вверх/вниз».
  • Фильтр Боллинджера — две полосы (2σ и 3σ). Покупка требует отскока от нижней полосы и подтверждения RSI перепроданностью. Продажа зеркальна.
  • Шумовой фильтр — ATR-аналог Damiani Volatmeter. Если волатильность ниже порога, новые сделки блокируются.
  • Автозакрытие — при включении позиция закрывается сразу, как только консенсус изменился на противоположный.

Сигналы индикаторов

  • Скользящие средние — три скользящие (настроенные тип и период). Режимы 1–5 повторяют оригинальные варианты пересечений.
  • RSI — четыре режима: перекупленность/перепроданность, направленность, комбинированный и «зональный» режим.
  • MACD — четыре режима: оценка наклона, пересечение гистограммы ниже/выше нуля, комбинированная логика и пересечение сигнальной линии через ноль.
  • Стохастик — обычное пересечение %K и %D либо пересечение с проверкой порогов High/Low.
  • Parabolic SAR — дополнительный голос, поддерживающий «запоминание» последнего сигнала, чтобы избежать повторных входов в одном тренде.

Управление рисками

  • Параметры StopLossOffset и TakeProfitOffset задают абсолютное расстояние для стопа и цели.
  • Метод StartProtection обеспечивает трейлинг-стоп (при включённом UseTrailingStop).
  • Управление объёмом и повторная отправка заявок полностью переданы инфраструктуре StockSharp.

Основные параметры

  • ОбщиеComboFactor, CandleType.
  • MAUseMa, MaMode, длины/типы трёх скользящих, таймфрейм, флаг «запоминать сигнал».
  • RSIUseRsi, RsiMode, RsiPeriod, уровни перекупленности/перепроданности, границы зоны, флаг «запоминать».
  • MACDUseMacd, MacdMode, длины быстрых/медленных EMA и сигнальной линии, таймфрейм, флаг «запоминать».
  • СтохастикUseStochastic, длины сглаживания, пороги, таймфрейм.
  • Parabolic SARUseSar, SarStep, SarMax, таймфрейм.
  • Фильтр трендаUseTrendDetection, AdxPeriod, AdxLevel, таймфрейм.
  • Фильтр БоллинджераUseBollingerFilter, BollingerPeriod, отклонения 2σ/3σ, глубина истории RSI.
  • Шумовой фильтрUseNoiseFilter, NoiseAtrLength, NoiseThreshold, таймфрейм.
  • Автозакрытие и рискUseAutoClose, AllowOppositeAfterClose, StopLossOffset, TakeProfitOffset, UseTrailingStop.

Каждый параметр оформлен через StrategyParam<T>, что упрощает оптимизацию и валидацию.

Отличия от MT4-версии

  • Используются только стандартные индикаторы StockSharp. Выбор между ZeroLag MACD и классическим вариантом заменён на встроенную реализацию MACD.
  • Скользящие и осцилляторы работают по цене закрытия свечи. MT4-параметры выбора цены и сдвигов (FastMa_Price, FastMa_Shift и т.п.) не поддерживаются.
  • Damiani Volatmeter заменён на фильтр ATR с регулируемым порогом NoiseThreshold.
  • Ручное управление ордерами отсутствует: заявки отправляются методами BuyMarket/SellMarket, а защита реализована через StartProtection.
  • Панель комментариев и графические объекты MT4 не переносятся. Для диагностики используйте LogInfo.

Как использовать

  1. Добавьте класс MultiStrategyComboStrategy в свой проект StockSharp и соберите решение.
  2. Настройте Security, Portfolio, Volume и при необходимости таймфреймы для отдельных индикаторов.
  3. Подкорректируйте пороги стопа/тейка и включите трейлинг при необходимости.
  4. Запустите стратегию. Сделки будут открываться по закрытию свечей, когда все включённые модули согласятся между собой согласно выбранному фактору комбинации.

Заметки по конверсии

  • Логика построена только на высокоуровневых подписках — никаких ручных буферов и прямых вызовов GetValue().
  • В коде используются табы, как требует репозиторий.
  • Встроенные комментарии на английском языке помогают сопоставить элементы MT4 и StockSharp.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Multi Combo: Dual EMA crossover with RSI filter and ATR stops.
/// </summary>
public class MultiComboStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public MultiComboStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastEmaLength = Param(nameof(FastEmaLength), 9)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowEmaLength = Param(nameof(SlowEmaLength), 21)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastEmaLength
	{
		get => _fastEmaLength.Value;
		set => _fastEmaLength.Value = value;
	}

	public int SlowEmaLength
	{
		get => _slowEmaLength.Value;
		set => _slowEmaLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, rsi, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastEma);
			DrawIndicator(area, slowEma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 45)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 55)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}