Quant BuffetRelax, Not Over Thinking

Productivity of Cash Effect in the Stock Market

Log in to collect

Academic paper

Strategy in a nutshell

: Annual U.S. Equity Cash Productivity Decile Rotation Strategy

This annual strategy invests in U.S. stocks by ranking them on “cash productivity”: (Market Value − Total Physical Assets) ÷ (Cash + Short-Term Investments). It goes long on the lowest productivity decile and shorts the highest, equally weighted and rebalanced yearly after financial data release.

Economic rationale

Firms with low cash productivity face higher equity costs, offering higher expected returns. High-productivity firms are priced with a premium, reducing future performance. Exploiting this risk-return imbalance allows the strategy to capture mispricings based on cash efficiency.

Backtest performance

Annualised return13%
Volatility11%
Beta-0.159
Sharpe ratio1
Sortino ratio0.106
Win rate58%

Full Python code

from AlgorithmImports import *
from typing import List, Dict
from numpy import isnan
class ProductivityCashEffectStockMarket(QCAlgorithm):
def Initialize(self)  -> None:
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
market:Symbol = self.AddEquity('SPY', Resolution.Daily).Symbol

self.exchange_codes:List[str] = ['NYS', 'NAS', 'ASE']
self.fundamental_count:int = 3000
self.fundamental_sorting_key = lambda x: x.MarketCap
self.quantile:int = 10
self.leverage:int = 10
self.min_share_price:int = 5

self.long:List[Symbol] = []
self.short:List[Symbol] = []

self.selection_month:int = 4
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.SetLeverage(self.leverage)
    security.SetFeeModel(CustomFeeModel())
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.Market == 'usa' and x.Price > self.min_share_price and not \
    isnan(x.FinancialStatements.BalanceSheet.TotalDebt.TwelveMonths) and x.FinancialStatements.BalanceSheet.TotalDebt.TwelveMonths != 0 and not \
    isnan(x.FinancialStatements.BalanceSheet.TotalEquity.TwelveMonths) and x.FinancialStatements.BalanceSheet.TotalEquity.TwelveMonths != 0 and not \
    isnan(x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths) and x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths != 0 and not \
    isnan(x.FinancialStatements.BalanceSheet.CashAndCashEquivalents.TwelveMonths) and x.FinancialStatements.BalanceSheet.CashAndCashEquivalents.TwelveMonths != 0 and
    x.SecurityReference.ExchangeId in self.exchange_codes
]

if len(selected) > self.fundamental_count:
    selected = [x for x in sorted(selected, key=self.fundamental_sorting_key, reverse=True)[:self.fundamental_count]]

if len(selected) >= self.quantile:
    # Sorting by productivity of cash.    
    sorted_by_poc:List[Fundamental] = sorted(selected, key = lambda x:((x.FinancialStatements.BalanceSheet.TotalDebt.TwelveMonths + x.FinancialStatements.BalanceSheet.TotalEquity.TwelveMonths - \
                                                                        x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths) / \
                                                                        x.FinancialStatements.BalanceSheet.CashAndCashEquivalents.TwelveMonths), reverse=True)
    quantile = int(len(sorted_by_poc) / self.quantile)
    self.long = [x.Symbol for x in sorted_by_poc[-quantile:]]
    self.short = [x.Symbol for x in sorted_by_poc[:quantile]]
return self.long + self.short

def OnData(self, data: Slice) -> None:
if not self.selection_flag:
    return
self.selection_flag = False
# Trade 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.selection_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"))