Finance

Contents

Finance#

Portfolio projection lives on the mixture itself (project()); there is no separate finance-layer wrapper.

Risk Measures#

Coherent risk measures for univariate normal-mixture portfolio returns.

The current implementation provides Conditional Value at Risk (CVaR), with value, first derivatives, and second derivatives in both the projected parameter space \((\tilde\mu, \tilde\gamma, \tilde\sigma)\) and the portfolio-weight space \(w \in \mathbb{R}^d\). Formulas follow docs/theory/cvar_derivatives.md.

Monte Carlo for CVaR value and derivatives is conditional over the subordinator \(Y\) (common random numbers). Deterministic VaR uses the Univariate* PINV ppf().

class normix.finance.risk.RiskMeasure[source]#

Bases: Module

Abstract base for portfolio risk measures.

abstractmethod value(univariate, Y)[source]#

Risk of the univariate normal mixture represented by univariate.

Parameters:
  • univariate (_UnivariateNormalMixtureMixin)

  • Y (Array)

Return type:

Array

abstractmethod value_reduced(mu, gamma, sigma, Y)[source]#

Risk from raw scalar parameters \((\tilde\mu, \tilde\gamma, \tilde\sigma)\).

The univariate normal mixture is \(\tilde\mu + \tilde\gamma Y + \tilde\sigma\sqrt{Y}Z\). Unlike value(), this signature takes plain scalars (no distribution object) and must be Bessel-/PINV-free so it can be jax.vmap()-ed across an efficient-surface grid that shares the subordinator draws Y.

Parameters:
Return type:

Array

value_grad_hess_w(model, w, Y)[source]#

Fused \((r(w), \nabla_w r, H_r(w))\) under one CRN sample Y.

Consumed by transaction_costs and diversification. Concrete measures override with an analytically fused implementation (CVaR shares one CMC VaR solve); the base raises — a value-only measure cannot participate in the derivative-consuming layers.

Parameters:
Return type:

tuple[Array, Array, Array]

class normix.finance.risk.CVaR(alpha)[source]#

Bases: RiskMeasure

Conditional Value at Risk at confidence \(\alpha \in (0, 1)\).

For a univariate normal mixture \(X = \tilde\mu + \tilde\gamma Y + \tilde\sigma \sqrt{Y} Z\), the value is computed in closed form conditional on \(Y\):

\[\operatorname{CVaR}_\alpha(X) = \frac{1}{\alpha} E_Y\!\left[ \tilde\sigma \sqrt{Y} \, \varphi(z_Y) - (\tilde\mu + \tilde\gamma Y) \Phi(z_Y) \right],\]

where \(z_Y = (x_\alpha - \tilde\mu - \tilde\gamma Y) / (\tilde\sigma \sqrt{Y})\) and \(x_\alpha = -\operatorname{VaR}_\alpha\) is found by bisection on the conditional Monte Carlo CDF (same Y as the integral, for common random numbers).

Parameters:

alpha (float)

alpha: float#
var(univariate)[source]#

\(\operatorname{VaR}_\alpha\) via deterministic PINV ppf().

Parameters:

univariate (_UnivariateNormalMixtureMixin)

Return type:

Array

value(univariate, Y)[source]#

Risk of the univariate normal mixture represented by univariate.

Parameters:
  • univariate (_UnivariateNormalMixtureMixin)

  • Y (Array)

Return type:

Array

value_reduced(mu, gamma, sigma, Y)[source]#

CVaR from raw scalar parameters, vectorizable over a surface grid.

Inverts the conditional-MC CDF for \(x_\alpha\) within an analytic bracket \(E[X] \pm 20\,\mathrm{std}[X]\) derived from the subordinator sample moments of Y — no PINV table, no Bessel evaluation — then reuses _cvar_from_quantile().

Parameters:
Return type:

Array

gradient_scalar(univariate, Y)[source]#

Return \((\partial r / \partial \tilde\mu, \partial r / \partial \tilde\gamma, \partial r / \partial \tilde\sigma)\).

Parameters:
  • univariate (_UnivariateNormalMixtureMixin)

  • Y (Array)

Return type:

Array

hessian_scalar(univariate, Y)[source]#

