pyvallocation.utils.projection module

pyvallocation.utils.projection.compose_repricers(instruments, invariant_columns)[source]

Build a repricing function mapping K invariant columns to N instrument P&Ls.

For mixed portfolios where each instrument may depend on one or more risk drivers (Prayer P4). Three specification formats are supported:

  • callable – 1-to-1: uses the invariant column with the same name as the instrument.

  • (“driver”, callable) or ([“driver”], callable) – single named driver whose name differs from the instrument.

  • ([“d1”, “d2”], callable) – multi-driver: the callable receives an (n_sim, n_drivers) array.

Parameters:
  • instruments – Dict mapping instrument name to repricing spec.

  • invariant_columns – Ordered column names of the invariant scenario matrix (length K).

Returns:

Tuple[Callable, List[str]](repricing_fn, instrument_names) where repricing_fn maps (n_sim, K) -> (n_sim, N) and instrument_names is the ordered list of output column labels.

Raises:
  • KeyError – If a required driver column is not in invariant_columns.

  • TypeError – If a spec is neither a callable nor a (drivers, callable) tuple.

Examples

>>> fn, names = compose_repricers(
...     {"Stock": reprice_exp,
...      "Bond": (["yield_10y"], lambda dy: reprice_taylor(dy, delta=-7, gamma=50)),
...      "Call": (["stock_logret", "iv_chg"], my_option_fn)},
...     invariant_columns=["stock_logret", "yield_10y", "iv_chg"],
... )
>>> names
['Stock', 'Bond', 'Call']
pyvallocation.utils.projection.convert_scenarios_compound_to_simple(scenarios)[source]

Convert compound returns to simple returns.

Parameters:

scenarios – Log/compound return scenarios.

Returns:

np.ndarray – Simple return scenarios.

Parameters:

scenarios (numpy.ndarray)

Return type:

numpy.ndarray

pyvallocation.utils.projection.convert_scenarios_simple_to_compound(scenarios)[source]

Convert simple returns to compound (log) returns.

Parameters:

scenarios – Simple return scenarios. All values must be strictly greater than -1 (i.e., 1 + r > 0).

Returns:

np.ndarray – Log/compound return scenarios.

Raises:

ValueError – If any scenario has a return <= -1.

Parameters:

scenarios (numpy.ndarray)

Return type:

numpy.ndarray

pyvallocation.utils.projection.log2simple(mu_g, cov_g)[source]

mu,Sigma of log-returns -> mu,Sigma of simple returns (vectorised, pandas-aware).

Parameters:
  • mu_g – Mean of log-returns.

  • cov_g – Covariance of log-returns.

Returns:

Tuple[ArrayLike, ArrayLike] – Mean and covariance in simple-return units.

pyvallocation.utils.projection.make_repricing_fn(pricing_fn, current_drivers)[source]

Build a repricing callable from an arbitrary pricing function.

For full repricing (Prayer P4), the user supplies a function pricing_fn(Y) that maps risk-driver levels to instrument prices. The returned callable computes:

P&L = pricing_fn(Y_T + Δy) - pricing_fn(Y_T)

Parameters:
  • pricing_fn – Callable f(Y) -> prices, where Y has the same columns as the risk-driver scenarios. Must be vectorised over the first (scenario) axis.

  • current_drivers – Current risk-driver levels Y_T (1-D array of length n_drivers).

Returns:

Callable – Repricing function suitable for the reprice parameter of project_scenarios().

Parameters:

current_drivers (numpy.ndarray)

Examples

>>> # Bond: price = face * exp(-yield * maturity)
>>> import numpy as np
>>> face, maturity = 100, 5
>>> pricing_fn = lambda Y: face * np.exp(-Y * maturity)
>>> current_yield = np.array([0.03])
>>> repricer = make_repricing_fn(pricing_fn, current_yield)
>>> # repricer(delta_y) returns bond P&L scenarios
pyvallocation.utils.projection.project_mean_covariance(mu, cov, annualization_factor)[source]

