
The strategy invests in firms with diverse management teams by going long on the top quintile and short on the bottom quintile, based on diversity scores calculated from executive data.
ASSET CLASS: stocks | REGION: United States | FREQUENCY:
Yearly | MARKET: equities | KEYWORD: Management, Diversity, Strategy
I. STRATEGY IN A NUTSHELL
This strategy trades NYSE, AMEX, and NASDAQ stocks by going long on firms with the most diverse management teams and short on firms with the most homogeneous teams. Stocks are equally weighted, held for one year, and the portfolio is rebalanced annually.
II. ECONOMIC RATIONALE
The anomaly arises from a combination of quality premiums and mispricing. Diverse management may signal higher firm quality, while firms with low analyst coverage are more prone to mispricing, allowing the strategy to capture alpha.
III. SOURCE PAPER
Diversity Investing [Click to Open PDF]
Alberto Manconi ,A. Emanuele Rizzo and Oliver Spalt.Bocconi University – Department of Finance; Centre for Economic Policy Research (CEPR).Nova School of Business and Economics.University of Mannheim – Business School; European Corporate Governance Institute (ECGI)
<Abstract>
Top management team diversity matters for stock returns. We develop a new text–based measure of team diversity and apply it to a sample of over 40,000 top executives in U.S. firms from 2001 to 2014. Buying firms with diverse teams and selling firms with homogenous teams — a strategy we call “diversity investing” — outperforms leading asset pricing anomalies over our sample period on a value-weighted basis. We examine a range of possible explanations and find strong evidence for the view that analysts and investors have downward-biased return expectations on firms with diverse teams, consistent with a mispricing explanation for diversity returns.


IV. BACKTEST PERFORMANCE
| Annualised Return | 6.55% |
| Volatility | 7.38% |
| Beta | 0.769 |
| Sharpe Ratio | 0.89 |
| Sortino Ratio | 0.236 |
| Maximum Drawdown | N/A |
| Win Rate | 77% |
V. FULL PYTHON CODE
from AlgorithmImports import *
from io import StringIO
from pandas.core.frame import dataframe
from typing import List, Dict
import pandas as pd
# endregion
class ManagementDiversityStrategy(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
self.market: Symbol = self.AddEquity('SPY', Resolution.Daily).Symbol
self.leverage: int = 3
self.selection_month: int = 6
self.short_allocation: float = -.0
self.weights: Dict[Symbol, float] = {}
# source: https://www.fair360.com/top-50-list/2023/
top_diversity_firms: str = self.Download('data.quantpedia.com/backtesting_data/economic/top50_diversity_firms.csv')
self.top_diversity_firms_df: dataframe = pd.read_csv(StringIO(top_diversity_firms), delimiter=';')
self.selection_flag: bool = False
self.settings.daily_precise_end_time = False
self.Settings.MinimumOrderMarginPortfolioPercentage = 0.
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.FundamentalSelectionFunction)
self.Schedule.On(self.DateRules.MonthStart(self.market), self.TimeRules.AfterMarketOpen(self.market), self.Selection)
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]:
# selection in the beginning of June
if not self.selection_flag:
return Universe.Unchanged
selected: Dict[str, Fundamental] = {
x.Symbol.Value: x for x in fundamental if x.HasFundamentalData and x.MarketCap != 0
}
long: List[Fundamental] = []
if str(self.Time.year) in list(self.top_diversity_firms_df.columns):
long = [selected[x] for x in self.top_diversity_firms_df[str(self.Time.year)].values if x in selected]
else:
self.Liquidate()
if len(long) != 0:
# calculate weights based on values
self.weights[self.market] = self.short_allocation
sum_long: float = sum([x.MarketCap for x in long])
for stock in long:
self.weights[stock.Symbol] = stock.MarketCap / sum_long
return list(self.weights.keys())
def OnData(self, data: Slice) -> None:
# yearly rebalance
if not self.selection_flag:
return
self.selection_flag = False
targets: List[PortfolioTarget] = [PortfolioTarget(symbol, weight) for symbol, weight in self.weights.items() if symbol in data and data[symbol]]
self.SetHoldings(targets, True)
self.weights.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: OrderFeeParameters) -> OrderFee:
fee: float = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
VI. Backtest Performance