Return the \(3 \times 3\) Hessian in \((\tilde\mu, \tilde\gamma, \tilde\sigma)\).

\(\partial^2/\partial \tilde\mu \, \cdot = 0\) exactly; the other non-trivial blocks follow docs/theory/cvar_derivatives.md (4).

Parameters:
  • univariate (_UnivariateNormalMixtureMixin)

  • Y (Array)

Return type:

Array

value_w(model, w, Y)[source]#
Parameters:
Return type:

Array

gradient_w(model, w, Y)[source]#

Gradient \(\nabla_w r_{\operatorname{CVaR}_\alpha}(w)\).

Parameters:
Return type:

Array

hessian_w(model, w, Y)[source]#

Hessian \(H_{r_{\operatorname{CVaR}_\alpha}}(w)\).

Parameters:
Return type:

Array

value_grad_hess_w(model, w, Y)[source]#

Fused \((r, \nabla_w r, H_r)\) with one CMC VaR solve.

Equivalent to calling value_w(), gradient_w(), and hessian_w() separately, but projects once and bisects the conditional-MC CDF once.

Parameters:
Return type:

tuple[Array, Array, Array]

Mean-Risk Optimization#

Mean-risk portfolio optimization for normal-mixture models.

For a multivariate normal mixture \(X \stackrel{d}{=} \mu + \gamma Y + \sqrt{Y} Z\) with \(Z \sim \mathcal{N}(0, \Sigma)\), the mean-risk problem

\[\min_w \rho(w^\top X) \quad \text{s.t.} \quad w^\top e = 1, \quad E[w^\top X] \ge m\]

reduces — for any coherent risk measure \(\rho\) — to a two-dimensional problem in the reduced coordinates \(\tilde\mu = w^\top\mu\) and \(\tilde\gamma = w^\top\gamma\). The minimum-dispersion weights that realise a given \((\tilde\mu, \tilde\gamma)\) are

\[w^*(\tilde\mu, \tilde\gamma) = \Sigma^{-1}[\mu\;\gamma\;e]\,A^{-1} [\tilde\mu\;\tilde\gamma\;1]^\top, \qquad A = [\mu\;\gamma\;e]^\top \Sigma^{-1} [\mu\;\gamma\;e],\]

and the realised dispersion is \(g(\tilde\mu, \tilde\gamma) = [\tilde\mu\;\tilde\gamma\;1] A^{-1} [\tilde\mu\;\tilde\gamma\;1]^\top\). The map \((\tilde\mu, \tilde\gamma) \mapsto \rho\) is the efficient surface; its lower envelope under the return constraint is the efficient frontier.

See Mean-Risk Optimization for Normal Mixture Distributions for the derivation.

class normix.finance.optimization.EfficientSurface(mu_tilde, gamma_tilde, risk, expected_return)[source]#

Bases: Module

Efficient surface \((\tilde\mu, \tilde\gamma) \mapsto \rho\) on a grid.

risk[i, j] is the risk of the minimum-dispersion portfolio with reduced coordinates (mu_tilde[i], gamma_tilde[j]); expected_return[i, j] = mu_tilde[i] + gamma_tilde[j] * E[Y].

Parameters:
mu_tilde: Array#
gamma_tilde: Array#
risk: Array#
expected_return: Array#
class normix.finance.optimization.EfficientFrontier(expected_return, risk, mu_tilde, gamma_tilde, weights)[source]#

Bases: Module

Mean-risk efficient frontier: minimum risk per target expected return.

Each entry is the solution of the reduced problem on the constraint line \(\tilde\mu + \tilde\gamma E[Y] = m\), including the realised portfolio weights of shape (K, d).

Parameters:
expected_return: Array#
risk: Array#
mu_tilde: Array#
gamma_tilde: Array#
weights: Array#
class normix.finance.optimization.MeanRiskProblem(model, risk)[source]#

Bases: Module

Mean-risk optimization in reduced \((\tilde\mu, \tilde\gamma)\) coordinates.

Bundles a fitted NormalMixture and a RiskMeasure. All heavy evaluations share a fixed subordinator sample Y (common random numbers); draw it once via model.joint.subordinator().rvs(n, seed).

