Multi Indicator Optimizer 策略
该策略在 StockSharp 高级 API 上还原 MetaTrader 专家顾问 MultiIndicatorOptimizer 的投票机制。五个经典振荡指标在每根已完成的 K 线上给出加权投票,最终汇总成一条综合信号,再根据用户设定的阈值决定是做多、做空还是平仓观望。
交易逻辑
- MACD 模块:检查柱状图符号以及主线与信号线的相对位置(均基于上一根收盘 K 线),两种判断结果求平均后乘以
MacdWeight。 - Awesome Oscillator 模块:判断指标是否位于零轴上方,同时比较动能相对于前一根 K 线是否增强,平均得分乘以
AoWeight。 - OsMA 模块:读取上一根 K 线的 MACD 柱状图符号并乘以
OsmaWeight。 - Williams %R 模块:检测是否突破
WilliamsLowerLevel与WilliamsUpperLevel所定义的超卖/超买区。向上离开下轨给出看涨票,向下跌破上轨给出看跌票,并乘以WilliamsWeight。 - Stochastic 模块:组合两个条件——%K 穿越
StochasticLowerLevel/StochasticUpperLevel,以及 %K 与 %D 的大小关系。两个子信号平均后乘以StochasticWeight。
综合得分会写入日志的 Signal 列,同时保存在 _lastSignal 字段。交易引擎按以下规则处理:
signal >= EntryThreshold:如有空头则平仓,并建立/保持多头。signal <= -EntryThreshold:如有多头则平仓,并建立/保持空头。abs(signal) <= ExitThreshold:若市场处于中性区间,则平仓观望。
所有判断均基于上一根完成的 K 线,与原始 MQL 版本使用的 shift = 1/2 索引保持一致。
参数
| 参数 | 说明 | 默认值 |
|---|---|---|
CandleType |
指标计算所用的主时间框架。 | H1 K 线 |
MacdFast / MacdSlow / MacdSignal |
MACD 模块的 EMA 周期。 | 12 / 26 / 9 |
MacdWeight |
MACD 模块的投票权重(可为负以反向)。 | 1 |
AoShortPeriod / AoLongPeriod |
Awesome Oscillator 的快/慢均线周期。 | 5 / 34 |
AoWeight |
Awesome 模块权重。 | 1 |
OsmaFastPeriod / OsmaSlowPeriod / OsmaSignalPeriod |
构建 OsMA 柱状图的 MACD 参数。 | 12 / 26 / 9 |
OsmaWeight |
OsMA 模块权重。 | 1 |
WilliamsPeriod |
Williams %R 的回溯长度。 | 14 |
WilliamsLowerLevel / WilliamsUpperLevel |
超卖/超买边界(百分比)。 | -80 / -20 |
WilliamsWeight |
Williams 模块权重。 | 1 |
StochasticKPeriod / StochasticDPeriod / StochasticSlowing |
随机指标 %K、%D 及平滑参数。 | 5 / 3 / 3 |
StochasticLowerLevel / StochasticUpperLevel |
%K 的超卖/超买阈值。 | 20 / 80 |
StochasticWeight |
Stochastic 模块权重。 | 1 |
EntryThreshold |
开仓或反向所需的最小绝对得分。 | 0.5 |
ExitThreshold |
中性区间宽度:当 信号的绝对值小于该值时全部平仓。 | 0.1 |
各个权重均允许设置为负值,以关闭或反向某个模块的投票,便于参数优化。
说明
- 全部逻辑基于高级 API:使用
SubscribeCandles订阅、指标绑定以及BuyMarket/SellMarket下单助手。 - 仅在蜡烛收盘后更新信号,确保决策来自确认数据。
- 持仓量由
Strategy.Volume决定,若需要止盈止损可以额外调用StartProtection设置。 - 代码内附有详细的英文注释,方便继续维护与扩展。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Multi-indicator voting strategy that aggregates MACD, Awesome Oscillator,
/// OsMA, Williams %R, and Stochastic Oscillator signals.
/// </summary>
public class MultiIndicatorOptimizerStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<decimal> _macdWeight;
private readonly StrategyParam<int> _aoShort;
private readonly StrategyParam<int> _aoLong;
private readonly StrategyParam<decimal> _aoWeight;
private readonly StrategyParam<int> _osmaFast;
private readonly StrategyParam<int> _osmaSlow;
private readonly StrategyParam<int> _osmaSignal;
private readonly StrategyParam<decimal> _osmaWeight;
private readonly StrategyParam<int> _williamsPeriod;
private readonly StrategyParam<decimal> _williamsLower;
private readonly StrategyParam<decimal> _williamsUpper;
private readonly StrategyParam<decimal> _williamsWeight;
private readonly StrategyParam<int> _stochKPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<int> _stochSlowing;
private readonly StrategyParam<decimal> _stochLower;
private readonly StrategyParam<decimal> _stochUpper;
private readonly StrategyParam<decimal> _stochWeight;
private readonly StrategyParam<decimal> _entryThreshold;
private readonly StrategyParam<decimal> _exitThreshold;
private decimal? _prevMacdMain;
private decimal? _prevMacdSignal;
private decimal? _prevOsma;
private decimal? _prevAo;
private decimal? _prevPrevAo;
private decimal? _prevWilliams;
private decimal? _prevPrevWilliams;
private decimal? _prevStochK;
private decimal? _prevPrevStochK;
private decimal? _prevStochSignal;
private decimal _lastSignal;
/// <summary>
/// Initializes a new instance of <see cref="MultiIndicatorOptimizerStrategy"/>.
/// </summary>
public MultiIndicatorOptimizerStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used for indicator calculations", "General");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA period for MACD", "MACD")
.SetOptimize(6, 24, 2);
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA period for MACD", "MACD")
.SetOptimize(20, 40, 2);
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal line period for MACD", "MACD")
.SetOptimize(5, 15, 1);
_macdWeight = Param(nameof(MacdWeight), 1m)
.SetDisplay("MACD Weight", "Voting weight of MACD block", "Weights")
.SetOptimize(-2m, 2m, 0.5m);
_aoShort = Param(nameof(AoShortPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("AO Short", "Short moving average for Awesome Oscillator", "Awesome")
.SetOptimize(3, 10, 1);
_aoLong = Param(nameof(AoLongPeriod), 34)
.SetGreaterThanZero()
.SetDisplay("AO Long", "Long moving average for Awesome Oscillator", "Awesome")
.SetOptimize(20, 50, 2);
_aoWeight = Param(nameof(AoWeight), 1m)
.SetDisplay("AO Weight", "Voting weight of Awesome Oscillator block", "Weights")
.SetOptimize(-2m, 2m, 0.5m);
_osmaFast = Param(nameof(OsmaFastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("OsMA Fast", "Fast EMA period for OsMA histogram", "OsMA")
.SetOptimize(6, 24, 2);
_osmaSlow = Param(nameof(OsmaSlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("OsMA Slow", "Slow EMA period for OsMA histogram", "OsMA")
.SetOptimize(20, 40, 2);
_osmaSignal = Param(nameof(OsmaSignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("OsMA Signal", "Signal EMA period for OsMA histogram", "OsMA")
.SetOptimize(5, 15, 1);
_osmaWeight = Param(nameof(OsmaWeight), 1m)
.SetDisplay("OsMA Weight", "Voting weight of OsMA block", "Weights")
.SetOptimize(-2m, 2m, 0.5m);
_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Lookback for Williams %R", "Williams %R")
.SetOptimize(10, 30, 2);
_williamsLower = Param(nameof(WilliamsLowerLevel), -80m)
.SetDisplay("Williams Lower", "Oversold boundary for Williams %R", "Williams %R");
_williamsUpper = Param(nameof(WilliamsUpperLevel), -20m)
.SetDisplay("Williams Upper", "Overbought boundary for Williams %R", "Williams %R");
_williamsWeight = Param(nameof(WilliamsWeight), 1m)
.SetDisplay("Williams Weight", "Voting weight of Williams %R block", "Weights")
.SetOptimize(-2m, 2m, 0.5m);
_stochKPeriod = Param(nameof(StochasticKPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "%K period for Stochastic Oscillator", "Stochastic")
.SetOptimize(3, 15, 1);
_stochDPeriod = Param(nameof(StochasticDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "%D period for Stochastic Oscillator", "Stochastic")
.SetOptimize(2, 9, 1);
_stochSlowing = Param(nameof(StochasticSlowing), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic Smoothing", "Smoothing applied to %K", "Stochastic")
.SetOptimize(1, 9, 1);
_stochLower = Param(nameof(StochasticLowerLevel), 20m)
.SetDisplay("Stochastic Lower", "Oversold threshold for Stochastic", "Stochastic");
_stochUpper = Param(nameof(StochasticUpperLevel), 80m)
.SetDisplay("Stochastic Upper", "Overbought threshold for Stochastic", "Stochastic");
_stochWeight = Param(nameof(StochasticWeight), 1m)
.SetDisplay("Stochastic Weight", "Voting weight of Stochastic block", "Weights")
.SetOptimize(-2m, 2m, 0.5m);
_entryThreshold = Param(nameof(EntryThreshold), 0.5m)
.SetDisplay("Entry Threshold", "Minimum aggregated signal required to open a position", "Trading")
.SetOptimize(0.25m, 2m, 0.25m);
_exitThreshold = Param(nameof(ExitThreshold), 0.1m)
.SetDisplay("Exit Threshold", "Signal absolute value required to flat the position", "Trading")
.SetOptimize(0.05m, 1m, 0.05m);
}
/// <summary>
/// Candle type for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Fast EMA period for the MACD block.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// Slow EMA period for the MACD block.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// Signal EMA period for the MACD block.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// Weight of the MACD voting block.
/// </summary>
public decimal MacdWeight
{
get => _macdWeight.Value;
set => _macdWeight.Value = value;
}
/// <summary>
/// Short period used by the Awesome Oscillator.
/// </summary>
public int AoShortPeriod
{
get => _aoShort.Value;
set => _aoShort.Value = value;
}
/// <summary>
/// Long period used by the Awesome Oscillator.
/// </summary>
public int AoLongPeriod
{
get => _aoLong.Value;
set => _aoLong.Value = value;
}
/// <summary>
/// Weight of the Awesome Oscillator block.
/// </summary>
public decimal AoWeight
{
get => _aoWeight.Value;
set => _aoWeight.Value = value;
}
/// <summary>
/// Fast EMA period for the OsMA histogram.
/// </summary>
public int OsmaFastPeriod
{
get => _osmaFast.Value;
set => _osmaFast.Value = value;
}
/// <summary>
/// Slow EMA period for the OsMA histogram.
/// </summary>
public int OsmaSlowPeriod
{
get => _osmaSlow.Value;
set => _osmaSlow.Value = value;
}
/// <summary>
/// Signal EMA period for the OsMA histogram.
/// </summary>
public int OsmaSignalPeriod
{
get => _osmaSignal.Value;
set => _osmaSignal.Value = value;
}
/// <summary>
/// Weight of the OsMA voting block.
/// </summary>
public decimal OsmaWeight
{
get => _osmaWeight.Value;
set => _osmaWeight.Value = value;
}
/// <summary>
/// Lookback length for Williams %R.
/// </summary>
public int WilliamsPeriod
{
get => _williamsPeriod.Value;
set => _williamsPeriod.Value = value;
}
/// <summary>
/// Oversold level for Williams %R.
/// </summary>
public decimal WilliamsLowerLevel
{
get => _williamsLower.Value;
set => _williamsLower.Value = value;
}
/// <summary>
/// Overbought level for Williams %R.
/// </summary>
public decimal WilliamsUpperLevel
{
get => _williamsUpper.Value;
set => _williamsUpper.Value = value;
}
/// <summary>
/// Weight of the Williams %R block.
/// </summary>
public decimal WilliamsWeight
{
get => _williamsWeight.Value;
set => _williamsWeight.Value = value;
}
/// <summary>
/// %K period for the Stochastic Oscillator.
/// </summary>
public int StochasticKPeriod
{
get => _stochKPeriod.Value;
set => _stochKPeriod.Value = value;
}
/// <summary>
/// %D period for the Stochastic Oscillator.
/// </summary>
public int StochasticDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
/// <summary>
/// Smoothing factor applied to %K.
/// </summary>
public int StochasticSlowing
{
get => _stochSlowing.Value;
set => _stochSlowing.Value = value;
}
/// <summary>
/// Oversold threshold for the Stochastic Oscillator.
/// </summary>
public decimal StochasticLowerLevel
{
get => _stochLower.Value;
set => _stochLower.Value = value;
}
/// <summary>
/// Overbought threshold for the Stochastic Oscillator.
/// </summary>
public decimal StochasticUpperLevel
{
get => _stochUpper.Value;
set => _stochUpper.Value = value;
}
/// <summary>
/// Weight of the Stochastic voting block.
/// </summary>
public decimal StochasticWeight
{
get => _stochWeight.Value;
set => _stochWeight.Value = value;
}
/// <summary>
/// Minimum aggregated score required to open a position.
/// </summary>
public decimal EntryThreshold
{
get => _entryThreshold.Value;
set => _entryThreshold.Value = value;
}
/// <summary>
/// Maximum absolute score to keep an existing position open.
/// </summary>
public decimal ExitThreshold
{
get => _exitThreshold.Value;
set => _exitThreshold.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacdMain = null;
_prevMacdSignal = null;
_prevOsma = null;
_prevAo = null;
_prevPrevAo = null;
_prevWilliams = null;
_prevPrevWilliams = null;
_prevStochK = null;
_prevPrevStochK = null;
_prevStochSignal = null;
_lastSignal = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow }
},
SignalMa = { Length = MacdSignal }
};
var osma = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = OsmaFastPeriod },
LongMa = { Length = OsmaSlowPeriod }
},
SignalMa = { Length = OsmaSignalPeriod }
};
var awesome = new AwesomeOscillator
{
ShortMa = { Length = AoShortPeriod },
LongMa = { Length = AoLongPeriod }
};
var williams = new WilliamsR { Length = WilliamsPeriod };
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochasticKPeriod;
stochastic.D.Length = StochasticDPeriod;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, osma, awesome, williams, stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawIndicator(area, awesome);
DrawIndicator(area, osma);
var extraArea = CreateChartArea();
if (extraArea != null)
{
DrawIndicator(extraArea, williams);
DrawIndicator(extraArea, stochastic);
}
DrawOwnTrades(area);
}
}
private void ProcessCandle(
ICandleMessage candle,
IIndicatorValue macdValue,
IIndicatorValue osmaValue,
IIndicatorValue awesomeValue,
IIndicatorValue williamsValue,
IIndicatorValue stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFinal || !osmaValue.IsFinal || !awesomeValue.IsFinal || !williamsValue.IsFinal || !stochasticValue.IsFinal)
return;
var macdSignal = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
var osmaSignal = (MovingAverageConvergenceDivergenceSignalValue)osmaValue;
if (macdSignal.Macd is not decimal currentMacd || macdSignal.Signal is not decimal currentMacdSignal)
return;
var currentOsma = osmaSignal.Macd is decimal osmaMacd && osmaSignal.Signal is decimal osmaSignalLine
? osmaMacd - osmaSignalLine
: (decimal?)null;
if (currentOsma is null)
return;
var currentAo = awesomeValue.ToDecimal();
var currentWilliams = williamsValue.ToDecimal();
var stoch = (StochasticOscillatorValue)stochasticValue;
if (stoch.K is not decimal currentStochK || stoch.D is not decimal currentStochD)
return;
decimal signal = 0m;
if (_prevMacdMain is decimal prevMacd && _prevMacdSignal is decimal prevSignal)
{
var mainScore = prevMacd > 0m ? 1m : prevMacd < 0m ? -1m : 0m;
var crossScore = prevMacd > prevSignal ? 1m : prevMacd < prevSignal ? -1m : 0m;
signal += (mainScore + crossScore) / 2m * MacdWeight;
}
if (_prevAo is decimal prevAo)
{
var directionScore = prevAo > 0m ? 1m : prevAo < 0m ? -1m : 0m;
var momentumScore = _prevPrevAo is decimal prevPrevAo
? prevAo > prevPrevAo ? 1m : prevAo < prevPrevAo ? -1m : 0m
: 0m;
signal += (directionScore + momentumScore) / 2m * AoWeight;
}
if (_prevOsma is decimal prevOsma)
{
var osmaScore = prevOsma > 0m ? 1m : prevOsma < 0m ? -1m : 0m;
signal += osmaScore * OsmaWeight;
}
if (_prevWilliams is decimal prevWilliams)
{
var wprScore = 0m;
if (_prevPrevWilliams is decimal prevPrevWilliams)
{
if (prevWilliams > WilliamsLowerLevel && prevPrevWilliams <= WilliamsLowerLevel)
wprScore = 1m;
else if (prevWilliams < WilliamsUpperLevel && prevPrevWilliams >= WilliamsUpperLevel)
wprScore = -1m;
}
signal += wprScore * WilliamsWeight;
}
if (_prevStochK is decimal prevStochK && _prevStochSignal is decimal prevStochSignal)
{
var stochScore1 = 0m;
if (_prevPrevStochK is decimal prevPrevStochK)
{
if (prevStochK > StochasticLowerLevel && prevPrevStochK <= StochasticLowerLevel)
stochScore1 = 1m;
else if (prevStochK < StochasticUpperLevel && prevPrevStochK >= StochasticUpperLevel)
stochScore1 = -1m;
}
var stochScore2 = prevStochK > prevStochSignal ? 1m : prevStochK < prevStochSignal ? -1m : 0m;
signal += (stochScore1 + stochScore2) / 2m * StochasticWeight;
}
_lastSignal = signal;
ExecuteTradingLogic(signal);
_prevMacdMain = currentMacd;
_prevMacdSignal = currentMacdSignal;
_prevOsma = currentOsma;
_prevPrevAo = _prevAo;
_prevAo = currentAo;
_prevPrevWilliams = _prevWilliams;
_prevWilliams = currentWilliams;
_prevPrevStochK = _prevStochK;
_prevStochK = currentStochK;
_prevStochSignal = currentStochD;
}
private void ExecuteTradingLogic(decimal signal)
{
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Volume <= 0)
return;
if (signal >= EntryThreshold)
{
if (Position < 0)
BuyMarket(Math.Abs(Position) + Volume);
else if (Position == 0)
BuyMarket(Volume);
return;
}
if (signal <= -EntryThreshold)
{
if (Position > 0)
SellMarket(Position + Volume);
else if (Position == 0)
SellMarket(Volume);
return;
}
if (Math.Abs(signal) <= ExitThreshold && Position != 0)
{
if (Position > 0)
SellMarket(Position);
else
BuyMarket(Math.Abs(Position));
}
}
}
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 (MovingAverageConvergenceDivergenceSignal,
AwesomeOscillator, WilliamsR, StochasticOscillator)
from StockSharp.Algo.Strategies import Strategy
class multi_indicator_optimizer_strategy(Strategy):
"""
Multi-indicator voting: MACD, AO, OsMA, Williams %R, Stochastic weighted scoring.
"""
def __init__(self):
super(multi_indicator_optimizer_strategy, self).__init__()
self._macd_weight = self.Param("MacdWeight", 1.0).SetDisplay("MACD Weight", "MACD vote weight", "Weights")
self._ao_weight = self.Param("AoWeight", 1.0).SetDisplay("AO Weight", "AO vote weight", "Weights")
self._osma_weight = self.Param("OsmaWeight", 1.0).SetDisplay("OsMA Weight", "OsMA vote weight", "Weights")
self._williams_weight = self.Param("WilliamsWeight", 1.0).SetDisplay("WPR Weight", "Williams vote weight", "Weights")
self._stoch_weight = self.Param("StochasticWeight", 1.0).SetDisplay("Stoch Weight", "Stoch vote weight", "Weights")
self._entry_threshold = self.Param("EntryThreshold", 0.5).SetDisplay("Entry Threshold", "Min score to enter", "Trading")
self._exit_threshold = self.Param("ExitThreshold", 0.1).SetDisplay("Exit Threshold", "Score to exit", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_macd_main = None
self._prev_macd_signal = None
self._prev_osma = None
self._prev_ao = None
self._prev_prev_ao = None
self._prev_williams = None
self._prev_prev_williams = None
self._prev_stoch_k = None
self._prev_prev_stoch_k = None
self._prev_stoch_signal = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(multi_indicator_optimizer_strategy, self).OnReseted()
self._prev_macd_main = None
self._prev_macd_signal = None
self._prev_osma = None
self._prev_ao = None
self._prev_prev_ao = None
self._prev_williams = None
self._prev_prev_williams = None
self._prev_stoch_k = None
self._prev_prev_stoch_k = None
self._prev_stoch_signal = None
def OnStarted2(self, time):
super(multi_indicator_optimizer_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = 12
macd.Macd.LongMa.Length = 26
macd.SignalMa.Length = 9
osma = MovingAverageConvergenceDivergenceSignal()
osma.Macd.ShortMa.Length = 12
osma.Macd.LongMa.Length = 26
osma.SignalMa.Length = 9
ao = AwesomeOscillator()
ao.ShortMa.Length = 5
ao.LongMa.Length = 34
williams = WilliamsR()
williams.Length = 14
stoch = StochasticOscillator()
stoch.K.Length = 5
stoch.D.Length = 3
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, osma, ao, williams, stoch, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, macd_value, osma_value, ao_value, williams_value, stoch_value):
if candle.State != CandleStates.Finished:
return
macd_typed = macd_value
cur_macd = macd_typed.Macd
cur_macd_sig = macd_typed.Signal
if cur_macd is None or cur_macd_sig is None:
return
cur_macd = float(cur_macd)
cur_macd_sig = float(cur_macd_sig)
osma_typed = osma_value
osma_m = osma_typed.Macd
osma_s = osma_typed.Signal
cur_osma = float(osma_m) - float(osma_s) if osma_m is not None and osma_s is not None else None
if cur_osma is None:
return
cur_ao = float(ao_value)
cur_williams = float(williams_value)
stoch_typed = stoch_value
cur_k = stoch_typed.K
cur_d = stoch_typed.D
if cur_k is None or cur_d is None:
return
cur_k = float(cur_k)
cur_d = float(cur_d)
signal = 0.0
macd_w = float(self._macd_weight.Value)
ao_w = float(self._ao_weight.Value)
osma_w = float(self._osma_weight.Value)
wpr_w = float(self._williams_weight.Value)
stoch_w = float(self._stoch_weight.Value)
if self._prev_macd_main is not None and self._prev_macd_signal is not None:
ms = 1.0 if self._prev_macd_main > 0 else (-1.0 if self._prev_macd_main < 0 else 0.0)
cs = 1.0 if self._prev_macd_main > self._prev_macd_signal else (-1.0 if self._prev_macd_main < self._prev_macd_signal else 0.0)
signal += (ms + cs) / 2.0 * macd_w
if self._prev_ao is not None:
ds = 1.0 if self._prev_ao > 0 else (-1.0 if self._prev_ao < 0 else 0.0)
mom = 0.0
if self._prev_prev_ao is not None:
mom = 1.0 if self._prev_ao > self._prev_prev_ao else (-1.0 if self._prev_ao < self._prev_prev_ao else 0.0)
signal += (ds + mom) / 2.0 * ao_w
if self._prev_osma is not None:
os = 1.0 if self._prev_osma > 0 else (-1.0 if self._prev_osma < 0 else 0.0)
signal += os * osma_w
if self._prev_williams is not None and self._prev_prev_williams is not None:
ws = 0.0
if self._prev_williams > -80 and self._prev_prev_williams <= -80:
ws = 1.0
elif self._prev_williams < -20 and self._prev_prev_williams >= -20:
ws = -1.0
signal += ws * wpr_w
if self._prev_stoch_k is not None and self._prev_stoch_signal is not None:
ss1 = 0.0
if self._prev_prev_stoch_k is not None:
if self._prev_stoch_k > 20 and self._prev_prev_stoch_k <= 20:
ss1 = 1.0
elif self._prev_stoch_k < 80 and self._prev_prev_stoch_k >= 80:
ss1 = -1.0
ss2 = 1.0 if self._prev_stoch_k > self._prev_stoch_signal else (-1.0 if self._prev_stoch_k < self._prev_stoch_signal else 0.0)
signal += (ss1 + ss2) / 2.0 * stoch_w
entry_th = float(self._entry_threshold.Value)
exit_th = float(self._exit_threshold.Value)
if signal >= entry_th and self.Position <= 0:
self.BuyMarket()
elif signal <= -entry_th and self.Position >= 0:
self.SellMarket()
elif abs(signal) <= exit_th and self.Position != 0:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._prev_macd_main = cur_macd
self._prev_macd_signal = cur_macd_sig
self._prev_osma = cur_osma
self._prev_prev_ao = self._prev_ao
self._prev_ao = cur_ao
self._prev_prev_williams = self._prev_williams
self._prev_williams = cur_williams
self._prev_prev_stoch_k = self._prev_stoch_k
self._prev_stoch_k = cur_k
self._prev_stoch_signal = cur_d
def CreateClone(self):
return multi_indicator_optimizer_strategy()