| Overall Statistics |
|
Total Trades 397 Average Win 1.33% Average Loss -0.63% Compounding Annual Return -31.632% Drawdown 98.000% Expectancy -0.186 Net Profit -97.664% Sharpe Ratio -0.049 Probabilistic Sharpe Ratio 0.004% Loss Rate 74% Win Rate 26% Profit-Loss Ratio 2.11 Alpha 0.269 Beta -3.072 Annual Standard Deviation 0.724 Annual Variance 0.524 Information Ratio -0.164 Tracking Error 0.82 Treynor Ratio 0.012 Total Fees $2162.42 Estimated Strategy Capacity $260000.00 Lowest Capacity Asset VIXY Y5BMQ7C65TD2|VIXY UT076X30D0MD |
# The investment universe consists of a stock/bond portfolio with a proportion of 60-percent stocks and 40-percent bonds.
# Stocks are represented by the SPDR S&P 500 ETF Trust (SPY) and bonds by the iShares 7-10 Year Treasury Bond ETF (IEF).
# The strategy firstly invests 0-100 basis points (bsp) in the desired VIX call option, then allocates 60 percent of the
# portfolio to the SPY and the remaining 40 percent to the IEF. The option is bought at the level of 135% of the moneyness
# of the underlying VIX futures price. The strategy is systematically purchasing an equal amount in one-month, two-month,
# three-month and four-month VIX call options on VIX futures. If the VIX Index is between 15 and 30, the weight of VIX calls
# in the portfolio is 1%. If the VIX Index is between 30 and 50, the weight in the portfolio is 0.5%. If the VIX Index is over
# 50 or under 15, then the weight of options in the portfolio is 0%. Each month, on the day before expiration, the options are
# rolled to the appropriate expiry. VIX call options are purchased at the offer and sold at the bid to keep the assumptions
# conservative. The options are held to maturity and closed the Tuesday afternoon before the Wednesday morning of VIX futures
# and options expiration. If the contracts have any intrinsic value, they are sold at the bid price, and the cash is used at
# the end of the month to rebalance the stock/bond portion of the portfolio.
from AlgorithmImports import *
class PortfolioHedgingUsingVIXOptions(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2013, 1, 1)
self.SetCash(1000000)
data = self.AddEquity("SPY", Resolution.Daily)
data.SetLeverage(5)
self.spy = data.Symbol
data = self.AddEquity("IEF", Resolution.Daily)
data.SetLeverage(5)
self.ief = data.Symbol
data = self.AddEquity("VIXY", Resolution.Daily)
data.SetLeverage(5)
self.vix = data.Symbol
option = self.AddOption('VIXY', Resolution.Daily)
#option = self.AddOption('SPY', Resolution.Daily)
self.SetWarmUp(30, Resolution.Daily)
#public void SetFilter(int minStrike, int maxStrike, TimeSpan minExpiry, TimeSpan maxExpiry)
# By default weekly options are excluded from options expiry so we get only monthly options.
option.SetFilter(-20, 20, 0, 180)
self.Debug("yolo") # Print to the console
def OnData(self,slice):
# OptionChains is a collection of OptionChain keyed by the option's underlying symbol.
# The elements in Slice.OptionChains have properties Key(the underlying symbol object) and Value(the option chain).
if self.IsWarmingUp:
return
for i in slice.OptionChains:
chains = i.Value
# If there are max of 2 positions open - spy and ief - that means option expired.
invested = [x.Key for x in self.Portfolio if x.Value.Invested]
#invested = ['SPX','VIXoption','Bond']
if len(invested) < 6:
# Get the right being purchased x.Right = 1 call option[right to buy]
# x.Right = 0 put option[right to sell]
calls = list(filter(lambda x: x.Right == OptionRight.Call, chains))
if not calls: return
underlying_price = self.Securities[self.vix].Price
expiries = list(set([i.Expiry for i in calls]))
#expiries = ['Dec', 'Jan', etc]
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]
otm_call = [i for i in calls if (j for j in expiries if j==i.Expiry) and i.Strike == otm_strike]
if len(otm_call)==4:
self.Debug("yolo1")
# Option weighting.
weight = 0.0
opt_price_1 = self.Securities[otm_call[0].Symbol].Price
opt_price_2 = self.Securities[otm_call[1].Symbol].Price
opt_price_3 = self.Securities[otm_call[2].Symbol].Price
opt_price_4 = self.Securities[otm_call[3].Symbol].Price
if underlying_price >= 15 and underlying_price <= 30:
weight = 0.01
w1=0.01/4
w2=0.01/4
w3=0.01/4
w4=0.01/4
elif underlying_price > 30 and underlying_price <= 50:
weight = 0.005
w1 = 0.005/4
w2 = 0.005/4
w3 = 0.005/4
w4 = 0.005/4
if weight != 0:
if opt_price_1!=0:
options_q1 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_1 * 100))
else:
options_q1=0
if opt_price_2!=0:
options_q2 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_2 * 100))
else:
options_q2=0
if opt_price_3!=0:
options_q3 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_3 * 100))
else:
options_q3=0
if opt_price_4!=0:
options_q4 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_4 * 100))
else:
options_q4=0
# Set max leverage.
self.Securities[otm_call[0].Symbol].MarginModel = BuyingPowerModel(5)
# Sell out-the-money call.
if options_q1:
self.Buy(otm_call[0].Symbol, options_q1)
if options_q2:
self.Buy(otm_call[1].Symbol, options_q2)
if options_q3:
self.Buy(otm_call[2].Symbol, options_q3)
if options_q4:
self.Buy(otm_call[3].Symbol, options_q4)
self.SetHoldings(self.spy, 0.6)
self.SetHoldings(self.ief, 0.4)