The Cholesky factors \(A\), \(A^{-1}\), and \(\Sigma^{-1}M\) are computed once at construction (w-independent).

Parameters:
model: NormalMixture#
risk: RiskMeasure#
A: Array#
A_inv: Array#
Sinv_M: Array#
E_Y()[source]#

\(E[Y]\) — subordinator mean.

Return type:

Array

weights(mu_tilde, gamma_tilde)[source]#

Minimum-dispersion weights realising \((\tilde\mu, \tilde\gamma)\).

Parameters:
Return type:

Array

dispersion(mu_tilde, gamma_tilde)[source]#

Realised dispersion \(g(\tilde\mu, \tilde\gamma) = w^{*\top}\Sigma w^*\).

Parameters:
Return type:

Array

expected_return(mu_tilde, gamma_tilde)[source]#

\(m = \tilde\mu + \tilde\gamma\,E[Y]\).

Parameters:
Return type:

Array

min_variance_point()[source]#

Reduced coordinates \((\tilde\mu, \tilde\gamma)\) of the global minimum-variance portfolio \(w = \Sigma^{-1}e / (e^\top\Sigma^{-1}e)\).

A convenient anchor for choosing efficient-surface grid ranges.

Return type:

tuple[Array, Array]

projection_at(mu_tilde, gamma_tilde)[source]#

Univariate portfolio return at \((\tilde\mu, \tilde\gamma)\).

Projects the minimum-dispersion weights(); the result is a Univariate* instance with location \(\tilde\mu\), skewness \(\tilde\gamma\), variance \(g(\tilde\mu, \tilde\gamma)\).

Parameters:
risk_at(mu_tilde, gamma_tilde, Y)[source]#

Efficient-surface risk at a single \((\tilde\mu, \tilde\gamma)\).

Parameters:
Return type:

Array

efficient_surface(mu_tilde, gamma_tilde, Y)[source]#

Evaluate the efficient surface over the grid mu_tilde × gamma_tilde.

mu_tilde and gamma_tilde are 1-D arrays; the returned risk has shape (len(mu_tilde), len(gamma_tilde)). Memory scales as len(mu_tilde) * len(gamma_tilde) * len(Y).

Parameters:
Return type:

EfficientSurface

efficient_frontier(returns, Y, gamma_bounds, n_iter=48)[source]#

Minimum risk for each target expected return in returns.

For every target \(m\), minimises the risk along the constraint line \(\tilde\mu = m - \tilde\gamma E[Y]\) over \(\tilde\gamma \in\) gamma_bounds by golden-section search (the surface is convex, so the restriction is unimodal).

Parameters:
Return type:

EfficientFrontier

Transaction Costs#

Local-quadratic transaction-cost portfolio rebalancing.

The mean-risk problem with an \(\ell_1\) turnover penalty

\[\max_w \; w^\top m - c_1\, r(w) - c_2 \|w - w_0\|_1 \quad\text{s.t.}\quad w^\top e = 1,\; A w \le b\]

cannot use the two-dimensional efficient-surface reduction, because the turnover term breaks translation invariance in the reduced coordinates. When costs keep the solution near the current portfolio \(w_0\), a second-order Taylor expansion of the convex risk \(r\) yields a convex quadratic program in buy/sell variables \(v = (v^+; v^-)\) with \(w = w_0 + v^+ - v^-\) (see Portfolio Optimization with Transaction Costs).

This module builds the QP matrices from any RiskMeasure that supplies gradient_w() / hessian_w(). Solving is optional and uses scipy.optimize (already a core dependency); heavier QP backends can consume the same matrices later.

class normix.finance.transaction_costs.QuadraticApproximation(w0, value, gradient, hessian)[source]#

Bases: Module

Local Taylor model of \(r\) at the current portfolio \(w_0\).

\[r(w) \approx r(w_0) + (w - w_0)^\top \nabla r(w_0) + \tfrac12 (w - w_0)^\top H_r(w_0)\,(w - w_0).\]
Parameters:
w0: Array#
value: Array#
gradient: Array#
hessian: Array#
class normix.finance.transaction_costs.TransactionCostQP(m_tilde, H_tilde, e_tilde, A_tilde, b_tilde, w0, c1, c2, approx, m)[source]#

