ColorXADX
Стратегия на основе пересечения линий +DI и -DI с подтверждением по ADX.
Система отслеживает индикаторы направленного движения. Когда +DI пересекает -DI сверху и значение Average Directional Index превышает заданный порог, открывается длинная позиция и закрывается возможная короткая. При обратном пересечении (-DI над +DI) и высоком ADX открывается короткая позиция и закрываются лонги. Управление рисками осуществляется через фиксированные уровни стоп-лосса и тейк-профита.
Детали
- Критерии входа: пересечение +DI/-DI при ADX выше порога.
- Лонг/Шорт: обе стороны.
- Критерии выхода: противоположный сигнал или стопы.
- Стопы: да, фиксированные стоп-лосс и тейк-профит.
- Значения по умолчанию:
AdxPeriod= 14AdxThreshold= 30mStopLoss= 1000TakeProfit= 2000CandleType= TimeSpan.FromHours(4)
- Фильтры:
- Категория: Тренд
- Направление: Обе
- Индикаторы: ADX, DMI
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Свинговый (4ч)
- Сезонность: Нет
- Нейросети: Нет
- Дивергенции: Нет
- Уровень риска: Средний
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>
/// Strategy based on crossing of +DI and -DI with ADX confirmation.
/// </summary>
public class ColorXAdxStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevPlusDi;
private decimal? _prevMinusDi;
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorXAdxStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 20m)
.SetDisplay("ADX Threshold", "Minimum ADX level for trades", "Indicators");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevPlusDi = null;
_prevMinusDi = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(adx, ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, adx);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!adxValue.IsFormed)
return;
var adx = (AverageDirectionalIndexValue)adxValue;
var plusDi = adx.Dx.Plus;
var minusDi = adx.Dx.Minus;
var adxMain = adx.MovingAverage;
if (plusDi is null || minusDi is null || adxMain is null)
return;
if (_prevPlusDi is null || _prevMinusDi is null)
{
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
return;
}
// Detect DI cross with ADX confirmation
if (plusDi > minusDi && _prevPlusDi <= _prevMinusDi && adxMain > AdxThreshold && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (minusDi > plusDi && _prevMinusDi <= _prevPlusDi && adxMain > AdxThreshold && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageDirectionalIndex
from StockSharp.Algo.Strategies import Strategy
class color_xadx_strategy(Strategy):
def __init__(self):
super(color_xadx_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 20.0) \
.SetDisplay("ADX Threshold", "Minimum ADX level for trades", "Indicators")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_plus_di = None
self._prev_minus_di = None
@property
def adx_period(self):
return self._adx_period.Value
@property
def adx_threshold(self):
return self._adx_threshold.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_xadx_strategy, self).OnReseted()
self._prev_plus_di = None
self._prev_minus_di = None
def OnStarted2(self, time):
super(color_xadx_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self.adx_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, adx)
self.DrawOwnTrades(area)
def process_candle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
if not adx_value.IsFormed:
return
plus_di = adx_value.Dx.Plus
minus_di = adx_value.Dx.Minus
adx_main = adx_value.MovingAverage
if plus_di is None or minus_di is None or adx_main is None:
return
plus_di = float(plus_di)
minus_di = float(minus_di)
adx_main = float(adx_main)
if self._prev_plus_di is None or self._prev_minus_di is None:
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
return
if (plus_di > minus_di and self._prev_plus_di <= self._prev_minus_di and
adx_main > float(self.adx_threshold) and self.Position <= 0):
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif (minus_di > plus_di and self._prev_minus_di <= self._prev_plus_di and
adx_main > float(self.adx_threshold) and self.Position >= 0):
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
def CreateClone(self):
return color_xadx_strategy()