Multi Indicator Optimizer Strategy
The strategy replicates the voting logic of the MetaTrader expert MultiIndicatorOptimizer on top of the high level StockSharp API. Five classic oscillators evaluate the finished candle and contribute a weighted vote toward the aggregated sentiment. The resulting score is then compared with user-defined thresholds to decide whether the strategy should go long, go short, or flatten an existing position.
Trading logic
- MACD block – inspects the sign of the histogram and the relation between the main and signal lines (both taken from the previous finished bar). The sum of these two signals is averaged and multiplied by
MacdWeight. - Awesome Oscillator block – measures whether the oscillator is above or below the zero line and whether momentum improves versus the bar before. The average vote is scaled by
AoWeight. - OsMA block – checks the sign of the MACD histogram from the previous candle and applies
OsmaWeight. - Williams %R block – reacts to oversold/overbought crossings defined by
WilliamsLowerLevelandWilliamsUpperLevel. A crossing upwards from the lower band votes bullish, while a crossing downwards from the upper band votes bearish. The result is multiplied byWilliamsWeight. - Stochastic block – combines two checks: a threshold crossing of %K vs.
StochasticLowerLevel/StochasticUpperLeveland a %K/%D relationship. The average of both sub-signals is multiplied byStochasticWeight.
The aggregated score is stored in the Signal column of the logs and exposed via the _lastSignal field inside the strategy. The trading engine evaluates the score as follows:
signal >= EntryThreshold: close any short position and open/maintain a long position.signal <= -EntryThreshold: close any long position and open/maintain a short position.abs(signal) <= ExitThreshold: flat the position to avoid trading in neutral market conditions.
All computations work on the previous finished candle to match the original MT4 implementation that used indexed indicator values (shift = 1/2).
Parameters
| Parameter | Description | Default |
|---|---|---|
CandleType |
Primary timeframe for all indicator calculations. | H1 candles |
MacdFast / MacdSlow / MacdSignal |
EMA lengths for the MACD block. | 12 / 26 / 9 |
MacdWeight |
Vote multiplier for the MACD block. Negative values invert the vote. | 1 |
AoShortPeriod / AoLongPeriod |
Moving-average lengths used by the Awesome Oscillator. | 5 / 34 |
AoWeight |
Vote multiplier for the Awesome block. | 1 |
OsmaFastPeriod / OsmaSlowPeriod / OsmaSignalPeriod |
MACD settings reused to build the OsMA histogram. | 12 / 26 / 9 |
OsmaWeight |
Vote multiplier for the OsMA block. | 1 |
WilliamsPeriod |
Lookback length for Williams %R. | 14 |
WilliamsLowerLevel / WilliamsUpperLevel |
Oversold/overbought boundaries (in percent). | -80 / -20 |
WilliamsWeight |
Vote multiplier for the Williams block. | 1 |
StochasticKPeriod / StochasticDPeriod / StochasticSlowing |
Periods for the Stochastic oscillator and its internal smoothing. | 5 / 3 / 3 |
StochasticLowerLevel / StochasticUpperLevel |
Oversold/overbought thresholds for %K. | 20 / 80 |
StochasticWeight |
Vote multiplier for the Stochastic block. | 1 |
EntryThreshold |
Minimum absolute vote required to open or reverse a position. | 0.5 |
ExitThreshold |
Neutral-zone width. Positions are closed when the absolute value of the signal falls below this value. | 0.1 |
All weights can be negative to suppress or invert the contribution of a block, which is convenient during optimization runs.
Notes
- The strategy relies purely on the high level API:
SubscribeCandles, indicator bindings, andBuyMarket/SellMarkethelpers. - Every indicator vote uses only completed candles, ensuring decisions are based on confirmed data.
- Position sizing is controlled by the base
Volumeproperty ofStrategy. Protecting orders (stop loss / take profit) can be added externally viaStartProtectionif needed. - Extensive comments are provided in English as requested to simplify further maintenance.
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()