Portfolio Optimization and Quantitative Strategic Asset Allocation in Python
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Statistical methods for estimating expected returns, covariance matrices, factor models, and implementing Black-Litterman and other advanced estimation techniques for portfolio optimization input parameters.
Methods for estimating expected returns using various statistical approaches.
def mean_vector(X, method='hist', d=0.94, **kwargs):
"""
Calculate expected returns vector.
Parameters:
- X (DataFrame): Returns data
- method (str): Estimation method ('hist', 'ewma1', 'ewma2')
- d (float): Decay factor for EWMA methods
Returns:
DataFrame: Expected returns vector
"""Methods for estimating covariance matrices with various shrinkage and robust estimation techniques.
def covar_matrix(X, method='hist', d=0.94, **kwargs):
"""
Calculate covariance matrix.
Parameters:
- X (DataFrame): Returns data
- method (str): Estimation method ('hist', 'ewma1', 'ewma2', 'ledoit', 'oas',
'shrunk', 'gl', 'jlogo', 'fixed', 'spectral', 'shrink')
- d (float): Decay factor for EWMA methods
Returns:
DataFrame: Covariance matrix
"""
def cokurt_matrix(X, method='hist', **kwargs):
"""
Calculate cokurtosis matrix.
Parameters:
- X (DataFrame): Returns data
- method (str): Estimation method
Returns:
DataFrame: Cokurtosis matrix
"""Methods for factor model construction and factor loading estimation.
def loadings_matrix(X, Y, method='stepwise', p_value=0.05, **kwargs):
"""
Calculate factor loadings matrix using regression methods.
Parameters:
- X (DataFrame): Asset returns
- Y (DataFrame): Factor returns
- method (str): Regression method ('stepwise', 'PCR', 'forward', 'backward')
- p_value (float): P-value threshold for stepwise methods
Returns:
DataFrame: Factor loadings matrix
"""
def risk_factors(X, B=None, method="stepwise", p_value=0.05, **kwargs):
"""
Estimate risk factors from returns data.
Parameters:
- X (DataFrame): Asset returns
- B (DataFrame): Factor loadings (optional)
- method (str): Factor estimation method
- p_value (float): P-value threshold
Returns:
DataFrame: Risk factors
"""
def forward_regression(X, Y, criterion='pvalue', threshold=0.05, verbose=False):
"""
Forward stepwise regression for factor selection.
Parameters:
- X (DataFrame): Predictor variables (factors)
- Y (DataFrame): Response variable (returns)
- criterion (str): Selection criterion ('pvalue', 'AIC', 'BIC', 'R2')
- threshold (float): Threshold for inclusion
- verbose (bool): Print progress
Returns:
tuple: (selected_factors, regression_results)
"""
def backward_regression(X, Y, criterion='pvalue', threshold=0.05, verbose=False):
"""
Backward stepwise regression for factor selection.
Parameters:
- X (DataFrame): Predictor variables (factors)
- Y (DataFrame): Response variable (returns)
- criterion (str): Selection criterion ('pvalue', 'AIC', 'BIC', 'R2')
- threshold (float): Threshold for removal
- verbose (bool): Print progress
Returns:
tuple: (selected_factors, regression_results)
"""
def PCR(X, Y, n_components=0.95):
"""
Principal Component Regression.
Parameters:
- X (DataFrame): Predictor variables
- Y (DataFrame): Response variable
- n_components (float or int): Number of components or variance ratio
Returns:
tuple: (loadings, principal_components, explained_variance)
"""Implementation of Black-Litterman and augmented Black-Litterman models for incorporating views into portfolio optimization.
def black_litterman(X_hist, P, Q, delta=None, eq=True, rf=0, w=None, **kwargs):
"""
Black-Litterman model for expected returns.
Parameters:
- X_hist (DataFrame): Historical returns
- P (DataFrame): Picking matrix (views)
- Q (DataFrame): Views vector (expected excess returns)
- delta (float): Risk aversion parameter
- eq (bool): Use equilibrium returns as prior
- rf (float): Risk-free rate
- w (DataFrame): Market cap weights for equilibrium returns
Returns:
tuple: (expected_returns, covariance_matrix)
"""
def augmented_black_litterman(X_hist, P, Q, delta=None, eq=True, rf=0, w=None, **kwargs):
"""
Augmented Black-Litterman model with factor structure.
Parameters:
- X_hist (DataFrame): Historical returns
- P (DataFrame): Picking matrix
- Q (DataFrame): Views vector
- delta (float): Risk aversion parameter
- eq (bool): Use equilibrium returns
- rf (float): Risk-free rate
- w (DataFrame): Market weights
Returns:
tuple: (expected_returns, covariance_matrix, factor_loadings)
"""
def black_litterman_bayesian(X_hist, P, Q, Omega, tau=0.05, rf=0, w=None):
"""
Bayesian Black-Litterman model.
Parameters:
- X_hist (DataFrame): Historical returns
- P (DataFrame): Picking matrix
- Q (DataFrame): Views vector
- Omega (DataFrame): Uncertainty matrix of views
- tau (float): Scaling factor for prior uncertainty
- rf (float): Risk-free rate
- w (DataFrame): Market weights
Returns:
tuple: (posterior_returns, posterior_covariance)
"""Methods for bootstrapping and Monte Carlo simulation of portfolio parameters.
def bootstrapping(X, size=1000, n_sim=1000, replace=True, random_state=None):
"""
Bootstrap resampling of returns data.
Parameters:
- X (DataFrame): Returns data
- size (int): Sample size for each bootstrap
- n_sim (int): Number of bootstrap simulations
- replace (bool): Sample with replacement
- random_state (int): Random seed
Returns:
list: Bootstrap samples
"""
def normal_simulation(mu, cov, size=1000, n_sim=1000, random_state=None):
"""
Normal distribution simulation.
Parameters:
- mu (DataFrame): Mean returns
- cov (DataFrame): Covariance matrix
- size (int): Sample size per simulation
- n_sim (int): Number of simulations
- random_state (int): Random seed
Returns:
DataFrame: Simulated returns
"""import riskfolio as rp
import pandas as pd
# Load returns data
returns = pd.read_csv('returns.csv', index_col=0, parse_dates=True)
# Historical estimates
mu_hist = rp.mean_vector(returns, method='hist')
cov_hist = rp.covar_matrix(returns, method='hist')
# EWMA estimates with custom decay
mu_ewma = rp.mean_vector(returns, method='ewma1', d=0.94)
cov_ewma = rp.covar_matrix(returns, method='ewma1', d=0.94)
# Ledoit-Wolf shrinkage covariance
cov_ledoit = rp.covar_matrix(returns, method='ledoit')
print("Historical Mean Returns:")
print(mu_hist.head())
print("\nEWMA Covariance (5x5):")
print(cov_ewma.iloc[:5, :5])import riskfolio as rp
import pandas as pd
# Load asset and factor returns
returns = pd.read_csv('returns.csv', index_col=0, parse_dates=True)
factors = pd.read_csv('factors.csv', index_col=0, parse_dates=True) # e.g., Fama-French factors
# Estimate factor loadings using stepwise regression
loadings = rp.loadings_matrix(
X=returns,
Y=factors,
method='stepwise',
p_value=0.05
)
# Forward regression for factor selection
selected_factors, results = rp.forward_regression(
X=factors,
Y=returns['AAPL'], # Single asset example
criterion='pvalue',
threshold=0.05,
verbose=True
)
print("Factor Loadings:")
print(loadings.head())
print(f"\nSelected Factors for AAPL: {selected_factors}")import riskfolio as rp
import pandas as pd
import numpy as np
# Load returns and market cap data
returns = pd.read_csv('returns.csv', index_col=0, parse_dates=True)
market_caps = pd.read_csv('market_caps.csv', index_col=0)
# Create views: AAPL will outperform MSFT by 2% annually
P = pd.DataFrame(0, index=['View1'], columns=returns.columns)
P.loc['View1', 'AAPL'] = 1
P.loc['View1', 'MSFT'] = -1
Q = pd.DataFrame([0.02], index=['View1'], columns=['Views'])
# Market cap weights (equilibrium portfolio)
w_market = market_caps / market_caps.sum()
# Apply Black-Litterman
mu_bl, cov_bl = rp.black_litterman(
X_hist=returns,
P=P,
Q=Q,
delta=None, # Will be estimated
eq=True,
rf=0.02,
w=w_market
)
print("Black-Litterman Expected Returns:")
print(mu_bl.head())
# Use in portfolio optimization
port = rp.Portfolio(returns=returns)
port.mu = mu_bl
port.cov = cov_bl
w_bl = port.optimization(model='Classic', rm='MV', obj='Sharpe', rf=0.02)
print("\nBlack-Litterman Optimal Weights:")
print(w_bl.head())import riskfolio as rp
import pandas as pd
import numpy as np
# Load returns
returns = pd.read_csv('returns.csv', index_col=0, parse_dates=True)
# Bootstrap resampling
bootstrap_samples = rp.bootstrapping(
X=returns,
size=252, # One year of daily returns
n_sim=1000,
replace=True,
random_state=42
)
# Calculate bootstrap statistics
bootstrap_means = [sample.mean() for sample in bootstrap_samples]
bootstrap_vars = [sample.var() for sample in bootstrap_samples]
# Convert to DataFrame for analysis
bootstrap_stats = pd.DataFrame({
'Mean': bootstrap_means,
'Variance': bootstrap_vars
})
print("Bootstrap Statistics Summary:")
print(bootstrap_stats.describe())
# Monte Carlo simulation
mu = returns.mean()
cov = returns.cov()
simulated_returns = rp.normal_simulation(
mu=mu,
cov=cov,
size=252,
n_sim=1000,
random_state=42
)
print("\nSimulated Returns Shape:", simulated_returns.shape)
print("Simulated vs Historical Mean:")
print(pd.DataFrame({
'Historical': mu,
'Simulated': simulated_returns.mean()
}).head())Install with Tessl CLI
npx tessl i tessl/pypi-riskfolio-lib