Стратегия Elli Ichimoku ADX
Описание
Порт эксперта MetaTrader 5 «Elli» (редакция barabashkakvn) на платформу StockSharp. Стратегия объединяет индикатор Ichimoku Kinko Hyo и фильтр по линии +DI индекса направленного движения (ADX). Сделки открываются только при наличии мощного импульса, подтверждённого упорядоченным расположением линий Ichimoku и резким ростом положительного направленного индекса.
В реализации для StockSharp сохранена работа с двумя потоками свечей: анализ Ichimoku выполняется на старшем таймфрейме (по умолчанию 1 час), а ADX рассчитывается на более быстром ряду (по умолчанию 1 минута). Рыночные заявки сопровождаются фиксированными стоп-лоссом и тейк-профитом в шагах цены, как в исходном советнике.
Индикаторы и данные
- Ichimoku (Tenkan 19, Kijun 60, Senkou Span B 120 по умолчанию).
- Average Directional Index (ADX) – используется только линия +DI, как в оригинале.
- На графике отображаются свечи выбранного таймфрейма, облако Ichimoku и линия ADX.
Создаются две подписки на свечи:
IchimokuCandleType (по умолчанию H1) – для расчёта Ichimoku и генерации сигналов.
AdxCandleType (по умолчанию M1) – для расчёта ADX и хранения текущего/предыдущего значения +DI.
Параметры
| Параметр |
Значение по умолчанию |
Назначение |
TakeProfitPoints |
60 |
Дистанция тейк-профита в шагах цены. 0 – отключить. |
StopLossPoints |
30 |
Дистанция стоп-лосса в шагах цены. 0 – отключить. |
TenkanPeriod |
19 |
Период линии Tenkan-sen. |
KijunPeriod |
60 |
Период линии Kijun-sen. |
SenkouSpanBPeriod |
120 |
Период линии Senkou Span B. |
AdxPeriod |
10 |
Период индикатора ADX. |
PlusDiHighThreshold |
13 |
Минимальное значение текущего +DI. |
PlusDiLowThreshold |
6 |
Максимальное значение предыдущего +DI. |
BaselineDistanceThreshold |
20 |
Минимальный зазор между Tenkan и Kijun в шагах цены. |
IchimokuCandleType |
Свечи 1 часа |
Таймфрейм для расчёта Ichimoku. |
AdxCandleType |
Свечи 1 минуты |
Таймфрейм для расчёта ADX. |
Логика торговли
- Дождаться закрытия свечи Ichimoku.
- Проверить наличие двух последних значений ADX и факта пробоя по +DI (
предыдущее +DI < PlusDiLowThreshold и текущее +DI > PlusDiHighThreshold).
- Перевести расстояние между Tenkan и Kijun в шаги цены и убедиться, что оно превышает
BaselineDistanceThreshold.
- Если уже есть открытая позиция, новые заявки не выставляются.
- Покупка выполняется, когда:
- Tenkan > Kijun;
- Kijun > Senkou Span A;
- Senkou Span A > Senkou Span B (бычье облако);
- Цена закрытия > Kijun.
- Продажа выполняется при зеркальной конфигурации (Tenkan < Kijun < Senkou Span A < Senkou Span B и цена закрытия ниже Kijun).
- Выход из позиции происходит по защитным ордерам, устанавливаемым через
StartProtection. Дополнительные условия выхода не используются, что соответствует оригинальному советнику.
Управление риском
Метод StartProtection запускается один раз при старте. Если стоп или тейк установлен в 0, соответствующий ордер не активируется. Заявки размещаются рыночными ордерами (BuyMarket/SellMarket) с заданными уровнями SL/TP.
Особенности реализации
- Входы в обе стороны фильтруются только по +DI, поскольку в исходном коде блок с -DI был закомментирован.
- Линия Chikou напрямую не контролируется: для проверки структуры облака достаточно соотношения Senkou Span A и B.
- Последние значения +DI сохраняются во внутренних переменных, без обращения к
GetValue, что соответствует рекомендациям по высокоуровневому API.
- Если таймфреймы Ichimoku и ADX совпадают, используется одна подписка на свечи.
Рекомендации по применению
- Для воспроизведения оригинала оставьте быстрый ADX (
AdxCandleType) и более медленный Ichimoku (IchimokuCandleType).
- Увеличивайте
BaselineDistanceThreshold на волатильных рынках, чтобы требовать большего отрыва Tenkan от Kijun.
- Советник открывает только одну позицию, поэтому при работе с несколькими инструментами добавьте портфельный контроль риска.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the MQL5 strategy "Elli" combining Ichimoku and ADX filters.
/// Focuses on impulsive moves confirmed by +DI acceleration and Ichimoku line alignment.
/// </summary>
public class ElliIchimokuAdxStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _plusDiHighThreshold;
private readonly StrategyParam<decimal> _plusDiLowThreshold;
private readonly StrategyParam<decimal> _baselineDistanceThreshold;
private readonly StrategyParam<DataType> _ichimokuCandleType;
private readonly StrategyParam<DataType> _adxCandleType;
private Ichimoku _ichimoku;
private decimal? _previousPlusDi;
private decimal? _currentPlusDi;
private bool _isAdxReady;
private decimal? _previousAdxHigh;
private decimal? _previousAdxLow;
private decimal? _previousAdxClose;
private decimal _smoothedTrueRange;
private decimal _smoothedPlusDm;
private int _adxSamples;
/// <summary>
/// Take profit distance expressed in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Stop loss distance expressed in price steps.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Tenkan-sen (conversion line) period.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Kijun-sen (base line) period.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Senkou Span B period.
/// </summary>
public int SenkouSpanBPeriod
{
get => _senkouSpanBPeriod.Value;
set => _senkouSpanBPeriod.Value = value;
}
/// <summary>
/// ADX calculation period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Upper threshold for +DI breakout confirmation.
/// </summary>
public decimal PlusDiHighThreshold
{
get => _plusDiHighThreshold.Value;
set => _plusDiHighThreshold.Value = value;
}
/// <summary>
/// Lower threshold that previous +DI must stay below before breakout.
/// </summary>
public decimal PlusDiLowThreshold
{
get => _plusDiLowThreshold.Value;
set => _plusDiLowThreshold.Value = value;
}
/// <summary>
/// Required Tenkan/Kijun separation measured in price steps.
/// </summary>
public decimal BaselineDistanceThreshold
{
get => _baselineDistanceThreshold.Value;
set => _baselineDistanceThreshold.Value = value;
}
/// <summary>
/// Candle type used for Ichimoku evaluation and trading decisions.
/// </summary>
public DataType IchimokuCandleType
{
get => _ichimokuCandleType.Value;
set => _ichimokuCandleType.Value = value;
}
/// <summary>
/// Candle type used for ADX calculation.
/// </summary>
public DataType AdxCandleType
{
get => _adxCandleType.Value;
set => _adxCandleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="ElliIchimokuAdxStrategy"/>.
/// </summary>
public ElliIchimokuAdxStrategy()
{
_takeProfitPoints = Param(nameof(TakeProfitPoints), 60m)
.SetDisplay("Take Profit", "Take profit distance in price steps", "Risk Management")
.SetNotNegative();
_stopLossPoints = Param(nameof(StopLossPoints), 30m)
.SetDisplay("Stop Loss", "Stop loss distance in price steps", "Risk Management")
.SetNotNegative();
_tenkanPeriod = Param(nameof(TenkanPeriod), 19)
.SetDisplay("Tenkan Period", "Tenkan-sen (conversion line) length", "Ichimoku")
.SetGreaterThanZero();
_kijunPeriod = Param(nameof(KijunPeriod), 60)
.SetDisplay("Kijun Period", "Kijun-sen (base line) length", "Ichimoku")
.SetGreaterThanZero();
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 120)
.SetDisplay("Senkou Span B Period", "Senkou Span B length", "Ichimoku")
.SetGreaterThanZero();
_adxPeriod = Param(nameof(AdxPeriod), 10)
.SetDisplay("ADX Period", "Average Directional Index period", "ADX")
.SetGreaterThanZero();
_plusDiHighThreshold = Param(nameof(PlusDiHighThreshold), 10m)
.SetDisplay("+DI High Threshold", "Level current +DI must exceed", "ADX")
.SetGreaterThanZero();
_plusDiLowThreshold = Param(nameof(PlusDiLowThreshold), 8m)
.SetDisplay("+DI Low Threshold", "Level previous +DI must stay below", "ADX")
.SetNotNegative();
_baselineDistanceThreshold = Param(nameof(BaselineDistanceThreshold), 5m)
.SetDisplay("Baseline Distance", "Minimum Tenkan/Kijun spread in steps", "Ichimoku")
.SetNotNegative();
_ichimokuCandleType = Param(nameof(IchimokuCandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Ichimoku Candle", "Candle series for Ichimoku", "General");
_adxCandleType = Param(nameof(AdxCandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("ADX Candle", "Candle series for ADX", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, IchimokuCandleType);
if (AdxCandleType != IchimokuCandleType)
yield return (Security, AdxCandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousPlusDi = null;
_currentPlusDi = null;
_isAdxReady = false;
_previousAdxHigh = null;
_previousAdxLow = null;
_previousAdxClose = null;
_smoothedTrueRange = 0m;
_smoothedPlusDm = 0m;
_adxSamples = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanBPeriod }
};
var ichimokuSubscription = SubscribeCandles(IchimokuCandleType);
ichimokuSubscription.BindEx(_ichimoku, ProcessIchimoku);
if (AdxCandleType == IchimokuCandleType)
{
ichimokuSubscription.Bind(ProcessAdxCandle);
ichimokuSubscription.Start();
}
else
{
ichimokuSubscription.Start();
var adxSubscription = SubscribeCandles(AdxCandleType);
adxSubscription.Bind(ProcessAdxCandle).Start();
}
if (TakeProfitPoints > 0m || StopLossPoints > 0m)
{
StartProtection(
StopLossPoints > 0m ? new Unit(StopLossPoints, UnitTypes.Absolute) : null,
TakeProfitPoints > 0m ? new Unit(TakeProfitPoints, UnitTypes.Absolute) : null);
}
var priceArea = CreateChartArea();
if (priceArea != null)
{
DrawCandles(priceArea, ichimokuSubscription);
DrawIndicator(priceArea, _ichimoku);
DrawOwnTrades(priceArea);
}
}
private void ProcessAdxCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_previousAdxHigh is not decimal previousHigh ||
_previousAdxLow is not decimal previousLow ||
_previousAdxClose is not decimal previousClose)
{
_previousAdxHigh = candle.HighPrice;
_previousAdxLow = candle.LowPrice;
_previousAdxClose = candle.ClosePrice;
return;
}
var upMove = candle.HighPrice - previousHigh;
var downMove = previousLow - candle.LowPrice;
var plusDm = upMove > downMove && upMove > 0m ? upMove : 0m;
var trueRange = Math.Max(
candle.HighPrice - candle.LowPrice,
Math.Max(
Math.Abs(candle.HighPrice - previousClose),
Math.Abs(candle.LowPrice - previousClose)));
if (_adxSamples < AdxPeriod)
{
_smoothedPlusDm += plusDm;
_smoothedTrueRange += trueRange;
_adxSamples++;
}
else
{
_smoothedPlusDm = _smoothedPlusDm - (_smoothedPlusDm / AdxPeriod) + plusDm;
_smoothedTrueRange = _smoothedTrueRange - (_smoothedTrueRange / AdxPeriod) + trueRange;
}
if (_adxSamples >= AdxPeriod && _smoothedTrueRange > 0m)
{
_previousPlusDi = _currentPlusDi;
_currentPlusDi = 100m * _smoothedPlusDm / _smoothedTrueRange;
_isAdxReady = _previousPlusDi.HasValue;
}
_previousAdxHigh = candle.HighPrice;
_previousAdxLow = candle.LowPrice;
_previousAdxClose = candle.ClosePrice;
}
private void ProcessIchimoku(ICandleMessage candle, IIndicatorValue ichimokuValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_currentPlusDi is not decimal currentPlus || _previousPlusDi is not decimal previousPlus)
return;
if (!_isAdxReady)
return;
var ich = (IchimokuValue)ichimokuValue;
if (ich.Tenkan is not decimal tenkan ||
ich.Kijun is not decimal kijun ||
ich.SenkouA is not decimal senkouA ||
ich.SenkouB is not decimal senkouB)
{
return;
}
var priceStep = Security?.PriceStep ?? 1m;
if (priceStep <= 0m)
priceStep = 1m;
var baselineDistance = Math.Abs(tenkan - kijun) / priceStep;
var hasPlusDiBreakout = currentPlus > PlusDiHighThreshold && previousPlus >= PlusDiLowThreshold && currentPlus >= previousPlus;
if (!hasPlusDiBreakout)
return;
if (baselineDistance < BaselineDistanceThreshold)
return;
if (Position != 0)
return;
var priceAboveCloud = senkouA > senkouB && kijun > senkouA && tenkan > kijun && candle.ClosePrice > kijun;
var priceBelowCloud = senkouA < senkouB && kijun < senkouA && tenkan < kijun && candle.ClosePrice < kijun;
if (priceAboveCloud)
{
this.LogInfo($"Bullish signal: Tenkan {tenkan:F2} > Kijun {kijun:F2}, cloud rising, +DI from {previousPlus:F2} to {currentPlus:F2}.");
BuyMarket();
}
else if (priceBelowCloud)
{
this.LogInfo($"Bearish signal: Tenkan {tenkan:F2} < Kijun {kijun:F2}, cloud falling, +DI from {previousPlus:F2} to {currentPlus:F2}.");
SellMarket();
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import Ichimoku
from StockSharp.Algo.Strategies import Strategy
class elli_ichimoku_adx_strategy(Strategy):
def __init__(self):
super(elli_ichimoku_adx_strategy, self).__init__()
self._take_profit_points = self.Param("TakeProfitPoints", 60.0)
self._stop_loss_points = self.Param("StopLossPoints", 30.0)
self._tenkan_period = self.Param("TenkanPeriod", 19)
self._kijun_period = self.Param("KijunPeriod", 60)
self._senkou_span_b_period = self.Param("SenkouSpanBPeriod", 120)
self._adx_period = self.Param("AdxPeriod", 10)
self._plus_di_high_threshold = self.Param("PlusDiHighThreshold", 10.0)
self._plus_di_low_threshold = self.Param("PlusDiLowThreshold", 8.0)
self._baseline_distance_threshold = self.Param("BaselineDistanceThreshold", 5.0)
self._ichimoku_candle_type = self.Param("IchimokuCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._adx_candle_type = self.Param("AdxCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._previous_plus_di = None
self._current_plus_di = None
self._is_adx_ready = False
self._previous_adx_high = None
self._previous_adx_low = None
self._previous_adx_close = None
self._smoothed_true_range = 0.0
self._smoothed_plus_dm = 0.0
self._adx_samples = 0
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TenkanPeriod(self):
return self._tenkan_period.Value
@TenkanPeriod.setter
def TenkanPeriod(self, value):
self._tenkan_period.Value = value
@property
def KijunPeriod(self):
return self._kijun_period.Value
@KijunPeriod.setter
def KijunPeriod(self, value):
self._kijun_period.Value = value
@property
def SenkouSpanBPeriod(self):
return self._senkou_span_b_period.Value
@SenkouSpanBPeriod.setter
def SenkouSpanBPeriod(self, value):
self._senkou_span_b_period.Value = value
@property
def AdxPeriod(self):
return self._adx_period.Value
@AdxPeriod.setter
def AdxPeriod(self, value):
self._adx_period.Value = value
@property
def PlusDiHighThreshold(self):
return self._plus_di_high_threshold.Value
@PlusDiHighThreshold.setter
def PlusDiHighThreshold(self, value):
self._plus_di_high_threshold.Value = value
@property
def PlusDiLowThreshold(self):
return self._plus_di_low_threshold.Value
@PlusDiLowThreshold.setter
def PlusDiLowThreshold(self, value):
self._plus_di_low_threshold.Value = value
@property
def BaselineDistanceThreshold(self):
return self._baseline_distance_threshold.Value
@BaselineDistanceThreshold.setter
def BaselineDistanceThreshold(self, value):
self._baseline_distance_threshold.Value = value
@property
def IchimokuCandleType(self):
return self._ichimoku_candle_type.Value
@IchimokuCandleType.setter
def IchimokuCandleType(self, value):
self._ichimoku_candle_type.Value = value
@property
def AdxCandleType(self):
return self._adx_candle_type.Value
@AdxCandleType.setter
def AdxCandleType(self, value):
self._adx_candle_type.Value = value
def OnStarted2(self, time):
super(elli_ichimoku_adx_strategy, self).OnStarted2(time)
self._ichimoku = Ichimoku()
self._ichimoku.Tenkan.Length = self.TenkanPeriod
self._ichimoku.Kijun.Length = self.KijunPeriod
self._ichimoku.SenkouB.Length = self.SenkouSpanBPeriod
self._previous_plus_di = None
self._current_plus_di = None
self._is_adx_ready = False
self._previous_adx_high = None
self._previous_adx_low = None
self._previous_adx_close = None
self._smoothed_true_range = 0.0
self._smoothed_plus_dm = 0.0
self._adx_samples = 0
ichi_sub = self.SubscribeCandles(self.IchimokuCandleType)
ichi_sub.BindEx(self._ichimoku, self._process_ichimoku)
if str(self.AdxCandleType) == str(self.IchimokuCandleType):
ichi_sub.Bind(self._process_adx_candle)
ichi_sub.Start()
else:
ichi_sub.Start()
adx_sub = self.SubscribeCandles(self.AdxCandleType)
adx_sub.Bind(self._process_adx_candle).Start()
tp = float(self.TakeProfitPoints)
sl = float(self.StopLossPoints)
sl_unit = Unit(sl, UnitTypes.Absolute) if sl > 0.0 else None
tp_unit = Unit(tp, UnitTypes.Absolute) if tp > 0.0 else None
if tp > 0.0 or sl > 0.0:
self.StartProtection(sl_unit, tp_unit)
def _process_adx_candle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._previous_adx_high is None or self._previous_adx_low is None or self._previous_adx_close is None:
self._previous_adx_high = high
self._previous_adx_low = low
self._previous_adx_close = close
return
up_move = high - self._previous_adx_high
down_move = self._previous_adx_low - low
plus_dm = up_move if (up_move > down_move and up_move > 0.0) else 0.0
true_range = max(high - low, max(abs(high - self._previous_adx_close), abs(low - self._previous_adx_close)))
adx_period = int(self.AdxPeriod)
if self._adx_samples < adx_period:
self._smoothed_plus_dm += plus_dm
self._smoothed_true_range += true_range
self._adx_samples += 1
else:
self._smoothed_plus_dm = self._smoothed_plus_dm - (self._smoothed_plus_dm / adx_period) + plus_dm
self._smoothed_true_range = self._smoothed_true_range - (self._smoothed_true_range / adx_period) + true_range
if self._adx_samples >= adx_period and self._smoothed_true_range > 0.0:
self._previous_plus_di = self._current_plus_di
self._current_plus_di = 100.0 * self._smoothed_plus_dm / self._smoothed_true_range
self._is_adx_ready = self._previous_plus_di is not None
self._previous_adx_high = high
self._previous_adx_low = low
self._previous_adx_close = close
def _process_ichimoku(self, candle, ichimoku_value):
if candle.State != CandleStates.Finished:
return
if self._current_plus_di is None or self._previous_plus_di is None:
return
if not self._is_adx_ready:
return
tenkan = ichimoku_value.Tenkan
kijun = ichimoku_value.Kijun
senkou_a = ichimoku_value.SenkouA
senkou_b = ichimoku_value.SenkouB
if tenkan is None or kijun is None or senkou_a is None or senkou_b is None:
return
tenkan_val = float(tenkan)
kijun_val = float(kijun)
senkou_a_val = float(senkou_a)
senkou_b_val = float(senkou_b)
close = float(candle.ClosePrice)
price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if price_step <= 0.0:
price_step = 1.0
baseline_distance = abs(tenkan_val - kijun_val) / price_step
di_high = float(self.PlusDiHighThreshold)
di_low = float(self.PlusDiLowThreshold)
has_plus_di_breakout = (self._current_plus_di > di_high and
self._previous_plus_di >= di_low and
self._current_plus_di >= self._previous_plus_di)
if not has_plus_di_breakout:
return
if baseline_distance < float(self.BaselineDistanceThreshold):
return
if self.Position != 0:
return
price_above_cloud = (senkou_a_val > senkou_b_val and kijun_val > senkou_a_val and
tenkan_val > kijun_val and close > kijun_val)
price_below_cloud = (senkou_a_val < senkou_b_val and kijun_val < senkou_a_val and
tenkan_val < kijun_val and close < kijun_val)
if price_above_cloud:
self.BuyMarket()
elif price_below_cloud:
self.SellMarket()
def OnReseted(self):
super(elli_ichimoku_adx_strategy, self).OnReseted()
self._previous_plus_di = None
self._current_plus_di = None
self._is_adx_ready = False
self._previous_adx_high = None
self._previous_adx_low = None
self._previous_adx_close = None
self._smoothed_true_range = 0.0
self._smoothed_plus_dm = 0.0
self._adx_samples = 0
def CreateClone(self):
return elli_ichimoku_adx_strategy()