非流动经营资产变化的影响
登录后收藏学术论文
Accrual Reliability, Earnings Persistence and Stock Prices
斯科特·A·理查德森
- ?理查德·G·斯隆,马克·T·索利曼和伊瑞姆·图纳,伦敦商学院
- ?阿卡迪安资产管理,南加州大学 - 莱文塔尔会计学院,南加州大学 - 马歇尔商学院,伦敦商学院。
这篇论文通过将应计项的可靠性与收益持续性联系起来,扩展了Sloan(1996)的研究。我们构建了一个模型,表明不太可靠的应计项会导致较低的收益持续性。接着,我们开发了一个综合性的资产负债表应计项分类,并根据应计项的可靠性对每个类别进行评级。实证测试普遍确认,不太可靠的应计项类别会导致较低的收益持续性,而投资者没有完全预见到这一点,导致证券的显著定价错误。我们得出结论,识别财务报表中不可靠信息的成本是显著的。
策略概要
该策略专注于在AMEX、NYSE和NASDAQ交易的非金融股票。每年,投资者计算非流动经营资产的年度变化,定义为总资产减去流动资产,再减去投资和预付款。根据这一变化,将股票按十等分排序。投资者对变化最负的股票(即最低增长或最大下降)做多,对变化最正的股票(即最大增长)做空。头寸等权重分配,投资组合每年重新平衡一次,旨在利用经营资产管理的变化。
策略合理性
∆ΝCOA的主要组成部分是固定资产(PP&E)和无形资产。这些应计项涉及相当大的不确定性。因此,拥有较低水平非流动经营资产且其变化为负的公司,应该会表现优于那些发生较大变化的公司。
回测表现
波动率7.26%
夏普比率1.67
索提诺比率-0.145
胜率52%
完整 Python 代码
from AlgorithmImports import *
class EffectofChangeinNonCurrentOperatingAssets(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100000)
market:Symbol = self.AddEquity('SPY', Resolution.Daily).Symbol
self.long:List[Symbol] = []
self.short:List[Symbol] = []
# Non-Current Operating Assets.
self.operating_assets:Dict[Symbol, float] = {}
self.quantile:int = 10
self.leverage:int = 5
self.rebalance_month:int = 12
self.min_share_price:float = 5.
self.exchange_codes:List[str] = ['NYS', 'NAS', 'ASE']
self.fundamental_count:int = 500
self.fundamental_sorting_key = lambda x: x.DollarVolume
self.selection_flag:bool = False
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.FundamentalSelectionFunction)
self.Settings.MinimumOrderMarginPortfolioPercentage = 0.
self.Schedule.On(self.DateRules.MonthEnd(market), self.TimeRules.AfterMarketOpen(market), self.Selection)
self.settings.daily_precise_end_time = False
def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
for security in changes.AddedSecurities:
security.SetFeeModel(CustomFeeModel())
security.SetLeverage(self.leverage)
def FundamentalSelectionFunction(self, fundamental: List[Fundamental]) -> List[Symbol]:
if not self.selection_flag:
return Universe.Unchanged
selected:List[Fundamental] = [
x for x in fundamental if x.HasFundamentalData and x.Price >= self.min_share_price and x.Market == 'usa' and x.SecurityReference.ExchangeId in self.exchange_codes and \
not np.isnan(x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths) and x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths != 0 and \
not np.isnan(x.FinancialStatements.BalanceSheet.CurrentAssets.TwelveMonths) and x.FinancialStatements.BalanceSheet.CurrentAssets.TwelveMonths != 0 and \
not np.isnan(x.FinancialStatements.BalanceSheet.InvestmentsAndAdvances.TwelveMonths) and x.FinancialStatements.BalanceSheet.InvestmentsAndAdvances.TwelveMonths != 0 and \
x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices
]
if len(selected) > self.fundamental_count:
selected = [x for x in sorted(selected, key=self.fundamental_sorting_key, reverse=True)[:self.fundamental_count]]
d_assets:Dict[Symbol, float] = {}
for stock in selected:
symbol:Symbol = stock.Symbol
if symbol not in self.operating_assets:
self.operating_assets[symbol] = -1.
assets:float = stock.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths - \
stock.FinancialStatements.BalanceSheet.CurrentAssets.TwelveMonths - \
stock.FinancialStatements.BalanceSheet.InvestmentsAndAdvances.TwelveMonths
if symbol in self.operating_assets and self.operating_assets[symbol] not in [-1, 0]:
d_assets[symbol] = assets / self.operating_assets[symbol] - 1
# Update assets value.
self.operating_assets[symbol] = assets
# NOTE: Get rid of old advertisment records so we work with latest values.
for symbol in self.operating_assets:
if symbol not in [x.Symbol for x in selected]:
self.operating_assets[symbol] = -1
if len(d_assets) >= self.quantile:
sorted_by_assets:List = sorted(d_assets.items(), key = lambda x: x[1], reverse = True)
quantile:int = int(len(sorted_by_assets) / self.quantile)
self.long = [x[0] for x in sorted_by_assets[-quantile:]]
self.short = [x[0] for x in sorted_by_assets[:quantile]]
return self.long + self.short
def OnData(self, data: Slice) -> None:
if not self.selection_flag:
return
self.selection_flag = False
# order execution
targets:List[PortfolioTarget] = []
for i, portfolio in enumerate([self.long, self.short]):
for symbol in portfolio:
if symbol in data and data[symbol]:
targets.append(PortfolioTarget(symbol, ((-1) ** i) / len(portfolio)))
self.SetHoldings(targets, True)
self.long.clear()
self.short.clear()
def Selection(self) -> None:
if self.Time.month == self.rebalance_month:
self.selection_flag = True
# Custom fee model.
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))