fastcashflow.pricing의 소스 코드

"""Premium solving -- pricing on the level-premium term product.

Fulfilment cash flows are linear in the premium: claims, expenses and the
in-force run-off do not depend on it, so ``FCF = A - premium * B``. Two
valuations pin down ``A`` and ``B``, and the premium that meets a
profitability target then has a closed form -- no iteration.
"""
from __future__ import annotations

from dataclasses import replace

import numpy as np

from fastcashflow._typing import FloatArray
from fastcashflow.basis import Basis
from fastcashflow.engine import measure
from fastcashflow.modelpoints import ModelPoints


def _with_premium(model_points: ModelPoints, premium: float) -> ModelPoints:
    """A copy of ``model_points`` with every level premium set to ``premium``.

    Every other field -- including the payment frequency -- is carried over
    unchanged, so the two valuations that pin down the premium see the same
    contract bar the premium itself.
    """
    return replace(
        model_points, premium=np.full(model_points.n_mp, premium)
    )


[문서] def solve_premium( model_points: ModelPoints, basis: Basis, *, break_even: bool = False, margin: float | None = None, csm: float | None = None, ) -> FloatArray: """Solve the level premium that meets a profitability target. Exactly one target must be given: * ``break_even`` -- the lowest non-onerous premium (FCF = 0, zero CSM). * ``margin`` -- a profit margin, ``CSM / PV(premiums) = margin`` (e.g. ``0.10`` for 10%); must satisfy ``0 <= margin < 1``. * ``csm`` -- an absolute target CSM (profit) per model point. Every product field of ``model_points`` is used as given -- only ``premium`` is ignored, since it is the unknown being solved for. Returns the solved premium per model point, shape ``(n_mp,)``. """ chosen = (break_even, margin is not None, csm is not None) if sum(chosen) != 1: raise ValueError( "specify exactly one target: break_even, margin or csm" ) if margin is not None and not 0.0 <= margin < 1.0: raise ValueError(f"margin must be in [0, 1), got {margin}") # FCF is linear in the premium -- FCF = A - premium * B -- so two # valuations (premium 0 and 1) pin the line down exactly. The fast path # computes the confidence-level RA only; cost-of-capital RA needs the # trajectory path (the inception headline is identical either way). A dict # (segmented) basis takes the trajectory path if any segment uses it. bases = basis.values() if isinstance(basis, dict) else (basis,) use_full = any(b.ra_method != "confidence_level" for b in bases) at_zero = measure(_with_premium(model_points, 0.0), basis, full=use_full) at_one = measure(_with_premium(model_points, 1.0), basis, full=use_full) a = at_zero.bel + at_zero.ra b = a - (at_one.bel + at_one.ra) zero_sens = np.abs(b) < 1e-12 if np.any(zero_sens): raise ValueError( "solve_premium: FCF is insensitive to the premium for " f"{int(zero_sens.sum())} model point(s) -- cannot solve. " "Check that premium enters the cash flows (non-zero " "premium term and payment frequency)." ) if break_even: return a / b if margin is not None: return a / (b * (1.0 - margin)) return (csm + a) / b