Advanced Insights into Style and Factor Exposures for Portfolio Optimization
Analysis of Factor Investing
Factor investing focuses on identifying variables that significantly influence asset returns. These factors represent common traits in the returns that originate from external sources to the individual asset. Typically, long-term exposure to factor risk offers a reward, known as the risk premium.
Categories of Factors
- Macro Factors: These include broad economic elements like industrial growth and inflation.
- Statistical Factors: These are derived from data analysis and may or may not have clear identification.
- Style (Intrinsic) Factors: Examples include value-growth, momentum, and low-volatility.
For instance, oil prices are often seen as a critical macro factor in determining equity returns, as they can significantly impact stock performance. This external influence categorizes it as a macro factor.
Factor Models
Factor models break down asset returns into several components:
Here, coefficients, and factor premia, representing returns obtained in exchange for factor exposure.
Essentially, factor models deconstruct an asset's return into a combination of returns from various other assets.
The Capital Asset Pricing Model (CAPM)
CAPM is an application of factor models used for estimating the appropriate return of an asset for portfolio decisions.
The model considers the asset's sensitivity to market-wide, non-diversifiable risks, represented by beta
For individual security pricing, CAPM establishes the Security Market Line (SML), calculating the reward-to-risk ratio in comparison to the overall market:
This equation suggests that an asset's excess return depends on the market's excess return, scaled by the asset's sensitivity to the market
A higher strong correlation with market movements. Conversely, a low minimal correlation with the market.
The expected market return is generally computed as the arithmetic mean of historical market returns. CAPM, being a one-factor model, ties an asset's excess return solely to the market's excess return.
Price-to-Book (P/B) Ratio
The book value in accounting is an asset's value on the balance sheet, adjusted for depreciation, amortization, or impairment. The P/B ratio compares a company's market price to its book value, calculated either by dividing the market capitalization by the total book value or the current share price by the book value per share.
Also known as the market-to-book ratio, it's a critical metric in financial analysis.
Value Stocks vs. Growth Stocks
The inverse of the P/B ratio, the book-to-price (B/P) or book-to-market ratio, categorizes stocks into 'Value' and 'Growth' stocks.
- Value Stocks: High book-to-market ratios indicate undervaluation by the market.
- Growth Stocks: Low book-to-market ratios suggest overvaluation, often due to expectations of continuing above-average growth.
Growth stocks are typically linked with robust, successful companies with expected ongoing growth. They are often classified as such if their Return on Equity (ROE) is 15% or higher.
Fama-French Model
The Fama-French model is a three-factor model enhancing the CAPM (one-factor model). The three factors are:
- The market risk (i.e., as in the CAPM),
- The outperformance of small versus big companies,
- The outperformance of high book/market versus low book/market companies.
Fama and French took the entire universe of stocks and categorized them into ten buckets (deciles), sorting them in two ways:
- By size (market capitalization), comparing the performance of the bottom 10% of companies to the top 10% in terms of size.
- By book-to-market ratios (B/P ratio), comparing the bottom 10% companies (Growth Stocks) to the top 10% (Value Stocks).
They observed that the stock classes consistently outperforming the market were:
Small caps (bottom decile with respect to size) and
Value Stocks (top decile with respect to B/P ratios).
Consequently, they introduced the size factor and the value factor to the market factor of the CAPM, enhancing the model in 1993:
where:
is the CAPM's market-related , stands for Small (size) Minus Big (size) stocks, represents High (B/P ratio) Minus Low (B/P ratio) stocks.
The Carhart four-factor model further expands on the Fama-French model by adding the Momentum factor. Momentum in a stock is its tendency to continue its current price trend. This factor is computed by subtracting the equal weighted average of the lowest performing firms from that of the highest performing firms, lagged one month.
Factor Benchmark
Factor models can be reinterpreted as benchmarks. For instance, the single CAPM model can be reformulated as follows:
This formulation implies that with $1 dollar, one can borrow
If an asset in the market yields a return exceeding this calculation, it indicates the manager's value addition to that asset. Using this model, regression analysis determines the
Therefore, the factor benchmark is conceptualized as a short position of
Style Analysis
Returns-based style analysis, introduced by W. Sharpe (also a contributor to CAPM), can be viewed as a constrained form of a factor model. It has been applied to evaluating the performance of active managers.
Sharpe's approach involved using a factor-model-like structure with explanatory variables instead of actual factors:
where
Factor Analysis of Warren Buffet's Berkshire Hathaway
To analyze Berkshire Hathaway's performance, the dataset containing daily returns is utilized. These daily returns are compounded into monthly returns for the period from January 1990 to December 2018.
# Load monthly returns of Berkshire Hathaway
brka_rets = pok.get_brka_rets(monthly=True)
# Display the first few rows of the data
brka_rets.head()Next, the factors constituting the Fama-French model are loaded. These factors serve as explanatory variables.
# Load the Fama-French factors
fff = pok.get_fff_returns()
# Display the first few rows of the factors
fff.head()The columns represent the market return minus the risk-free rate, the Small Minus Big (Size) factor, the High Minus Low (Value) factor, and the pure risk-free rate, likely representing T-Bill returns.
A common analysis period, from January 1990 to May 2015, is selected. The first step involves factor analysis using the CAPM model to decompose Berkshire Hathaway's observed return into market-driven and other components.
# Calculate the excess return of Berkshire Hathaway over the risk-free rate
brka_excess_rets = brka_rets["1990":"2015-05"] - fff.loc["1990":"2015-05"][["RF"]].values
# Store the excess return of the market over the risk-free rate
mkt_excess_rets = fff.loc["1990":"2015-05"][["Mkt-RF"]]
# Copy market excess returns and add a constant for regression
factors = mkt_excess_rets.copy()
factors["alpha"] = 1
# Perform Ordinary Least Squares (OLS) regression
lm = sm.OLS(brka_excess_rets, factors).fit()
# Print summary of regression results
print(lm.summary())
# Display the regression coefficients
print("The coefficients of the regression are:")
print(lm.params)The regression results indicate a
The analysis then extends to the complete Fama-French model, incorporating additional factors:
# Copy market excess returns and add Size and Value factors
factors["Size"] = fff.loc["1990":"2015-05"][["SMB"]]
factors["Value"] = fff.loc["1990":"2015-05"][["HML"]]
factors["alpha"] = 1
# Perform OLS regression with the extended Fama-French model
lm_ff = sm.OLS(brka_excess_rets, factors).fit()
# Print summary of regression results
print(lm_ff.summary())With the Fama-French model,
This analysis translates each dollar invested in Berkshire Hathaway to approximately:
- $0.68 in the market and $0.32 in T-Bills,
- $0.39 in Value stocks and short $0.38 in Growth stocks,
- Short $0.48 in SmallCap stocks and long $0.50 in LargeCap stocks.
Despite these allocations, there's still an underperformance relative to Berkshire Hathaway by about 0.54% (54 basis points) per month.
The pok.linear_regression(dep_var, expl_vars, alpha=True) method in the toolkit can be used for linear regression.
Sharpe Style Analysis
Sharpe style analysis applies constraints on
To conduct Sharpe style analysis, a quadratic optimizer is used to find the weights that minimize the squared difference between the observed series and the returns of a benchmark portfolio containing the explanatory assets in corresponding weights. The objective is to minimize the tracking error between the two return series:
The tracking error is defined as:
Initially, industry returns are utilized:
# Retrieving industry returns from 2000 onwards
ind_rets = pok.get_ind_returns()["2000":]
# Displaying the first few rows of industry returns
print(ind_rets.head())An artificial manager return series is then constructed, assuming investments in specific industry proportions:
# Creating an artificial manager return series
mgr_rets = 0.3*ind_rets["Beer"] + 0.5*ind_rets["Smoke"] + 0.2*np.random.normal(loc=0.0, scale=0.15/np.sqrt(12), size=ind_rets.shape[0])
# Displaying the first few rows of the manager returns
print(mgr_rets.head())Without prior knowledge of this manager's investments, the goal is to deduce the investment pattern from observed returns using Sharpe style analysis:
# Running Sharpe style analysis to determine investment pattern
weights = pok.style_analysis(mgr_rets, ind_rets)
# Visualizing the weights in a bar chart
weights.sort_values(ascending=False).plot.bar(grid=True, figsize=(11,4))
plt.show()The results approximate the manager's actual investments, although some industries might appear falsely due to the synthetic nature of the return series.
A linear regression approach could also be applied, but it would lack the constraints of style analysis:
# Performing linear regression to compare with style analysis
betas = pok.linear_regression(mgr_rets, ind_rets).params
# Visualizing the beta coefficients in a bar chart
betas.sort_values(ascending=False).plot.bar(grid=True, figsize=(11,4))
plt.show()Linear regression might yield negative coefficients, which can be challenging to interpret, especially with real-life data.
Warning: Potential Misuse of Style Analysis
Style analysis is most effective when the explanatory indices accurately reflect the underlying strategy. It provides valuable insights when using broad market indices or ETFs. However, it will always produce a portfolio, no matter how unreasonable, and the reliability of the results can sometimes be questionable.
To analyze major industries that Buffet invested in since 2000:
# Retrieving Berkshire Hathaway's returns from 2000 onwards
brka_rets = pok.get_brka_rets(monthly=True)["2000":]
# Applying style analysis to Berkshire Hathaway's returns
weights = pok.style_analysis(brka_rets, ind_rets["2000":])
# Visualizing the weights in a bar chart
weights.sort_values(ascending=False).plot.bar(grid=True, figsize=(11,4))
plt.show()If only returns from 2009 are considered:
# Applying style analysis to Berkshire Hathaway's returns from 2009
weights = pok.style_analysis(brka_rets["2009":], ind_rets["2009":])
# Visualizing the weights in a bar chart
weights.sort_values(ascending=False).plot.bar(grid=True, figsize=(11,4))
plt.show()While the analysis can be informative, caution should be exercised in interpreting the results, especially when the specification might not be accurate.
Style Drift: Time Varying Exposures using Style Analysis
Sharpe style analysis can be used to detect style drift by analyzing style exposures over a rolling window of 1 to 5 years. This can reveal changes in a manager's investment approach.
Comparing Equally Weighted (EW) and Cap-Weighted (CW) Portfolios
Loading the Equally-Weighted (EW) and Value-Weighted (VW) versions of industry portfolio returns:
# Loading EW and VW industry portfolio returns
ind_rets_cw = pok.get_ind_file(filetype="rets", nind=30, ew=False)
ind_rets_ew = pok.get_ind_file(filetype="rets", nind=30, ew=True)It's important to note that "Value-Weighted" here refers to market capitalization weighting, not weighting by value stocks.
Comparing Sharpe ratios of these portfolios:
# Setting the risk-free rate for Sharpe ratio calculation
risk_free_rate = 0.03
# Calculating Sharpe ratios for both CW and EW portfolios
sr = pd.DataFrame({
"CW": pok.sharpe_ratio(ind_rets_cw["1945":], risk_free_rate, 12),
"EW": pok.sharpe_ratio(ind_rets_ew["1945":], risk_free_rate, 12)
})
# Calculating the percentage of times EW Sharpe ratio outperforms CW
pew_er = (sr["EW"] > sr["CW"]).sum()/sr.shape[0] * 100
# Visualizing Sharpe ratios in a bar chart
ax = sr.plot.bar(grid=True, figsize=(12,5))
ax.set_title("EW-SR higher than VW-SR: {:.1f}%".format(pew_er))
plt.show()Improving EW Schemes with CapWeight Tethering
Equally weighted portfolios often undergo modifications to improve tradeability or reduce tracking error relative to Cap-Weighted indices. Modifications may include limiting exposure to microcap stocks and ensuring that no stock's weight exceeds a certain multiple of its Cap-Weighted proportion.
nind = 49
ind_rets = pok.get_ind_file(filetype="rets", nind=nind)["1974":]
ind_mcap = pok.get_ind_market_caps(nind=nind, weights=True)["1974":]
ind_mcap.head()
# Setting the window for the backtest
window = 52
# Backtesting various weighting schemes
ew_rets = pok.backtest_weight_scheme(ind_rets, window=window, weight_scheme=pok.weight_ew)
ew_tr_rest = pok.backtest_weight_scheme(ind_rets, window=window, weight_scheme=pok.weight_ew, cap_ws=ind_mcap, max_cw_mult=5, microcap_thr=0.005)
cw_rets = pok.backtest_weight_scheme(ind_rets, window=window, weight_scheme=pok.weight_cw, cap_ws=ind_mcap)
# Combining the returns for comparison
bt_rets = pd.DataFrame({"EW": ew_rets, "EW-Tethered": ew_tr_rest, "CW": cw_rets})
# Calculating cumulative growth of each strategy
bt_growth = (1 + bt_rets).cumprod()
# Plotting the growth of strategies
bt_growth.plot(grid=True, figsize=(11,6), title="49 Industries - CapWeighted vs Equally Weighted vs Tethered EW")
plt.show()
# Displaying summary statistics
print(pok.summary_stats( bt_rets.dropna() ))