Bases: Module

Buy/sell quadratic program for the local transaction-cost problem.

Maximises \(v^\top \tilde m - (c_1/2)\, v^\top \tilde H\, v\) over \(v \ge 0\) subject to \(v^\top \tilde e = 0\) and optional \(\tilde A v \le \tilde b\). Reconstruct \(w^* = w_0 + (I\;-I)\,v^*\).

Parameters:
m_tilde#

Linear term \(\tilde m \in \mathbb{R}^{2d}\).

Type:

jax.Array

H_tilde#

Block Hessian \(\tilde H\) (optionally Tikhonov-regularised).

Type:

jax.Array

e_tilde#

Budget dual vector \((e; -e)\).

Type:

jax.Array

A_tilde, b_tilde

Optional inequality block; None when only the budget constraint is active.

w0, c1, c2

Anchor portfolio and objective coefficients.

approx#

Underlying risk Taylor model (for objective bookkeeping).

Type:

normix.finance.transaction_costs.QuadraticApproximation

m#

Expected-return vector used to build \(\tilde m\).

Type:

jax.Array

m_tilde: Array#
H_tilde: Array#
e_tilde: Array#
A_tilde: Array | None#
b_tilde: Array | None#
w0: Array#
c1: Array#
c2: Array#
approx: QuadraticApproximation#
m: Array#
property d: int#
weights_from_v(v)[source]#

Map buy/sell variables to portfolio weights \(w_0 + v^+ - v^-\).

Parameters:

v (Array)

Return type:

Array

approx_objective(w)[source]#

Local quadratic objective (constants retained) at weights w.

Parameters:

w (Array)

Return type:

Array

hold_objective()[source]#

Objective of holding \(w_0\) (zero turnover).

Return type:

Array

class normix.finance.transaction_costs.TransactionCostResult(weights, v, turnover, approx_objective, hold_objective, improved, qp)[source]#

Bases: Module

Solution of the local transaction-cost QP.

improved is True when the approximate objective at weights exceeds the hold objective; otherwise the theory recommends keeping \(w_0\) (see the note in Portfolio Optimization with Transaction Costs).

Parameters:
weights: Array#
v: Array#
turnover: Array#
approx_objective: Array#
hold_objective: Array#
improved: Array#
qp: TransactionCostQP#
normix.finance.transaction_costs.build_quadratic_approximation(risk, model, w0, Y)[source]#

Evaluate \(r(w_0)\), \(\nabla r(w_0)\), and \(H_r(w_0)\).

Uses value_grad_hess_w() so the CMC VaR bisection runs once rather than three times.

Parameters:
Return type:

QuadraticApproximation

normix.finance.transaction_costs.build_transaction_cost_qp(approx, m, c1, c2, A=None, b=None, hess_reg=1e-06)[source]#

Assemble the buy/sell QP matrices from a risk Taylor model.

Parameters:
  • approx (QuadraticApproximation) – Local approximation of \(r\) at \(w_0\).

  • m (Array) – Expected-return vector (\(E[X]\) for a normal mixture).

  • c1 (float | Array) – Risk and turnover coefficients.

  • c2 (float | Array) – Risk and turnover coefficients.

  • A (Array | None) – Optional inequality \(A w \le b\) (same \(w_0\) must be feasible). Pass None for the budget-only problem.

  • b (Array | None) – Optional inequality \(A w \le b\) (same \(w_0\) must be feasible). Pass None for the budget-only problem.

  • hess_reg (float) – Tikhonov damping added to \(\tilde H\) so the QP is strictly convex (\(\tilde H\) has a nontrivial nullspace along \(v^+ = v^-\)).

Return type:

TransactionCostQP

normix.finance.transaction_costs.solve_transaction_cost_qp(qp, *, x0=None, options=None)[source]#

Solve the local QP with scipy.optimize.minimize (SLSQP).

Minimises \((c_1/2)\, v^\top \tilde H\, v - \tilde m^\top v\) subject to \(v \ge 0\), \(\tilde e^\top v = 0\), and optional \(\tilde A v \le \tilde b\). If the approximate objective does not beat holding \(w_0\), returns the hold portfolio.

Parameters:
Return type:

TransactionCostResult

