Overall Statistics
Total Trades
2388
Average Win
0.71%
Average Loss
-0.32%
Compounding Annual Return
25.608%
Drawdown
60.800%
Expectancy
1.341
Net Profit
12410.150%
Sharpe Ratio
1.024
Probabilistic Sharpe Ratio
32.039%
Loss Rate
28%
Win Rate
72%
Profit-Loss Ratio
2.25
Alpha
0.242
Beta
-0.071
Annual Standard Deviation
0.231
Annual Variance
0.054
Information Ratio
0.558
Tracking Error
0.299
Treynor Ratio
-3.363
Total Fees
$73801.59
from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm


class NCAVsimple(QCAlgorithm):
    def Initialize(self):
        #rebalancing should occur in July
        self.SetStartDate(2000, 1, 1)  
        self.SetCash(100000) 
        self.UniverseSettings.Resolution = Resolution.Daily

        self.filtered_fine = None
        self.filtered_coarse = None
        self.symbol = self.AddEquity('SPY', Resolution.Daily).Symbol

        self.coarse_count = 3000
        # self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
        
        self.yearly_rebalance = False
        self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
        self.Schedule.On(self.DateRules.MonthEnd(self.symbol), self.TimeRules.AfterMarketOpen(self.symbol), self.rebalance)
        
    def CoarseSelectionFunction(self, coarse):
        if self.yearly_rebalance:
            # drop stocks which have no fundamental data or have low price
            self.filtered_coarse = [x.Symbol for x in coarse if (x.HasFundamentalData) and x.Market == 'usa']
            return self.filtered_coarse
        else: 
            return Universe.Unchanged      
    
    def FineSelectionFunction(self, fine):
        if self.yearly_rebalance:
            # #calculate the NCAV/MV and add the property to fine universe object
            # #filters out the companies in the financial sector as suggested
            # fine = [x for x in fine if (float(x.FinancialStatements.BalanceSheet.CurrentAssets.Value) > 0) 
            #                         and (float(x.EarningReports.BasicAverageShares.Value) > 0)
            #                         and (float(x.FinancialStatements.BalanceSheet.CurrentLiabilities.Value) > 0)
            #                         and (float(x.MarketCap) > 0)
            #                         #  This indicator will denote which one of the six industry data collection templates applies to the company. 
            #                         # Each industry data collection template includes data elements that are commonly reported by companies in that industry. 
            #                         # N=Normal (Manufacturing), M=Mining, U=Utility, T=Transportation, B=Bank, I=Insurance 
            #                         and (x.CompanyReference.IndustryTemplateCode!="B")
            #                         and (x.CompanyReference.IndustryTemplateCode!="I")]
            # fine = sorted(fine, key = lambda x:x.MarketCap, reverse=True)
            # self.Debug("fine len: " + str(len(fine)))
            
            # for i in fine:
            #     #calculates the net current asset value per share
            #     self.Debug(i.Symbol)
            #     total_liabilities = i.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.TwelveMonths
            #     current_assets = i.FinancialStatements.BalanceSheet.CurrentAssets.TwelveMonths
            #     i.ncav = (current_assets - total_liabilities)/float(i.MarketCap)
            #     if i.ncav > 1.5:
            #         self.Debug("-total_liabilities: " + str(total_liabilities))
            #         self.Debug("-current_assets: " + str(current_assets))
            #         self.Debug("-market_cap: " + str(i.MarketCap))
            #         self.Debug("-ncav: " + str(i.ncav))
                            
            # # keeps all symbols that have a NCAV/MV higher than 1.5
            # # sorted_fine = sorted(fine, lambda x: x.ncav)
            # self.filtered_fine = [i.Symbol for i in fine if (i.ncav > 1.5)]
            fine = [x for x in fine if x.EarningReports.BasicAverageShares.ThreeMonths > 0 and x.MarketCap != 0 and x.ValuationRatios.WorkingCapitalPerShare != 0]
            sorted_by_market_cap = sorted(fine, key = lambda x:x.MarketCap, reverse=True)
            top_by_market_cap = [x for x in sorted_by_market_cap[:self.coarse_count]]
            
            # NCAV/MV calc.
            # self.filtered_fine = [x.Symbol for x in top_by_market_cap if ((x.ValuationRatios.WorkingCapitalPerShare * x.EarningReports.BasicAverageShares.ThreeMonths) / x.MarketCap) > 1.5]
        
            self.filtered_fine = [x.Symbol for x in top_by_market_cap if ((x.FinancialStatements.BalanceSheet.CurrentAssets.TwelveMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.TwelveMonths) / x.MarketCap) > 1.5]

            return self.filtered_fine
        else:
            return []
    
    def rebalance(self):
        #yearly rebalance
        if self.Time.month == 6:
            self.Debug("Rebalance" + str(self.Time))
            self.yearly_rebalance = True
        
    def OnData(self, data):
        if not self.yearly_rebalance: return 

        stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested]
        for symbol in stocks_invested:
            if symbol not in self.filtered_fine:
                self.Liquidate(symbol)

        for symbol in self.filtered_fine:
            if self.Securities[symbol].Price != 0 and self.Securities[symbol].IsTradable:  # Prevent error message.
                self.SetHoldings(symbol, 1 / len(self.filtered_fine))

        self.yearly_rebalance = False