ADX CCI MA 戦略
この戦略はADX、CCI、設定可能な移動平均を組み合わせて強いトレンドを取引します。
+DIが-DIを上抜け、CCI > 100かつADXが閾値を超えたとき(オプションで終値がMA上)に買います。-DIが+DIを上抜け、CCI < -100かつADXが閾値を超えたとき(終値がMA下)にショートします。
パーセンテージベースのストップロスとテイクプロフィットに加え、移動平均に逆らう複数のローソク足後に決済するオプションのMAリスク管理を含みます。
詳細
- エントリー条件: CCI極値とADX >
AdxThresholdを伴う+DI/-DIクロス、オプションの終値vs MA。 - ロング/ショート: 両方。
- エグジット条件: ストップロスまたはテイクプロフィット到達、オプションのMAリスク管理。
- ストップ: はい、テイクプロフィットとストップロス。
- デフォルト値:
EnableLong= trueEnableShort= trueTakeProfitPercent= 2mStopLossPercent= 1mCciPeriod= 15AdxLength= 10AdxThreshold= 20mUseMaTrend= trueMaType= MovingAverageTypeEnum.SimpleMaLength= 200UseMaRiskManagement= falseMaRiskExitCandles= 2CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: ADX, CCI, MA
- ストップ: はい
- 複雑さ: 中程度
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// ADX CCI MA strategy.
/// A long opens when +DI crosses above -DI, CCI is above 100 and ADX exceeds AdxThreshold (and the close is above the moving average
/// when UseMaTrend is set); a short mirrors this with -DI crossing above +DI and CCI below -100. Percent take profit and stop loss
/// protect positions, and the optional MA risk management exits after MaRiskExitCandles consecutive closes on the wrong side of the
/// moving average.
/// </summary>
public class AdxCciMaStrategy : Strategy
{
/// <summary>
/// Moving average types.
/// </summary>
public enum MovingAverageTypeEnum
{
/// <summary>
/// Simple moving average.
/// </summary>
Simple,
/// <summary>
/// Exponential moving average.
/// </summary>
Exponential,
/// <summary>
/// Weighted moving average.
/// </summary>
Weighted,
/// <summary>
/// Smoothed moving average.
/// </summary>
Smoothed,
}
private const decimal _cciLevel = 100m;
private readonly StrategyParam<bool> _enableLong;
private readonly StrategyParam<bool> _enableShort;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _adxLength;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<bool> _useMaTrend;
private readonly StrategyParam<MovingAverageTypeEnum> _maType;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<bool> _useMaRiskManagement;
private readonly StrategyParam<int> _maRiskExitCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevPlusDi;
private decimal? _prevMinusDi;
private int _againstMaCount;
/// <summary>
/// Allow long trades.
/// </summary>
public bool EnableLong
{
get => _enableLong.Value;
set => _enableLong.Value = value;
}
/// <summary>
/// Allow short trades.
/// </summary>
public bool EnableShort
{
get => _enableShort.Value;
set => _enableShort.Value = value;
}
/// <summary>
/// Take profit percentage.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// ADX period.
/// </summary>
public int AdxLength
{
get => _adxLength.Value;
set => _adxLength.Value = value;
}
/// <summary>
/// ADX level a trend must exceed.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Require the close on the trade side of the moving average.
/// </summary>
public bool UseMaTrend
{
get => _useMaTrend.Value;
set => _useMaTrend.Value = value;
}
/// <summary>
/// Moving average type.
/// </summary>
public MovingAverageTypeEnum MaType
{
get => _maType.Value;
set => _maType.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Exit after several closes against the moving average.
/// </summary>
public bool UseMaRiskManagement
{
get => _useMaRiskManagement.Value;
set => _useMaRiskManagement.Value = value;
}
/// <summary>
/// Consecutive closes against the moving average that trigger the exit.
/// </summary>
public int MaRiskExitCandles
{
get => _maRiskExitCandles.Value;
set => _maRiskExitCandles.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AdxCciMaStrategy()
{
_enableLong = Param(nameof(EnableLong), true)
.SetDisplay("Enable Long", "Allow long trades", "General");
_enableShort = Param(nameof(EnableShort), true)
.SetDisplay("Enable Short", "Allow short trades", "General");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_cciPeriod = Param(nameof(CciPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period", "Indicators");
_adxLength = Param(nameof(AdxLength), 10)
.SetGreaterThanZero()
.SetDisplay("ADX Length", "ADX period", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 20m)
.SetDisplay("ADX Threshold", "ADX level a trend must exceed", "Indicators");
_useMaTrend = Param(nameof(UseMaTrend), true)
.SetDisplay("Use MA Trend", "Require the close on the trade side of the moving average", "MA");
_maType = Param(nameof(MaType), MovingAverageTypeEnum.Simple)
.SetDisplay("MA Type", "Moving average type", "MA");
_maLength = Param(nameof(MaLength), 200)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving average period", "MA");
_useMaRiskManagement = Param(nameof(UseMaRiskManagement), false)
.SetDisplay("Use MA Risk Management", "Exit after several closes against the moving average", "Risk");
_maRiskExitCandles = Param(nameof(MaRiskExitCandles), 2)
.SetGreaterThanZero()
.SetDisplay("MA Risk Exit Candles", "Consecutive closes against the moving average that trigger the exit", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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;
_againstMaCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevPlusDi = null;
_prevMinusDi = null;
_againstMaCount = 0;
var adx = new AverageDirectionalIndex { Length = AdxLength };
var cci = new CommodityChannelIndex { Length = CciPeriod };
IIndicator ma = MaType switch
{
MovingAverageTypeEnum.Exponential => new ExponentialMovingAverage { Length = MaLength },
MovingAverageTypeEnum.Weighted => new WeightedMovingAverage { Length = MaLength },
MovingAverageTypeEnum.Smoothed => new SmoothedMovingAverage { Length = MaLength },
_ => new SimpleMovingAverage { Length = MaLength },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, cci, ma, ProcessCandle)
.Start();
StartProtection(new Unit(TakeProfitPercent, UnitTypes.Percent), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue cciValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!adxValue.IsFormed || !cciValue.IsFormed || !maValue.IsFormed)
return;
var adxTyped = (IAverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adx || adxTyped.Dx.Plus is not decimal plusDi || adxTyped.Dx.Minus is not decimal minusDi)
return;
var prevPlus = _prevPlusDi;
var prevMinus = _prevMinusDi;
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
if (prevPlus is not decimal pPlus || prevMinus is not decimal pMinus)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var cci = cciValue.ToDecimal();
var ma = maValue.ToDecimal();
var close = candle.ClosePrice;
if (Position > 0 && close < ma || Position < 0 && close > ma)
_againstMaCount++;
else
_againstMaCount = 0;
if (UseMaRiskManagement && Position != 0 && _againstMaCount >= MaRiskExitCandles)
{
if (Position > 0)
SellMarket(Position);
else
BuyMarket(-Position);
_againstMaCount = 0;
return;
}
var longSignal = EnableLong && pPlus <= pMinus && plusDi > minusDi && cci > _cciLevel && adx > AdxThreshold
&& (!UseMaTrend || close > ma);
var shortSignal = EnableShort && pMinus <= pPlus && minusDi > plusDi && cci < -_cciLevel && adx > AdxThreshold
&& (!UseMaTrend || close < ma);
if (longSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_againstMaCount = 0;
}
else if (shortSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_againstMaCount = 0;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import (AverageDirectionalIndex, CommodityChannelIndex, SimpleMovingAverage, ExponentialMovingAverage,
WeightedMovingAverage, SmoothedMovingAverage)
from StockSharp.Algo.Strategies import Strategy
CCI_LEVEL = 100
class MovingAverageTypeEnum:
"""Moving average types."""
Simple = 0
Exponential = 1
Weighted = 2
Smoothed = 3
class adx_cci_ma_strategy(Strategy):
"""
ADX CCI MA strategy.
A long opens when +DI crosses above -DI, CCI is above 100 and ADX exceeds AdxThreshold (and the close is above the moving average
when UseMaTrend is set); a short mirrors this with -DI crossing above +DI and CCI below -100. Percent take profit and stop loss
protect positions, and the optional MA risk management exits after MaRiskExitCandles consecutive closes on the wrong side of the
moving average.
"""
def __init__(self):
super(adx_cci_ma_strategy, self).__init__()
self._enable_long = self.Param("EnableLong", True).SetDisplay("Enable Long", "Allow long trades", "General")
self._enable_short = self.Param("EnableShort", True).SetDisplay("Enable Short", "Allow short trades", "General")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0).SetNotNegative().SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._cci_period = self.Param("CciPeriod", 15).SetGreaterThanZero().SetDisplay("CCI Period", "CCI period", "Indicators")
self._adx_length = self.Param("AdxLength", 10).SetGreaterThanZero().SetDisplay("ADX Length", "ADX period", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 20.0).SetDisplay("ADX Threshold", "ADX level a trend must exceed", "Indicators")
self._use_ma_trend = self.Param("UseMaTrend", True).SetDisplay("Use MA Trend", "Require the close on the trade side of the moving average", "MA")
self._ma_type = self.Param("MaType", MovingAverageTypeEnum.Simple).SetDisplay("MA Type", "Moving average type", "MA")
self._ma_length = self.Param("MaLength", 200).SetGreaterThanZero().SetDisplay("MA Length", "Moving average period", "MA")
self._use_ma_risk_management = self.Param("UseMaRiskManagement", False).SetDisplay("Use MA Risk Management", "Exit after several closes against the moving average", "Risk")
self._ma_risk_exit_candles = self.Param("MaRiskExitCandles", 2).SetGreaterThanZero().SetDisplay("MA Risk Exit Candles", "Consecutive closes against the moving average that trigger the exit", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_plus_di = None
self._prev_minus_di = None
self._against_ma_count = 0
def OnReseted(self):
super(adx_cci_ma_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(adx_cci_ma_strategy, self).OnStarted2(time)
self._reset_state()
adx = AverageDirectionalIndex()
adx.Length = self._adx_length.Value
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
ma_type = self._ma_type.Value
if ma_type == MovingAverageTypeEnum.Exponential:
ma = ExponentialMovingAverage()
elif ma_type == MovingAverageTypeEnum.Weighted:
ma = WeightedMovingAverage()
elif ma_type == MovingAverageTypeEnum.Smoothed:
ma = SmoothedMovingAverage()
else:
ma = SimpleMovingAverage()
ma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, cci, ma, self._process_candle).Start()
self.StartProtection(Unit(Decimal(self._take_profit_percent.Value), UnitTypes.Percent), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, adx_value, cci_value, ma_value):
if candle.State != CandleStates.Finished:
return
if not adx_value.IsFormed or not cci_value.IsFormed or not ma_value.IsFormed:
return
adx = adx_value.MovingAverage
plus_di = adx_value.Dx.Plus
minus_di = adx_value.Dx.Minus
if adx is None or plus_di is None or minus_di is None:
return
prev_plus = self._prev_plus_di
prev_minus = self._prev_minus_di
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
if prev_plus is None or prev_minus is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
cci = cci_value.GetValue[Decimal](None)
ma = ma_value.GetValue[Decimal](None)
close = candle.ClosePrice
if (self.Position > 0 and close < ma) or (self.Position < 0 and close > ma):
self._against_ma_count += 1
else:
self._against_ma_count = 0
if self._use_ma_risk_management.Value and self.Position != 0 and self._against_ma_count >= self._ma_risk_exit_candles.Value:
if self.Position > 0:
self.SellMarket(self.Position)
else:
self.BuyMarket(-self.Position)
self._against_ma_count = 0
return
threshold = Decimal(self._adx_threshold.Value)
use_ma = self._use_ma_trend.Value
long_signal = (self._enable_long.Value and prev_plus <= prev_minus and plus_di > minus_di and cci > CCI_LEVEL
and adx > threshold and (not use_ma or close > ma))
short_signal = (self._enable_short.Value and prev_minus <= prev_plus and minus_di > plus_di and cci < -CCI_LEVEL
and adx > threshold and (not use_ma or close < ma))
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._against_ma_count = 0
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._against_ma_count = 0
def CreateClone(self):
return adx_cci_ma_strategy()