Overall Statistics |
Total Trades 91000 Average Win 0.28% Average Loss -0.26% Compounding Annual Return -2.290% Drawdown 88.500% Expectancy 0.004 Net Profit -38.704% Sharpe Ratio 0.075 Probabilistic Sharpe Ratio 0.000% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.10 Alpha 0.007 Beta 0.157 Annual Standard Deviation 0.245 Annual Variance 0.06 Information Ratio -0.185 Tracking Error 0.286 Treynor Ratio 0.118 Total Fees $0.00 |
import numpy as np from time import sleep class CalibratedParticleReplicator(QCAlgorithm): def Initialize(self): self.SetStartDate(2000, 1, 1) # Set Start #self.SetEndDate(2010, 7, 1) # Set end self.SetCash(100000) # Set Strategy Cash self.SetWarmUp(timedelta(2)) #self.SetBrokerageModel(BrokerageName.Alpaca, AccountType.Margin) # Ensure that we have data available self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse( self.SelectCoarse, self.SelectFine ) self.MRKT = self.AddEquity('SPY', Resolution.Minute).Symbol # market #self.BND = self.AddEquity('TLT', Resolution.Minute).Symbol # bonds self.stop_limits = {} # Max leverage self.max_lever = 1.0 self.max_lever_per_sec = 0.1 # Market self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 5), self.EveryDayAfterMarketOpen) # schedule an event to fire every trading day for a security the # time rule here tells it to fire 10 minutes before SPY's market close self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 10), self.EveryDayBeforeMarketClose) self.SetBenchmark("SPY") def GetCalls(self, symbol, start_days = 30, end_days = 60): # Get price hist = self.History(symbol, 1, Resolution.Minute) #pulls contract data for select equity at current time contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time) #sorts contracts by closet expiring date date and closest strike price (sorts in ascending order) calls = sorted(sorted([x for x in contracts if x.ID.OptionRight == OptionRight.Call], key = lambda x: x.ID.Date), key = lambda x: x.ID.StrikePrice) calls = [x for x in calls if x.ID.StrikePrice > hist.iloc[-1]['close']] #then selects all contracts that meet our expiration criteria calls = [x for x in calls if start_days<(x.ID.Date - self.Time).days <= end_days] if not calls: return [] #will eventually return array of optimal puts and calls return calls def add_overnight_returns(self, securities): new_securities = [] for sec in securities: try: hist = self.History(sec.Symbol, 2, Resolution.Daily) #if len(hist.index) != 2: # self.Debug("unable to get proper history for {}, got {}".format(sec, hist.index)) overnight = (hist.iloc[-1]['open'] / hist.iloc[-2]['close']) - 1 sec.overnight_returns = overnight new_securities.append(sec) except IndexError as ex: pass #self.Debug("could not analyze {}: {}".format(sec, ex)) return new_securities def SelectCoarse(self, coarse): dollarvolume_array = np.array([ x.DollarVolume for x in coarse ]) dollarvolume_percentile = np.percentile(dollarvolume_array, 90) # Select top volume coarse = [x for x in coarse if x.DollarVolume >= dollarvolume_percentile] # Low price price_array = np.array([ x.Price for x in coarse ]) price_percentile = np.percentile(price_array, 10) coarse = [x.Symbol for x in coarse if x.Price <= price_percentile] return coarse def SelectFine(self, fine): # Overnight returns selected = self.add_overnight_returns(fine) overnight_returns = np.array([ s.overnight_returns for s in selected ]) mask = np.all(np.isnan(overnight_returns), axis=0) if not len(overnight_returns[~mask] > 0): return [] overnight_returns_pct_high = np.percentile(overnight_returns[~mask], 90) overnight_returns_pct_low = np.percentile(overnight_returns[~mask], 10) if not selected: return selected self.shorts = [f.Symbol for f in sorted(selected, key=lambda x: x.overnight_returns, reverse=True) if f.overnight_returns >= overnight_returns_pct_high][:5] self.longs = [f.Symbol for f in sorted(selected, key=lambda x: x.overnight_returns) if f.overnight_returns <= overnight_returns_pct_low][:5] self.symbols = self.longs+self.shorts return self.symbols def EveryDayAfterMarketOpen(self): if self.IsWarmingUp: return if not self.symbols: return #self.Debug("got {} from fine filter".format(len(self.symbols))) self.lever_per_sec = min(self.max_lever / len(self.symbols), self.max_lever_per_sec) # Set excess to SPY/TLT self.leftover = 1.0 - len(self.symbols)*self.lever_per_sec # SPY self.SetHoldings(self.MRKT, self.leftover) # BNDs #self.SetHoldings(self.BND, self.leftover) # Shorts for symbol in self.shorts: # Get call options #calls = self.GetCalls(symbol) self.SetHoldings(symbol, -self.lever_per_sec) self.stop_limits[symbol] = { "stop": self.Securities[symbol].Price*(1.2), "limit": self.Securities[symbol].Price*(0.5), } # Longs for symbol in self.longs: # Get call options #calls = self.GetCalls(symbol) self.SetHoldings(symbol, self.lever_per_sec) self.stop_limits[symbol] = { "limit": self.Securities[symbol].Price*(1.5), "stop": self.Securities[symbol].Price*(0.8), } #if len(calls) > 0: # try: # call_option = calls[0] # self.AddOptionContract(call_option, Resolution.Minute) #self.Debug("buying call contract: {}".format(calls[0])) # call = self.MarketOrder(call_option, 1, True) # self.stop_limits[symbol]["option"] = call # except Exception as ex: # pass #self.Debug("error trying to order call contract for {}".format(symbol)) #self.Debug(ex) #else: # pass #self.Debug("no call contracts found for {}".format(symbol)) self.Plot("Holdings", "number", len(self.Securities.keys())) self.Plot("Allocation", "mrkt", self.leftover) self.Plot("Allocation", "long", len(self.longs)*self.lever_per_sec) self.Plot("Allocation", "short", len(self.shorts)*self.lever_per_sec) def EveryDayBeforeMarketClose(self): if self.IsWarmingUp: return self.Plot("Holdings", "number", len(self.Securities.keys())) self.Plot("Allocation", "mrkt", self.leftover) self.Plot("Allocation", "long", len(self.longs)*self.lever_per_sec) self.Plot("Allocation", "short", len(self.shorts)*self.lever_per_sec) self.Liquidate() def OnSecuritiesChanged(self, changes): if self.IsWarmingUp: return # Subscribe to minute bars for added equities for instrument in changes.AddedSecurities: if instrument.Symbol.SecurityType == SecurityType.Equity: self.AddEquity(instrument.Symbol, Resolution.Minute) self.Securities[instrument.Symbol].FeeModel = ConstantFeeModel(0.0) self.Securities[instrument.Symbol].MarginModel = PatternDayTradingMarginModel() #.Securities[instrument.Symbol].SetDataNormalizationMode(DataNormalizationMode.Raw) # Remove equities for instrument in changes.RemovedSecurities: if instrument.Symbol.SecurityType == SecurityType.Equity: self.RemoveSecurity(instrument.Symbol) #if "option" in self.stop_limits[instrument.Symbol].keys(): # call = self.stop_limits[instrument.Symbol]["option"] # self.RemoveSecurity(call.Symbol) def OnData(self, data): if self.IsWarmingUp: return # Limits and stops for symbol, security in self.Portfolio.items(): if symbol == self.MRKT: continue if not security.Invested: continue if not data.Bars.ContainsKey(symbol): continue if not symbol.SecurityType == SecurityType.Equity: continue last_price = data.Bars[symbol].Close stop_limit = self.stop_limits[symbol] limit_price = stop_limit["limit"] stop_price = stop_limit["stop"] # Handle shorts if symbol in self.shorts: if last_price >= stop_price: #self.Debug("{} triggered stop at {}, liquidating".format(symbol, last_price)) self.Liquidate(symbol) self.Plot("Holdings", "number", len(self.Securities.keys())) self.Plot("Allocation", "mrkt", self.leftover) self.Plot("Allocation", "long", len(self.longs)*self.lever_per_sec) self.Plot("Allocation", "short", len(self.shorts)*self.lever_per_sec) elif last_price <= limit_price: #self.Debug("{} triggered limit at {}, liquidating".format(symbol, last_price)) self.Liquidate(symbol) self.Plot("Holdings", "number", len(self.Securities.keys())) self.Plot("Allocation", "mrkt", self.leftover) self.Plot("Allocation", "long", len(self.longs)*self.lever_per_sec) self.Plot("Allocation", "short", len(self.shorts)*self.lever_per_sec) #if "option" in stop_limit.keys(): # call = stop_limit["option"] # #self.Debug("liquidating call contract {}".format(call.Symbol, last_price)) # self.Liquidate(call.Symbol) # Longs elif symbol in self.longs: if last_price >= limit_price: #self.Debug("{} triggered stop at {}, liquidating".format(symbol, last_price)) self.Liquidate(symbol) self.Plot("Holdings", "number", len(self.Securities.keys())) self.Plot("Allocation", "mrkt", self.leftover) self.Plot("Allocation", "long", len(self.longs)*self.lever_per_sec) self.Plot("Allocation", "short", len(self.shorts)*self.lever_per_sec) elif last_price <= stop_price: #self.Debug("{} triggered limit at {}, liquidating".format(symbol, last_price)) self.Liquidate(symbol) self.Plot("Holdings", "number", len(self.Securities.keys())) self.Plot("Allocation", "mrkt", self.leftover) self.Plot("Allocation", "long", len(self.longs)*self.lever_per_sec) self.Plot("Allocation", "short", len(self.shorts)*self.lever_per_sec) #if "option" in stop_limit.keys(): # call = stop_limit["option"] # #self.Debug("liquidating call contract {}".format(call.Symbol, last_price)) # self.Liquidate(call.Symbol)