Skip to content

Latest commit

 

History

History
114 lines (81 loc) · 4.11 KB

File metadata and controls

114 lines (81 loc) · 4.11 KB

Portfolio Management

Portfolio optimization, risk analysis, and performance analytics.

Optimizers

MeanVariance, HierarchicalRiskParity, RiskParity, and BlackLitterman share an optimize interface. Each takes expected returns as a pandas Series and a covariance matrix as a pandas DataFrame. Annualize both before you pass them in. Each call returns an OptimizationResult with weights, expected_return, volatility, sharpe_ratio, and a success flag.

from meridianalgo import MeanVariance, HierarchicalRiskParity, RiskParity, BlackLitterman

expected_returns = returns.mean() * 252
covariance = returns.cov() * 252

max_sharpe = MeanVariance().optimize(expected_returns, covariance, objective="max_sharpe")
min_vol = MeanVariance().optimize(expected_returns, covariance, objective="min_volatility")
hrp = HierarchicalRiskParity().optimize(expected_returns, covariance, returns_data=returns)
rp = RiskParity().optimize(expected_returns, covariance)
bl = BlackLitterman().optimize(expected_returns, covariance)

print(max_sharpe.weights.sort_values(ascending=False))
print(f"Max sharpe ratio {max_sharpe.sharpe_ratio:.4f}")
print(f"Min variance vol {min_vol.volatility:.4f}")

MeanVariance supports objective="max_sharpe" and objective="min_volatility". HierarchicalRiskParity accepts the raw return data through returns_data for its clustering step.

Kelly Criterion

from meridianalgo import KellyCriterion

kc = KellyCriterion(fraction=0.5)                       # half kelly
f = kc.single_asset(win_prob=0.55, win_loss_ratio=1.0)  # discrete binary bet
weights = kc.optimize(returns)                          # continuous multi asset
f_moments = kc.from_moments(expected_return=0.12, volatility=0.18)
g = kc.growth_rate(expected_return=0.12, volatility=0.18)

Risk Analysis

RiskAnalyzer, VaRCalculator, and CVaRCalculator share the same interface. Value at risk supports "historical", "parametric", "cornish_fisher", and "monte_carlo".

from meridianalgo import RiskAnalyzer

portfolio_returns = returns.mean(axis=1)
risk = RiskAnalyzer(portfolio_returns)

var_95 = risk.value_at_risk(confidence=0.95, method="historical")
var_99 = risk.value_at_risk(confidence=0.99, method="cornish_fisher")
cvar_95 = risk.conditional_var(confidence=0.95)

Stress Testing

import numpy as np
from meridianalgo import StressTesting

stress = StressTesting()
weights = np.repeat(1 / returns.shape[1], returns.shape[1])

# Smallest shock that produces a 10 percent loss
result = stress.reverse_stress_test(weights, returns, target_loss=-0.10)

For correlated, scenario based stress testing see ScenarioAnalyzer and CorrelationScenario.

from meridianalgo import CorrelationScenario

gen = CorrelationScenario(mean_returns, correlation_matrix, volatilities, weights)
scenarios = gen.generate(n_scenarios=100_000, stress_correlation=True, stress_factor=0.5)
print(f"Stressed 99 percent VaR {scenarios['var_99']:.2%}")

Performance Analytics

from meridianalgo import PerformanceAnalyzer

analyzer = PerformanceAnalyzer(portfolio_returns, risk_free_rate=0.05)
metrics = analyzer.calculate_all_metrics()

# Or the one call helper
import meridianalgo as ma
print(ma.tearsheet(portfolio_returns))

Benchmark relative

from meridianalgo import BenchmarkAnalytics, ActiveShare, BrinsonAttribution

analytics = BenchmarkAnalytics(
    portfolio_returns=portfolio_returns,
    benchmark_returns=benchmark_returns,
    risk_free_rate=0.05,
)
m = analytics.active_metrics()
print(f"Active return {m.active_return:.2%}, tracking error {m.tracking_error:.2%}")
print(f"Information ratio {m.information_ratio:.3f}, beta {m.beta:.3f}")

active_share = ActiveShare.compute(portfolio_weights, benchmark_weights)

attribution = BrinsonAttribution(
    portfolio_weights=sector_w_port, benchmark_weights=sector_w_bench,
    portfolio_returns=sector_r_port, benchmark_returns=sector_r_bench,
).compute()
print(f"Total active return {attribution.total_active_return:.4f}")

See the project README for end to end examples.