Nifty Options トレンド市場戦略(TSL付き)
Bollinger BandsにADXとSupertrendフィルターを組み合わせたブレイクアウト戦略。エントリーには出来高スパイクが必要。ポジションはMACDクロス、ADXの弱体化、またはATRベースのトレーリングストップで決済される。
詳細
- エントリー条件:
- ロング: 価格がBollinger Band上限を上抜け && ADX > 閾値 && 出来高スパイク && 価格がSupertrendより上
- ショート: 価格がBollinger Band下限を下抜け && ADX > 閾値 && 出来高スパイク && 価格がSupertrendより下
- ロング/ショート: 両方
- エグジット条件: MACDクロス、ADX低下またはATRトレーリングストップ
- ストップ: ATRトレーリングストップ
- デフォルト値:
BollingerPeriod= 20BollingerMultiplier= 2mAdxLength= 14AdxEntryThreshold= 25mAdxExitThreshold= 20mSuperTrendLength= 10SuperTrendMultiplier= 3mMacdFast= 12MacdSlow= 26MacdSignal= 9AtrLength= 14AtrMultiplier= 1.5mVolumeSpikeMultiplier= 1.5mCandleType= TimeSpan.FromMinutes(15).TimeFrame()
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: Bollinger Bands, ADX, Supertrend, MACD, ATR
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Nifty options trendy markets with trailing stop strategy.
/// A long opens when the close crosses above the upper Bollinger Band while ADX is above AdxEntryThreshold, volume spikes above
/// VolumeSpikeMultiplier times its average and the close is above SuperTrend; a short mirrors this at the lower band.
/// A position closes on an opposite MACD/signal cross, when ADX falls below AdxExitThreshold, or at an ATR trailing stop.
/// </summary>
public class NiftyOptionsTrendyMarketsWithTslStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerMultiplier;
private readonly StrategyParam<int> _adxLength;
private readonly StrategyParam<decimal> _adxEntryThreshold;
private readonly StrategyParam<decimal> _adxExitThreshold;
private readonly StrategyParam<int> _superTrendLength;
private readonly StrategyParam<decimal> _superTrendMultiplier;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _volumeSpikeMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _volumes = new();
private decimal? _prevClose;
private decimal? _prevUpper;
private decimal? _prevLower;
private decimal? _prevMacd;
private decimal? _prevSignal;
private decimal? _trailStop;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands deviation multiplier.
/// </summary>
public decimal BollingerMultiplier
{
get => _bollingerMultiplier.Value;
set => _bollingerMultiplier.Value = value;
}
/// <summary>
/// ADX period.
/// </summary>
public int AdxLength
{
get => _adxLength.Value;
set => _adxLength.Value = value;
}
/// <summary>
/// Minimum ADX to enter.
/// </summary>
public decimal AdxEntryThreshold
{
get => _adxEntryThreshold.Value;
set => _adxEntryThreshold.Value = value;
}
/// <summary>
/// ADX level below which positions close.
/// </summary>
public decimal AdxExitThreshold
{
get => _adxExitThreshold.Value;
set => _adxExitThreshold.Value = value;
}
/// <summary>
/// SuperTrend ATR period.
/// </summary>
public int SuperTrendLength
{
get => _superTrendLength.Value;
set => _superTrendLength.Value = value;
}
/// <summary>
/// SuperTrend ATR multiplier.
/// </summary>
public decimal SuperTrendMultiplier
{
get => _superTrendMultiplier.Value;
set => _superTrendMultiplier.Value = value;
}
/// <summary>
/// MACD fast period.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// MACD slow period.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// MACD signal period.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// ATR period for the trailing stop.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// ATR multiplier for the trailing stop.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Volume must exceed its average by this factor.
/// </summary>
public decimal VolumeSpikeMultiplier
{
get => _volumeSpikeMultiplier.Value;
set => _volumeSpikeMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public NiftyOptionsTrendyMarketsWithTslStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Bollinger Bands period", "Bollinger");
_bollingerMultiplier = Param(nameof(BollingerMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Multiplier", "Bollinger Bands deviation multiplier", "Bollinger");
_adxLength = Param(nameof(AdxLength), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Length", "ADX period", "ADX");
_adxEntryThreshold = Param(nameof(AdxEntryThreshold), 25m)
.SetNotNegative()
.SetDisplay("ADX Entry", "Minimum ADX to enter", "ADX");
_adxExitThreshold = Param(nameof(AdxExitThreshold), 20m)
.SetNotNegative()
.SetDisplay("ADX Exit", "ADX level below which positions close", "ADX");
_superTrendLength = Param(nameof(SuperTrendLength), 10)
.SetGreaterThanZero()
.SetDisplay("SuperTrend Length", "SuperTrend ATR period", "SuperTrend");
_superTrendMultiplier = Param(nameof(SuperTrendMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("SuperTrend Multiplier", "SuperTrend ATR multiplier", "SuperTrend");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "MACD fast period", "MACD");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "MACD slow period", "MACD");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "MACD signal period", "MACD");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period for the trailing stop", "Risk");
_atrMultiplier = Param(nameof(AtrMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR multiplier for the trailing stop", "Risk");
_volumeSpikeMultiplier = Param(nameof(VolumeSpikeMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Volume Spike", "Volume must exceed its average by this factor", "Volume");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
ResetState();
}
private void ResetState()
{
_volumes.Clear();
_prevClose = null;
_prevUpper = null;
_prevLower = null;
_prevMacd = null;
_prevSignal = null;
_trailStop = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerMultiplier };
var adx = new AverageDirectionalIndex { Length = AdxLength };
var supertrend = new SuperTrend { Length = SuperTrendLength, Multiplier = SuperTrendMultiplier };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal }
};
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, adx, supertrend, macd, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawIndicator(area, supertrend);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, adx);
DrawIndicator(oscillators, macd);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue adxValue,
IIndicatorValue supertrendValue, IIndicatorValue macdValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// The volume average covers the BollingerPeriod candles before this one.
var avgVolume = _volumes.Count >= BollingerPeriod ? _volumes.Average() : (decimal?)null;
_volumes.Enqueue(candle.TotalVolume);
while (_volumes.Count > BollingerPeriod)
_volumes.Dequeue();
var prevClose = _prevClose;
var prevUpper = _prevUpper;
var prevLower = _prevLower;
_prevClose = close;
if (!bollingerValue.IsFormed || bollingerValue is not BollingerBandsValue { UpBand: decimal upper, LowBand: decimal lower })
return;
_prevUpper = upper;
_prevLower = lower;
if (macdValue is not IMovingAverageConvergenceDivergenceSignalValue { Macd: decimal macdLine, Signal: decimal signalLine })
return;
var prevMacd = _prevMacd;
var prevSignal = _prevSignal;
_prevMacd = macdLine;
_prevSignal = signalLine;
if (!adxValue.IsFormed || !supertrendValue.IsFormed || !macdValue.IsFormed || !atrValue.IsFormed)
return;
if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal adx })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var atr = atrValue.GetValue<decimal>();
var trailDistance = atr * AtrMultiplier;
var macdCrossDown = prevMacd is decimal pm && prevSignal is decimal ps && pm >= ps && macdLine < signalLine;
var macdCrossUp = prevMacd is decimal pm2 && prevSignal is decimal ps2 && pm2 <= ps2 && macdLine > signalLine;
if (Position > 0)
{
if ((_trailStop is decimal stop && candle.LowPrice <= stop) || macdCrossDown || adx < AdxExitThreshold)
{
SellMarket(Position);
_trailStop = null;
return;
}
var candidate = close - trailDistance;
_trailStop = _trailStop is decimal s ? Math.Max(s, candidate) : candidate;
}
else if (Position < 0)
{
if ((_trailStop is decimal stop && candle.HighPrice >= stop) || macdCrossUp || adx < AdxExitThreshold)
{
BuyMarket(-Position);
_trailStop = null;
return;
}
var candidate = close + trailDistance;
_trailStop = _trailStop is decimal s ? Math.Min(s, candidate) : candidate;
}
if (prevClose is not decimal pc || prevUpper is not decimal pu || prevLower is not decimal pl || avgVolume is not decimal avgVol)
return;
var st = supertrendValue.GetValue<decimal>();
var volumeSpike = candle.TotalVolume > avgVol * VolumeSpikeMultiplier;
var strongTrend = adx > AdxEntryThreshold;
if (pc <= pu && close > upper && strongTrend && volumeSpike && close > st && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_trailStop = close - trailDistance;
}
else if (pc >= pl && close < lower && strongTrend && volumeSpike && close < st && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_trailStop = close + trailDistance;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (BollingerBands, AverageDirectionalIndex, SuperTrend,
MovingAverageConvergenceDivergenceSignal, AverageTrueRange)
from StockSharp.Algo.Strategies import Strategy
class nifty_options_trendy_markets_with_tsl_strategy(Strategy):
"""
Nifty options trendy markets with trailing stop strategy.
A long opens when the close crosses above the upper Bollinger Band while ADX is above AdxEntryThreshold, volume spikes above
VolumeSpikeMultiplier times its average and the close is above SuperTrend; a short mirrors this at the lower band.
A position closes on an opposite MACD/signal cross, when ADX falls below AdxExitThreshold, or at an ATR trailing stop.
"""
def __init__(self):
super(nifty_options_trendy_markets_with_tsl_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 20).SetGreaterThanZero().SetDisplay("Bollinger Period", "Bollinger Bands period", "Bollinger")
self._bollinger_multiplier = self.Param("BollingerMultiplier", 2.0).SetGreaterThanZero().SetDisplay("Bollinger Multiplier", "Bollinger Bands deviation multiplier", "Bollinger")
self._adx_length = self.Param("AdxLength", 14).SetGreaterThanZero().SetDisplay("ADX Length", "ADX period", "ADX")
self._adx_entry_threshold = self.Param("AdxEntryThreshold", 25.0).SetNotNegative().SetDisplay("ADX Entry", "Minimum ADX to enter", "ADX")
self._adx_exit_threshold = self.Param("AdxExitThreshold", 20.0).SetNotNegative().SetDisplay("ADX Exit", "ADX level below which positions close", "ADX")
self._super_trend_length = self.Param("SuperTrendLength", 10).SetGreaterThanZero().SetDisplay("SuperTrend Length", "SuperTrend ATR period", "SuperTrend")
self._super_trend_multiplier = self.Param("SuperTrendMultiplier", 3.0).SetGreaterThanZero().SetDisplay("SuperTrend Multiplier", "SuperTrend ATR multiplier", "SuperTrend")
self._macd_fast = self.Param("MacdFast", 12).SetGreaterThanZero().SetDisplay("MACD Fast", "MACD fast period", "MACD")
self._macd_slow = self.Param("MacdSlow", 26).SetGreaterThanZero().SetDisplay("MACD Slow", "MACD slow period", "MACD")
self._macd_signal = self.Param("MacdSignal", 9).SetGreaterThanZero().SetDisplay("MACD Signal", "MACD signal period", "MACD")
self._atr_length = self.Param("AtrLength", 14).SetGreaterThanZero().SetDisplay("ATR Length", "ATR period for the trailing stop", "Risk")
self._atr_multiplier = self.Param("AtrMultiplier", 1.5).SetGreaterThanZero().SetDisplay("ATR Multiplier", "ATR multiplier for the trailing stop", "Risk")
self._volume_spike_multiplier = self.Param("VolumeSpikeMultiplier", 1.5).SetGreaterThanZero().SetDisplay("Volume Spike", "Volume must exceed its average by this factor", "Volume")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).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._volumes = []
self._prev_close = None
self._prev_upper = None
self._prev_lower = None
self._prev_macd = None
self._prev_signal = None
self._trail_stop = None
def OnReseted(self):
super(nifty_options_trendy_markets_with_tsl_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(nifty_options_trendy_markets_with_tsl_strategy, self).OnStarted2(time)
self._reset_state()
bollinger = BollingerBands()
bollinger.Length = self._bollinger_period.Value
bollinger.Width = Decimal(self._bollinger_multiplier.Value)
adx = AverageDirectionalIndex()
adx.Length = self._adx_length.Value
supertrend = SuperTrend()
supertrend.Length = self._super_trend_length.Value
supertrend.Multiplier = Decimal(self._super_trend_multiplier.Value)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast.Value
macd.Macd.LongMa.Length = self._macd_slow.Value
macd.SignalMa.Length = self._macd_signal.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, adx, supertrend, macd, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawIndicator(area, supertrend)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, adx)
self.DrawIndicator(oscillators, macd)
def _process_candle(self, candle, bollinger_value, adx_value, supertrend_value, macd_value, atr_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
period = self._bollinger_period.Value
# The volume average covers the BollingerPeriod candles before this one.
avg_volume = sum(self._volumes, Decimal(0)) / Decimal(len(self._volumes)) if len(self._volumes) >= period else None
self._volumes.append(candle.TotalVolume)
while len(self._volumes) > period:
self._volumes.pop(0)
prev_close = self._prev_close
prev_upper = self._prev_upper
prev_lower = self._prev_lower
self._prev_close = close
if not bollinger_value.IsFormed or bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
upper = bollinger_value.UpBand
lower = bollinger_value.LowBand
self._prev_upper = upper
self._prev_lower = lower
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
prev_macd = self._prev_macd
prev_signal = self._prev_signal
self._prev_macd = macd_line
self._prev_signal = signal_line
if not adx_value.IsFormed or not supertrend_value.IsFormed or not macd_value.IsFormed or not atr_value.IsFormed:
return
adx = adx_value.MovingAverage
if adx is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
atr = atr_value.GetValue[Decimal](None)
trail_distance = atr * Decimal(self._atr_multiplier.Value)
has_prev_macd = prev_macd is not None and prev_signal is not None
macd_cross_down = has_prev_macd and prev_macd >= prev_signal and macd_line < signal_line
macd_cross_up = has_prev_macd and prev_macd <= prev_signal and macd_line > signal_line
adx_weak = adx < Decimal(self._adx_exit_threshold.Value)
if self.Position > 0:
if (self._trail_stop is not None and candle.LowPrice <= self._trail_stop) or macd_cross_down or adx_weak:
self.SellMarket(self.Position)
self._trail_stop = None
return
candidate = close - trail_distance
self._trail_stop = candidate if self._trail_stop is None else max(self._trail_stop, candidate)
elif self.Position < 0:
if (self._trail_stop is not None and candle.HighPrice >= self._trail_stop) or macd_cross_up or adx_weak:
self.BuyMarket(-self.Position)
self._trail_stop = None
return
candidate = close + trail_distance
self._trail_stop = candidate if self._trail_stop is None else min(self._trail_stop, candidate)
if prev_close is None or prev_upper is None or prev_lower is None or avg_volume is None:
return
st = supertrend_value.GetValue[Decimal](None)
volume_spike = candle.TotalVolume > avg_volume * Decimal(self._volume_spike_multiplier.Value)
strong_trend = adx > Decimal(self._adx_entry_threshold.Value)
if prev_close <= prev_upper and close > upper and strong_trend and volume_spike and close > st and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._trail_stop = close - trail_distance
elif prev_close >= prev_lower and close < lower and strong_trend and volume_spike and close < st and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._trail_stop = close + trail_distance
def CreateClone(self):
return nifty_options_trendy_markets_with_tsl_strategy()