test: add red contracts for quant core boundaries
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""Risk contribution contracts and validation tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from quant_engine.risk import component_var, marginal_risk_contribution, risk_contribution
|
||||
|
||||
|
||||
def test_risk_contribution_sums_to_one_for_positive_portfolio_variance() -> None:
|
||||
weights = np.array([0.5, 0.5])
|
||||
covariance = np.diag([1.0, 4.0])
|
||||
|
||||
result = risk_contribution(weights, covariance)
|
||||
|
||||
np.testing.assert_allclose(result, [0.2, 0.8])
|
||||
assert result.sum() == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_zero_variance_portfolio_falls_back_to_equal_contribution() -> None:
|
||||
result = risk_contribution(np.array([0.2, 0.3, 0.5]), np.zeros((3, 3)))
|
||||
|
||||
np.testing.assert_allclose(result, np.full(3, 1 / 3))
|
||||
|
||||
|
||||
def test_marginal_and_component_risk_follow_matrix_identities() -> None:
|
||||
weights = np.array([0.25, 0.75])
|
||||
covariance = np.array([[0.04, 0.01], [0.01, 0.09]])
|
||||
|
||||
marginal = marginal_risk_contribution(weights, covariance)
|
||||
component = component_var(weights, covariance)
|
||||
|
||||
np.testing.assert_allclose(marginal, covariance @ weights)
|
||||
np.testing.assert_allclose(component, weights * marginal)
|
||||
assert component.sum() == pytest.approx(weights @ covariance @ weights)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[risk_contribution, marginal_risk_contribution, component_var],
|
||||
)
|
||||
def test_risk_functions_reject_covariance_shape_mismatch(function) -> None:
|
||||
with pytest.raises(ValueError, match="does not match weights length"):
|
||||
function(np.array([0.5, 0.5]), np.eye(3))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[risk_contribution, marginal_risk_contribution, component_var],
|
||||
)
|
||||
def test_risk_functions_reject_empty_portfolio(function) -> None:
|
||||
with pytest.raises(ValueError, match="at least one asset"):
|
||||
function(np.array([]), np.empty((0, 0)))
|
||||
Reference in New Issue
Block a user