Hi Team, 

I´m new to QC & trying out some basic stuff.
Goal here is to track the breakpoints of a classic "value" portfolio (as outlined in https://www.quantconnect.com/tutorials/strategy-library/book-to-market-value-anomaly#Book-to-Market-Value-Anomaly-Source), then pick out one ticker (the one with largest market cap on the first rebalancing period) and track its inclusion  / exclusion in the value portfolio. 
More precisely I want to track its P/B at each rebalancing date and see whether it´s inside the value portfolio or not. You can see this from the custom charts I added.

Besides massive speed issues (not sure how to improve on them) I seem to misunderstand some basic concepts on the flow of the algorithm. For example, why is the P/B Ratio of the asset I´m tracking always constant? Is this the way to fetch the current P/B ratio (line 102). Equally unsure about fetching last Price on rebalancing date (line 125).. 
Then for to me inexplicable reasons the backtest broke down midway (!) in my last attempt due to line 125.

Also wondering if this type of analysis should better be done in a research notebook? But unclear how to import universes, etc. 

Always knew I was dumb & slow, but hardly been as frustrated as this. Thank you so much for taking the time to look into it

Below is the original backtest, while here is the code with some changes:

from QuantConnect.Data.UniverseSelection import * from System.Drawing import Color import operator import math import numpy as np import pandas as pd import scipy as sp class BooktoMarketAnomaly(QCAlgorithm): def Initialize(self): self.SetStartDate(2016,1,2) # Set Start Date self.SetEndDate(2019,3,15) # Set End Date self.SetCash(50000) # Set Strategy Cash self.flag0 = 1 # variable to select test-asset in first rebalancing month self.flag1 = 1 # variable to control the monthly rebalance of coarse and fine selection function self.flag2 = 0 # variable to control the monthly rebalance of OnData function self.flag3 = 0 # variable to record the number of rebalancing times # Initialize Trading Variables self.test_asset_object = None self.test_asset_ticker = None self.long_list = None self._changes = None # Initialize Universe + Settings self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) # Initialize Benchmark self.SetBenchmark("SPY") self.AddEquity("SPY", Resolution.Daily) # Schedule functions # Trigger an event every day a specific symbol is trading --> here monthly self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY"), Action(self.Rebalancing)) # Initialize Charts breakpPlot = Chart('Fundamentals') breakpPlot.AddSeries(Series('Test-Asset-PB', SeriesType.Line)) breakpPlot.AddSeries(Series('Breakpoint-Min', SeriesType.Line)) breakpPlot.AddSeries(Series('Breakpoint-Max', SeriesType.Line)) self.AddChart(breakpPlot) stockPlot = Chart('Trade Plot') stockPlot.AddSeries(Series('Price', SeriesType.Line, '$', Color.Blue)) stockPlot.AddSeries(Series('Buy', SeriesType.Scatter, '$', Color.Green, ScatterMarkerSymbol.Triangle)) stockPlot.AddSeries(Series('Sell', SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.TriangleDown)) self.AddChart(stockPlot) def CoarseSelectionFunction(self, coarse): if self.flag1: self.filtered_coarse = [x.Symbol for x in coarse if (x.HasFundamentalData and x.AdjustedPrice > 5)] return self.filtered_coarse else: return [] def FineSelectionFunction(self, fine): if self.flag1: self.flag1 = 0 self.flag2 = 1 fine = [x for x in fine if (x.ValuationRatios.PBRatio > 0)] # Calculate the market cap and add the "MarketCap" property to fine universe object for i in fine: i.MarketCap = float(i.EarningReports.BasicAverageShares.ThreeMonths * (i.EarningReports.BasicEPS.TwelveMonths*i.ValuationRatios.PERatio)) # Syntax : sorted(iterable, key, reverse) --> reverse means from highest (expensive) to lowest (cheap) top_market_cap = sorted(fine, key = lambda x: x.MarketCap, reverse=True)[:int(len(fine)*0.2)] # Pick out top 20% mcap of all stock top_bm = sorted(top_market_cap, key = lambda x: 1 / x.ValuationRatios.PBRatio, reverse=True)[:int(len(top_market_cap)*0.2)] # From top 20% mcap pick out top 20% value stocks self.sorted_by_bm = [i.Symbol for i in top_bm] top_bm_ratios = [i.ValuationRatios.PBRatio for i in top_bm] current_universe_ratios = dict(zip(top_bm, top_bm_ratios)) current_min = min(current_universe_ratios.items(), key=lambda x: x[1]) current_max = max(current_universe_ratios.items(), key=lambda x: x[1]) # Save P/B Ratio cut-off breakpoint for plot self.breakpoint_min = current_min[1] self.breakpoint_max = current_max[1] # FOR TESTING if self.flag0: top_bm_mcaps = [i.MarketCap for i in top_bm] first_universe_mcaps = dict(zip(top_bm, top_bm_mcaps)) max_mcap = max(first_universe_mcaps.items(), key=lambda x: x[1]) self.test_asset_object = max_mcap[0] # this is a security object just like in top_bm self.test_asset_ticker = self.test_asset_object.SecurityReference.SecuritySymbol self.long_list = [self.test_asset_object.Symbol] self.test_asset_pb = self.test_asset_object.ValuationRatios.PBRatio # Add test-asset to our securities self.AddEquity(str(self.test_asset_ticker), Resolution.Daily) # Close this if-statement; never return again self.flag0 = 0 else: self.test_asset_pb = self.test_asset_object.ValuationRatios.PBRatio self.Debug('Current P/B Ratio: ' +str(self.test_asset_pb)) if self.test_asset_pb > self.breakpoint_max: self.long_list = list() else: long_set = set(self.long_list) long_set.add(self.test_asset_object.Symbol) self.long_list = list(long_set) self.flag3 += 1 return self.long_list else: return [] def OnData(self, data): if self.flag3 > 0: if self.flag2 == 1: self.flag2 = 0 if self.test_asset_ticker: self.test_lastPrice = self.ActiveSecurities[str(self.test_asset_ticker)].Price # if we have no changes, do nothing if self._changes == None: return # liquidate removed securities for security in self._changes.RemovedSecurities: if security.Invested: self.Liquidate(security.Symbol) self.Plot("Trade Plot", "Sell", self.test_lastPrice) for security in self._changes.AddedSecurities: self.SetHoldings(security.Symbol, 1) self.Plot("Trade Plot", "Buy", self.test_lastPrice) self._changes = None self.Plot('Fundamentals', 'Test-Asset P/B Ratio', self.test_asset_pb) self.Plot('Fundamentals', 'Breakpoint Min', self.breakpoint_min) self.Plot('Fundamentals', 'Breakpoint Max', self.breakpoint_max) # this event fires whenever we have changes to our universe def OnSecuritiesChanged(self, changes): self._changes = changes def Rebalancing(self): self.flag1 = 1

 

Author