In the earliest days of empirical work in academic finance, the size effect was the first market anomaly to challenge the standard asset pricing model and prompt debates about market efficiency. The notion that small stocks have higher average returns than large stocks, even after risk-adjustment, was a pathbreaking discovery, one that for decades has been taken as an unwavering fact of financial markets. In practice, the discovery of the size effect fueled a crowd of small cap indices and active funds to a point where the investment landscape is now segmented into large and small stock universes. Despite its long and illustrious history in academia and its commonplace acceptance in practice, there is still confusion and debate about the size effect. We examine many claims about the size effect and aim to clarify some of the misunderstanding surrounding it by performing simple tests using publicly available data.
Keywords: Size, Size effect, Size anomaly, Small Cap, Microcap, Market efficiency, Illiquidity, January effect
- Scope of investment: Stocks from NYSE, AMEX, and NASDAQ.
- Segment portfolios into five groups by market size.
- Select the bottom 20%, considering market capitalization.
- Hold for a 12-month period.
- Adjust the portfolio once every year.
import backtrader as bt import datetime class MarketCapStrategy(bt.Strategy): params = ( ('quintile', 5), ('rebalance_days', 252), ) def __init__(self): self.counter = 0 def next(self): self.counter += 1 if self.counter % self.params.rebalance_days == 0: self.rebalance_portfolio() def rebalance_portfolio(self): market_caps =  for data in self.datas: market_cap = data.close * data.volume market_caps.append((data, market_cap)) sorted_data = sorted(market_caps, key=lambda x: x) quintile_size = len(sorted_data) // self.params.quintile lowest_quintile = sorted_data[:quintile_size] # Calculate total market cap for lowest quintile total_market_cap = sum([x for x in lowest_quintile]) # Sell all current holdings for data in self.datas: self.order_target_percent(data, target=0) # Buy stocks in lowest quintile for data, market_cap in lowest_quintile: target_weight = market_cap / total_market_cap self.order_target_percent(data, target=target_weight) if __name__ == '__main__': cerebro = bt.Cerebro() # Add investment universe for ticker in ['NYSE_ticker1', 'NYSE_ticker2', 'AMEX_ticker1', 'NASDAQ_ticker1']: data = bt.feeds.YahooFinanceData( dataname=ticker, fromdate=datetime.datetime(2015, 1, 1), todate=datetime.datetime(2021, 9, 30), adjclose=True, plot=False, ) cerebro.adddata(data) cerebro.addstrategy(MarketCapStrategy) cerebro.broker.setcash(100000.0) cerebro.broker.setcommission(commission=0.001) print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.run() print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())