Overall Statistics |
Total Trades 6 Average Win 22.55% Average Loss -7.71% Compounding Annual Return 235.347% Drawdown 8.100% Expectancy 0.962 Net Profit 26.800% Sharpe Ratio 7.496 Probabilistic Sharpe Ratio 96.319% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 2.92 Alpha 0.801 Beta 1.867 Annual Standard Deviation 0.232 Annual Variance 0.054 Information Ratio 7.403 Tracking Error 0.167 Treynor Ratio 0.93 Total Fees $150.00 Estimated Strategy Capacity $600000.00 Lowest Capacity Asset AAPL 30IZW3B3L6QH2|AAPL R735QTJ8XC9X |
''' Original def TradeOptions(self,slice): # If there is undelying assets in portfolio at expiration, liquidate the stocks in order to roll into new contracts if self.Portfolio["GOOG"].Quantity != 0: self.Liquidate() if not self.Portfolio.Invested and self.Time.hour != 0 and self.Time.minute != 0: for i in slice.OptionChains: chain = i.Value contract_list = [x for x in chain] # if there is no optionchain or no contracts in this optionchain, pass the instance if (slice.OptionChains.Count == 0) or (len(contract_list) == 0): return # sorted the optionchain by expiration date and choose the furthest date expiry = sorted(chain,key = lambda x: x.Expiry)[-1].Expiry # filter the call and put options from the contracts call = [i for i in chain if i.Expiry == expiry and i.Right == 0] put = [i for i in chain if i.Expiry == expiry and i.Right == 1] # sorted the contracts according to their strike prices call_contracts = sorted(call,key = lambda x: x.Strike) put_contracts = sorted(put,key = lambda x: x.Strike) if len(call_contracts) == 0 or len(put_contracts) == 0 : continue otm_put_lower = put_contracts[0] otm_put = put_contracts[10] otm_call = call_contracts[-10] otm_call_higher = call_contracts[-1] self.trade_contracts = [otm_call.Symbol,otm_call_higher.Symbol,otm_put.Symbol,otm_put_lower.Symbol] # if there is no securities in portfolio, trade the options self.Buy(otm_put_lower.Symbol ,1) self.Sell(otm_put.Symbol ,1) self.Sell(otm_call.Symbol ,1) self.Buy(otm_call_higher.Symbol ,1) '''
from datetime import timedelta import QuantConnect as qc ''' Questions Portfolio vs Securities usage of Chain / Contract? (Heirarchy + Return) Delta (not seemingly accurate?) -- Pricing Model needed? Margin Issue? Extras: Managing Limits Incrementally with OnOrder event? ''' class IronCondorAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2017, 2, 1) self.SetEndDate(2017, 4, 15) self.SetCash(100000) #CHANGE THIS BACK self.tickers = ['AAPL'] for i in self.tickers: equity = self.AddEquity(i, Resolution.Minute) option = self.AddOption(i, Resolution.Minute) # some of the OptionPriceModels are unreliable option.PriceModel = qc.Securities.Option.OptionPriceModels.CrankNicolsonFD() option.PriceModel.EnableGreekApproximation = True #THESE only work because it's ONE symbol self.symbol = option.Symbol # identifier for the OptionChain for AAPL not any specific contract self.ul = equity.Symbol option.SetFilter(self.UniverseFunc) self.SetBenchmark(equity.Symbol) self.stk_dist = 5 #CHANGED FROM 10 self.max_delta = .5 #Not used bc not accurate ...? (Need pricingmodel maybe?) self.tgt_dte = 40 self.use_limits = False self.pf_util = .5 self.cts = 100 ## IF DEFINED: Override ^^ Calc w Margin # symbols = self.OptionChainProvider.GetOptionContractList(underlying_symbol, self.Time) # symbols[0].Underlying def OnData(self,slice): ''' #How to check for equity holdings / Clear them for kvp in self.Securities: #-- ORIGINALLY this was SECURITIES symbol = kvp.Key id = symbol.ID security_type = id.SecurityType if security_type != SecurityType.Option: if self.Portfolio[symbol].Invested: self.Liquidate() ''' #Simple version of clearing any Assigned. if self.Portfolio[self.ul].Quantity != 0: self.Liquidate() #If holding options if self.Portfolio.Invested: self.ExitExpiring() #Check for Expiring options. return # no positions, every minute short_put, long_put = self.GetBullPut(slice, self.max_delta, self.stk_dist, self.tgt_dte) if not short_put or not long_put: return usd_per_spread = self.GetMarginReq(short_put, long_put) if usd_per_spread == 0: usd_per_spread = self.stk_distance num_cts = self.Portfolio.MarginRemaining * self.pf_util / usd_per_spread self.Debug(f'Number Contracts -- {num_cts}') self.Debug(f'PF$ {self.Portfolio.MarginRemaining} vs {num_cts * usd_per_spread}') if self.cts: num_cts = self.cts self.ExecuteOptions(short_put, long_put, num_cts, use_lmts = self.use_limits) #Close Expiring? (Drag in from my example in other script) def GetMarginReq(self, sell, buy): ## Accepts FULL OptionChains (from SetFilter) #for i in zip(sell, buy): ## For IC? credit = sell.BidPrice - buy.AskPrice distance = sell.Strike - buy.Strike self.Debug(f'Credit: {credit}, Distance: {distance}') return (distance - credit) * 100 ''' Alternate Lookup -- if easier to look up one opt at a time def GetOption(self, slice, right, dte, stk=None, delta=None): if isinstance(dte, int): tgt_expiry = self.Time + timedelta(dte) else: tgt_expiry = dte for i in slice.OptionChains: symbol = i.Key chain = i.Value if len(x for x in chain) == 0: return rights = [i for i in chain if i.Right == right] #Expiry sort_by_exp = sorted(rights, key=lambda x: abs(x.Expiry - tgt_expiry), reverse=False) #ASC #if not sort_by_exp[0]: return exp = sort_by_exp[0].Expiry exp_rts = [i for i in rights if i.Expiry == exp] if len(exp_rts) == 0: return if stk: return sorted(exp_rts, key=lambda x: abs(x.Strike - stk), reverse=False)[0] if delta: return sorted(exp_rts, key=lambda x: abs(x.Greeks.Delta - delta), reverse=False)[0] ''' ## Easier to write a 'findOption' function, and find EACH component of the spread? def GetBullPut(self, slice, max_delta, stk_dist, dte): inner, outer = None, None # # Slice.OptionChains most recent option chains data, most recent minute # aapl_option_chain = slice.OptionChains[self.symbol] # pick out aapl option # if you know the symbol of a contract, you can use this # self.AddOptionContract for i in slice.OptionChains: symbol = i.Key chain = i.Value if len([x for x in chain]) == 0: return #Expiry tgt_expiry = self.Time + timedelta(dte) dist_to_expiry = sorted(chain, key=lambda x: abs(tgt_expiry - x.Expiry), reverse=False) #ASCENDING if len(dist_to_expiry) == 0: return expiry = dist_to_expiry[0].Expiry exp_puts = [i for i in chain if i.Right == OptionRight.Put and i.Expiry == expiry] #clusters = [i for i in exp_puts if i.Strike % 5 == 0] # HOW TO GET EVEN NUMS ## Ignore Greeks Initially -- being wierd? #Greeks filter_delta = [i for i in exp_puts if abs(i.Greeks.Delta) < max_delta] if len(filter_delta) < 2: self.Debug(f'Not enough options meet delta filter.') return #Get HIGHEST abs delta here sort_by_delta = sorted(exp_puts, key=lambda x: abs(x.Greeks.Delta), reverse=True) #DESC inner = sort_by_delta[0] self.Debug(f'Inner -- {inner.Strike} d: {inner.Greeks.Delta}') #USING SOLELY STK BASED CALC ul = chain.Underlying.Price sort_by_ul_dist = sorted(exp_puts, key=lambda x: abs(x.Strike - ul), reverse=False) #ASC #How to get 5 multiples... filter by them early (if x.Strike % 5 == 0) ? inner = sort_by_ul_dist[0] tgt_outer_stk = inner.Strike - stk_dist #HARD CODED TEMPORARILY self.Debug(f'Tgt Lower Stk {tgt_outer_stk}') outer = sorted(exp_puts, key=lambda x: abs(tgt_outer_stk - x.Strike), reverse = False)[0] #ASC self.Debug(f'Lower -- {outer.Strike}') return inner, outer #def GetRiskRewardBullPut(self, slice, stk_dist, ) #How to even search this one? Use Margin + Credit to FIND them def ExecuteOptions(self, sell, buy, num_cts=1, use_lmts = False): if use_lmts: ask = buy.AskPrice bid = sell.BidPrice ## THINK this needs symbol BC this is the FULL Security (vs the Symbol object) #Do I really need Symbol here? ----------------------------------- TRY W + W/OUT self.LimitOrder(buy.Symbol, num_cts, ask) self.LimitOrder(sell.Symbol, -num_cts, bid) #Could manage these limits with OnOrderEvent ...(Decrease price slowly until filled... if pending) else: self.MarketOrder(buy.Symbol, num_cts) self.MarketOrder(sell.Symbol, -num_cts) self.Debug(f'Order Submitted: -{sell.Strike} / +{buy.Strike}') #This is always showing DTE of 1...? def ExitExpiring(self, only_itm=False): #invested = [kvp for kvp in self.Portfolio if self.Portfolio[kvp.Key].Invested] #? # expiries = [x.Key.ID.Date for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option] # self.Debug(f'expiries -- {expiries}') # self.Debug(f'Diffs -- {[(i - self.Time) for i in expiries]}') # self.Debug(f'DTEs -- {[(i-self.Time).days for i in expiries]}') # return # collection of dictionaries, 1 dictionary for slice.TradeBars (symbol, tradebars), 1 dictionary for # OptionChains (symbol, OptionChain) # for i in myDict.items() # for kvp in self.CurrentSlice.OptionChains: # chain_symbol = kvp.Key # chain_contracts = kvp.Value # # AAPL chain, QQQ Chain, # # loop through specific contracts of that chain # for contract in chain_contracts: # list_of_symbol_objects = self.OptionChainProvider.GetOptionContractList() # Symbol.cs # Symbol.ID - SecurityIdentifier.cs # SecurityIdentifier.cs contains basic information about security #Shold this be self.Securities? # iterating through every security, including otions, futures, equities # Portfolio <keys=Symbol.cs, value=PortfolioHolding.cs>, element for every security invested and not invested, # that has a data subscription for kvp in self.Portfolio: symbol = kvp.Key holding = kvp.Value id = symbol.ID if id.SecurityType == SecurityType.Option and self.Portfolio[symbol].Invested: #kvp.Value.Invested expiry = symbol.ID.Date #WHY is this always one? dte = (expiry - self.Time).days self.Debug(f'DTE -- {dte}') if dte > 1: continue stk = symbol.ID.StrikePrice if only_itm: ul = symbol.Underlying ul_price = self.Portfolio[ul].Price if symbol.ID.OptionRight == OptionRight.Call: if ul_price > stk: self.Debug(f'Exitting ITM Call') self.Liquidate(symbol) else: if ul_price < stk: self.Debug(f'Exitting ITM Put') self.Liquidate(symbol) else: self.Liquidate(symbol) self.Debug(f'Exitting Expiring Symbol') def OnOrderEvent(self, orderEvent): self.Log(str(orderEvent)) def UniverseFunc(self, universe): return universe.IncludeWeeklys().Strikes(-99, 99).Expiration(timedelta(0), timedelta(self.tgt_dte + 10))