Introduction
Accrual anomaly is based upon the reasoning that it is important to measure if a company's earnings are based on real cash inflow or on revenue recognition from questionable accounting practices. Since companies with lower levels of accruals have more certain real earnings, they should earn higher market returns. This strategy will take a long position in low accrual companies and a short position in high accrual accompanies. It is important to note that this strategy requires fundamental data from the current and past year in order to perform its analysis. As a result, it won't be implementable in live trading at this time.
Method
The first step is coarse and fine universe selection. Using coarse selection, we create an investment universe with stocks that have fundamental data. The universes will be saved so that we can perform analysis on the annual changes of specific balance sheet data in the following year.
def coarse_selection_function(self, coarse):
if self.yearly_rebalance:
self.filtered_coarse = [x.symbol for x in coarse if (x.has_fundamental_data)
and (x.market == "usa")]
return self.filtered_coarse
else:
return []
def fine_selection_function(self, fine):
if self.yearly_rebalance:
fine = [x for x in fine if (float(x.financial_statements.balance_sheet.current_assets.value) > 0)
and (float(x.financial_statements.balance_sheet.cash_and_cash_equivalents.value) > 0)
and (float(x.financial_statements.balance_sheet.current_liabilities.value) > 0)
and (float(x.financial_statements.balance_sheet.current_debt.value) > 0)
and (float(x.financial_statements.balance_sheet.income_tax_payable.value) > 0)
and (float(x.financial_statements.income_statement.depreciation_and_amortization.value) > 0)]
if not self.previous_fine:
self.previous_fine = fine
self.yearly_rebalance = False
return []
else:
self.filtered_fine = self.calculate_accruals(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]
self.previous_fine = fine
return self.filtered_fine
else:
return []
During fine universe selection we take the stocks from the previous and current year and calculate their balance sheet based accrual values. The stocks are then sorted in ascending order based upon the implemented formula. Note that we account for size difference across the sample firms by scaling the accruals by the average of the beginning and end-of-year book value of total assets.
def calculate_accruals(self, current, previous):
accruals = []
for stock_data in current:
try:
prev_data = None
for x in previous:
if x.symbol == stock_data.symbol:
prev_data = x
break
delta_assets = float(stock_data.financial_statements.balance_sheet.current_assets.value)-float(prev_data.financial_statements.balance_sheet.current_assets.value)
delta_cash = float(stock_data.financial_statements.balance_sheet.cash_and_cash_equivalents.value)-float(prev_data.financial_statements.balance_sheet.cash_and_cash_equivalents.value)
delta_liabilities = float(stock_data.financial_statements.balance_sheet.current_liabilities.value)-float(prev_data.financial_statements.balance_sheet.current_liabilities.value)
delta_debt = float(stock_data.financial_statements.balance_sheet.current_debt.value)-float(prev_data.financial_statements.balance_sheet.current_debt.value)
delta_tax = float(stock_data.financial_statements.balance_sheet.income_tax_payable.value)-float(prev_data.financial_statements.balance_sheet.income_tax_payable.value)
dep = float(stock_data.financial_statements.income_statement.depreciation_and_amortization.value)
avg_total = (float(stock_data.financial_statements.balance_sheet.total_assets.value)+float(prev_data.financial_statements.balance_sheet.total_assets.value))/2
stock_data.bs_acc = ((delta_assets-delta_cash)-(delta_liabilities-delta_debt-delta_tax)-dep)/avg_total
accruals.append(stock_data)
except:
pass
return accruals
In OnData()
, we short top decile of the stocks in the sorted list and long the bottom decile of stocks. The portfolio is rebalanced every year at the start of June.
Derek Melchin
See the attached backtest for an updated version of the algorithm with the following changes:
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Gurumeher Sawhney
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!