| Overall Statistics |
|
Total Trades 8 Average Win 0% Average Loss -1.67% Compounding Annual Return -76.666% Drawdown 52.400% Expectancy -1 Net Profit -22.199% Sharpe Ratio -0.281 Probabilistic Sharpe Ratio 28.545% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.389 Beta 1.972 Annual Standard Deviation 1.358 Annual Variance 1.844 Information Ratio -0.565 Tracking Error 0.681 Treynor Ratio -0.194 Total Fees $735.25 |
#From V1, this V2 adds stop loss to hedge after it exceeds certain performance
class TailHedge(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 3, 1)
self.SetEndDate(2020, 5, 1)
self.SetCash(1000000)
stock = self.AddEquity("SSO", Resolution.Minute) #portfolio holdings
hedge = self.AddEquity("VXX", Resolution.Minute) #hedge instrument
stock.SetDataNormalizationMode(DataNormalizationMode.Raw)
hedge.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
self.stock = stock.Symbol
self.hedge = hedge.Symbol
self.contract = None
self.SetWarmUp(200)
self.Schedule.On(self.DateRules.MonthStart(self.stock), self.TimeRules.AfterMarketOpen(self.stock, 90), self.Rebalance)
### Hedge Paramaters
self.hedge_weight = 0.05 # % of portfolio invested into hedge each month
self.hedge_premium = 1.80 # % strike price premium on option call hedge
self.hedge_stoploss_ticket = None
self.hedge_peak_price = None
self.hedge_init_stoploss = 0.4 # stoploss set at original hedge purchase, in % of contract price
self.hedge_vol_stoploss = 0.9 # stoploss that kicks in after volatility spike, in % of contract price
self.hedge_vol_spike = 1.00 # % increase in hedge purchase price that triggers new vol_stoploss
def OnData(self, data):
if self.IsWarmingUp:
return
def Rebalance(self):
self.Log("Rebalance fired at : {0}".format(self.Time))
self.SetHoldings(self.stock, (1-self.hedge_weight))
if self.contract is None:
self.contract = self.GetContract()
self.SetHoldings(self.contract, self.hedge_weight)
else:
self.Liquidate(self.contract)
self.RemoveSecurity(self.contract)
self.contract = None
self.contract = self.GetContract()
self.SetHoldings(self.contract, self.hedge_weight)
# calculate initial stoploss price and quantity
price = self.Securities[self.contract].Price
self.hedge_peak_price = price
quantity = self.Portfolio[self.contract].Quantity
stop_loss = price * self.hedge_init_stoploss
# setup initial stop loss sell order
self.hedge_stoploss_ticket = self.StopMarketOrder(self.contract, -quantity, stop_loss)
# Everyday we will update our stoploss order if necessary
def OnEndOfDay(self):
# only check if hedge position exists
if self.contract is None or self.hedge_peak_price is None:
return
# current market price of contract
current_contract_price = self.Securities[self.contract].Price
if current_contract_price > self.hedge_peak_price:
self.hedge_peak_price = current_contract_price
# if volatily spike exceeds threshold, we'll update with trailing stoploss price
if self.Portfolio[self.contract].UnrealizedProfitPercent > self.hedge_vol_spike:
# % drawdown from peak price of contract
stop_loss = self.hedge_peak_price * self.hedge_vol_stoploss
self.hedge_stoploss_ticket.UpdateStopPrice(stop_loss)
self.Debug(f"!!! updated contract price: {current_contract_price}")
self.Debug(f"!!! new stop loss: {stop_loss}")
# if profits not yet > volatily spike threshold %, we don't trail our stop loss
else:
pass
# do nothing
def GetContract(self):
targetStrike = self.Securities[self.hedge].Price * self.hedge_premium
contracts = self.OptionChainProvider.GetOptionContractList(self.hedge, self.Time)
self.Debug(f"VXX Total Contracts Found: {len(contracts)}")
calls = [x for x in contracts if x.ID.OptionRight == OptionRight.Call]
calls = sorted(sorted(calls, key = lambda x: x.ID.Date, reverse = True),
key = lambda x: x.ID.StrikePrice)
self.Debug(f"VXX Calls found: {len(calls)}")
calls = [x for x in calls if (x.ID.StrikePrice - targetStrike) >= 0
and (x.ID.StrikePrice - targetStrike) < 6]
calls = [x for x in calls if 60 < (x.ID.Date - self.Time).days <= 150]
if len(calls) == 0:
self.Debug(f"!!! no options available")
return None
self.AddOptionContract(calls[0], Resolution.Minute)
return calls[0]