portfolio — Portfolio Optimisation

Portfolio construction and optimisation. In the SIP context, used for:

  • Index constituent weighting — risk parity or volume-weighted allocation across commodity grades

  • Cross-cluster capital allocation — HRP for allocating trading capital across multiple SIP index products

  • OTC book hedge optimisation — mean-variance for computing optimal hedge ratios

Key Functions

riskParity(covMatrix)

Risk parity weights: equalise risk contribution across assets. Robust to estimation error — preferred for sparse commodity return histories.

hierarchicalRiskParity(returns)

HRP (López de Prado 2016). Uses hierarchical clustering + inverse-variance weighting. Best for allocating across non-correlated SIP cluster products.

meanVariance(mu, covMatrix, targetReturn)

Mean-variance optimisation for a given target return (Markowitz frontier).

minVariance(covMatrix)

Minimum variance portfolio — no return estimate required.

equalWeight(n)

Equal weight across n assets.

maxDiversification(covMatrix)

Maximum diversification ratio portfolio.

tangency(mu, covMatrix, riskFreeRate=0.0) / maxSharpe(mu, covMatrix, riskFreeRate=0.0)

Maximum Sharpe ratio (tangency) portfolio.

efficientFrontier(mu, covMatrix, nPoints=50)

Efficient frontier computation — returns array of (return, vol, weights) tuples.

blackLitterman(mu, covMatrix, views, viewConfidence, tau=0.05)

Black-Litterman model incorporating analyst views on relative commodity returns.

minCvar(returns, alpha=0.05)

Minimum CVaR portfolio.

import sipQuant as sq
import numpy as np

# 5 SIP index products: AHI, SKS, MBO, BCF, and a new cluster
returns_matrix = np.random.normal(0.001, 0.02, (252, 5))  # 1 year daily
cov = np.cov(returns_matrix.T)

# Risk parity — preferred for commodity indices with sparse history
rp_weights = sq.portfolio.riskParity(cov)
print(f"Risk parity weights: {rp_weights.round(3)}")

# HRP — for allocating capital across uncorrelated clusters
hrp_weights = sq.portfolio.hierarchicalRiskParity(returns_matrix)
print(f"HRP weights: {hrp_weights.round(3)}")

# Equal weight as baseline
ew_weights = sq.portfolio.equalWeight(5)
print(f"Equal weights: {ew_weights}")