历史波动率比率策略
该策略基于历史波动率比率 (HVR)。利用对数收益率计算6根K线的短期波动率与100根K线的长期波动率之比。当比率高于阈值时,系统做多,预期波动率扩张;当比率低于阈值时,系统做空。
细节
- 入场条件:
- 多头:
HVR > RatioThreshold - 空头:
HVR < RatioThreshold
- 多头:
- 多/空:双向
- 出场条件:相反信号
- 止损:无
- 默认值:
ShortPeriod= 6LongPeriod= 100RatioThreshold= 1.0CandleType=TimeSpan.FromMinutes(15).TimeFrame()
- 过滤器:
- 类别:波动率
- 方向:双向
- 指标:历史波动率(短期与长期)
- 止损:无
- 复杂度:中等
- 时间框架:日内
- 季节性:无
- 神经网络:无
- 背离:无
- 风险水平:中等
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>
/// Strategy based on the Historical Volatility Ratio (HVR).
/// Compares short-term volatility against long-term volatility.
/// Buys when short-term vol exceeds long-term, sells when below.
/// </summary>
public class HvrStrategy : Strategy
{
private readonly StrategyParam<int> _shortPeriod;
private readonly StrategyParam<int> _longPeriod;
private readonly StrategyParam<decimal> _ratioThreshold;
private readonly StrategyParam<DataType> _candleType;
private StandardDeviation _shortSd;
private StandardDeviation _longSd;
private decimal? _prevClose;
public int ShortPeriod { get => _shortPeriod.Value; set => _shortPeriod.Value = value; }
public int LongPeriod { get => _longPeriod.Value; set => _longPeriod.Value = value; }
public decimal RatioThreshold { get => _ratioThreshold.Value; set => _ratioThreshold.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public HvrStrategy()
{
_shortPeriod = Param(nameof(ShortPeriod), 6)
.SetGreaterThanZero()
.SetDisplay("Short HV Period", "Bars for short-term volatility", "Parameters");
_longPeriod = Param(nameof(LongPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Long HV Period", "Bars for long-term volatility", "Parameters");
_ratioThreshold = Param(nameof(RatioThreshold), 1m)
.SetDisplay("Ratio Threshold", "HVR level for trade direction", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_shortSd = default;
_longSd = default;
_prevClose = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_shortSd = new StandardDeviation { Length = ShortPeriod };
_longSd = new StandardDeviation { Length = LongPeriod };
Indicators.Add(_shortSd);
Indicators.Add(_longSd);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevClose is not decimal prevClose || prevClose <= 0)
{
_prevClose = candle.ClosePrice;
return;
}
var logReturn = (decimal)Math.Log((double)(candle.ClosePrice / prevClose));
_prevClose = candle.ClosePrice;
var shortResult = _shortSd.Process(logReturn, candle.OpenTime, true);
var longResult = _longSd.Process(logReturn, candle.OpenTime, true);
if (!shortResult.IsFormed || !longResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var shortVal = shortResult.ToDecimal();
var longVal = longResult.ToDecimal();
if (longVal == 0)
return;
var ratio = shortVal / longVal;
if (ratio > RatioThreshold && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (ratio < RatioThreshold && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
}
import clr
import math
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
from StockSharp.Algo.Indicators import StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class hvr_strategy(Strategy):
def __init__(self):
super(hvr_strategy, self).__init__()
self._short_period = self.Param("ShortPeriod", 6) \
.SetDisplay("Short HV Period", "Bars for short-term volatility", "Parameters")
self._long_period = self.Param("LongPeriod", 100) \
.SetDisplay("Long HV Period", "Bars for long-term volatility", "Parameters")
self._ratio_threshold = self.Param("RatioThreshold", 1.0) \
.SetDisplay("Ratio Threshold", "HVR level for trade direction", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used for calculation", "General")
self._short_sd = None
self._long_sd = None
self._prev_close = None
@property
def ShortPeriod(self):
return self._short_period.Value
@ShortPeriod.setter
def ShortPeriod(self, value):
self._short_period.Value = value
@property
def LongPeriod(self):
return self._long_period.Value
@LongPeriod.setter
def LongPeriod(self, value):
self._long_period.Value = value
@property
def RatioThreshold(self):
return self._ratio_threshold.Value
@RatioThreshold.setter
def RatioThreshold(self, value):
self._ratio_threshold.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(hvr_strategy, self).OnStarted2(time)
self._prev_close = None
self._short_sd = StandardDeviation()
self._short_sd.Length = self.ShortPeriod
self._long_sd = StandardDeviation()
self._long_sd.Length = self.LongPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if self._prev_close is None or self._prev_close <= 0:
self._prev_close = close
return
log_return = math.log(close / self._prev_close)
self._prev_close = close
t = candle.OpenTime
short_result = process_float(self._short_sd, log_return, t, True)
long_result = process_float(self._long_sd, log_return, t, True)
if not short_result.IsFormed or not long_result.IsFormed:
return
short_val = float(short_result)
long_val = float(long_result)
if long_val == 0:
return
ratio = short_val / long_val
threshold = float(self.RatioThreshold)
if ratio > threshold and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif ratio < threshold and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def OnReseted(self):
super(hvr_strategy, self).OnReseted()
self._short_sd = None
self._long_sd = None
self._prev_close = None
def CreateClone(self):
return hvr_strategy()