MultiStrategyEA v1.2(StockSharp 版本)
概述
本策略基于 MetaTrader 平台的 MultiStrategyEA v1.2,使用 StockSharp 的高级 API 重新实现。原始 EA 通过七个指标模块、网格与加倍手数管理订单。移植版本仅保留信号生成部分,并使用单一净头寸进行交易,以便更好地集成到 StockSharp Designer、Runner 等工具中。网格、递增手数、自动资金管理等复杂模块均未移植。
指标模块
策略在所选周期上计算以下指标:
- 加速/减速指标(AC):计算 Awesome Oscillator 与其 5 周期均线的差值。当当前值高于
AcLevel且继续上升/下降时给出买入/卖出信号。 - 平均趋向指数(ADX):当 ADX 超过
AdxTrendLevel且主导方向的 DI 超过AdxDirectionalLevel时确认趋势方向。 - Awesome Oscillator(AO):当指标突破
AoLevel并维持同方向变化时触发信号。 - DeMarker:当指标从超卖(
100 - DeMarkerThreshold)或超买(DeMarkerThreshold)区域离开时提示反转。 - Force Index + 布林带:价格触及布林带同时 Force Index(按照原始 EA 相同方式缩放)超过
ForceConfirmationLevel才认为有效。BandDistanceFilter可限制布林带宽度(以点数表示)。 - 资金流量指数(MFI):与 DeMarker 类似,识别超买/超卖反转。
- MACD + 随机指标:MACD 必须达到
MacdLevel且高于/低于信号线,同时随机指标超过/低于StochasticLevel并与信号线方向一致。
每个模块都会根据最新完成的 K 线投票:买入、卖出或保持中性。
共识逻辑
TradeAllStrategies = true(默认)时,至少需要RequiredConfirmations个买入或卖出投票,并且没有相反投票,才会进场。TradeAllStrategies = false时,单个投票即可触发交易。- 当启用
CloseInReverse时,若出现反向共识,策略会先平掉当前仓位再开新仓。
移植版本只维护一个净头寸,不再区分各模块独立持仓。
风险管理
StopLossPips与TakeProfitPips根据品种的PriceStep自动换算为价格偏移。当价格步长为 0.001 或 0.00001 时,会自动乘以 10 以匹配外汇“pip”。- 每根完成的 K 线都会检查最高价/最低价是否触及止损或止盈,触发后立即平仓。
与 MT5 版本的差异
- 无网格、无马丁加仓,仓位大小仅由
Volume控制。 - 未实现 MT5 中的
CloseOrdersType关闭模式,退出主要依赖止损/止盈或CloseInReverse反向信号。 - 指标模块仅保留最常用的判断方式,未涵盖原始 EA 中所有枚举组合。
- 自动批量控制、账户保护及其他账户层面逻辑未包含在内。
参数
| 参数 | 说明 |
|---|---|
CandleType |
所有指标使用的 K 线类型。 |
Volume |
共识信号出现时的交易数量。 |
TradeAllStrategies |
是否需要多个模块共识。 |
RequiredConfirmations |
开仓所需的一致投票数量(在共识模式下)。 |
CloseInReverse |
出现反向信号时先平仓再反向开仓。 |
StopLossPips / TakeProfitPips |
以点数表示的止损和止盈距离。 |
其他 Use* 与阈值/周期参数 |
对应模块的启用开关及阈值设置。 |
使用建议
- 确保历史数据足以让所有指标形成;初期可能需要数十根 K 线。
- 根据品种波动情况调整各模块阈值;默认值与 MT5 输入参数一致。
- 如果禁用部分模块,请同步调低
RequiredConfirmations,避免策略因为投票不足而不交易。 - 策略仅维护单笔净头寸,适合集成到 StockSharp Designer、Runner 等高层工具中,无需额外的投资组合路由。
注意事项
由于省略了网格、资金管理和部分退出逻辑,运行结果与 MT5 原版会有差异。本移植旨在提供清晰、可扩展的信号框架,用户可以在此基础上继续定制更复杂的仓位管理或组合策略。
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()