Original paper
Abstract
It is widely recognized that value strategies - those that invest in stocks with low market values relative to measures of their fundamentals (e.g. low prices relative to earnings, dividends, book assets and cash flows) - tend to show higher returns. In this paper we focus on the early value metric devised and employed by Benjamin Graham - net current asset value to market value (NCAV/MV) - to see if it is still useful in the modern context. Examining stocks listed on the London Stock Exchange for the period 1981 to 2005 we observe that those with an NCAV/MV greater than 1.5 display significantly positive market-adjusted returns (annualized return up to 19.7% per year) over five holding years. We allow for the possibility that the phenomenon being observed is due to the additional return experienced on smaller companies (the "size effect") and still find an NCAV/MV premium. The profitability of this NCAV/MV strategy in the UK cannot be explained using Capital Asset Pricing Model (CAPM). Further, Fama and French's three-factor model (FF3M) can not explain the abnormal return of the NCAV/MV strategy. These premiums might be due to irrational pricing.
Keywords: Market efficiency; Net asset value; Benjamin Graham
Trading rules
- Investment universe: All stocks on London Exchange (excluding multiple-class ordinary shares, foreign companies, lightly regulated markets, and financial sector stocks)
- Portfolio formation: Annually in July
- Inclusion criteria: NCAV/MV ratio higher than 1.5
- Portfolio type: Buy-and-hold
- Holding period: One year
- Stock weighting: Equally weighted
Python code
Backtrader
import backtrader as bt
import datetime
class NCAVStrategy(bt.Strategy):
def __init__(self):
self.ncav_ratio_threshold = 1.5
def next(self):
if self.datetime.month[0] == 7 and self.datetime.day[0] == 1:
self.rebalance_portfolio()
def rebalance_portfolio(self):
self.sell_all_stocks()
selected_stocks = self.select_stocks()
if not selected_stocks:
return
weight = 1.0 / len(selected_stocks)
for stock in selected_stocks:
self.buy(stock, target=weight)
def sell_all_stocks(self):
for position in self.positions:
if position.is_long:
self.sell(position.data)
def select_stocks(self):
ncav_stocks = []
for stock in self.getdatanames():
data = self.getdatabyname(stock)
ncav = data.ncav[0]
mv = data.close[0]
if mv == 0:
continue
ncav_mv_ratio = ncav / mv
if ncav_mv_ratio > self.ncav_ratio_threshold:
ncav_stocks.append(data)
return ncav_stocks
if __name__ == '__main__':
cerebro = bt.Cerebro()
cerebro.addstrategy(NCAVStrategy)
# Load data and add to cerebro
# ...
cerebro.run()
Note that this code assumes that the NCAV data is available for each stock in the dataset. You’ll need to load the stock data and NCAV data for each stock on the London Exchange and add them to the Cerebro instance.