该策略基于VIX水平,在标普500期权写入与指数投资之间每月切换。若前一个月的VIX中位数高于历史中位数,则卖出标普500看跌期权;若低于历史中位数,则直接投资标普500。

I. 策略概述

基本规则:

每月再平衡:

II. 策略合理性

波动率风险溢价:

VIX状态切换:

风险调整优势:

III. 论文来源

Option Writing: Using VIX to Improve Returns [点击浏览原文]

<摘要>

买入-写入(Buy-Write)和看跌-写入(Put-Write)策略已被证明能够匹配市场回报,同时降低波动性,从而实现更高的风险调整表现。这些策略受益于期权隐含波动率通常高于实际波动率的事实。本文显示,这种溢价在VIX水平较高时尤为显著。基于此发现,我们提出一种简单的条件策略:在VIX处于高位时卖出期权。基于1990年至2018年的数据,我们发现这种条件策略在绝对收益和风险调整收益方面均优于市场和持续期权写入策略。

IV. 回测表现

年化收益率10.88%
波动率11.36%
Beta0.652
夏普比率0.61
索提诺比率0.262
最大回撤-31.15%
胜率60%

V. 完整python代码

from collections import deque
from AlgorithmImports import *
import numpy as np
from QuantConnect.Python import PythonQuandl
class UsingVIXtoTimeOptionsWriting(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetCash(100000)
        
        self.symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
        data = self.AddEquity("BIL", Resolution.Minute)
        data.SetLeverage(2)
        self.bills = data.Symbol
        
        # SPY options.
        option = self.AddOption("SPY", Resolution.Minute)
        option.SetFilter(-20, 20, 25, 35)
        # Vix spot.
        self.vix_spot = self.AddData(CBOE, 'VIX', Resolution.Daily).Symbol
        
        # VIX historical monthly data.
        self.data = None
        # Get vix history.
        history = self.History(self.vix_spot, 10*12*30, Resolution.Daily)
        if 'close' in history.columns:
            closes = history['close']
            self.data = deque(closes)
        
        # Next expiration date.
        self.expiration_date = None
    def OnData(self, slice):
        # store VIX price
        if self.vix_spot in slice and slice[self.vix_spot]:
            price = slice[self.vix_spot].Value
            self.data.append(price)
            
        # Open new trades only on market close.
        if not (self.Time.hour == 15 and self.Time.minute == 59):
            return
        
        # At least year of data is ready.
        if len(self.data) < 12 * 30: return
        if self.expiration_date:
            if self.Time.date() < self.expiration_date.date(): 
                return
        
        if self.Portfolio.Invested:
            self.Liquidate()
        
        vix_median = np.median(self.data)
        # Last month VIX median.
        vix_median_t1 = np.median([x for x in self.data][-21:])
                
        for i in slice.OptionChains:
            chains = i.Value
            if not self.Portfolio.Invested:
                puts = list(filter(lambda x: x.Right == OptionRight.Put, chains))
                if not puts: return
            
                underlying_price = self.Securities[self.symbol].Price
                expiries = [i.Expiry for i in puts]
                # 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 puts]
                # determine at-the-money strike
                strike = min(strikes, key=lambda x: abs(x-underlying_price))
                atm_put = [i for i in puts if i.Expiry == expiry and i.Strike == strike]
                if atm_put:
                    if not self.expiration_date:
                        self.expiration_date = atm_put[0].Expiry
                        return
                    
                    self.expiration_date = atm_put[0].Expiry
                    if vix_median_t1 < vix_median:
                        self.SetHoldings(self.symbol, 1)
                        return
                    options_q = int(self.Portfolio.MarginRemaining / (underlying_price * 100))
                    self.Securities[atm_put[0].Symbol].MarginModel = BuyingPowerModel(5)
                    
                    self.SetHoldings(self.bills, 1)
                    self.Sell(atm_put[0].Symbol, options_q)
            if self.Portfolio.Invested:
                self.Liquidate(self.symbol)




发表评论

了解 Quant Buffet 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读