MultiStrategyEA v1.2 (StockSharp Port)
Overview
This strategy is a high-level StockSharp port of the MetaTrader expert advisor MultiStrategyEA v1.2. The original EA aggregates seven oscillators and manages multiple grids of orders. The StockSharp version focuses on the signal generation aspect and trades a single net position that is driven by a consensus between the indicator modules. Order management, money management profiles, grids and recovery features from the MT5 code are intentionally omitted to keep the implementation aligned with StockSharp's high-level API and to maintain clarity.
Modules
The strategy evaluates the following indicator modules on the selected timeframe:
- Acceleration/Deceleration Oscillator (AC) – Uses the difference between the Awesome Oscillator and its 5-period SMA. Requires the current value to exceed the
AcLevel threshold and rise (or fall) relative to the previous reading.
- Average Directional Index (ADX) – Confirms trends when the ADX strength is above
AdxTrendLevel and the directional movement that dominates also exceeds AdxDirectionalLevel.
- Awesome Oscillator (AO) – Detects momentum bursts when the oscillator crosses beyond
AoLevel and continues in the same direction.
- DeMarker – Flags possible reversals when the oscillator leaves oversold (
100 - DeMarkerThreshold) or overbought (DeMarkerThreshold) territories.
- Force Index + Bollinger Bands – Requires the price to touch a Bollinger band while the Force Index (scaled in the port exactly as in the MT5 script) confirms momentum beyond
ForceConfirmationLevel. An optional BandDistanceFilter rejects signals when the band width, measured in pips, is too narrow or too wide.
- Money Flow Index (MFI) – Similar to DeMarker; reacts to overbought and oversold zones determined by
MfiThreshold.
- MACD + Stochastic – Demands that both MACD (
MacdLevel) and Stochastic (StochasticLevel) confirm the same directional bias. MACD must be above/below the level and above/below its signal line. Stochastic must be over/under the threshold and above/below the signal line.
Each module contributes a buy, sell or neutral vote based on the latest finished candle.
Consensus Logic
- When
TradeAllStrategies is true (default), the strategy waits until at least RequiredConfirmations bullish votes with zero bearish votes appear before entering long. The same logic is mirrored for shorts.
- When
TradeAllStrategies is false, a single bullish or bearish vote is enough to trade.
- If
CloseInReverse is enabled, the strategy immediately closes an opposite position before opening a new one.
The implementation operates only one aggregate position and does not attempt to recreate the original EA's per-module order bookkeeping.
Risk Management
StopLossPips and TakeProfitPips translate to price offsets using the instrument's PriceStep. For symbols with 3 or 5 decimal digits the pip size is automatically multiplied by 10, mimicking FX pip behaviour.
- Stops and targets are checked on every finished candle using candle highs/lows. When either threshold is reached the entire position is closed.
Differences from the MT5 Expert Advisor
- No grid, martingale or recovery features. Position sizing is fixed via the
Volume parameter.
- Close-signal variants (
CloseOrdersType options in MT5) are not implemented; exits rely on global stop-loss/take-profit or the optional reverse-on-opposite-signal behaviour.
- Indicator configuration in StockSharp mirrors the main idea of each module but supports only the most common interpretation instead of the many mode enumerations found in the original script.
- Money-management blocks (auto lot, account protection, symbol-specific pip valuation) are out of scope for this high-level port.
Parameters
| Parameter |
Description |
CandleType |
Data series used by every indicator module. |
Volume |
Net volume traded when a consensus signal appears. |
TradeAllStrategies |
Enables consensus voting; otherwise any single vote triggers a trade. |
RequiredConfirmations |
Number of matching bullish or bearish votes needed when consensus is enabled. |
CloseInReverse |
Close an existing position before opening the opposite side. |
StopLossPips / TakeProfitPips |
Protective stop and profit target measured in pips. |
UseAcModule, AcLevel |
Toggle and threshold for the Accelerator Oscillator module. |
UseAdxModule, AdxPeriod, AdxTrendLevel, AdxDirectionalLevel |
ADX configuration. |
UseAoModule, AoLevel |
Awesome Oscillator configuration. |
UseDeMarkerModule, DeMarkerPeriod, DeMarkerThreshold |
DeMarker oscillator settings. |
UseForceBollingerModule, BollingerPeriod, BollingerDeviation, ForceConfirmationLevel, BandDistanceFilter |
Force Index + Bollinger band filter settings. |
UseMfiModule, MfiPeriod, MfiThreshold |
Money Flow Index settings. |
UseMacdStochasticModule, MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod, MacdLevel, StochasticPeriod, StochasticSignalPeriod, StochasticSlowing, StochasticLevel |
MACD and Stochastic combo configuration. |
Usage Notes
- Attach the strategy to an instrument with sufficient historical data for all indicators to form.
- Configure the timeframe and module thresholds to match the desired market conditions. The defaults replicate the values used in the MT5 EA inputs.
- The consensus logic is sensitive to how many modules are active. If you disable modules, consider lowering
RequiredConfirmations accordingly.
- Because the strategy trades a single net position, it is suitable for use inside Designer, Runner or other StockSharp high-level environments without additional portfolio routing.
Disclaimer
This port focuses on signal parity rather than reproducing the entire risk and money management stack of the original MetaTrader expert. The simplified architecture makes it easier to test, extend, or integrate into StockSharp-based solutions, but results will differ from the MT5 version when complex features (grids, recovery lots, partial closes) were the main performance driver.
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()