Overall Statistics |
Total Orders 15 Average Win 0.12% Average Loss -2.00% Compounding Annual Return -5.347% Drawdown 8.700% Expectancy -1 Start Equity 100000 End Equity 91628 Net Profit -8.372% Sharpe Ratio -1.447 Sortino Ratio -0.9 Probabilistic Sharpe Ratio 0.001% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0.06 Alpha -0.015 Beta -0.104 Annual Standard Deviation 0.027 Annual Variance 0.001 Information Ratio -1.919 Tracking Error 0.143 Treynor Ratio 0.38 Total Fees $11.00 Estimated Strategy Capacity $77000.00 Lowest Capacity Asset BGZ XVD806C6S8KM|BGZ U7EC123NWZTX Portfolio Turnover 0.04% |
# region imports from AlgorithmImports import * from datetime import timedelta # endregion class LETFShorter(QCAlgorithm): def initialize(self): #Set up initialization parameters self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN) self.set_start_date(int(self.get_parameter('Start Year')), int(self.get_parameter('Start Month')), int(self.get_parameter('Start Day'))) self.set_end_date(int(self.get_parameter('End Year')), int(self.get_parameter('End Month')), int(self.get_parameter('End Day'))) self.StartingBalance = int(self.get_parameter('Starting Balance')) self.set_cash(self.StartingBalance) self.set_warm_up(timedelta(1)) #Set up the benchmark equity for comparison self.BMequity = self.add_equity(self.get_parameter('Benchmark Equity'), Resolution.MINUTE) self.set_benchmark(self.BMequity.symbol) self.BM_priceHistory = [] self.schedule.on(self.date_rules.every_day(), self.time_rules.before_market_close(self.BMequity.symbol, 0), self.plotBM) #Add the LETFs self.longLETF = self.add_equity(self.get_parameter('Long 3X LETF'), Resolution.MINUTE) self.longLETF.set_data_normalization_mode(DataNormalizationMode.Raw) self.shortLETF = self.add_equity(self.get_parameter('Short 3X LETF'), Resolution.MINUTE) self.shortLETF.set_data_normalization_mode(DataNormalizationMode.Raw) #self.add_option(self.get_parameter('Long 3X LETF')) #self.add_option(self.get_parameter('Short 3X LETF')) self.longLETF_Ratio = 0 self.shortLETF_Ratio = 0 self.longLETF_CALL = None self.longLETF_PUT = None self.shortLETF_CALL = None self.shortLETF_PUT = None self.options_Exp = None #Add reserve equity self.reserveEquity = self.add_equity(self.get_parameter("Reserve Equity"), Resolution.MINUTE) def on_data(self, data: Slice): #default if self.is_warming_up: return #liquidate options if they are too close to expiration if not self.options_Exp == None: if (self.options_Exp - self.time) <= timedelta(int(self.get_parameter('Min Expiration DTE'))): self.LiquidateAllOptions() #self.log('Closed options positions: too close to expiration.') #liquidate options if a split is impending if data.Splits.contains_key(str(self.longLETF.symbol)): if data.Splits[self.longLETF.symbol].Type == SplitType.Warning: self.LiquidateAllOptions() self.log('Closed options positions: impending stock split in ' + str(self.longLETF.symbol) + '.') elif data.Splits.contains_key(str(self.shortLETF.symbol)): if data.Splits[self.shortLETF.symbol].Type == SplitType.Warning: self.LiquidateAllOptions() self.log('Closed options positions: impending stock split in ' + str(self.shortLETF.symbol) + '.') if not self.portfolio.invested: #self.set_holdings(self.reserveEquity.symbol, float(self.get_parameter("Reserve Allocation"))) self.BuyContracts(data) #Calculate synthetic option position ratios self.longLETF_Ratio = (int(self.get_parameter('Long Weighting')) * self.longLETF.price) / ((self.longLETF.price + self.shortLETF.price) * (int(self.get_parameter('Long Weighting')) + int(self.get_parameter('Short Weighting')))) self.shortLETF_Ratio = (int(self.get_parameter('Short Weighting')) * self.shortLETF.price) / ((self.longLETF.price + self.shortLETF.price) * (int(self.get_parameter('Long Weighting')) + int(self.get_parameter('Short Weighting')))) def debugPlot(self): return def plotBM(self): #Plot benchmark on strategy equity graph and plot alpha on alpha graph self.BM_priceHistory.append(self.History(self.BMequity.symbol, 2, Resolution.DAILY)['close'].unstack(level= 0).dropna()[self.BMequity.symbol].iloc[-1]) self.Plot('Strategy Equity', self.BMequity.symbol, self.BM_priceHistory[-1] / self.BM_priceHistory[0] * self.StartingBalance) self.Plot('Alpha', 'Alpha', self.portfolio.total_portfolio_value / self.StartingBalance - self.BM_priceHistory[-1] / self.BM_priceHistory[0]) def BuyContracts(self, data): if (self.options_Exp == None): self.GetOptions(data) return elif not (self.Portfolio[self.longLETF_CALL].invested and \ self.Portfolio[self.longLETF_PUT].invested and \ self.Portfolio[self.shortLETF_CALL].invested and \ self.Portfolio[self.shortLETF_PUT].invested): if data.contains_key(self.longLETF_CALL) and \ data.contains_key(self.longLETF_PUT) and \ data.contains_key(self.shortLETF_CALL) and \ data.contains_key(self.shortLETF_PUT): self.buy(self.longLETF_PUT, 1) self.sell(self.longLETF_CALL, 1) self.buy(self.shortLETF_PUT, 1) self.sell(self.shortLETF_CALL, 1) def GetOptions(self, data): #get prices of the underlying longLETF_underlyingPrice = self.Securities[self.longLETF.symbol].price shortLETF_underlyingPrice = self.Securities[self.shortLETF.symbol].price #get the list of contracts for each underlying longLETF_contracts = self.option_chain_provider.get_option_contract_list(self.longLETF.symbol, data.time) shortLETF_contracts = self.option_chain_provider.get_option_contract_list(self.shortLETF.symbol, data.time) #first filter the options for calls with an OTM strike greater than the parameter setting and at least the parameter setting for opening DTE longLETF_calls = [i for i in longLETF_contracts if i.ID.OptionRight == OptionRight.CALL and \ i.ID.StrikePrice - longLETF_underlyingPrice > (1 + float(self.get_parameter('OTM Percentage'))) * longLETF_underlyingPrice and \ int(self.get_parameter('Open Expiration DTE')) < (i.ID.Date - data.time).days] shortLETF_calls = [i for i in shortLETF_contracts if i.ID.OptionRight == OptionRight.CALL and \ i.ID.StrikePrice - shortLETF_underlyingPrice > (1 + float(self.get_parameter('OTM Percentage'))) * shortLETF_underlyingPrice and \ int(self.get_parameter('Open Expiration DTE')) < (i.ID.Date - data.time).days] #temporarily store old expiration value old_exp = self.options_Exp self.options_Exp = None #now we need to find a common expiration date between both option chains for both underlyings if len(longLETF_calls) > 0 and len(shortLETF_calls) > 0: for i in sorted(longLETF_calls, key = lambda x: abs((x.ID.Date - self.Time).days)): for z in sorted(shortLETF_calls, key = lambda x: abs((x.ID.Date - self.Time).days)): if i.ID.Date == z.ID.Date: self.options_Exp = i.ID.Date break else: continue break if not self.options_Exp == None: #now we sort the calls to find the appropriate call to sell for each underlying, using the contract date and then sort again based on the strike price longLETF_call_contract = sorted(sorted(longLETF_calls, key = lambda x: abs((x.ID.Date - self.Time).days - (self.options_Exp - self.Time).days)), key = lambda x: x.ID.StrikePrice - longLETF_underlyingPrice)[0] shortLETF_call_contract = sorted(sorted(shortLETF_calls, key = lambda x: abs((x.ID.Date - self.Time).days - (self.options_Exp - self.Time).days)), key = lambda x: x.ID.StrikePrice - shortLETF_underlyingPrice)[0] #now that we have the calls, we have to get puts with matching strikes and expirations. This is easier because we did most of the work to select the call. longLETF_puts = [i for i in longLETF_contracts if i.ID.OptionRight == OptionRight.PUT and i.ID.StrikePrice == longLETF_call_contract.ID.StrikePrice and i.ID.Date == self.options_Exp] shortLETF_puts = [i for i in shortLETF_contracts if i.ID.OptionRight == OptionRight.PUT and i.ID.StrikePrice == shortLETF_call_contract.ID.StrikePrice and i.ID.Date == self.options_Exp] if len(longLETF_puts) > 0 and len(shortLETF_puts) > 0: self.longLETF_CALL = longLETF_call_contract self.add_option_contract(longLETF_call_contract, Resolution.MINUTE) self.longLETF_PUT = longLETF_puts[0] self.add_option_contract(longLETF_puts[0], Resolution.MINUTE) self.shortLETF_CALL = shortLETF_call_contract self.add_option_contract(shortLETF_call_contract, Resolution.MINUTE) self.shortLETF_PUT = shortLETF_puts[0] self.add_option_contract(shortLETF_puts[0], Resolution.MINUTE) else: self.options_Exp = old_exp def LiquidateAllOptions(self): if not self.longLETF_CALL == None: self.remove_option_contract(self.longLETF_CALL) if not self.longLETF_PUT == None: self.remove_option_contract(self.longLETF_PUT) if not self.shortLETF_CALL == None: self.remove_option_contract(self.shortLETF_CALL) if not self.shortLETF_PUT == None: self.remove_option_contract(self.shortLETF_PUT) self.longLETF_CALL = None self.longLETF_PUT = None self.shortLETF_CALL = None self.shortLETF_PUT = None self.options_Exp = None def OnOrderEvent(self, orderEvent): self.log(str(orderEvent)) order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Type == OrderType.OptionExercise: self.Liquidate(orderEvent.Symbol.Underlying) self.LiquidateAllOptions()