Overall Statistics |
Total Trades 90 Average Win 0% Average Loss 0% Compounding Annual Return -15.343% Drawdown 33.600% Expectancy 0 Net Profit -15.343% Sharpe Ratio -0.781 Probabilistic Sharpe Ratio 2.100% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.107 Beta -0.07 Annual Standard Deviation 0.152 Annual Variance 0.023 Information Ratio -1.088 Tracking Error 0.259 Treynor Ratio 1.701 Total Fees $0.00 |
# This backtest is designed to test Benjamin Graham's Net Current Asset Value strategy # Also commonly known as the Net Net strategy from Execution.ImmediateExecutionModel import ImmediateExecutionModel from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel class CustomFeeModel: def GetOrderFee(self, parameters): return OrderFee(CashAmount(0, 'USD')) class NetNet(QCAlgorithm): def Initialize(self): self.SetStartDate(1998, 4, 1) self.SetEndDate(1999, 3, 31) self.SetCash(100000) self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction, None, None)) self.UniverseSettings.Resolution = Resolution.Daily self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw)) self.SetSecurityInitializer(lambda x: x.SetFeeModel(CustomFeeModel())) self.SetAlpha(NetNetAlpha()) # By default the EqualWeightingPortfolioConstructionModel rebalances with the specified resolution (which is Resolution.Daily here). # To avoid this, just pass "lambda time: None" to your PortfolioConstructionModel and use: # self.Settings.RebalancePortfolioOnInsightChanges = True # self.Settings.RebalancePortfolioOnSecurityChanges = False # to make sure the algorithm will rebalance only when there were changes in your universe. self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: None)) self.Settings.RebalancePortfolioOnInsightChanges = True self.Settings.RebalancePortfolioOnSecurityChanges = False self.SetExecution(ImmediateExecutionModel()) # on 1 Apr, filter for securities with fundamental data def CoarseSelectionFunction(self, coarse): if self.Time.month == 4 and self.Time.day == 1: filtered = [ x.Symbol for x in coarse if x.HasFundamentalData ] return filtered else: return Universe.Unchanged # the two filters are for avoiding zero division errors def FineSelectionFunction(self, fine): # filter first for securities with positive NCAV and share count filtered = [ x for x in fine if ((x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths - x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths) > 0) and (x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths > 0) ] # then filter a second time for net net stocks filtered = [ x.Symbol for x in filtered if (x.Price / ((x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths - x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths) / x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths)) <= 0.66 ] return filtered class NetNetAlpha(AlphaModel): def Update(self, algorithm, data): insights = [] if not algorithm.Portfolio.Invested: for security in algorithm.ActiveSecurities.Values: insights.append(Insight.Price(security.Symbol, timedelta(days=1000), InsightDirection.Up)) return insights else: return insights