Стратегия 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.
- MA —
UseMa, MaMode, длины/типы трёх скользящих, таймфрейм, флаг «запоминать сигнал».
- RSI —
UseRsi, RsiMode, RsiPeriod, уровни перекупленности/перепроданности, границы зоны, флаг «запоминать».
- MACD —
UseMacd, MacdMode, длины быстрых/медленных EMA и сигнальной линии, таймфрейм, флаг «запоминать».
- Стохастик —
UseStochastic, длины сглаживания, пороги, таймфрейм.
- Parabolic SAR —
UseSar, 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.
Как использовать
- Добавьте класс
MultiStrategyComboStrategy в свой проект StockSharp и соберите решение.
- Настройте
Security, Portfolio, Volume и при необходимости таймфреймы для отдельных индикаторов.
- Подкорректируйте пороги стопа/тейка и включите трейлинг при необходимости.
- Запустите стратегию. Сделки будут открываться по закрытию свечей, когда все включённые модули согласятся между собой согласно выбранному фактору комбинации.
Заметки по конверсии
- Логика построена только на высокоуровневых подписках — никаких ручных буферов и прямых вызовов
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class multi_combo_strategy(Strategy):
"""
Multi Combo: EMA crossover with RSI filter and ATR-based stops.
"""
def __init__(self):
super(multi_combo_strategy, self).__init__()
self._fast_ema = self.Param("FastEmaLength", 9).SetDisplay("Fast EMA", "Fast EMA", "Indicators")
self._slow_ema = self.Param("SlowEmaLength", 21).SetDisplay("Slow EMA", "Slow EMA", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI", "RSI period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(multi_combo_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(multi_combo_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self._fast_ema.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_ema.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, rsi, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
rsi = float(rsi_val)
atr = float(atr_val)
close = float(candle.ClosePrice)
if self._prev_fast == 0 or self._prev_slow == 0 or atr <= 0:
self._prev_fast = fast
self._prev_slow = slow
return
if self.Position > 0:
if fast < slow and self._prev_fast >= self._prev_slow:
self.SellMarket()
self._entry_price = 0
elif close <= self._entry_price - atr * 2.0:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if fast > slow and self._prev_fast <= self._prev_slow:
self.BuyMarket()
self._entry_price = 0
elif close >= self._entry_price + atr * 2.0:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if fast > slow and self._prev_fast <= self._prev_slow and rsi > 45:
self._entry_price = close
self.BuyMarket()
elif fast < slow and self._prev_fast >= self._prev_slow and rsi < 55:
self._entry_price = close
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return multi_combo_strategy()