Стратегия Multi Indicator Optimizer
Стратегия воспроизводит голосующую логику советника MetaTrader MultiIndicatorOptimizer на высокоуровневом API StockSharp. Пять классических осцилляторов анализируют завершившуюся свечу и вносят взвешённый вклад в суммарный индикатор настроения. Полученное значение сравнивается с заданными пользователем порогами, после чего стратегия принимает решение открыть длинную позицию, короткую позицию либо закрыть имеющиеся сделки.
Логика торговли
- Блок MACD — проверяет знак гистограммы и соотношение основной и сигнальной линий (данные берутся по предыдущей завершённой свече). Сумма двух сигналов усредняется и умножается на
MacdWeight. - Блок Awesome Oscillator — определяет, находится ли осциллятор выше либо ниже нуля и усиливается ли импульс относительно бара ранее. Средний сигнал масштабируется коэффициентом
AoWeight. - Блок OsMA — анализирует знак гистограммы MACD предыдущей свечи и умножает его на
OsmaWeight. - Блок Williams %R — отслеживает пробои зон перепроданности/перекупленности, заданных
WilliamsLowerLevelиWilliamsUpperLevel. Выход из нижней зоны даёт бычий сигнал, вход из верхней зоны — медвежий. Результат умножается наWilliamsWeight. - Блок Stochastic — объединяет два критерия: пересечение %K порогов
StochasticLowerLevel/StochasticUpperLevelи взаимное положение %K и %D. Среднее значение обоих под-сигналов умножается наStochasticWeight.
Суммарный показатель сохраняется в журнале (колонка Signal) и доступен через поле _lastSignal. Торговый движок реагирует на величину следующим образом:
signal >= EntryThreshold— закрыть шорты и открыть/поддерживать длинную позицию.signal <= -EntryThreshold— закрыть лонги и открыть/поддерживать короткую позицию.abs(signal) <= ExitThreshold— выйти в кэш, чтобы не торговать во флэтовой зоне.
Все расчёты ведутся по данным предыдущей завершённой свечи, что соответствует оригинальной MQL-реализации, использовавшей обращения сдвигами (shift = 1/2).
Параметры
| Параметр | Описание | Значение по умолчанию |
|---|---|---|
CandleType |
Основной таймфрейм для расчёта индикаторов. | Свечи H1 |
MacdFast / MacdSlow / MacdSignal |
Периоды EMA для блока MACD. | 12 / 26 / 9 |
MacdWeight |
Вес голоса блока MACD (отрицательное значение инвертирует вклад). | 1 |
AoShortPeriod / AoLongPeriod |
Периоды скользящих средних осциллятора Awesome. | 5 / 34 |
AoWeight |
Вес голоса блока Awesome. | 1 |
OsmaFastPeriod / OsmaSlowPeriod / OsmaSignalPeriod |
Настройки MACD для расчёта гистограммы OsMA. | 12 / 26 / 9 |
OsmaWeight |
Вес голоса блока OsMA. | 1 |
WilliamsPeriod |
Длина окна для Williams %R. | 14 |
WilliamsLowerLevel / WilliamsUpperLevel |
Границы перепроданности/перекупленности (в процентах). | -80 / -20 |
WilliamsWeight |
Вес голоса блока Williams. | 1 |
StochasticKPeriod / StochasticDPeriod / StochasticSlowing |
Параметры стохастика и сглаживания %K. | 5 / 3 / 3 |
StochasticLowerLevel / StochasticUpperLevel |
Пороги перепроданности/перекупленности для %K. | 20 / 80 |
StochasticWeight |
Вес голоса блока Stochastic. | 1 |
EntryThreshold |
Минимальный модуль голоса для открытия или разворота позиции. | 0.5 |
ExitThreshold |
Ширина нейтральной зоны: позиции закрываются, когда модуль сигнала падает ниже порога. | 0.1 |
Каждый вес может быть отрицательным, что позволяет выключить или инвертировать вклад соответствующего блока и облегчает оптимизацию параметров.
Особенности
- Используется только высокоуровневый API:
SubscribeCandles, привязка индикаторов и готовые методыBuyMarket/SellMarket. - Все сигналы формируются по завершённым свечам, поэтому решения принимаются на подтверждённых данных.
- Размер позиции задаётся базовым свойством
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()