ColorXADX
Strategy based on the crossing of +DI and -DI lines confirmed by ADX strength.
The system monitors the Directional Movement indicators. When +DI crosses above -DI with the Average Directional Index exceeding a set threshold, it enters a long position and exits any existing short. Conversely, a bearish cross (-DI above +DI) with strong ADX opens a short and closes longs. Stop-loss and take-profit levels are applied to manage risk.
Details
- Entry Criteria: +DI/-DI cross with ADX above threshold.
- Long/Short: Both directions.
- Exit Criteria: Opposite signal or stop levels.
- Stops: Yes, fixed stop-loss and take-profit.
- Default Values:
AdxPeriod= 14AdxThreshold= 30mStopLoss= 1000TakeProfit= 2000CandleType= TimeSpan.FromHours(4)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: ADX, DMI
- Stops: Yes
- Complexity: Basic
- Timeframe: Swing (4h)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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()