ADX System
ADX System 策略使用平均方向指数 (ADX) 及其 +DI 和 -DI 线进行交易。当 ADX 上升且某一方向线突破 ADX 时开仓。策略设定固定的止盈和止损,并使用追踪止损来保护盈利。
细节
- 入场条件
- ADX 上升(前一值低于当前值)。
- 多头:前一 +DI 低于前一 ADX,且当前 +DI 高于当前 ADX。
- 空头:前一 -DI 低于前一 ADX,且当前 -DI 高于当前 ADX。
- 出场条件
- ADX 与 DI 线给出相反信号。
- 价格触及追踪止损。
- 价格达到固定的止盈或止损。
- 方向:双向。
- 止损/止盈:以绝对价位设置固定止损、止盈和追踪止损。
- 默认值:
AdxPeriod= 14TakeProfit= 15StopLoss= 100TrailingStop= 20CandleType= TimeSpan.FromMinutes(5)
- 筛选器:
- 分类:趋势跟随
- 方向:双向
- 指标:ADX、+DI、-DI
- 止损:有
- 复杂度:初级
- 时间框架:短期
- 季节性:无
- 神经网络:无
- 背离:无
- 风险级别:中等
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>
/// ADX based trading system with trailing stop and fixed take profit/stop loss.
/// </summary>
public class AdxSystemStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<DataType> _candleType;
private AverageDirectionalIndex _adx;
private decimal _prevAdx;
private decimal _prevPlusDi;
private decimal _prevMinusDi;
private decimal? _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// ADX period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Trailing stop distance in price units.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="AdxSystemStrategy"/> class.
/// </summary>
public AdxSystemStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period for ADX indicator", "Indicators");
_takeProfit = Param(nameof(TakeProfit), 150m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Distance for profit target", "Risk");
_stopLoss = Param(nameof(StopLoss), 250m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Distance for protective stop", "Risk");
_trailingStop = Param(nameof(TrailingStop), 120m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Distance for trailing stop", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAdx = 0m;
_prevPlusDi = 0m;
_prevMinusDi = 0m;
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_adx = new AverageDirectionalIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_adx, ProcessCandle)
.Start();
StartProtection(null, null);
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.IsFinal)
return;
var adxVal = (AverageDirectionalIndexValue)adxValue;
if (adxVal.MovingAverage is not decimal currentAdx)
return;
var dx = adxVal.Dx;
if (dx.Plus is not decimal currentPlusDi || dx.Minus is not decimal currentMinusDi)
return;
if (_prevAdx != 0m)
{
if (Position == 0)
{
if (currentAdx >= 20m && currentAdx > _prevAdx && _prevPlusDi <= _prevMinusDi && currentPlusDi > currentMinusDi)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLoss;
_takePrice = _entryPrice + TakeProfit;
}
else if (currentAdx >= 20m && currentAdx > _prevAdx && _prevMinusDi <= _prevPlusDi && currentMinusDi > currentPlusDi)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLoss;
_takePrice = _entryPrice - TakeProfit;
}
}
else if (Position > 0)
{
if (_prevPlusDi >= _prevMinusDi && currentPlusDi < currentMinusDi)
{
SellMarket(Position);
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
return;
}
if (_entryPrice is decimal entry)
{
if (candle.ClosePrice - entry > TrailingStop)
{
var newStop = candle.ClosePrice - TrailingStop;
if (_stopPrice is decimal stop && newStop > stop)
_stopPrice = newStop;
}
if (_takePrice is decimal take && candle.HighPrice >= take)
{
SellMarket(Position);
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
else if (_stopPrice is decimal s && candle.LowPrice <= s)
{
SellMarket(Position);
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
}
}
else if (Position < 0)
{
if (_prevMinusDi >= _prevPlusDi && currentMinusDi < currentPlusDi)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
return;
}
if (_entryPrice is decimal entry)
{
if (entry - candle.ClosePrice > TrailingStop)
{
var newStop = candle.ClosePrice + TrailingStop;
if (_stopPrice is decimal stop && newStop < stop)
_stopPrice = newStop;
}
if (_takePrice is decimal take && candle.LowPrice <= take)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
else if (_stopPrice is decimal s && candle.HighPrice >= s)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
}
}
}
_prevAdx = currentAdx;
_prevPlusDi = currentPlusDi;
_prevMinusDi = currentMinusDi;
}
}
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 adx_system_strategy(Strategy):
def __init__(self):
super(adx_system_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 10)
self._take_profit = self.Param("TakeProfit", 150.0)
self._stop_loss = self.Param("StopLoss", 250.0)
self._trailing_stop = self.Param("TrailingStop", 120.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._prev_adx = 0.0
self._prev_plus_di = 0.0
self._prev_minus_di = 0.0
self._entry_price = None
self._stop_price = None
self._take_price = None
@property
def AdxPeriod(self):
return self._adx_period.Value
@AdxPeriod.setter
def AdxPeriod(self, value):
self._adx_period.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TrailingStop(self):
return self._trailing_stop.Value
@TrailingStop.setter
def TrailingStop(self, value):
self._trailing_stop.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(adx_system_strategy, self).OnStarted2(time)
self._prev_adx = 0.0
self._prev_plus_di = 0.0
self._prev_minus_di = 0.0
self._entry_price = None
self._stop_price = None
self._take_price = None
adx = AverageDirectionalIndex()
adx.Length = self.AdxPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(adx, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
ma_val = adx_value.MovingAverage
if ma_val is None:
return
current_adx = float(ma_val)
dx = adx_value.Dx
plus_val = dx.Plus
minus_val = dx.Minus
if plus_val is None or minus_val is None:
return
current_plus_di = float(plus_val)
current_minus_di = float(minus_val)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
tp = float(self.TakeProfit)
sl = float(self.StopLoss)
trail = float(self.TrailingStop)
if self._prev_adx != 0.0:
if self.Position == 0:
if current_adx >= 20.0 and current_adx > self._prev_adx and self._prev_plus_di <= self._prev_minus_di and current_plus_di > current_minus_di:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - sl
self._take_price = close + tp
elif current_adx >= 20.0 and current_adx > self._prev_adx and self._prev_minus_di <= self._prev_plus_di and current_minus_di > current_plus_di:
self.SellMarket()
self._entry_price = close
self._stop_price = close + sl
self._take_price = close - tp
elif self.Position > 0:
if self._prev_plus_di >= self._prev_minus_di and current_plus_di < current_minus_di:
self.SellMarket()
self._entry_price = None
self._stop_price = None
self._take_price = None
elif self._entry_price is not None:
entry = self._entry_price
if close - entry > trail:
new_stop = close - trail
if self._stop_price is not None and new_stop > self._stop_price:
self._stop_price = new_stop
if self._take_price is not None and high >= self._take_price:
self.SellMarket()
self._entry_price = None
self._stop_price = None
self._take_price = None
elif self._stop_price is not None and low <= self._stop_price:
self.SellMarket()
self._entry_price = None
self._stop_price = None
self._take_price = None
elif self.Position < 0:
if self._prev_minus_di >= self._prev_plus_di and current_minus_di < current_plus_di:
self.BuyMarket()
self._entry_price = None
self._stop_price = None
self._take_price = None
elif self._entry_price is not None:
entry = self._entry_price
if entry - close > trail:
new_stop = close + trail
if self._stop_price is not None and new_stop < self._stop_price:
self._stop_price = new_stop
if self._take_price is not None and low <= self._take_price:
self.BuyMarket()
self._entry_price = None
self._stop_price = None
self._take_price = None
elif self._stop_price is not None and high >= self._stop_price:
self.BuyMarket()
self._entry_price = None
self._stop_price = None
self._take_price = None
self._prev_adx = current_adx
self._prev_plus_di = current_plus_di
self._prev_minus_di = current_minus_di
def OnReseted(self):
super(adx_system_strategy, self).OnReseted()
self._prev_adx = 0.0
self._prev_plus_di = 0.0
self._prev_minus_di = 0.0
self._entry_price = None
self._stop_price = None
self._take_price = None
def CreateClone(self):
return adx_system_strategy()