MultiStrategyEA v1.2 (порт для StockSharp)
Обзор
Эта стратегия представляет собой порт советника MultiStrategyEA v1.2 с платформы MetaTrader на высокоуровневый API StockSharp. В оригинальной реализации используется семь осцилляторов, несколько сеток ордеров, восстановление позиций и автоматический манименеджмент. В портированной версии основное внимание уделено генерации сигналов: стратегия управляет одной совокупной позицией и принимает решения на основе согласия модулей. Сетки, усреднение, профили лотов и другие сервисные блоки MT5 намеренно опущены, чтобы сохранить простоту и совместимость с Designer/Runner.
Модули
Стратегия рассчитывает следующие индикаторы на выбранном таймфрейме:
- Acceleration/Deceleration Oscillator (AC) – разница между Awesome Oscillator и его SMA(5). Сигнал формируется, когда значение превышает
AcLevel и продолжает расти/падать относительно предыдущего бара.
- Average Directional Index (ADX) – подтверждает тренд, если ADX выше
AdxTrendLevel, а доминирующее направление (+DI или –DI) превышает AdxDirectionalLevel.
- Awesome Oscillator (AO) – фиксирует импульс при пересечении порога
AoLevel с дальнейшим движением в ту же сторону.
- DeMarker – отслеживает развороты при выходе из зон перепроданности (
100 - DeMarkerThreshold) и перекупленности (DeMarkerThreshold).
- Force Index + Bollinger Bands – требует касания полос Боллинджера и одновременного подтверждения импульса Force Index выше
ForceConfirmationLevel. Параметр BandDistanceFilter задаёт минимальную/максимальную ширину полос (в пипсах).
- Money Flow Index (MFI) – аналогично DeMarker, но использует денежные потоки.
- MACD + Stochastic – объединяет MACD (
MacdLevel) и Stochastic (StochasticLevel): обе кривые должны указывать в одну сторону (MACD относительно сигнальной линии, Stochastic относительно линии %D).
Каждый модуль отдаёт голос «покупка», «продажа» или «нет сигнала» для последней завершённой свечи.
Логика консенсуса
- При
TradeAllStrategies = true стратегия ждёт, пока количество голосов «покупка» (или «продажа») не достигнет RequiredConfirmations, причём противоположных голосов быть не должно.
- При
TradeAllStrategies = false достаточно одного голоса.
- Если активирован
CloseInReverse, противоположная позиция закрывается до открытия новой.
Порт работает только с одной чистой позицией и не пытается воссоздать раздельный учёт заявок по каждому модулю, как в MT5.
Управление рисками
StopLossPips и TakeProfitPips конвертируются в цену через PriceStep. Для инструментов с 3/5 знаками после запятой размер пипса автоматически умножается на 10.
- Стопы и цели проверяются на закрытых свечах по максимуму/минимуму. При достижении уровня вся позиция закрывается.
Отличия от версии MT5
- Нет сеток, мартингейла и восстановления объёмов — размер сделки задаётся параметром
Volume.
- Варианты закрытия (
CloseOrdersType) не реализованы; выход выполняется по стопу/тейку или при перевороте сигнала (если включён CloseInReverse).
- Для каждого модуля реализована наиболее распространённая логика вместо многочисленных режимов, присутствующих в оригинальном коде.
- Блоки автолотов, контроля прибыли по символам и защитных алгоритмов опущены.
Параметры
| Параметр |
Описание |
CandleType |
Таймфрейм свечей для всех индикаторов. |
Volume |
Объём сделки при срабатывании консенсуса. |
TradeAllStrategies |
Включает требование согласия нескольких модулей. |
RequiredConfirmations |
Количество совпадающих голосов для входа при включённом консенсусе. |
CloseInReverse |
Закрывать текущую позицию перед разворотом. |
StopLossPips / TakeProfitPips |
Стоп-лосс и тейк-профит в пипсах. |
Настройки UseAcModule, AcLevel и т.д. |
Управляют соответствующими индикаторными модулями (порогами, периодами, коэффициентами). |
Рекомендации по использованию
- Обеспечьте наличие истории для формирования всех индикаторов.
- Настройте таймфрейм и пороги под свой рынок; значения по умолчанию соответствуют входным параметрам MT5.
- При отключении модулей снизьте
RequiredConfirmations, чтобы стратегия продолжила генерировать сигналы.
- Порт торгует одной позицией и подходит для Designer, Runner и других инструментов StockSharp без дополнительной маршрутизации.
Примечание
Результаты будут отличаться от оригинального советника из-за отсутствия сеток и сложного манименеджмента. Порт создан как отправная точка для дальнейшей адаптации в экосистеме StockSharp, поэтому акцент сделан на повторении логики сигналов и прозрачности параметров.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Simplified port of the MetaTrader expert advisor "MultiStrategyEA v1.2".
/// Combines multiple oscillators (RSI, Stochastic, MACD, Bollinger, ADX)
/// and requires a configurable number of bullish or bearish confirmations before entering a trade.
/// </summary>
public class MultiEaV12Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _requiredConfirmations;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiUpper;
private readonly StrategyParam<decimal> _rsiLower;
private readonly StrategyParam<int> _stochKPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<decimal> _stochUpper;
private readonly StrategyParam<decimal> _stochLower;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxTrendLevel;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RequiredConfirmations { get => _requiredConfirmations.Value; set => _requiredConfirmations.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiUpper { get => _rsiUpper.Value; set => _rsiUpper.Value = value; }
public decimal RsiLower { get => _rsiLower.Value; set => _rsiLower.Value = value; }
public int StochKPeriod { get => _stochKPeriod.Value; set => _stochKPeriod.Value = value; }
public int StochDPeriod { get => _stochDPeriod.Value; set => _stochDPeriod.Value = value; }
public decimal StochUpper { get => _stochUpper.Value; set => _stochUpper.Value = value; }
public decimal StochLower { get => _stochLower.Value; set => _stochLower.Value = value; }
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public decimal BollingerDeviation { get => _bollingerDeviation.Value; set => _bollingerDeviation.Value = value; }
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal AdxTrendLevel { get => _adxTrendLevel.Value; set => _adxTrendLevel.Value = value; }
public int MacdFast { get => _macdFast.Value; set => _macdFast.Value = value; }
public int MacdSlow { get => _macdSlow.Value; set => _macdSlow.Value = value; }
public int MacdSignal { get => _macdSignal.Value; set => _macdSignal.Value = value; }
public MultiEaV12Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_requiredConfirmations = Param(nameof(RequiredConfirmations), 3)
.SetDisplay("Required Confirmations", "Number of modules required for entry", "Consensus");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI length", "RSI");
_rsiUpper = Param(nameof(RsiUpper), 65m)
.SetDisplay("RSI Upper", "Overbought level", "RSI");
_rsiLower = Param(nameof(RsiLower), 35m)
.SetDisplay("RSI Lower", "Oversold level", "RSI");
_stochKPeriod = Param(nameof(StochKPeriod), 10)
.SetDisplay("Stochastic %K", "%K period", "Stochastic");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetDisplay("Stochastic %D", "%D period", "Stochastic");
_stochUpper = Param(nameof(StochUpper), 70m)
.SetDisplay("Stoch Upper", "Overbought", "Stochastic");
_stochLower = Param(nameof(StochLower), 30m)
.SetDisplay("Stoch Lower", "Oversold", "Stochastic");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetDisplay("Bollinger Period", "BB length", "Bollinger");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetDisplay("Bollinger Deviation", "BB width", "Bollinger");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "ADX length", "ADX");
_adxTrendLevel = Param(nameof(AdxTrendLevel), 20m)
.SetDisplay("ADX Trend Level", "Min ADX for trend", "ADX");
_macdFast = Param(nameof(MacdFast), 12)
.SetDisplay("MACD Fast", "Fast EMA period", "MACD");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetDisplay("MACD Slow", "Slow EMA period", "MACD");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetDisplay("MACD Signal", "Signal line period", "MACD");
}
/// <inheritdoc />
public override System.Collections.Generic.IEnumerable<(StockSharp.BusinessEntities.Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochKPeriod;
stochastic.D.Length = StochDPeriod;
var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var macd = new MovingAverageConvergenceDivergenceSignal();
macd.Macd.ShortMa.Length = MacdFast;
macd.Macd.LongMa.Length = MacdSlow;
macd.SignalMa.Length = MacdSignal;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, stochastic, bollinger, adx, macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiVal, IIndicatorValue stochVal, IIndicatorValue bbVal, IIndicatorValue adxVal, IIndicatorValue macdVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!rsiVal.IsFinal || !stochVal.IsFinal || !bbVal.IsFinal || !adxVal.IsFinal || !macdVal.IsFinal)
return;
if (!rsiVal.IsFormed || !stochVal.IsFormed || !bbVal.IsFormed || !adxVal.IsFormed || !macdVal.IsFormed)
return;
var rsi = rsiVal.GetValue<decimal>();
var stoch = (StochasticOscillatorValue)stochVal;
var stochK = stoch.K ?? 50m;
var bb = (BollingerBandsValue)bbVal;
var bbUpper = bb.UpBand ?? candle.ClosePrice;
var bbLower = bb.LowBand ?? candle.ClosePrice;
var adxTyped = (AverageDirectionalIndexValue)adxVal;
var adxMain = adxTyped.MovingAverage ?? 0m;
var adxPlus = adxTyped.Dx.Plus ?? 0m;
var adxMinus = adxTyped.Dx.Minus ?? 0m;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdVal;
var macdLine = macdTyped.Macd ?? 0m;
var macdSignalLine = macdTyped.Signal ?? 0m;
var close = candle.ClosePrice;
// Count bullish and bearish signals from each module
var bullish = 0;
var bearish = 0;
// Module 1: RSI
if (rsi < RsiLower) bullish++;
else if (rsi > RsiUpper) bearish++;
// Module 2: Stochastic
if (stochK < StochLower) bullish++;
else if (stochK > StochUpper) bearish++;
// Module 3: Bollinger Bands
if (close <= bbLower) bullish++;
else if (close >= bbUpper) bearish++;
// Module 4: ADX directional
if (adxMain >= AdxTrendLevel)
{
if (adxPlus > adxMinus) bullish++;
else if (adxMinus > adxPlus) bearish++;
}
// Module 5: MACD
if (macdLine > macdSignalLine && macdLine > 0) bullish++;
else if (macdLine < macdSignalLine && macdLine < 0) bearish++;
var minConfirmations = RequiredConfirmations;
// Enter on consensus
if (bullish >= minConfirmations && bearish == 0 && Position <= 0)
{
BuyMarket();
}
else if (bearish >= minConfirmations && bullish == 0 && Position >= 0)
{
SellMarket();
}
// Exit when consensus breaks
else if (Position > 0 && bearish >= 2)
{
SellMarket();
}
else if (Position < 0 && bullish >= 2)
{
BuyMarket();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (
RelativeStrengthIndex, StochasticOscillator, BollingerBands,
AverageDirectionalIndex, MovingAverageConvergenceDivergenceSignal
)
from StockSharp.Algo.Strategies import Strategy
class multi_ea_v12_strategy(Strategy):
"""5-indicator consensus strategy (RSI, Stochastic, Bollinger, ADX, MACD)."""
def __init__(self):
super(multi_ea_v12_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._required_confirmations = self.Param("RequiredConfirmations", 3) \
.SetDisplay("Required Confirmations", "Number of modules required for entry", "Consensus")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI length", "RSI")
self._rsi_upper = self.Param("RsiUpper", 65.0) \
.SetDisplay("RSI Upper", "Overbought level", "RSI")
self._rsi_lower = self.Param("RsiLower", 35.0) \
.SetDisplay("RSI Lower", "Oversold level", "RSI")
self._stoch_k_period = self.Param("StochKPeriod", 10) \
.SetDisplay("Stochastic %K", "%K period", "Stochastic")
self._stoch_d_period = self.Param("StochDPeriod", 3) \
.SetDisplay("Stochastic %D", "%D period", "Stochastic")
self._stoch_upper = self.Param("StochUpper", 70.0) \
.SetDisplay("Stoch Upper", "Overbought", "Stochastic")
self._stoch_lower = self.Param("StochLower", 30.0) \
.SetDisplay("Stoch Lower", "Oversold", "Stochastic")
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetDisplay("Bollinger Period", "BB length", "Bollinger")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetDisplay("Bollinger Deviation", "BB width", "Bollinger")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "ADX length", "ADX")
self._adx_trend_level = self.Param("AdxTrendLevel", 20.0) \
.SetDisplay("ADX Trend Level", "Min ADX for trend", "ADX")
self._macd_fast = self.Param("MacdFast", 12) \
.SetDisplay("MACD Fast", "Fast EMA period", "MACD")
self._macd_slow = self.Param("MacdSlow", 26) \
.SetDisplay("MACD Slow", "Slow EMA period", "MACD")
self._macd_signal = self.Param("MacdSignal", 9) \
.SetDisplay("MACD Signal", "Signal line period", "MACD")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RequiredConfirmations(self):
return self._required_confirmations.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def RsiUpper(self):
return self._rsi_upper.Value
@property
def RsiLower(self):
return self._rsi_lower.Value
@property
def StochKPeriod(self):
return self._stoch_k_period.Value
@property
def StochDPeriod(self):
return self._stoch_d_period.Value
@property
def StochUpper(self):
return self._stoch_upper.Value
@property
def StochLower(self):
return self._stoch_lower.Value
@property
def BollingerPeriod(self):
return self._bollinger_period.Value
@property
def BollingerDeviation(self):
return self._bollinger_deviation.Value
@property
def AdxPeriod(self):
return self._adx_period.Value
@property
def AdxTrendLevel(self):
return self._adx_trend_level.Value
@property
def MacdFast(self):
return self._macd_fast.Value
@property
def MacdSlow(self):
return self._macd_slow.Value
@property
def MacdSignal(self):
return self._macd_signal.Value
def OnStarted2(self, time):
super(multi_ea_v12_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
stochastic = StochasticOscillator()
stochastic.K.Length = self.StochKPeriod
stochastic.D.Length = self.StochDPeriod
bollinger = BollingerBands()
bollinger.Length = self.BollingerPeriod
bollinger.Width = float(self.BollingerDeviation)
adx = AverageDirectionalIndex()
adx.Length = self.AdxPeriod
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self.MacdFast
macd.Macd.LongMa.Length = self.MacdSlow
macd.SignalMa.Length = self.MacdSignal
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(rsi, stochastic, bollinger, adx, macd, self._process_candle).Start()
def _process_candle(self, candle, rsi_val, stoch_val, bb_val, adx_val, macd_val):
if candle.State != CandleStates.Finished:
return
if not rsi_val.IsFormed or not stoch_val.IsFormed or not bb_val.IsFormed or \
not adx_val.IsFormed or not macd_val.IsFormed:
return
rsi = float(rsi_val)
stoch_k_raw = stoch_val.K
stoch_k = float(stoch_k_raw) if stoch_k_raw is not None else 50.0
bb_upper_raw = bb_val.UpBand
bb_lower_raw = bb_val.LowBand
bb_upper = float(bb_upper_raw) if bb_upper_raw is not None else float(candle.ClosePrice)
bb_lower = float(bb_lower_raw) if bb_lower_raw is not None else float(candle.ClosePrice)
adx_ma_raw = adx_val.MovingAverage
adx_main = float(adx_ma_raw) if adx_ma_raw is not None else 0.0
adx_plus_raw = adx_val.Dx.Plus
adx_minus_raw = adx_val.Dx.Minus
adx_plus = float(adx_plus_raw) if adx_plus_raw is not None else 0.0
adx_minus = float(adx_minus_raw) if adx_minus_raw is not None else 0.0
macd_line_raw = macd_val.Macd
macd_signal_raw = macd_val.Signal
macd_line = float(macd_line_raw) if macd_line_raw is not None else 0.0
macd_signal_line = float(macd_signal_raw) if macd_signal_raw is not None else 0.0
close = float(candle.ClosePrice)
bullish = 0
bearish = 0
# Module 1: RSI
if rsi < float(self.RsiLower):
bullish += 1
elif rsi > float(self.RsiUpper):
bearish += 1
# Module 2: Stochastic
if stoch_k < float(self.StochLower):
bullish += 1
elif stoch_k > float(self.StochUpper):
bearish += 1
# Module 3: Bollinger Bands
if close <= bb_lower:
bullish += 1
elif close >= bb_upper:
bearish += 1
# Module 4: ADX directional
if adx_main >= float(self.AdxTrendLevel):
if adx_plus > adx_minus:
bullish += 1
elif adx_minus > adx_plus:
bearish += 1
# Module 5: MACD
if macd_line > macd_signal_line and macd_line > 0:
bullish += 1
elif macd_line < macd_signal_line and macd_line < 0:
bearish += 1
min_confirmations = self.RequiredConfirmations
# Enter on consensus
if bullish >= min_confirmations and bearish == 0 and self.Position <= 0:
self.BuyMarket()
elif bearish >= min_confirmations and bullish == 0 and self.Position >= 0:
self.SellMarket()
# Exit when consensus breaks
elif self.Position > 0 and bearish >= 2:
self.SellMarket()
elif self.Position < 0 and bullish >= 2:
self.BuyMarket()
def CreateClone(self):
return multi_ea_v12_strategy()