Scale mean and covariance by annualization_factor.

Parameters:
  • mu – Mean vector.

  • cov – Covariance matrix.

  • annualization_factor – Scaling factor (e.g., 12 for monthly to annual).

Returns:

tuple – Scaled mean vector and covariance matrix.

Parameters:
  • mu (numpy.ndarray | pandas.Series)

  • cov (numpy.ndarray | pandas.DataFrame)

  • annualization_factor (float)

Return type:

tuple[numpy.ndarray | pandas.Series, numpy.ndarray | pandas.DataFrame]

pyvallocation.utils.projection.project_risk_drivers(R, investment_horizon=2, p=None, n_simulations=1000, reprice=None, seed=None)

Simulate horizon sums by sampling invariants with replacement.

Implements P3 (Projection) of Meucci’s Prayer framework: invariants are bootstrapped over investment_horizon steps and summed (random walk). If a reprice callable is supplied, it is applied to the projected risk drivers to obtain P&L scenarios (P4 Pricing).

Parameters:
  • R – Historical or simulated invariants (e.g. log-returns, yield changes). One-dimensional inputs represent single-instrument (length T). Two-dimensional inputs represent T scenarios across N risk drivers.

  • investment_horizon – Number of draws (with replacement) per simulated path. Defaults to 2.

  • p – Scenario probabilities. When omitted, draws are uniform. Length must match the number of rows in R.

  • n_simulations – Number of simulated paths to generate. Defaults to 1000.

  • reprice – Optional callable f(projected_risk_drivers) -> pnl_scenarios that converts projected risk-driver changes into P&L or simple-return scenarios. Built-in options: reprice_exp() (stocks: exp(Δy) - 1), reprice_taylor() (greeks/duration: θτ + δΔy + ½γΔy²).

  • seed – Random seed for reproducibility. Defaults to None.

Returns:

numpy.ndarray or pandas.Series or pandas.DataFrame – Simulated sums whose structure mirrors the input type:

  • 1-D inputs yield length-n_simulations vectors.

  • 2-D inputs yield (n_simulations, n_assets) matrices.

Examples

>>> import numpy as np
>>> project_scenarios(
...     np.array([0.01, -0.02, 0.03]),
...     investment_horizon=2,
...     n_simulations=4,
... ).shape
(4,)
>>> import pandas as pd
>>> df = pd.DataFrame({"a": [0.01, -0.02], "b": [0.0, 0.02]})
>>> project_scenarios(df, investment_horizon=2, n_simulations=3).shape
(3, 2)
pyvallocation.utils.projection.project_scenarios(R, investment_horizon=2, p=None, n_simulations=1000, reprice=None, seed=None)[source]

Simulate horizon sums by sampling invariants with replacement.

Implements P3 (Projection) of Meucci’s Prayer framework: invariants are bootstrapped over investment_horizon steps and summed (random walk). If a reprice callable is supplied, it is applied to the projected risk drivers to obtain P&L scenarios (P4 Pricing).

Parameters:
  • R – Historical or simulated invariants (e.g. log-returns, yield changes). One-dimensional inputs represent single-instrument (length T). Two-dimensional inputs represent T scenarios across N risk drivers.

  • investment_horizon – Number of draws (with replacement) per simulated path. Defaults to 2.

  • p – Scenario probabilities. When omitted, draws are uniform. Length must match the number of rows in R.

  • n_simulations – Number of simulated paths to generate. Defaults to 1000.

  • reprice – Optional callable f(projected_risk_drivers) -> pnl_scenarios that converts projected risk-driver changes into P&L or simple-return scenarios. Built-in options: reprice_exp() (stocks: exp(Δy) - 1), reprice_taylor() (greeks/duration: θτ + δΔy + ½γΔy²).

  • seed – Random seed for reproducibility. Defaults to None.

Returns:

numpy.ndarray or pandas.Series or pandas.DataFrame – Simulated sums whose structure mirrors the input type:

  • 1-D inputs yield length-n_simulations vectors.

  • 2-D inputs yield (n_simulations, n_assets) matrices.

