累積デルタ・ブレイクアウト (Cumulative Delta Breakout)
Cumulative Deltaは買いと売りの出来高の差を累積します。この戦略は累積合計を監視し、ルックバック期間内の最高値を上回るか最安値を下回ったときに取引します。
テストでは年平均リターンが約49%となっています。暗号通貨市場での運用に最も適しています。
累積デルタのブレイクアウトは価格のフォロースルーに先行することが多くあります。デルタがゼロに戻るか、ストップロスレベルに達したときにトレードを決済します。
詳細
- エントリー条件: 累積デルタがルックバック内の最高値または最安値を超える。
- ロング/ショート: 両方。
- エグジット条件: デルタがゼロをクロスするかストップ。
- ストップ: はい。
- デフォルト値:
LookbackPeriod= 20CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: Cumulative Delta
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades breakouts of the running aggressor-side cumulative delta beyond its prior lookback range.
/// Fully exits when the cumulative delta crosses zero or actual-fill protection triggers.
/// </summary>
public class CumulativeDeltaBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly Queue<decimal> _priorCumulative = new();
private decimal _cumulativeDelta;
private decimal? _previousCumulative;
private Order _pendingOrder;
public int LookbackPeriod { get => _lookbackPeriod.Value; set => _lookbackPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public CumulativeDeltaBreakoutStrategy()
{
_lookbackPeriod = Param(nameof(LookbackPeriod), 20).SetGreaterThanZero()
.SetDisplay("Lookback Period", "Number of prior cumulative delta values", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Trade-built signal candles", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 2m).SetNotNegative()
.SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection");
OrderRegistering += order => _pendingOrder = order;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
protected override void OnReseted()
{
base.OnReseted();
ClearSignalState();
}
private void ClearSignalState()
{
_priorCumulative.Clear();
_cumulativeDelta = 0m;
_previousCumulative = null;
_pendingOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ClearSignalState();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
// Archived minute candles do not always contain sided volumes; build bars from signed trades.
var candles = new Subscription(CandleType, Security);
candles.MarketData.BuildMode = MarketDataBuildModes.Build;
candles.MarketData.BuildFrom = DataType.Ticks;
SubscribeCandles(candles).Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, candles);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// Native protection also watches the market between finished signal candles.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var previous = _previousCumulative;
_cumulativeDelta += (candle.BuyVolume ?? 0m) - (candle.SellVolume ?? 0m);
var full = _priorCumulative.Count == LookbackPeriod;
var up = full && _cumulativeDelta > _priorCumulative.Max();
var down = full && _cumulativeDelta < _priorCumulative.Min();
if (full)
_priorCumulative.Dequeue();
_priorCumulative.Enqueue(_cumulativeDelta);
_previousCumulative = _cumulativeDelta;
if (!IsFormedAndOnlineAndAllowTrading() ||
_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed))
return;
if (Position > 0m && previous is decimal priorLong && priorLong >= 0m && _cumulativeDelta < 0m)
SellMarket(Position);
else if (Position < 0m && previous is decimal priorShort && priorShort <= 0m && _cumulativeDelta > 0m)
BuyMarket(Math.Abs(Position));
else if (Position == 0m)
{
if (up) BuyMarket(Volume);
else if (down) SellMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, OrderStates, Level1Fields, MarketDataBuildModes
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class cumulative_delta_breakout_strategy(Strategy):
"""
Trades breakouts of the running aggressor-side cumulative delta beyond its prior lookback range.
Fully exits when the cumulative delta crosses zero or actual-fill protection triggers.
"""
def __init__(self):
super(cumulative_delta_breakout_strategy, self).__init__()
self._lookback_period = self.Param("LookbackPeriod", 20).SetGreaterThanZero().SetDisplay("Lookback Period", "Number of prior cumulative delta values", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Trade-built signal candles", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection")
self._prior_cumulative = []
self._cumulative_delta = Decimal(0)
self._previous_cumulative = None
self._pending_order = None
self.OrderRegistering += self._track_pending
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.Level1)]
def _track_pending(self, order):
self._pending_order = order
def _clear_signal_state(self):
self._prior_cumulative = []
self._cumulative_delta = Decimal(0)
self._previous_cumulative = None
self._pending_order = None
def OnReseted(self):
super(cumulative_delta_breakout_strategy, self).OnReseted()
self._clear_signal_state()
def OnStarted2(self, time):
super(cumulative_delta_breakout_strategy, self).OnStarted2(time)
self._clear_signal_state()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
# Archived minute bars are not consistently signed; build bars from true signed trades.
candles = Subscription(self.candle_type, self.Security)
candles.MarketData.BuildMode = MarketDataBuildModes.Build
candles.MarketData.BuildFrom = DataType.Ticks
self.SubscribeCandles(candles).Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, candles)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# Native protection also watches the market between finished signal candles.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
zero = Decimal(0)
buy = candle.BuyVolume if candle.BuyVolume is not None else zero
sell = candle.SellVolume if candle.SellVolume is not None else zero
previous = self._previous_cumulative
self._cumulative_delta += buy - sell
period = self._lookback_period.Value
full = len(self._prior_cumulative) == period
up = full and self._cumulative_delta > max(self._prior_cumulative)
down = full and self._cumulative_delta < min(self._prior_cumulative)
if full:
self._prior_cumulative.pop(0)
self._prior_cumulative.append(self._cumulative_delta)
self._previous_cumulative = self._cumulative_delta
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
return
if self.Position > 0 and previous is not None and previous >= zero and self._cumulative_delta < zero:
self.SellMarket(self.Position)
elif self.Position < 0 and previous is not None and previous <= zero and self._cumulative_delta > zero:
self.BuyMarket(Math.Abs(self.Position))
elif self.Position == 0:
if up:
self.BuyMarket(self.Volume)
elif down:
self.SellMarket(self.Volume)
def CreateClone(self):
return cumulative_delta_breakout_strategy()