epo

Lifecycle: experimental R-CMD-check Codecov test coverage CRAN status

The Enhanced Portfolio Optimization (EPO) method, described in Pedersen, Babu and Levine (2021), proposes a unifying theory on portfolio optimization. Employing Principal Component Analysis (PCA), the EPO method ranks portfolios based on their variance, from the most to the least important principal components. Notably, the least important principal components emerge as “problem portfolios”, primarily due to their low estimated risk, leading to the underestimation of their true risks. These portfolios offer high expected returns (ex-ante) and low realized Sharpe Ratios (ex-post), underscoring the challenges faced when using them through standard approaches.

To fix this issue, EPO introduces a straightforward yet powerful strategy: it shrinks correlations! The key insight from Pedersen, Babu, and Levine (2021) is that by reducing correlations close to zero, the volatilities of these “problem portfolios” are effectively increased. Consequently, the EPO method stabilizes Mean-Variance Optimization (MVO) by adjusting downward the Sharpe-Ratios of the least important components.

The elegance of the EPO approach lies in its connection to three leading methods: MVO, Bayesian Optimization, and Robust Optimization. By incorporating a closed-form solution with a single shrinkage parameter, denoted as \(w \in \{0, 1\}\), the investor can seamlessly navigate through the optimization process. In the “Simple EPO”, a \(w=0\) coincides with the classical MVO. Conversely, a \(w=1\) completely disregards correlations, resulting in a portfolio allocation that do not optimize.

In real-world applications, it is crucial to consider the potential deviation from a reference point or benchmark. EPO effectively handles this concern through the “Anchored EPO”. When a anchor needs to be considered, a \(w=0\) aligns with the classical MVO, while a \(w=1\) precisely matches the benchmark. The most interesting outcome arise when \(0 < w < 1\), leading to portfolios resembling the Black-Litterman model. Here, the shrinking parameter, \(w\), tunes the confidence in the prior, offering a flexible and dynamic optimization process. However, unlike Black-Litterman, the “Anchored EPO” does not restrict the reference point to the “Market Portfolio,” making it more general and widely applicable.

Overall, the Enhanced Portfolio Optimization (EPO) method presents a novel, efficient, and adaptable framework for portfolio optimization. Its ability to address the limitations of traditional methods while incorporating various optimization approaches through a single parameter makes it a compelling tool for investors seeking more stable and well-tailored portfolios.

Installation

Install the official version from CRAN with:

install.packages("epo")

Install the development version from github with:

# install.packages("devtools")
devtools::install_github("Reckziegel/epo")

Example

library(epo)

x <- diff(log(EuStockMarkets)) # stock returns
s <- colMeans(x) # it could be any signal 

##################
### The Simple EPO
##################

# Traditional Mean-Variance Analysis
epo(x = x, signal = s, lambda = 10, method = "simple", w = 0)
#> [1]  0.1914569  0.9894828 -0.3681779  0.1872382

# 100% Shrinkage
epo(x = x, signal = s, lambda = 10, method = "simple", w = 1)
#> [1] 0.2352863 0.3659986 0.1375249 0.2611902

# 50% Classical MVO and 50% Shrinkage
epo(x = x, signal = s, lambda = 10, method = "simple", w = 0.5)
#> [1]  0.223281853  0.564005906 -0.009868083  0.222580324

####################
### The Anchored EPO 
####################

benchmark <- rep(0.25, 4) # 1/N Portfolio

# Traditional Mean-Variance Analysis
epo(x = x, signal = s, lambda = 10, method = "anchored", w = 0.0, anchor = benchmark)
#> [1]  0.1914569  0.9894828 -0.3681779  0.1872382

# 100% on the Anchor portfolio
epo(x = x, signal = s, lambda = 10, method = "anchored", w = 1.0, anchor = benchmark)
#> [1] 0.25 0.25 0.25 0.25

# 50% on Mean-Variance Analysis and 50% on the Anchor Portfolio
epo(x = x, signal = s, lambda = 10, method = "anchored", w = 0.5, anchor = benchmark)
#> [1] 0.2374674 0.4557503 0.1004711 0.2063111

References