Examples

>>> import numpy as np
>>> project_scenarios(
...     np.array([0.01, -0.02, 0.03]),
...     investment_horizon=2,
...     n_simulations=4,
... ).shape
(4,)
>>> import pandas as pd
>>> df = pd.DataFrame({"a": [0.01, -0.02], "b": [0.0, 0.02]})
>>> project_scenarios(df, investment_horizon=2, n_simulations=3).shape
(3, 2)
pyvallocation.utils.projection.reprice_exp(delta_y)[source]

Reprice via exponentiation (stocks, equity indices).

Maps projected log-return invariants to simple returns: P&L / V_0 = exp(Δy) - 1.

This is the exact repricing for instruments whose risk driver is the log-price (Meucci Prayer P4, Eq. 17 for stocks).

Parameters:

delta_y – Projected risk-driver changes Y_{T+τ} - Y_T (log-returns).

Returns:

np.ndarray – Simple-return scenarios.

Parameters:

delta_y (numpy.ndarray)

Return type:

numpy.ndarray

pyvallocation.utils.projection.reprice_taylor(delta_y, *, theta=None, delta=None, gamma=None, tau=0.0)[source]

Reprice via Taylor / Greek approximation (options, bonds).

Approximates the P&L using a second-order expansion around the current risk-driver values (Meucci Prayer P4, Eq. 18):

\[\text{P\&L} \approx \theta\,\tau + \delta\,\Delta y + \tfrac12\,\gamma\,(\Delta y)^2.\]

The coefficients are instrument-specific sensitivities:

  • Equities: delta=1, others zero (reduces to linear return).

  • Options: theta (time decay), delta (option delta), gamma (option gamma). For multi-factor options, delta and gamma can be vectors/matrices matching risk-driver columns.

  • Bonds: delta = -duration * price, gamma = convexity * price.

Parameters:
  • delta_y – Projected risk-driver changes (n_sim × n_drivers).

  • theta – Time-decay coefficient(s). Scalar or per-instrument array.

  • delta – First-order sensitivity (delta, -duration, etc.).

  • gamma – Second-order sensitivity (gamma, convexity, etc.).

  • tau – Time step (e.g. 1/252 for daily, 1/12 for monthly).

Returns:

np.ndarray – Approximate P&L scenarios, same shape as delta_y.

Parameters:
  • delta_y (numpy.ndarray)

  • theta (numpy.ndarray | float | None)

  • delta (numpy.ndarray | float | None)

  • gamma (numpy.ndarray | float | None)

  • tau (float)

Return type:

numpy.ndarray

pyvallocation.utils.projection.simple2log(mu_r, cov_r)[source]

mu,Sigma of simple returns -> mu,Sigma of log-returns (log-normal assumption).

Parameters:
  • mu_r – Mean of simple returns.

  • cov_r – Covariance of simple returns.

Returns:

Tuple[ArrayLike, ArrayLike] – Mean and covariance in log-return units.

pyvallocation.utils.projection.simulate_paths(R, horizon=2, n_paths=1000, p=None, reprice=None, seed=None)[source]

Simulate full trajectory paths by bootstrapping invariants.

Unlike project_scenarios() which returns only the terminal sum, this function returns the cumulative risk-driver change at every intermediate step, enabling drawdown analysis and fan charts.

Parameters:
  • R – Invariant scenarios (T, N) or (T,) (e.g. log-returns).

  • horizon – Number of bootstrap steps per path.

  • n_paths – Number of simulated paths.

  • p – Optional scenario probabilities.

  • reprice – Optional callable applied to cumulative risk-driver changes at each step (e.g. reprice_exp for cumulative simple returns).

  • seed – Random seed for reproducibility.

Returns:

np.ndarray – Shape (n_paths, horizon, N) — cumulative risk-driver changes (or repriced values) at each time step.

Parameters:
  • horizon (int)

  • n_paths (int)