Quant Buffet放轻松,别过度思虑

克隆对冲基金指数

登录后收藏

学术论文

The Cost of Capital for Alternative Investments

作者另类投资的资本成本 [点击查看论文]

机构
  • National Bureau of Economic Research
  • University of Pennsylvania
  • ?National Bureau of Economic Research (NBER)
  • ?University of Pennsylvania - Finance Department
  • ?Harvard Business School - Finance Unit

策略概要

每个月,该策略都涉及做空标普500指数的虚值看跌期权,其行权价设置为指数月度标准差的1.0倍。投资组合应用2.0倍的恒定杠杆,将未使用的保证金资本投资于无风险利率以提高回报。投资组合每月进行再平衡,编写新的看跌期权以维持策略的结构和一致的敞口。这种方法旨在通过杠杆和将多余资本分配给无风险投资来利用期权溢价,同时管理风险。

II. 策略合理性

学术研究表明,对冲基金可能因获得跳跃和波动性风险溢价的补偿。因此,使用简单的期权卖出策略进行非线性复制,理论上可以很好地克隆对冲基金指数。

回测表现

波动率7.7%
夏普比率1.34
索提诺比率0.394
最大回撤-21.8%
胜率61%

完整 Python 代码

import numpy as np
from AlgorithmImports import *
from math import floor
from datetime import datetime
class CloningHedgeFundIndexes(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2013, 1, 1)
self.SetCash(100000)

self.symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
self.leverage:float = 2.

# spy consolidator
self.consolidator:TradeBarConsolidator = TradeBarConsolidator(timedelta(days=1))
self.consolidator.DataConsolidated += self.CustomHandler
self.SubscriptionManager.AddConsolidator(self.symbol, self.consolidator)            

self.period:int = 21

# Daily price data.
self.data:RollingWindow = RollingWindow[float](self.period)

option:Option = self.AddOption("SPY", Resolution.Minute)

def CustomHandler(self, sender: object, consolidated_bar: TradeBar) -> None:
self.data.Add(consolidated_bar.Close)

def OnData(self, slice:Slice) -> None:
for i in slice.OptionChains:
    chains:OptionChain = i.Value
    
    if self.Portfolio[self.symbol].Invested:
        self.Liquidate(self.symbol)
    
    if not self.Portfolio.Invested:
        # Market data is ready.
        if self.data.IsReady:
            market_closes:np.ndarray = np.array(list(self.data))
            market_returns:np.ndarray = market_closes[:-1] / market_closes[1:] - 1
            market_std:float = np.std(market_returns)
            
            # Divide option chains into put options
            puts:List = list(filter(lambda x: x.Right == OptionRight.Put, chains))
            if not puts: return
            underlying_price:float = self.Securities[self.symbol].Price
            expiries:List[datetime] = list(map(lambda x: x.Expiry, puts))

            # Determine expiration date nearly one month.
            expiry:datetime = min(expiries, key=lambda x: abs((x.date() - self.Time.date()).days - 30))
            strikes:List[float] = list(map(lambda x: x.Strike, puts))

            # Determine out-of-the-money strike.
            otm_strike:float = min(strikes, key = lambda x:abs(x - float(1 - market_std) * underlying_price))
            
            # Leverage calc.
            otm_put:OptionContract = [i for i in puts if i.Expiry == expiry and i.Strike == otm_strike]
            q:float = floor(float(self.Portfolio.MarginRemaining / (underlying_price * 100)) * self.leverage)

            if otm_put:
                # Sell with 2x leverage.
                self.Sell(otm_put[0].Symbol, q)