# Original paper

**Abstract**

I replicate 102 return signals across 48 countries and find a consistently low success rate under

modern empirical standards. A simple anomaly composite (AC) aggregating similar signals (e.g., B/M and E/P) significantly improves the return predictability of major effects (e.g., value) and shows that their performance highly varies across emerging and developed markets. ACs also challenge single signals on their cross-country evidence and suggest that 1) value premium is best explained by investor overextrapolation, 2) investment effect is driven by mispricing rather than optimal investment, and 3) profitability effect likely reflects both optimal investment and investor sentiment.

**Keywords:**Â empirical asset pricing, anomalies, international markets, market efficiency, mispricing, limits-to-arbitrage

# Trading rules

- Target investment stocks: Focus on companies listed on NYSE, Amex, and NASDAQ.
- Stock categorization:
- Segment stocks into five distinct size categories, from smallest to largest, based on NYSE market capitalization cutoffs every June end.
- Independently divide stocks into five investment levels, from minimal to maximal, following NYSE benchmarks.
- Cross-match the two classifications, resulting in 25 combined Size-Investment groups.
- Investment metric computation:
- For portfolios formed in June of year t, calculate Inv as the growth of total assets for the fiscal year ending in t-1 divided by total assets at the end of t-1
- Adopting a bullish position:
- Choose the group reflecting the maximum Size but the minimum Investment value.
- Adopting a bearish position:
- Choose the group indicating the maximum Size and also the maximum Investment value.
- Portfolio rebalancing:
- Use a value-weighted approach for portfolio distribution.

# Python code

## Backtrader

```
import backtrader as bt
class SizeInvStrategy(bt.Strategy):
def __init__(self):
self.market_caps = {}
self.total_assets = {}
self.investments = {}
def next(self):
# Check if June
if self.datetime.date().month != 6:
return
# Allocate stocks to Size and Investment groups
self.market_caps.clear()
self.total_assets.clear()
self.investments.clear()
for data in self.datas:
symbol = data._name
self.market_caps[symbol] = data.market_cap[0]
self.total_assets[symbol] = data.total_assets[0]
self.investments[symbol] = (data.total_assets[0] - data.total_assets[-252]) / data.total_assets[-252]
size_sorted = sorted(self.market_caps.items(), key=lambda x: x[1])
inv_sorted = sorted(self.investments.items(), key=lambda x: x[1])
size_groups = [size_sorted[i::5] for i in range(5)]
inv_groups = [inv_sorted[i::5] for i in range(5)]
# Find long and short positions
long_position = size_groups[-1][0]
short_position = size_groups[-1][-1]
# Close existing positions
for data in self.datas:
if self.getposition(data).size > 0:
self.sell(data)
elif self.getposition(data).size < 0:
self.buy(data)
# Open new long and short positions
self.buy(data=self.getdatabyname(long_position), target=0.5)
self.sell(data=self.getdatabyname(short_position), target=0.5)
# Load data, set up cerebro, and run the strategy
cerebro = bt.Cerebro()
# Add your data feed here using the 'cerebro.adddata()' method
cerebro.addstrategy(SizeInvStrategy)
results = cerebro.run()
```

Please note that this code assumes the availability of custom data fields like market_cap and total_assets in the data feed. You would need to create a custom data feed class in Backtrader that includes these fields for the code to work correctly.