Quant Buffet放轻松,别过度思虑

使用 VIX 期权进行投资组合对冲

登录后收藏

学术论文

作者A Study in Portfolio Diversification Using VIX Options [点击查看论文]

策略概要

该策略采用60%的股票和40%的债券投资组合,股票使用SPDR标准普尔500指数ETF(SPY),债券使用iShares 7-10年期美国国债ETF(IEF)。投资组合分配0-100个基点(bps)给VIX看涨期权,投资于VIX期货的1个月、2个月、3个月和4个月期权。VIX看涨期权的权重根据VIX指数水平调整:若VIX在15至30之间,则为1%;若在30至50之间,则为0.5%;否则为0%。期权在到期前展期,若具有内在价值则卖出。股票/债券部分每月重新平衡,期权销售所得现金再投资于该投资组合。该策略利用VIX期权对冲波动性,重点是基于市场状况的系统性动态分配。

II. 策略合理性

该策略通过每月将投资组合的固定百分比分配给VIX看涨期权,利用波动率的均值回复特性。当VIX低于均值时,购买更多期权;当VIX高于均值时,购买较少期权。该策略旨在确定期权的最佳实值性、到期时间和最小资本要求。仅当VIX在15%至50%之间时才购买看涨期权,确保投资者避免为可能不必要的期权支付过高的费用,从而调整投资组合的风险和回报状况。

回测表现

波动率8.44%
夏普比率1.02
索提诺比率0.504
最大回撤-10.47%
胜率35%

完整 Python 代码

from AlgorithmImports import *
class PortfolioHedgingUsingVIXOptions(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(1000000)

data = self.AddEquity("SPY", Resolution.Minute)
data.SetLeverage(5)
self.spy = data.Symbol

data = self.AddEquity("IEF", Resolution.Minute)
data.SetLeverage(5)
self.ief = data.Symbol

data = self.AddEquity("VIXY", Resolution.Minute)
data.SetLeverage(5)
self.vix = data.Symbol

option = self.AddOption('VIXY', Resolution.Minute)
option.SetFilter(-20, 20, 25, 35)

def OnData(self,slice):
for i in slice.OptionChains:
    chains = i.Value
    # Max 2 positions - spy and ief are opened. That means option expired.
    invested = [x.Key for x in self.Portfolio if x.Value.Invested]
    if len(invested) <= 2:
        calls = list(filter(lambda x: x.Right == OptionRight.Call, chains))
        
        if not calls: return
    
        underlying_price = self.Securities[self.vix].Price
        expiries = [i.Expiry for i in calls]
        
        # Determine expiration date nearly one month.
        expiry = min(expiries, key=lambda x: abs((x.date() - self.Time.date()).days - 30))
        strikes = [i.Strike for i in calls]
        
        # Determine out-of-the-money strike.
        otm_strike = min(strikes, key = lambda x:abs(x - (float(1.35) * underlying_price)))
        otm_call = [i for i in calls if i.Expiry == expiry and i.Strike == otm_strike]

        if otm_call:
            # Option weighting.
            weight = 0.0
            
            if underlying_price >= 15 and underlying_price <= 30:
                weight = 0.01
            elif underlying_price > 30 and underlying_price <= 50:
                weight = 0.005
              
            if weight != 0: 
                options_q = int((self.Portfolio.MarginRemaining * weight) / (underlying_price * 100))

                # Set max leverage.
                self.Securities[otm_call[0].Symbol].MarginModel = BuyingPowerModel(5)
                
                # Sell out-the-money call.
                self.Buy(otm_call[0].Symbol, options_q)
                
                self.SetHoldings(self.spy, 0.6)
                self.SetHoldings(self.ief, 0.4)