class normix.finance.transaction_costs.TransactionCostProblem(model, risk, c1, c2)[source]#

Bases: Module

Local-quadratic transaction-cost rebalancing for a normal mixture.

Bundles a fitted NormalMixture and a RiskMeasure with coefficients \((c_1, c_2)\). The risk object is reused unchanged — only its weight-space gradient and Hessian at \(w_0\) enter the QP.

Parameters:
model: NormalMixture#
risk: RiskMeasure#
c1: float#
c2: float#
expected_return_vector()[source]#

Asset expected returns \(m = E[X] = \mu + \gamma\, E[Y]\).

Return type:

Array

approximate(w0, Y)[source]#

Local Taylor model of risk at w0 under subordinator sample Y.

Parameters:
Return type:

QuadraticApproximation

build_qp(w0, Y, *, m=None, A=None, b=None, hess_reg=1e-06)[source]#

Build the buy/sell QP at w0.

m defaults to expected_return_vector(). Optional A, b encode \(A w \le b\) (e.g. long-only via A = -I, b = 0).

Parameters:
Return type:

TransactionCostQP

solve(w0, Y, *, m=None, A=None, b=None, hess_reg=1e-06, options=None)[source]#

Build and solve the local QP; see solve_transaction_cost_qp().

Parameters:
Return type:

TransactionCostResult

true_objective_at(w, w0, Y, m=None)[source]#

Exact objective \(w^\top m - c_1 r(w) - c_2 \|w - w_0\|_1\).

Parameters:
Return type:

Array

Weight Functional#

Risk measures as JIT-able functions of portfolio weights.

WeightFunctional bundles a RiskMeasure, a multivariate normal-mixture model, and a fixed subordinator sample Y into a callable w -> with gradient and Hessian companions for optimisation.

class normix.finance.functional.WeightFunctional(risk, model, Y)[source]#

Bases: Module

Risk measure as a function of weights, with frozen model and Y.

Bundles a RiskMeasure, a NormalMixture model, and a realisation Y of the subordinator into JIT-able callables intended for mean-risk optimisation (Phase E).

Parameters:
risk: RiskMeasure#
model: NormalMixture#
Y: Array#
grad(w)[source]#
Parameters:

w (Array)

Return type:

Array

hess(w)[source]#
Parameters:

w (Array)

Return type:

Array

Diversification#

Diversification analytics: effective number of bets under a torsion.

Variance ENB diagonalizes the return covariance \(\mathrm{Cov}[X] = E[Y]\Sigma + \mathrm{Var}[Y]\,\gamma\gamma^\top\) (Effective Number of Bets and Minimum Torsion); generalized ENB diagonalizes the Hessian of the squared coherent risk \(H_{r^2} = 2\nabla r\,\nabla r^\top + 2 r H_r\) (Generalized Effective Number of Bets). Both reduce to one core: normalize \(d_k v_k^2\) over a torsion \(T H T^\top = \operatorname{diag}(d)\), \(v = (T^\top)^{-1} w\), and exponentiate the entropy.

Minimum torsion is a diagonalization strategy, not a diversification measure.

class normix.finance.diversification.TorsionDecomposition(T, T_inv_T, d, eigenvalues, valid)[source]#

Bases: Module

Diagonalization \(T H T^\top = \operatorname{diag}(d)\) of a PSD matrix.

T_inv_T is \((T^\top)^{-1}\), the map from portfolio weights \(w\) to torsion-adjusted weights \(v\). Both concrete torsions supply it in closed form (no solve at evaluation time). eigenvalues is the pre-clamp spectrum used for validity checks. valid is False when a material negative eigenvalue was present.

Parameters:
T: Array#
T_inv_T: Array#
d: Array#
eigenvalues: Array#
valid: Array#
class normix.finance.diversification.Torsion[source]#

Bases: Module

Strategy: how to diagonalize a PSD matrix into uncorrelated bets.

abstractmethod decompose(H)[source]#

Return \((T, (T^\top)^{-1}, d)\) with \(THT^\top = \operatorname{diag}(d)\).

Roundoff-scale negative eigenvalues are projected to zero; material indefiniteness sets valid=False (callers NaN the ENB).

Parameters:

