| Overall Statistics |
|
Total Trades 102 Average Win 0% Average Loss 0% Compounding Annual Return -20.366% Drawdown 38.400% Expectancy 0 Net Profit -20.366% Sharpe Ratio -0.842 Probabilistic Sharpe Ratio 1.649% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.141 Beta -0.097 Annual Standard Deviation 0.186 Annual Variance 0.035 Information Ratio -1.127 Tracking Error 0.285 Treynor Ratio 1.625 Total Fees $153.98 |
# 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 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.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