distributions — Distribution Fitting

Parametric distribution fitting, goodness-of-fit testing, and divergence measures for commodity return distributions. Used to select the best marginal distribution before copula fitting, and to characterise tail behaviour.

Key Functions

fitNormal(data) / fitLognormal(data) / fitExponential(data)

Fit named parametric distributions by MLE. Return {params, logLikelihood, aic, bic}.

fitGamma(data) / fitBeta(data) / fitT(data)

Additional parametric families. Gamma is useful for convenience yields. Student-t captures fat tails common in commodity returns.

fitMixture(data, nComponents=2)

Gaussian mixture model fitted by EM algorithm. Use for bimodal return distributions (e.g. Cluster R under two regulatory regimes).

moments(data)

Returns {mean, variance, skewness, kurtosis, excessKurtosis}.

ksTest(data, distribution, params)

Kolmogorov-Smirnov goodness-of-fit test. Returns {ksStat, pValue, passed}.

adTest(data)

Anderson-Darling test for normality.

klDivergence(p, q)

Kullback-Leibler divergence D(p||q). Use to compare empirical vs fitted distributions or to detect regime changes by comparing rolling KL against a baseline.

jsDivergence(p, q)

Jensen-Shannon divergence (symmetric KL). Values near 0 = distributions are similar.

quantile(data, q) / qqPlot(data, distribution)

Quantile computation and Q-Q plot data for visual diagnostics.

fitDistributions(data)

Fits all available distributions and returns a list sorted by AIC. The primary tool for selecting the marginal distribution for a new cluster.

import sipQuant as sq
import numpy as np

log_returns = np.random.normal(0.001, 0.025, 260)

# Rank all distributions by AIC
fits = sq.distributions.fitDistributions(log_returns)
print("Distribution fits by AIC:")
for f in fits[:4]:
    print(f"  {f['distribution']:15s}  AIC={f['aic']:.2f}  BIC={f['bic']:.2f}")

# Return distribution moments
m = sq.distributions.moments(log_returns)
print(f"Skewness: {m['skewness']:.4f}  Excess kurtosis: {m['excessKurtosis']:.4f}")

# Fit Student-t for fat-tailed commodity returns
t_fit = sq.distributions.fitT(log_returns)
ks = sq.distributions.ksTest(log_returns, 't', t_fit['params'])
print(f"KS test p-value: {ks['pValue']:.4f}  (>0.05 = good fit)")

# KL divergence to detect regime change
baseline = log_returns[:130]
recent   = log_returns[130:]
p = np.histogram(baseline, bins=30, density=True)[0] + 1e-10
q = np.histogram(recent,   bins=30, density=True)[0] + 1e-10
kl = sq.distributions.klDivergence(p / p.sum(), q / q.sum())
print(f"KL divergence (regime change indicator): {kl:.4f}")