H (Array)

Return type:

TorsionDecomposition

class normix.finance.diversification.MinimumTorsion[source]#

Bases: Torsion

Constrained minimum torsion (Meucci 2014): \(T = C^{-1/2}\operatorname{diag}(s)^{-1}\).

Closed form at \(D = I\). The iterative algorithm (unconstrained \(D\)) is deferred; it would land as a sibling IterativeMinimumTorsion without touching callers.

decompose(H)[source]#

Return \((T, (T^\top)^{-1}, d)\) with \(THT^\top = \operatorname{diag}(d)\).

Roundoff-scale negative eigenvalues are projected to zero; material indefiniteness sets valid=False (callers NaN the ENB).

Parameters:

H (Array)

Return type:

TorsionDecomposition

class normix.finance.diversification.PCATorsion[source]#

Bases: Torsion

Principal-components torsion (Meucci 2010): \(T = E^\top\), \(d = \Lambda\).

Rows ordered by descending eigenvalue. Eigenvector sign/order is unstable under near-ties — documented; MinimumTorsion is the default.

decompose(H)[source]#

Return \((T, (T^\top)^{-1}, d)\) with \(THT^\top = \operatorname{diag}(d)\).

Roundoff-scale negative eigenvalues are projected to zero; material indefiniteness sets valid=False (callers NaN the ENB).

Parameters:

H (Array)

Return type:

TorsionDecomposition

class normix.finance.diversification.ENBResult(enb, p, risk, v, d, T, eigenvalues)[source]#

Bases: Module

Effective number of bets and its decomposition at one portfolio.

p is normalized by \(\sum_k d_k v_k^2\) (a simplex up to floating point). risk is the 1-homogeneous risk whose square was diagonalized: portfolio volatility \(\sqrt{w^\top\Sigma_X w}\) for VarianceENB, \(\rho(w^\top X)\) for GeneralizedENB. enb is NaN when the local matrix is materially indefinite, the portfolio risk is non-positive, or the total contribution vanishes.

Parameters:
enb: Array#
p: Array#
risk: Array#
v: Array#
d: Array#
T: Array#
eigenvalues: Array#
class normix.finance.diversification.VarianceENB(torsion=MinimumTorsion())[source]#

Bases: Module

Variance-based effective number of bets (Effective Number of Bets and Minimum Torsion).

Diagonalizes the return covariance model.cov() \(= E[Y]\Sigma + \mathrm{Var}[Y]\,\gamma\gamma^\top\) — not the dispersion \(\Sigma\). Deterministic: no subordinator sample. Requires a finite Var[Y] (same moment caveat as skewness()).

Parameters:

torsion (Torsion)

torsion: Torsion = MinimumTorsion()#
evaluate(model, w)[source]#

ENB of \(w^\top X\) under the fitted model. Y-free by design.

vmap-able over a weight grid: jax.vmap(enb.evaluate, in_axes=(None, 0)).

Parameters:
Return type:

ENBResult

evaluate_covariance(Sigma_X, w)[source]#

ENB from an explicit covariance (empirical, factor-model, stress).

Precondition: Sigma_X PSD with nonzero diagonal, w != 0.

Parameters:
Return type:

ENBResult

class normix.finance.diversification.GeneralizedENB(risk, torsion=MinimumTorsion())[source]#

Bases: Module

ENB of a squared coherent risk measure (Generalized Effective Number of Bets).

For 1-homogeneous \(\rho\), diagonalizes \(H_{r^2}(w) = 2\nabla r\,\nabla r^\top + 2 r H_r\) where \((r, \nabla r, H_r)\) come from one call to risk.value_grad_hess_w(model, w, Y). Meaningful only for \(r(w) > 0\) (else \(r^2\) is not convex at w and enb is NaN).

Parameters:
risk: RiskMeasure#
torsion: Torsion = MinimumTorsion()#
evaluate(model, w, Y)[source]#

ENB at w under subordinator sample Y (common random numbers).

Y is mandatory — draw once via model.joint.subordinator().rvs(n, seed) and share across finance evaluations. Not promised vmap-able over w: the fused bundle seeds its CMC bracket from the PINV ppf.

Parameters:
Return type:

ENBResult