Overall Statistics |
Total Trades 181 Average Win 0.97% Average Loss -0.59% Compounding Annual Return 15.243% Drawdown 9.500% Expectancy 0.006 Net Profit 1.725% Sharpe Ratio 0.57 Probabilistic Sharpe Ratio 42.712% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 1.66 Alpha -0.463 Beta 0.403 Annual Standard Deviation 0.324 Annual Variance 0.105 Information Ratio -4.24 Tracking Error 0.335 Treynor Ratio 0.458 Total Fees $450.00 |
from clr import AddReference AddReference("System") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Common") from System import * from QuantConnect import * from QuantConnect.Data import * from QuantConnect.Algorithm import * from QuantConnect.Securities.Option import OptionPriceModels import numpy as np from datetime import timedelta ### <summary> ### Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to recieve ### data for and manually subscribing to them. ### </summary> ### <meta name="tag" content="strategy example" /> ### <meta name="tag" content="options" /> ### <meta name="tag" content="using data" /> ### <meta name="tag" content="selecting options" /> ### <meta name="tag" content="manual selection" /> class OptionChainProviderAlgorithm(QCAlgorithm): highestPrice = 0 trailing_stop_pct = .93 trailing_stop_price = 0 def Initialize(self): self.SetStartDate(2020, 4, 21) #self.SetEndDate(2016, 6, 24) self.SetCash(20000) # add the underlying asset #self.equity = self.AddEquity("SPY", Resolution.Minute) self.equity = self.AddEquity("SPY R735QTJ8XC9X", Resolution.Minute) #self.equity = self.Symbol("SPY R735QTJ8XC9X", Resolution.Minute) #spy = self.Symbol("SPY R735QTJ8XC9X") #SPY R735QTJ8XC9X self.syl = "SPY" self.spy = "SPY" self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw) # create a XX-period exponential moving average self.fast = self.EMA("SPY", 30, Resolution.Minute) # create a XX-period exponential moving average since this is minute, may have to multiply by 2 if want 2 min chart #self.slow = self.EMA("SPY", 50, Resolution.Minute) slow_ema_lookback = 100 self.slow = self.EMA("SPY", slow_ema_lookback, Resolution.Minute); self.SetWarmUp(slow_ema_lookback) # initialize the option contract with empty string self.contract_put = str() self.contract_call = str () self.contractsAdded = set() equity = self.AddEquity(self.syl, Resolution.Minute) self.underlyingsymbol = equity.Symbol def OnData(self, data): if self.IsWarmingUp: return #if not self.Portfolio[self.equity.Symbol].Invested: # self.MarketOrder(self.equity.Symbol, 100) #put entry if not (self.Portfolio.Invested) and (self.fast.Current.Value <= self.slow.Current.Value) and (self.fast.Current.Value >= self.equity.Price >= (self.fast.Current.Value - 0.03)): # worked self.equity.Price > 0: #(self.fast.Current.Value < self.slow.Current.Value): #and (self.equity ["GOOG"] >= (self.fast.Current.Value - 0.03)): #if not (self.Portfolio[self.contract_put].Invested) #(self.Securities.ContainsKey(self.contract_put) and ): #(self.fast.Current.Value < self.slow.Current.Value) and (slice["SPY"].Price >= (self.fast.Current.Value - 0.03) self.contract_put = self.OptionsFilter(data) self.MarketOrder(self.contract_put, +10) self.trailing_stop_price = self.trailing_stop_pct * self.Securities[self.contract_put].Close #call entry if not (self.Portfolio.Invested) and (self.fast.Current.Value >= self.slow.Current.Value) and (self.fast.Current.Value <= self.equity.Price <= (self.fast.Current.Value + 0.03)): #(self.Securities.ContainsKey(self.contract_call) and self.Portfolio[self.contract_call].Invested): self.contract_call = self.OptionsFilter(data) self.MarketOrder(self.contract_call, +10) self.trailing_stop_price = self.trailing_stop_pct * self.Securities[self.contract_call].Close #exit if (self.Portfolio.Invested): contract_price_call = self.Securities[self.contract_call].Close contract_price_put = self.Securities[self.contract_put].Close self.Log(f"Stop price: {self.trailing_stop_price}") #near 20% exit if (self.Securities[self.contract_call].Close >= (self.trailing_stop_price * 1.3)): self.Log(f"20% profit call contract price: {contract_price_call}") self.MarketOrder(self.contract_call, -10) self.Log("Liquidated") elif (self.Securities[self.contract_put].Close >= (self.trailing_stop_price * 1.3)): self.MarketOrder(self.contract_put, -10) self.Log(f"20% profit put contract price: {contract_price_put}") self.Log("Liquidated") #set trailing stop for calls elif (self.Portfolio[self.contract_call].Invested) and (contract_price_call < self.trailing_stop_price): self.Log(f"Call contract price: {contract_price_call}") self.MarketOrder(self.contract_call, -10) self.Log("Liquidated") elif (self.Portfolio[self.contract_call].Invested) and (contract_price_call > self.highestPrice): self.Log(f"Call contract price: {contract_price_call}") self.highestPrice = contract_price_call self.trailing_stop_price = self.highestPrice * self.trailing_stop_pct #take 20% off puts #set trailing stop for puts elif (self.Portfolio[self.contract_put].Invested) and (contract_price_put < self.trailing_stop_price): self.Log(f"Put contract price: {contract_price_put}") self.MarketOrder(self.contract_put, -10) self.trailing_stop_price = 0 self.highestPrice = 0 self.Log("Liquidated") elif (self.Portfolio[self.contract_put].Invested) and (contract_price_put > self.highestPrice): self.Log(f"Put contract price: {contract_price_put}") self.highestPrice = contract_price_put self.trailing_stop_price = self.highestPrice * self.trailing_stop_pct #if not (self.Securities.ContainsKey(self.contract) and self.Portfolio[self.contract].Invested): # self.contract = self.OptionsFilter(data) #if self.Securities.ContainsKey(self.contract) and not self.Portfolio[self.contract].Invested: # self.MarketOrder(self.contract, -1) #def contract_put(self): # contracts = self.OptionChainProvider.GetOptionContractList(self.security.symbol, self.Time.date()) # if len(contracts) == 0: return # filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -1, 1, 0, 14) # put = [x for x in filtered_contracts if x.ID.OptionRight == OptionRight.Put] # if len(put) == 0: # return # sorted the contracts according to their expiration dates and choose the ATM options # contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), # key = lambda x: x.ID.Date, reverse=False) # self.contract = contracts[0] # self.AddOptionContract(self.contract, Resolution.Minute) # self.Buy(self.contract, 5) def OptionsFilter(self, data): ''' OptionChainProvider gets a list of option contracts for an underlying symbol at requested date. Then you can manually filter the contract list returned by GetOptionContractList. The manual filtering will be limited to the information included in the Symbol (strike, expiration, type, style) and/or prices from a History call ''' contracts = self.OptionChainProvider.GetOptionContractList(self.equity.Symbol, data.Time) self.underlyingPrice = self.Securities[self.equity.Symbol].Price # filter the out-of-money call options from the contract list which expire in 0 to 7 days from now on otm_calls = [i for i in contracts if i.ID.OptionRight == OptionRight.Call] #and #i.ID.StrikePrice - self.underlyingPrice > 0 and #0 < (i.ID.Date - data.Time).days < 3] #filter the out-of-money put options fromt the contract list which expire in 0 to 7 days from now otm_puts = [i for i in contracts if i.ID.OptionRight == OptionRight.Put] #and #i.ID.StrikePrice - self.underlyingPrice > 0 and #0 < (i.ID.Date - data.Time).days < 3] #this should help select the soonest exp. #if len(otm_puts) == 0: # return #this should help select the soonest exp. #if len(otm_calls) == 0: # return if len(otm_calls) > 0 and (self.fast.Current.Value >= self.slow.Current.Value) and (self.fast.Current.Value <= self.equity.Price <= (self.fast.Current.Value + 0.03)): contract_call = sorted(sorted(otm_calls, key = lambda x: x.ID.Date, reverse=False), key = lambda x: abs(self.underlyingPrice - x.ID.StrikePrice))[0] if contract_call not in self.contractsAdded: self.contractsAdded.add(contract_call) # use AddOptionContract() to subscribe the data for specified contract self.AddOptionContract(contract_call, Resolution.Minute) return contract_call if len(otm_puts) > 0 and (self.fast.Current.Value <= self.slow.Current.Value) and (self.fast.Current.Value >= self.equity.Price >= (self.fast.Current.Value - 0.03)): contract_put = sorted(sorted(otm_puts, key = lambda x: x.ID.Date, reverse=False), key = lambda x: abs(self.underlyingPrice - x.ID.StrikePrice))[0] if contract_put not in self.contractsAdded: self.contractsAdded.add(contract_put) # use AddOptionContract() to subscribe the data for specified contract self.AddOptionContract(contract_put, Resolution.Minute) return contract_put else: return str() #attempt to select closest expiry. #def contract_put(self): # ontracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date()) # if len(contracts) == 0: return # filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -1, 1, 0, 14) # put = [x for x in filtered_contracts if x.ID.OptionRight == OptionRight.Put] # if len(put) == 0: # return # sorted the contracts according to their expiration dates and choose the ATM options # contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), # key = lambda x: x.ID.Date, reverse=False) # self.contract = contracts[0] # self.AddOptionContract(self.contract, Resolution.Minute) # self.Buy(self.contract_put, 1) # def BuyCall(self): # contracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date()) # if len(contracts) == 0: return # filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -3, 3, 0, 30) # call = [x for x in filtered_contracts if x.ID.OptionRight == 1] # sorted the contracts according to their expiration dates and choose the ATM options # contracts = sorted(sorted(call, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), # key = lambda x: x.ID.Date, reverse=True) # self.contract = contracts[0] # self.AddOptionContract(self.contract, Resolution.Minute) # self.Buy(self.contract, 1) #def InitialFilter(self, underlyingsymbol, symbol_list, min_strike_rank, max_strike_rank, min_expiry, max_expiry): # ''' This method is an initial filter of option contracts # according to the range of strike price and the expiration date ''' # if len(symbol_list) == 0 : return # fitler the contracts based on the expiry range # contract_list = [i for i in symbol_list if min_expiry < (i.ID.Date.date() - self.Time.date()).days < max_expiry] # if len(contract_list) == 0: # return [] # find the strike price of ATM option # atm_strike = sorted(contract_list, # key = lambda x: abs(x.ID.StrikePrice - self.Securities[underlyingsymbol].Price))[0].ID.StrikePrice # strike_list = sorted(set([i.ID.StrikePrice for i in contract_list])) # find the index of ATM strike in the sorted strike list # atm_strike_rank = strike_list.index(atm_strike) # try: # min_strike = strike_list[atm_strike_rank + min_strike_rank] # max_strike = strike_list[atm_strike_rank + max_strike_rank] # except: # min_strike = strike_list[0] # max_strike = strike_list[-1] # filtered_contracts = [i for i in contract_list if i.ID.StrikePrice >= min_strike and i.ID.StrikePrice <= max_strike] # return filtered_contracts