Historical Volatility Breakout 戦略
このブレイクアウト手法は、動的な閾値を設定するために歴史的ボラティリティを使用します。価格が現在のボラティリティを超えて参照レベルを超えると、潜在的なトレンドを示します。
テストでは年間平均リターン約154%が示されています。株式市場で最も優れたパフォーマンスを発揮します。
この戦略は、標準偏差と単純移動平均から導出されたレベルに対して価格を比較します。それらのレベルを上回るまたは下回るブレイクアウトが取引を引き起こします。
価格が移動平均を逆にクロスするかストップに達するとエグジットが発生します。
詳細
- エントリー条件: 価格がHVベースのレベルを上または下にブレイク。
- ロング/ショート: 両方向。
- エグジット条件: 価格がMAをクロスするかストップ。
- ストップ: はい。
- デフォルト値:
HvPeriod= 20MAPeriod= 20StopLossPercent= 2.0mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: HV, MA
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Breakouts beyond SMA-anchored levels widened by relative population price dispersion (StdDev / Close).
/// Exits on an adverse price/SMA crossing or native actual-fill percent protection.
/// </summary>
public class HvBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _hvPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _previousClose;
private decimal _previousMean;
private Order _pendingOrder;
public int HvPeriod { get => _hvPeriod.Value; set => _hvPeriod.Value = value; }
public int MAPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public HvBreakoutStrategy()
{
_hvPeriod = Param(nameof(HvPeriod), 20).SetGreaterThanZero()
.SetDisplay("HV Period", "Period for Historical Volatility calculation", "Indicators")
.SetOptimize(10, 30, 5);
_maPeriod = Param(nameof(MAPeriod), 20).SetGreaterThanZero()
.SetDisplay("MA Period", "Period for the Moving Average that anchors the breakout levels and exits", "Indicators")
.SetOptimize(10, 50, 5);
_stopLossPercent = Param(nameof(StopLossPercent), 2m).SetNotNegative()
.SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
OrderRegistering += order => _pendingOrder = order;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
protected override void OnReseted()
{
base.OnReseted();
_previousClose = null;
_previousMean = default;
_pendingOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousClose = null;
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();
}
var deviation = new StandardDeviation { Length = HvPeriod };
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(deviation, sma, ProcessCandle, false).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, deviation);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// Native protection runs before this callback, including between finished candles.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue deviationValue, IIndicatorValue smaValue)
{
if (candle.State != CandleStates.Finished || !deviationValue.Indicator.IsFormed || !smaValue.Indicator.IsFormed
|| !IsFormedAndOnlineAndAllowTrading() || candle.ClosePrice <= 0m)
return;
var close = candle.ClosePrice;
var mean = smaValue.GetValue<decimal>();
if (_previousClose is not decimal prior)
{
_previousClose = close;
_previousMean = mean;
return;
}
var upwardCross = prior <= _previousMean && close > mean;
var downwardCross = prior >= _previousMean && close < mean;
_previousClose = close;
_previousMean = mean;
if (_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed))
return;
if (Position > 0m && downwardCross)
SellMarket(Position);
else if (Position < 0m && upwardCross)
BuyMarket(Math.Abs(Position));
else if (Position == 0m)
{
// HV is the relative price dispersion StdDev / Close.
var relativeDeviation = deviationValue.GetValue<decimal>() / close;
if (close > mean * (1m + relativeDeviation))
BuyMarket(Volume);
else if (close < mean * (1m - relativeDeviation))
SellMarket(Volume);
}
}
}
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, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, OrderStates, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import StandardDeviation, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class hv_breakout_strategy(Strategy):
"""
Breakouts beyond SMA-anchored levels widened by relative population price dispersion (StdDev / Close).
Exits on an adverse price/SMA crossing or actual-fill percent protection.
"""
def __init__(self):
super(hv_breakout_strategy, self).__init__()
self._hv_period = self.Param("HvPeriod", 20).SetGreaterThanZero().SetDisplay("HV Period", "Period for Historical Volatility calculation", "Indicators")
self._ma_period = self.Param("MAPeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "Period for the Moving Average that anchors the breakout levels and exits", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._previous_close = None
self._previous_mean = Decimal.Zero
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 OnReseted(self):
super(hv_breakout_strategy, self).OnReseted()
self._previous_close = None
self._previous_mean = Decimal.Zero
self._pending_order = None
def OnStarted2(self, time):
super(hv_breakout_strategy, self).OnStarted2(time)
self._previous_close = None
self._pending_order = None
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()
deviation = StandardDeviation()
deviation.Length = self._hv_period.Value
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(deviation, sma, self._process_candle, False).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, deviation)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# Native protection runs before this callback, including between finished candles.
pass
def _process_candle(self, candle, deviation_value, sma_value):
if candle.State != CandleStates.Finished or not deviation_value.Indicator.IsFormed or not sma_value.Indicator.IsFormed or not self.IsFormedAndOnlineAndAllowTrading() or candle.ClosePrice <= 0:
return
close = candle.ClosePrice
mean = sma_value.GetValue[Decimal](None)
if self._previous_close is None:
self._previous_close = close
self._previous_mean = mean
return
upward_cross = self._previous_close <= self._previous_mean and close > mean
downward_cross = self._previous_close >= self._previous_mean and close < mean
self._previous_close = close
self._previous_mean = mean
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
return
if self.Position > 0 and downward_cross:
self.SellMarket(self.Position)
elif self.Position < 0 and upward_cross:
self.BuyMarket(Math.Abs(self.Position))
elif self.Position == 0:
# HV is the relative price dispersion StdDev / Close.
relative_deviation = deviation_value.GetValue[Decimal](None) / close
if close > mean * (Decimal.One + relative_deviation):
self.BuyMarket(self.Volume)
elif close < mean * (Decimal.One - relative_deviation):
self.SellMarket(self.Volume)
def CreateClone(self):
return hv_breakout_strategy()