Overall Statistics
Total Trades
1131
Average Win
6.09%
Average Loss
-3.30%
Compounding Annual Return
16.683%
Drawdown
61.500%
Expectancy
0.494
Net Profit
3620.319%
Sharpe Ratio
0.625
Probabilistic Sharpe Ratio
0.783%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.85
Alpha
0.111
Beta
0.484
Annual Standard Deviation
0.226
Annual Variance
0.051
Information Ratio
0.344
Tracking Error
0.228
Treynor Ratio
0.292
Total Fees
$99957.51
Estimated Strategy Capacity
$47000.00
Lowest Capacity Asset
CIGC TTE756T8QPNP
#region imports
from AlgorithmImports import *
#endregion
class NetCurrentAssetValue(QCAlgorithm):

    def Initialize(self):
        #rebalancing should occur in July
        self.SetStartDate(1999,1,1)  #Set Start Date
        # self.SetEndDate(2018,7,15)    #Set End Date
        self.SetCash(1000000)           #Set Strategy Cash
        self.UniverseSettings.Resolution = Resolution.Daily
        self.previous_fine = None
        self.filtered_fine = None
        self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
        self.AddEquity("SPY", Resolution.Daily)
        #monthly scheduled event but will only rebalance once a year
        self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(23, 0), self.rebalance)
        self.months = -1
        self.yearly_rebalance = False
        
    def CoarseSelectionFunction(self, coarse):
        if self.yearly_rebalance:
            # drop stocks which have no fundamental data
            self.filtered_coarse = [x.Symbol for x in coarse if (x.HasFundamentalData)
                                                            and (x.Market == "usa")]
            return self.filtered_coarse
        else: 
            return []      
    
    def FineSelectionFunction(self, fine):
        if self.yearly_rebalance:
            #filters out the companies that don't contain the necessary data
            fine = [x for x in fine if (float(x.FinancialStatements.BalanceSheet.CurrentAssets.Value) > 0) 
                                    and (float(x.FinancialStatements.BalanceSheet.CashAndCashEquivalents.Value) > 0)
                                   and (float(x.FinancialStatements.BalanceSheet.CurrentLiabilities.Value) > 0)
                                    and (float(x.FinancialStatements.BalanceSheet.CurrentDebt.Value) > 0)
                                    and (float(x.FinancialStatements.BalanceSheet.IncomeTaxPayable.Value) > 0)
                                    and (float(x.FinancialStatements.IncomeStatement.DepreciationAndAmortization.Value) > 0)]
            
            if not self.previous_fine:
                #will wait one year in order to have the historical fundamental data
                self.previous_fine = fine
                self.yearly_rebalance = False
                return []
            else:
                #will calculate and sort the balance sheet accruals
                self.filtered_fine = self.CalculateAccruals(fine,self.previous_fine)
                sorted_filter = sorted(self.filtered_fine, key=lambda x: x.bs_acc)
                self.filtered_fine = [i.Symbol for i in sorted_filter]
                #we save the fine data for the next year's analysis
                self.previous_fine = fine
                return self.filtered_fine
        else:
            return []
    
    def CalculateAccruals(self, current, previous):
        accruals = []
        for stock_data in current:
            #compares this and last year's fine fundamental objects
            try:
                prev_data = None
                for x in previous:
                    if x.Symbol == stock_data.Symbol:
                        prev_data = x
                        break
                
                #calculates the balance sheet accruals and adds the property to the fine fundamental object
                delta_assets = float(stock_data.FinancialStatements.BalanceSheet.CurrentAssets.Value)-float(prev_data.FinancialStatements.BalanceSheet.CurrentAssets.Value)
                delta_cash = float(stock_data.FinancialStatements.BalanceSheet.CashAndCashEquivalents.Value)-float(prev_data.FinancialStatements.BalanceSheet.CashAndCashEquivalents.Value)
                delta_liabilities = float(stock_data.FinancialStatements.BalanceSheet.CurrentLiabilities.Value)-float(prev_data.FinancialStatements.BalanceSheet.CurrentLiabilities.Value)
                delta_debt = float(stock_data.FinancialStatements.BalanceSheet.CurrentDebt.Value)-float(prev_data.FinancialStatements.BalanceSheet.CurrentDebt.Value)
                delta_tax = float(stock_data.FinancialStatements.BalanceSheet.IncomeTaxPayable.Value)-float(prev_data.FinancialStatements.BalanceSheet.IncomeTaxPayable.Value)
                dep = float(stock_data.FinancialStatements.IncomeStatement.DepreciationAndAmortization.Value)
                avg_total = (float(stock_data.FinancialStatements.BalanceSheet.TotalAssets.Value)+float(prev_data.FinancialStatements.BalanceSheet.TotalAssets.Value))/2
                #accounts for the size difference
                stock_data.bs_acc = ((delta_assets-delta_cash)-(delta_liabilities-delta_debt-delta_tax)-dep)/avg_total
                accruals.append(stock_data)
            except:
                #value in current universe does not exist in the previous universe
                pass
        return accruals
    
    def rebalance(self):
        #yearly rebalance
        self.months+=1
        if self.months%12 == 0:
            self.yearly_rebalance = True

    def OnData(self, data):
        if not self.yearly_rebalance: return 
        if self.filtered_fine:
            portfolio_size = int(len(self.filtered_fine)/10)
            #pick the upper decile to short and the lower decile to long
            short_stocks = self.filtered_fine[-portfolio_size:]
            long_stocks = self.filtered_fine[:portfolio_size]
            stocks_invested = [x.Key for x in self.Portfolio]
            for i in stocks_invested:
                #liquidate the stocks not in the filtered balance sheet accrual list
                if i not in self.filtered_fine:
                    self.Liquidate(i) 
                #long the stocks in the list
                elif i in long_stocks:
                    self.SetHoldings(i, 1/(portfolio_size*2))
                #short the stocks in the list
                elif i in short_stocks:
                    self.SetHoldings(i,-1/(portfolio_size*2))
            self.yearly_rebalance = False
            self.filtered_fine = False