API reference#
Inputs#
- class fastcashflow.ModelPoints(issue_age, premium, term_months, benefits=None, maturity_benefit=None, annuity_payment=None, disability_income=None, disability_benefit=None, surrender_base_amount=None, contract_boundary_months=None, premium_term_months=None, premium_frequency_months=None, annuity_frequency_months=None, account_value=None, minimum_crediting_rate=None, minimum_death_benefit=None, minimum_accumulation_benefit=None, annuitization_months=None, annuitization_rate=None, annuity_air_annual=None, annuity_start_months=None, annuity_term_months=None, annuity_guarantee_months=None, coverage_index=None, coverage_amount=None, coverage_offset=None, coverage_waiting=None, coverage_reduction_end=None, coverage_reduction_factor=None, coverage_step_month=None, coverage_step_factor=None, coverage_escalation_annual=None, coverage_escalation_cap=None, coverage_term=None, coverage_is_main=None, count=None, sex=None, state=None, issue_class=None, elapsed_months=None, product=None, channel=None, calculation_methods=None, coverage_codes=None, issue_date=None, attributes=None, mp_id=None)[소스]#
Columnar model point data.
Every scalar field is a numpy array of length
n_mp; the model-point axis is the vectorised dimension throughout the engine. Monetary amounts are stated per single policy;countis how many policies the model point stands for – it defaults to one (one row per policy: seriatim), and a larger value scales the policy linearly through the projection.A policy’s claim benefits are a variable-length list of coverages (see
fastcashflow.coverage), held in CSR (Compressed Sparse Row) form so the kernels loop them generically – new benefit types add no fields:coverage_index[k]– the coverage code; an integer index intoBasis.coverages(entryiof that tuple lives at codei). No code is reserved.coverage_amount[k]– the benefit amount of coveragek.coverage_offset–(n_mp+1,); policymp’s coverages are the slice[coverage_offset[mp] : coverage_offset[mp+1]].
Each coverage may carry a benefit rule:
coverage_waiting(months from issue with no benefit),coverage_reduction_end/coverage_reduction_factor(a benefit multiplier in force until a cut-off month) andcoverage_term(the coverage’s own maturity in months from issue – it pays nothing from that month on, even when the contract boundary runs longer, e.g. a whole-life main with an 80-age term rider; 0 = run to the contract boundary). All are CSR arrays aligned withcoverage_indexand default to off – no waiting, full benefit, no per-coverage maturity.The coverage list is built one of two ways.
benefitsis the general form: a{cov_idx: amount array}map keyed by coverage code (the index intoBasis.coverages). Or pass the CSR arrayscoverage_index/coverage_amount/coverage_offsetdirectly – the preferred form for a portfolio with per-coverage benefit rules (waiting / reduction periods).Premiums and survival benefits stay as plain fields – they do not proliferate the way claim benefits do:
premium– premium charged each payment occurrence.premium_term_months– months the level premium is collected, defaulting to the full coverage term.premium_frequency_months– months between level-premium payments (1 monthly, 3 quarterly, 6 half-yearly, 12 annual), defaulting to 1.maturity_benefit– benefit on survival to the end of the term.annuity_payment– survival income paid each payout occurrence.annuity_frequency_months– months between annuity payouts, defaulting to 1.disability_income– income paid each month a benefit state is occupied (disability income on a disabled state).disability_benefit– lump sum paid when a lump-sum transition fires (a disability lump sum on becoming disabled).
- 매개변수:
benefits (dict[int, ndarray[tuple[Any, ...], dtype[float64]]] | None)
maturity_benefit (ndarray[tuple[Any, ...], dtype[float64]] | None)
annuity_payment (ndarray[tuple[Any, ...], dtype[float64]] | None)
disability_income (ndarray[tuple[Any, ...], dtype[float64]] | None)
disability_benefit (ndarray[tuple[Any, ...], dtype[float64]] | None)
surrender_base_amount (ndarray[tuple[Any, ...], dtype[float64]] | None)
contract_boundary_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
premium_term_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
premium_frequency_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
annuity_frequency_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
account_value (ndarray[tuple[Any, ...], dtype[float64]] | None)
minimum_crediting_rate (ndarray[tuple[Any, ...], dtype[float64]] | None)
minimum_death_benefit (ndarray[tuple[Any, ...], dtype[float64]] | None)
minimum_accumulation_benefit (ndarray[tuple[Any, ...], dtype[float64]] | None)
annuitization_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
annuitization_rate (ndarray[tuple[Any, ...], dtype[float64]] | None)
annuity_air_annual (ndarray[tuple[Any, ...], dtype[float64]] | None)
annuity_start_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
annuity_term_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
annuity_guarantee_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_index (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_amount (ndarray[tuple[Any, ...], dtype[float64]] | None)
coverage_offset (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_waiting (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_reduction_end (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_reduction_factor (ndarray[tuple[Any, ...], dtype[float64]] | None)
coverage_step_month (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_step_factor (ndarray[tuple[Any, ...], dtype[float64]] | None)
coverage_escalation_annual (ndarray[tuple[Any, ...], dtype[float64]] | None)
coverage_escalation_cap (ndarray[tuple[Any, ...], dtype[float64]] | None)
coverage_term (ndarray[tuple[Any, ...], dtype[int64]] | None)
coverage_is_main (ndarray[tuple[Any, ...], dtype[int64]] | None)
elapsed_months (ndarray[tuple[Any, ...], dtype[int64]] | None)
product (ndarray | None)
channel (ndarray | None)
calculation_methods (dict[str, CalculationMethod] | None)
issue_date (ndarray | None)
mp_id (ndarray | None)
- axis(name)[소스]#
Resolve a grouping axis to a
(n_mp,)label array by name.Used by
fastcashflow.group()to aggregate on any axis. Resolution order: the derivedissue_year(calendar year ofissue_date); the named source fieldsproduct/channel/issue_date; then any key inattributes(portfolio_id, profitability_group, risk_class, …). RaisesKeyErrorlisting the available axes when the name is unknown.
- classmethod single(issue_age, premium, term_months, benefits=None, maturity_benefit=0.0, annuity_payment=0.0, disability_income=0.0, disability_benefit=0.0, premium_term_months=None, premium_frequency_months=1, annuity_frequency_months=1, account_value=0.0, minimum_crediting_rate=None, minimum_death_benefit=0.0, minimum_accumulation_benefit=0.0, annuitization_months=0, annuitization_rate=0.0, annuity_air_annual=nan, annuity_start_months=0, annuity_term_months=0, annuity_guarantee_months=0, count=1.0, sex=0, state=0, calculation_methods=None)[소스]#
Build a single-model-point set – a convenience for hand checks.
benefitsis the per-coverage benefit-amount map keyed by coverage CODE (str), e.g.{"DEATH": 1_000_000.0}– the engine aligns it toBasis.coveragesby code, not by position. Each code must also be mapped to aCalculationMethodviacalculation_methods(no code-as-method auto-inference). None means no claim benefits.- 매개변수:
issue_age (float)
premium (float)
term_months (int)
maturity_benefit (float)
annuity_payment (float)
disability_income (float)
disability_benefit (float)
premium_term_months (int | None)
premium_frequency_months (int)
annuity_frequency_months (int)
account_value (float)
minimum_crediting_rate (float | None)
minimum_death_benefit (float)
minimum_accumulation_benefit (float)
annuitization_months (int)
annuitization_rate (float)
annuity_air_annual (float)
annuity_start_months (int)
annuity_term_months (int)
annuity_guarantee_months (int)
count (float)
sex (int)
state (int)
calculation_methods (dict[str, CalculationMethod] | None)
- 반환 형식:
- subset(indices)[소스]#
Return a new
ModelPointscarrying the rows atindices.Per-row fields (issue_age, premium, …) and the segment metadata (product, channel) are sliced. The coverage CSR is rebuilt: each selected row’s coverage slice
coverage_index[coverage_offset[i]:coverage_offset[i+1]]is concatenated, andcoverage_offsetis reset to the new running cumulative sum. Used byfastcashflow.gmm.measure()to split a portfolio by (product, channel) before per-segment measurement.indicesis expected to select distinct rows – it is a row selection, not a gather. As an optimisation the result skips the re-validation the constructor runs (the parent was already validated), so a repeated index (subset([0, 0])) would carry a duplicate mp_id the constructor would otherwise reject. Every engine caller passes a unique segment index, so this is safe on the hot path; pass distinct indices when calling it directly.- 반환 형식:
- class fastcashflow.Basis(mortality_annual, lapse_annual, discount_annual, ra_confidence, mortality_cv, expense_items=(), expense_inflation=0.0, surrender_value_curve=None, surrender_value_basis='cum_premium_factor', waiver_incidence_annual=None, lapse_paidup_annual=None, lapse_waiver_annual=None, premium_factor_annual=None, annuity_factor_annual=None, surrender_charge_annual=None, ci_incidence_annual=None, ci_reincidence_annual=None, disability_recovery_annual=None, state_mortality_annual=None, longevity_cv=0.0, morbidity_cv=0.0, expense_cv=0.0, disability_cv=0.0, ra_method='confidence_level', cost_of_capital_rate=0.06, coverage_unit_discount=False, investment_return=0.0, fund_fee=0.0, coi_annual=None, premium_load=0.0, settlement_pattern=None, coverages=(), state_machine=None)[소스]#
Deterministic assumption set – no assumption changes over time.
- 매개변수:
mortality_annual (collections.abc.Callable[[numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]]) –
Annual mortality-rate callable. Like every rate function on
Basis, it takes the unified five positional grids(sex, issue_age, duration, issue_class, elapsed)and returns an array of annual rates of the same shape – seeRateFn(infastcashflow._typing) for the full contract:sex(0 male, 1 female),issue_age(years),duration(completed policy years, 0-based),issue_class(at-issue / underwriting class),elapsed(semi-Markov sojourn). A table without a given axis broadcasts over it. The engine converts the annual rate to a monthly one (seeannual_to_monthly()). A select-and-ultimate basis lets the rate depend on duration within the select period and on attained age (issue_age + duration) beyond it; that logic lives in this callable, not the engine.A legacy three-arg
(sex, issue_age, duration)callable still works (it is auto-wrapped to the five-arg shape). WARNING: do not bake a constant in as a fourth default parameter –lambda s, a, d, f=factor: ...is read as a four-arg rate, and the engine passesissue_classintof, silently overriding it (wrong rates, no error). Capture the constant in a closure instead.lapse_annual (collections.abc.Callable[[numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]]) – Same five-arg
RateFnshape asmortality_annual. Typical lapse depends only on duration, but the signature also lets a table key on sex / issue_age / issue_class when the workbook carries those axes (the engine reads the callable on the full grid either way).discount_annual (float | numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Annual locked-in discount rate (paragraph 36). Either a flat scalar or a per-year
(n_years,)array; the engine expands either to a per-month rate curve viafastcashflow.curves.discount_monthly_curve(). Used for discounting cash flows and for CSM interest accretion.expense_items (tuple[fastcashflow.basis.ExpenseItem, ...]) –
Row-form expense ledger – a tuple of
ExpenseItem. Each row carries acategory(EXPENSE_CATEGORIES– acquisition / maintenance / collection / lae; the first three are the Korean alpha / beta / gamma convention), abase(EXPENSE_BASES– per_policy / premium / surrender_value / face / claim) and a numeric value. The engine projects every row throughderive_expense_components()into the kernel-side primitives (named<category>_<base>). An empty tuple is the no-expense basis.IFRS 17 (paragraphs B65-B66): only DIRECTLY ATTRIBUTABLE expenses enter the fulfilment cash flows. Put the directly-attributable amount in
value– i.e. gross expense already multiplied by the company’s direct-allocation ratio. The engine does NOT split direct from indirect; the non-attributable portion is excluded from the FCF by simply not being entered (that allocation is an ETL / expense-policy step upstream of measurement).expense_inflation (float | numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Global annual inflation applied to the recurring expense items (
maintenance_per_policyandlae). Either a flat scalar – closed-form(1+i)^(t/12)growth – or a per-year(n_years,)array (compounds across years, in-year fractional ramp on the current year, held flat past the end). Macro-economic assumption, defined once per segment; the I/O layer points the segments sheet at one named scenario in theinflation_tablessheet (analogous todiscount_annual/discount_tables). Does not apply to the two_initbases (one-time at t=0) or topremium_pct(which already rides the premium).ra_confidence (float) – Confidence level for the Risk Adjustment (e.g. 0.75). The RA lifts the liability from its best estimate to this percentile.
mortality_cv (float) – Coefficient of variation of death claims – the mortality-risk component of the RA.
waiver_incidence_annual (collections.abc.Callable[[numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]] | None) – Maps
(sex, issue_age, duration_years)to an array of annual waiver-incidence rates – the rate at which active in-force transitions to the premium-waived state. Same signature asmortality_annual.Nonemeans no transitions: every model point keeps its input state for the whole projection. The spelling matches the standard actuarial termincidence– a per-unit-time event rate – used by the rest of the engine for analogous rates (ci_incidence_annual,ci_reincidence_annual).longevity_cv (float) – Coefficient of variation of survival benefits (maturity benefits and annuity payments) – the longevity-risk component of the RA. The RA components are added (the natural mortality / longevity hedge is not credited – conservative for mixed contracts).
morbidity_cv (float) – Coefficient of variation of morbidity claims (hospitalisation, surgery, outpatient) – the morbidity-risk component of the RA.
expense_cv (float) – Coefficient of variation of expense cash flows – the expense-risk component of the Risk Adjustment. VFA-only in v1:
vfa.measureuses it directly, but the GMM / PAA RA sums the mortality / morbidity / disability / longevity components only. Adding the expense term to the GMM RA – and so closing the gap to the IFRS 17 non-financial-risk RA – is future work; a non-zeroexpense_cvon a GMM / PAA measurement raisesNotImplementedErrorrather than silently doing nothing (set it to 0, or use VFA).disability_cv (float) – Coefficient of variation of disability cash flows – disability income and the on-transition lump sum – the disability-risk component of the Risk Adjustment.
ra_method (str) – Which Risk Adjustment technique to use –
"confidence_level"(the default; a percentile margin on the benefit present values) or"cost_of_capital". The cost-of-capital method is available throughmeasure(..., full=True); the fast path (full=False) computes the confidence-level RA.cost_of_capital_rate (float) – Annual cost-of-capital rate for the cost-of-capital RA – the rate charged on the non-financial-risk capital held over the run-off.
investment_return (float) – Annual return earned on the underlying items backing an account-value (VFA) contract.
fund_fee (float) – Annual variable-fee rate – the entity’s share of the underlying items, deducted from the account value each period (VFA).
coi_annual (collections.abc.Callable[[numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]] | None) – Universal-life cost-of-insurance charge rate – the same five-arg
RateFnshape asmortality_annual. The monthly COI deducted from a UL account isannual_to_monthly(coi_annual(grid)) * NAR, where the net amount at riskNAR = max(0, face - account value)and the face is the model point’sminimum_death_benefit. It is a contractual charge, DISTINCT from the best-estimatemortality_annualused to value actual death claims; their spread is the mortality margin that drives the UL CSM.Nonecharges no COI. UL-only.premium_load (float) – Universal-life premium load – the fraction (0..1) of each premium withheld before crediting to the account (
prem_to_av = premium * (1 - premium_load)). The full premium is still the insurer inflow; the load margin emerges in the fulfilment cash flows because only the net-of-load amount grows the account. An account-mechanics parameter, not an expense-ledger row. UL-only.settlement_pattern (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]] | None) – Claims run-off pattern – the fractions of an incurred claim paid in the month it is incurred, the next month, and so on, summing to 1.
Nonesettles every claim immediately. It measures the liability for incurred claims and discounts claims to their payment dates in the best-estimate liability.coverages (tuple[fastcashflow.basis.CoverageRate, ...]) – Ordered tuple of
CoverageRate– the rate-driven coverages (death-type, morbidity and diagnosis), one per coverage code. No code is reserved: entryilives at codei, the integer the portfolio’scoverage_indexCSR uses to index this tuple. A contract’s death coverage, if any, is just one entry whoserate_tabletypically references the same mortality table the engine uses as the in-force decrement (mortality_annual) – the two are different mathematical quantities (decrement vs claim payout) that happen to share a table in most products. The taxonomy side – whether a coverage code runs as a diagnosis pool vs a recurring claim – lives on the portfolio (fastcashflow.model_points.ModelPoints.calculation_methods), not here.state_machine (fastcashflow.multistate.Model | None) – The product’s in-force state machine – a
Modeldeclaring the transient states, their transitions and which states pay premium or a benefit.Noneuses the default active / waiver model (ACTIVE_WAIVER_MODEL); thewaiver_incidence_annualrate then drives the active -> waiver transition. A product with a different state set supplies its own.surrender_value_curve (ndarray[tuple[Any, ...], dtype[float64]] | None)
surrender_value_basis (str)
lapse_paidup_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
lapse_waiver_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
premium_factor_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
annuity_factor_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
surrender_charge_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
ci_incidence_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
ci_reincidence_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
disability_recovery_annual (Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]] | None)
state_mortality_annual (dict[str, Callable[[ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]], ndarray[tuple[Any, ...], dtype[int64]]], ndarray[tuple[Any, ...], dtype[float64]]]] | None)
coverage_unit_discount (bool)
- property discount_monthly: float#
First-year monthly discount rate, used as a representative scalar.
Reserved for the few places that need a single rate – the claims settlement-pattern present-value factor (paragraph 40 / B71) – where the in-year rate is the right reference. The per-month rate curve the kernels consume is composed by
fastcashflow.curves.discount_monthly_curve(), which handles both a flat scalar and a per-year curve uniformly.
- class fastcashflow.BasisRouter(segments, segment_axes=('product', 'channel'), measurement_models=None)[소스]#
Routes a model point to its segment’s
SegmentSpec.Returned by
fastcashflow.read_basis(). ABasisRouteris not aBasisand not adict– it is the routing policy that maps a segment key (a tuple oversegment_axes, e.g.("TERM_LIFE_A", "GA")) to that segment’sSegmentSpec(itsBasis+ measurement model).measure(mp, router)readssegment_axesto route each model point with nosegment_byargument; an entry point that needs oneBasiscallsresolve_one().- 매개변수:
segments –
{segment-key: Basis}mapping. Copied; the router does not alias it.segment_axes – The axis names a segment key is read over –
("product", "channel")by default, or whatever non-assumption columns the segments sheet declares.measurement_models – Optional
{segment-key: "GMM"|"PAA"|"VFA"}; every other segment defaults to"GMM". Validated keyed tosegmentsso a model can never name a non-existent segment.
참고
It deliberately does not implement the mapping protocol (no
[]/ iteration /len) – reach the underlying mapping explicitly throughsegments(a read-only view of{key: Basis}), or resolve throughresolve()/resolve_spec()/resolve_one(). Both internal stores are immutable, so the per-segment model can never drift from itsBasisafter construction.- property segments#
Read-only
{segment-key: Basis}view (immutable).
- resolve_spec(key)[소스]#
The full
SegmentSpec(Basis + measurement model) for a key.- 반환 형식:
SegmentSpec
- class fastcashflow.CoverageRate(code, rate, funds_from_account=False, pays_account_balance=False)[소스]#
One rate-driven coverage’s assumption – a coverage code and how it runs.
- 매개변수:
code (str) – The coverage’s code label. The engine works in the integer grid index this factorises to; the label is what the model-point file names a coverage by.
rate (float | int | collections.abc.Sequence[float] | collections.abc.Callable[[numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.int64]]], numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]]) – A
RateLike– a flat scalar, a per-policy-year array, a polars / pandas rate table, or aRateFncallable (the same signature asmortality_annual). Whatever the form, it is normalised to aRateFnhere; the engine converts the annual rate to a monthly one (seeannual_to_monthly()).funds_from_account (bool) – Account-chassis interaction flag (universal-life funding mechanism). When
Truethe coverage’s monthly risk charge is drawn from the policy’s account value – the death leg’s cost-of-insurance on the net amount at risk. The coverage’srateis then the COI rate (coi_annual). Defaults toFalse(a plain rate-driven claim).pays_account_balance (bool) – Account-chassis interaction flag. When
Truethe coverage’s benefit reads the account balance – death paysmax(account value, face). Such a coverage is EXCLUDED from the aggregate claim-rate accumulator and the rule-bearing claim loop; the account death benefit is written once from the rolled account value. Defaults toFalse.
참고
Whether a coverage runs as a depleting diagnosis pool vs a recurring claim, and which risk class the RA prices it as, is derived from the portfolio-level
CalculationMethodtaxonomy (thecalculation_methods.csvfile, surfaced asfastcashflow.model_points.ModelPoints.calculation_methods). Those two flags do not live onCoverageRate. The two account-chassis flags above DO live here – they are a contract-level funding choice, not a benefit-method routing key.
- class fastcashflow.CalculationMethod(*values)[소스]#
How a benefit pays out – the engine’s calculation routing key.
Five uniform methods: every rate-driven death coverage (main contract or attached, accidental or all-cause, ADB / disease / disaster) is the same DEATH method; the rate table is what differentiates them. The method is purely a calculation-routing label – there is no “main-contract” method, because the engine has no reserved coverage slot.
str, Enum– members compare equal to their string value (CalculationMethod.MORBIDITY == "MORBIDITY"), so existing numpy array comparisons and dict keys keep working unchanged.
- class fastcashflow.ExpenseItem(category, base, value, months=None)[소스]#
One typed entry in the expense ledger.
An expense is two orthogonal axes – WHAT it is for (
category) and WHAT it is proportional to (base) – plus avalue. The pair(category, base)dispatches the row onto a kernel-side primitive; the timing (at issue, every in-force month, or paying months only) is implied by the category, so it is not a separate field. Inflation is not a row attribute – it lives onBasis(expense_inflation, matching the waydiscount_annuallives onBasis), so a company’s economic basis is named in one place and every inflation-bearing row picks it up automatically.- 매개변수:
category (str) – WHAT the expense is for – one of
EXPENSE_CATEGORIES:"acquisition"(at issue),"maintenance"(every in-force month),"collection"(premium-collection cost, paying months),"lae"(Loss Adjustment Expense, claims-handling cost). The first three map to the Korean actuarial alpha / beta / gamma convention – acquisition = alpha, maintenance = beta, collection = gamma. The category also fixes the timing and whetherexpense_inflationapplies (see Notes).base (str) – WHAT the expense is proportional to – one of
EXPENSE_BASES:"per_policy"(a flat amount per policy),"premium"(a fraction of premium),"surrender_value"(a fraction of the in-force cash surrender value),"claim"(a fraction of the month’s claim outflow). The valid(category, base)pairs are the keys of the dispatch table; e.g."surrender_value"is a maintenance base only (the Korean%-of-surrender-reserve loading), requires asurrender_value_curveon the basis, and is rejected on an account-backed (universal-life / VFA) book where the accountfund_feealready charges the account value.value (float) – Numeric value – an amount per policy for
base="per_policy", a fraction (0..1) for the proportional bases.months (int | None) – Optional window (in policy months) over which the item is charged, in force, instead of the category’s natural timing.
None(the default) keeps that timing –acquisitiononce att=0,maintenanceevery in-force month. An integerN >= 1charges the item only on policy months[0, N)– the installment / front-loaded commission shape (the Korean installment new-business commission): e.g.ExpenseItem("acquisition", "premium", 0.02, months=12)is a first-year % premium commission trail, charged on the in-force, premium-paying book for the first 12 months. Supported only on the"premium"base (the commission use case); the t=0 lump stays a plainmonths=None(acquisition, premium)row.
참고
The category fixes the inflation treatment:
maintenance/per_policyandclaims/claimrecur every month and so inflate;acquisitionpays once att=0, thepremiumbases ride the premium itself, andmaintenance/surrender_valuerides the surrender-value curve’s own growth, so for those a second inflation factor would double-count.
- fastcashflow.derive_expense_components(expense_items, n_time, inflation_index=None)[소스]#
Project
expense_itemsonto the seven kernel-side primitives.Each
ExpenseItemis dispatched by its(category, base)pair (see_EXPENSE_DISPATCH) onto one of these primitives. Returns(acquisition_premium, acquisition_per_policy, maintenance_premium, maintenance_per_policy, lae, maintenance_surrender_value, maintenance_face):acquisition_premium– sum ofvalueover(acquisition, premium)rows. Paid att=0on annualized premium.acquisition_per_policy– sum ofvalueover(acquisition, per_policy)rows. Paid att=0per policy.maintenance_premium[t]– per-month % premium rate:(maintenance, premium)AND(collection, premium)rows chargevalueevery premium-paying month, and anymonths-windowedpremiumrow (a commission / installment leg, including a windowed(acquisition, premium)row) chargesvalueonly on months[0, months). The kernel appliesinforce_t * maintenance_premium[t] * monthly_premiumwhile premium is paid. Not inflated (it rides the premium).maintenance_per_policy[t]– per-month per-policy maintenance: each(maintenance, per_policy)row contributesvalue / 12 * inflation_index[t].lae[t]– LAE (Loss Adjustment Expense) fraction: each(claims, claim)row contributesvalue * inflation_index[t]. Applied to the month’s claim + morbidity + disability total.maintenance_surrender_value– sum ofvalueover(maintenance, surrender_value)rows. A scalar annual rate charged each in-force month on the in-force surrender value (value / 12 * inforce_surrender_value[t]); the surrender-value base is built downstream inproject_cashflowsfrom the basis’surrender_value_curve/surrender_value_basis. NOT inflated: the base (a reserve-/surrender-value curve) already carries its own growth, so a second inflation factor would double-count – the same reasoning that exempts thealpha_*andmaintenance_premiumbases.maintenance_face– sum ofvalueover(maintenance, face)rows. A scalar annual rate charged each in-force month on the policy’s sum assured (value / 12 * inflation_index[t] * face_amount); the face amount (the main coverage’scoverage_amount, flagged byModelPoints.coverage_is_main) is applied downstream inproject_cashflows. Inflated likemaintenance_per_policy: the base (a level sum assured) does not grow, so the maintenance rate inflates.
inflation_indexis the(n_time,)per-month inflation multiplier produced byfastcashflow.curves.inflation_index(); a scalar economicexpense_inflation = igivesinflation_index[t] = (1+i)^(t/12)and a per-year curve compounds across years. PassNonefor a no-inflation basis (every month equal to 1.0).
- fastcashflow.EXPENSE_BASES = ('per_policy', 'premium', 'surrender_value', 'face', 'claim')#
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable’s items.
If the argument is a tuple, the return value is the same object.
- fastcashflow.RA_METHODS = ('confidence_level', 'cost_of_capital')#
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable’s items.
If the argument is a tuple, the return value is the same object.
- fastcashflow.SURRENDER_VALUE_BASES = ('cum_premium_factor', 'amount_per_policy', 'amount_per_unit')#
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable’s items.
If the argument is a tuple, the return value is the same object.
- fastcashflow.RISK_MORTALITY = 0#
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4
- fastcashflow.RISK_MORBIDITY = 1#
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4
Measurement (GMM)#
- fastcashflow.gmm.measure(model_points, basis, *, full=True, backend='cpu', discount_curve=None, segment_by=None, state_reserve=False, sum_at_risk=False)[소스]#
GMM measurement – the single entry point.
full=True(default) returns the complete roll-forward: the(n_mp,)inception headline and the(n_mp, n_time+1)*_pathtrajectories. Those trajectories make it memory-bound – several dense(n_mp, n_time+1)float64 arrays, on the order of ~100 KB per model point for a long horizon, so a million-policyfull=Truerun needs ~100 GB and will OOM on a typical box.full=Falseis the fused, memory-minimal fast path – it fills only the headline (*_pathareNone) at a few hundred bytes per model point, and is the right choice for large-scale valuation; reservefull=Truefor movement analysis or per-segment / chunked runs.basismay be a singleBasis(uniform portfolio) or a{(product, channel): Basis}dict; with a dict each segment is routed to its own basis.segment_bynames the routing axes (resolved viaModelPoints.axis(), so anyattributescolumn works) and the dict keys are tuples of those axes in order. Left asNone(the default) it is taken from the basis: aBasisRouterfromread_basis()carries the axes its workbook declared, and a plain dict falls back to("product", "channel"). So a workbook keyed by(product, channel, risk_class)routes by all three with no extra argument; passingsegment_byexplicitly overrides. Cost scales with the number of distinct segments, not the number of axes.backend("cpu"/"gpu") anddiscount_curveapply to the fast path only.state_reserve(full=True, singleBasis, Markov book) also fillsMeasurement.state_reserve– the per-state policy valueV^i(t)shaped(n_mp, n_states, n_time+1), withsum_i occ_i(t) V^i(t) == bel_path[t].sum_at_riskadditionally fillsMeasurement.sum_at_risk/.transitions– the per-transition net exposureS^ij + V^j - V^i(death / lapse / inter-state), shaped(n_mp, n_transition, n_time+1).
- fastcashflow.gmm.measure_aggregate(model_points, basis, *, chunk_size=200000)[소스]#
Portfolio-aggregate
full=Truemeasurement in bounded memory.measure(full=True)materialises dense(n_mp, n_time+1)trajectories – ~100 KB per model point – so a million-policy book needs ~100 GB and OOMs. But BEL / RA / CSM are additive across contracts, so the portfolio’s liability run-off is the per-model-point trajectories summed over the model-point axis. This runs the full trajectory kernel over row-blocks ofchunk_sizemodel points and accumulates only that(n_time+1,)sum, so peak memory isO(chunk_size x n_time)regardless ofn_mp.Returns an
Aggregate(scalar totals + aggregatebel_path/ra_path/csm_path). For the per-model-point detail (movement, in-force slicing) usemeasure()on a book small enough to hold every trajectory.basismay be a singleBasisor a per-segment dict, routed per chunk exactly asmeasure()routes it.
- fastcashflow.gmm.measure_inforce(model_points, state, basis, *, period_months=None, full=True)[소스]#
In-force diagnostic / runoff valuation at a single date.
The diagnostic companion to
fastcashflow.gmm.settle()(the paragraph-44 period-close settlement). Each model point is valued at itselapsed_monthsduration: the BEL / RA are full current estimates at that date (paragraph 40), while the CSM is a carry-only approximation – the prior period’s closing CSM (state.prior_csm) accreted atstate.lock_in_rateand released over coverage units acrossperiod_months(default 12), with no paragraph-44(c) unlocking. The result is stampedmeasurement_basis='settlement_carry'and the inception-only consumers (group/group_of_contracts/roll_forward/report/transition/ the plots) reject it; period-close balances come fromsettle.The
bel/raare re-based to the valuation date: the projection runs from inception, so the slice is scaled bycount / inforce[elapsed]to set the as-of in-force to the input count – exact for every cash flow linear in the in-force (premium, claim, morbidity, expense, maturity, annuity, and theamount_per_policy/amount_per_unitsurrender value). The one approximation is thecum_premium_factorsurrender mode: it reconstructs the base from the projected cumulative premium (lapse x cum_premium x factor), which ignores premiums paid before the valuation date, so it is path-dependent and only sample-grade. AUserWarningfires only in that mode (basis carries acum_premium_factorsurrender curve and anyelapsed_months > 0); the contractualamount_per_policy/amount_per_unitcurves are linear in the in-force and re-base exactly, so they are the production-grade surrender input and warn-free.stateis theInforceStatereturned byread_inforce_policies()(it carriesprior_csm/lock_in_rate, plus theelapsed_months/countreconciled ontomodel_points).model_pointsandstatemust be reconciled byapply_inforce_state()first –read_inforce_policiesreturns the pair already reconciled; the two-file path (read_model_points+read_inforce_state) calls it explicitly. A model_points whoseelapsed_months/countdisagree withstateis rejected (a stale snapshot must not borrow a fresh state’s CSM).full=True(default) returns the BEL / RA / CSM trajectories and cash flows for movement analysis;full=Falsereturns just the headline numbers (faster).Paragraph-44(c) unlocking, experience adjustments and the loss-component movement live in
fastcashflow.gmm.settle()(loss_componentis zero in this mode); the opening -> closing movement of a reporting period comes fromsettle’sSettlementMovement, not from this projector.- 매개변수:
model_points (ModelPoints)
state (InforceState)
period_months (int | None)
full (bool)
- 반환 형식:
- fastcashflow.gmm.settle(model_points, state, basis, *, period_months=None, premium_experience_future_fraction=0.0)[소스]#
Paragraph-44 subsequent-measurement settlement of a GMM in-force book.
The opening -> closing movement over one reporting period: BEL / RA re-measured at current rates (B72(a)), the CSM accreted at the locked-in rate (44(b)/B72(b), direct compounding), adjusted for the future-service change measured at the locked-in rate (44(c)/B72(c) – the gap to the current-rate measure is the
finance_wedge, insurance finance income/expense per B97(a), outside the CSM block), run through the paragraph-48/50(b) loss-component algebra, and released once at the period end over coverage units (44(e)/B119, em_open denominator).GMM carries no account value, so the expected and observed legs share one unit projection (
count = 1) and differ only by scale:k_exp = prior_count / unit_inforce[em_open](the on-track expectation) andk_obs = count / unit_inforce[em_close](the observation). On-track counts make every experience line zero and the closing CSM telescopes tomeasure_inforce’s monthly carry exactly.Premium experience (B96(a)/B97(c)): pass
state.actual_premium(the premium cash actually received over the period) and the entity’spremium_experience_future_fractionto split the experience adjustmentactual_premium - expected_premiumbetween future service (the CSM, at the B72(c) locked-in measure –csm_premium_experience) and current/past service (a P&L memo –premium_experience_revenue, recognised in insurance revenue, TRG 2018-09). The fraction defaults to 0.0 (all current/past, the BC233 general rule); the standard leaves the split to entity judgment. The lapse-driven future-premium effect is already carried by the count channel, so default 0.0 avoids double-counting – set the fraction above 0 only for premium received now for genuinely future coverage the count deviation does not capture.An onerous book amortises its loss component through the paragraph-50(a)/51 incurred-service channel (
loss_component_finance/loss_component_amortisedon the movement): the period’s released claims and expenses (51a), RA release (51b) and finance (51c) are split on the systematic loss-component ratior = loss_component_opening / pool_openingbetween the loss component and the LRC excluding it, running the loss component to zero by the end of coverage (52).A
settlement_patternbasis is supported: the movement carries the liability for incurred claims (lic_opening/claims_incurred/claims_paid/lic_closing, paragraphs 40(b) / 42 / 103(b)) – claims build it up as incurred and run it off over the pattern, undiscounted and at the expected scale, reconstructed from the projection each period.Within-period claims and expense experience (B97(b)/(c)) is surfaced when
state.actual_claims/state.actual_expensesare given: the actual-minus-expected difference is recognised in the insurance service result (claims_experience/expense_experience, P&L memos, not the CSM and not a balance recursion). Absent the inputs they are zero.v1 scope (documented cuts, mirroring
vfa.settle): the closing balances (BEL / RA / CSM / LIC) are still built on the expected within-period run (the experience above is a P&L memo, not a re-derivation of the balances); the LIC roll is expected-scale and undiscounted (no 42(c) finance / 33-37 discount + RA on the LIC, the same cut the measure takes); no B96(c) investment-component split, so the paragraph-50(a) pool includes the whole non-premium outflow (surrender / maturity not separated as investment components, B124(ii)); the RA change enters the CSM at its current measure (B96(d) prescribes no rate); no OCI – thefinance_wedgeis the period’s P&L line, not an accumulated-OCI state. A maturity falling inside the period is expected service (it seeds the unit BEL at the boundary and runs off through the release line), not experience.- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
period_months (int | None)
premium_experience_future_fraction (float | FloatArray)
- 반환 형식:
SettlementMovement
- fastcashflow.gmm.settle_aggregate(model_points, state, basis, *, period_months=None, premium_experience_future_fraction=0.0, chunk_size=200000)[소스]#
Portfolio-total paragraph-44 settlement in bounded memory.
settle()materialises(n_mp, n_time)projection intermediates – two backward kernel passes over the whole book – so a million-policy close would peak far beyond memory. Every line of the settlement movement is additive across contracts, so this runssettle()over row blocks ofchunk_sizemodel points and accumulates only the scalar line totals; peak memory isO(chunk_size x n_time)regardless ofn_mp.Returns a
SettlementAggregate: the movement’s lines summed, movement-positive (reconcileapplies the display negation and reproduces the per-MP movement’s table exactly). The aggregate cannot be chained –closing_inputs()raises; chain per-MP movements instead.statejoinsmodel_pointsby mp_id once, before chunking, so a period-close file in its own row order never pairs one contract’s rows with another’s prior balances.- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
period_months (int | None)
premium_experience_future_fraction (float | FloatArray)
chunk_size (int)
- 반환 형식:
SettlementAggregate
- fastcashflow.gmm.settle_stream(input_path, output_dir, basis, *, coverages=None, calculation_methods=None, state_path=None, period_months=None, chunk_size=200000, id_column=None, validate_unique_mp_id=True)[소스]#
Stream a paragraph-44 period close through a parquet file, chunk by chunk.
The out-of-core variant of
fastcashflow.gmm.settle(): reads the in-force book inchunk_sizeblocks, settles each block, and writes the per-MP settlement movements as a parquet dataset – onepart-NNNNN.parquetper chunk underoutput_dir, every movement line plus themeasurement_basismarker. Peak memory is one chunk’s projection, so a book whose per-MP movements would not fit in memory still closes. Returns the number of model points processed.Input layouts (both produce identical output):
One combined file (primary):
input_pathcarries the policies spec plus the closing-state columns (elapsed_months,count,prior_csm,lock_in_rate,prior_count,prior_loss_component) – the period-close snapshot ofread_inforce_policies().Two files:
input_pathis the standard policies parquet andstate_paththe state parquet (theread_inforce_state()layout), semi-joined per chunk onmp_id. The global id sets must match in both directions (validated up front – a semi-join would silently drop a mismatch); a duplicate statemp_idis rejected like a duplicate policiesmp_id.
coveragesis the per-contract coverage parquet (required, as inmeasure_stream()).lock_in_ratemay vary by row – a cohort-aware book whose issue cohorts / GoCs locked in different inception rates (paragraph B72(b)): each chunk’s settle partitions by rate, so the streamed close equals the in-memorysettle()per contract.Chaining on disk: each part carries the closing-state columns –
count,lock_in_rate,elapsed_monthsand the closing balances – so the next period’s state file is assembled from the parts alone:prior_csm <- csm_closing,prior_loss_component <- loss_component_closing,prior_count <- count, then advanceelapsed_months/countto the next observation. The disk side ofSettlementMovement.closing_inputs().
- fastcashflow.gmm.recognition_schedule(model_points, state, basis, *, band_edges_months=(12, 36, 60), period_months=None)[소스]#
Paragraph-109 maturity-band disclosure for a settled GMM book.
Allocates the closing CSM (the
settle()closing ofmodel_points/state) to maturity bands by each contract’s forward coverage-unit fraction, so the bands SUM TO the closing CSM – when, in maturity terms, the remaining CSM is expected to be recognised in profit or loss. The coverage units are the in-force count from the valuation date (the B119 amortisation proxy, undiscounted), so the schedule matches the actual CSM release. Onerous contracts carry no CSM and contribute nothing.band_edges_monthsare the band boundaries in months from the valuation date (default 12 / 36 / 60, the four-band disclosure axis);period_monthsis the settlement period (default 12), as forsettle().- 매개변수:
model_points (ModelPoints)
state (InforceState)
period_months (int | None)
- 반환 형식:
- class fastcashflow.gmm.CSMRecognitionSchedule(band_edges_months, csm, closing_csm)[소스]#
IFRS 17 paragraph-109 disclosure: the closing CSM allocated to maturity bands by expected coverage-unit recognition.
band_edges_monthsare the band boundaries in months from the valuation date (default 12 / 36 / 60, the four-band disclosure axis); the bands are[0, e0), [e0, e1), ..., [e_last, end).csm[b]is the closing CSM expected to be recognised in bandb– allocated by each contract’s forward coverage-unit fraction, so the bands SUM TOclosing_csm. It is an allocation of the remaining balance, not the accreted nominal release; the coverage-unit proxy is the in-force count, undiscounted, matching the B119 amortisation kernel, so the schedule tracks the actual release pattern.
- class fastcashflow.gmm.Measurement(bel, ra, csm, loss_component, bel_path=None, ra_path=None, csm_path=None, csm_accretion=None, csm_release=None, lic_path=None, state_reserve=None, sum_at_risk=None, transitions=None, cashflows=None, discount_factor_bom=None, discount_factor_mid=None, model_points=None, group_labels=None, group_sizes=None, measurement_basis='inception')[소스]#
IFRS 17 GMM measurement: BEL, RA and CSM.
The headline fields (
bel,ra,csm,loss_component) are(n_mp,)inception values and are always present.The trajectory fields are the roll-forward over time and are populated only by
measure(..., full=True); on the headline-only fast path (full=False) they areNone.bel_path/ra_path/csm_pathare the(n_mp, n_time+1)trajectories whose column 0 is the inception value (sobel == bel_path[:, 0]when full). The CSM roll-forward decomposes ascsm_path[:, t+1] = csm_path[:, t] + csm_accretion[:, t] - csm_release[:, t].lic_pathis the liability for incurred claims – zero unless a claims settlement pattern is set, which also discounts claims to their payment dates in the BEL.- 매개변수:
bel (FloatArray)
ra (FloatArray)
csm (FloatArray)
loss_component (FloatArray)
bel_path (FloatArray | None)
ra_path (FloatArray | None)
csm_path (FloatArray | None)
csm_accretion (FloatArray | None)
csm_release (FloatArray | None)
lic_path (FloatArray | None)
state_reserve (FloatArray | None)
sum_at_risk (FloatArray | None)
transitions (tuple | None)
cashflows (Cashflows | None)
discount_factor_bom (FloatArray | None)
discount_factor_mid (FloatArray | None)
model_points (ModelPoints | None)
group_labels (np.ndarray | None)
group_sizes (IntArray | None)
measurement_basis (str)
- estimate_at(month)[소스]#
The current estimate (BEL / RA / CSM / LIC) at a future
month.This is the deterministic nested-projection view (IFRS 17 paragraph 40): the cohort liability the entity would carry at month
tif the central best-estimate scenario unfolds to it. It is columntof the trajectories – soestimate_at(0).belequals the inception headlinebel– and equals a fresh in-force measurement at that month,gmm.measure_inforce(elapsed=t)carrying the deterministic survivor count (reading the trajectory and re-projecting agree; the tests assert this for everyt). The returned object’sper_survivorre-bases every figure to one surviving policy.Requires a
full=Truemeasurement (the trajectory paths); the fast path carries only the inception headline. GMM only for now – VFA / PAA carry the same*_pathshape and can gain this later.- 매개변수:
month (int)
- 반환 형식:
CurrentEstimate
Variable fee approach#
- fastcashflow.vfa.measure(model_points, basis, return_scenarios=None, *, full=True, lapse_sensitivity=None, lapse_floor=0.0, lapse_cap=None)[소스]#
Measure a direct-participation portfolio under the Variable Fee Approach.
The account value rolls forward as
AV[t+1] = AV[t] * (1 + max(r, g)) * (1 - f)– the credited rate (the underlying-items returnrfloored at any guaranteed rateg) less the variable feef– fromAV[0]= the model point’saccount_value. A surrender pays the account value; a death exit paysmax(account value, minimum_death_benefit)(GMDB) and the survivors reaching term paymax(account value, minimum_accumulation_benefit)(GMAB), so the excess over the account value is each guarantee’s intrinsic cost. Whenreturn_scenariosis given, each guarantee’s time value (the extra cost from return volatility) is folded into the CSM too – the credit-rate guarantee through the account-value growth, the GMDB and GMAB floors as put options on the account value.BEL is the present value of benefits and expenses less the premium, all at the underlying-items return; the CSM is
max(0, -(BEL + RA))– the entity’s unearned variable fee – accreted at the same return and released by coverage units. The RA is a confidence-level margin for expense risk.full=True(default) returns the BEL / RA / CSM / account-value trajectories;full=Falsefills only the headlinebel/ra/csm/variable_fee/time_value/loss_component(the inception CSM iscsm0, so the release kernel is skipped) and leaves the trajectory and cash-flow fieldsNone– the building block the portfolio orchestrator chunks to bound memory.basismust resolve to a singleBasis; multi-segment routers are not accepted.BEL, RA and CSM are returned as month-by-month trajectories. The deterministic BEL carries the guarantee’s intrinsic value only; when
return_scenarios– an(n_scenarios, n_time)array of monthly underlying-items returns – is supplied, the time value of the guarantee enters the inception fulfilment cash flows too, so the CSM absorbs it, andtime_valuerecords that amount per model point.lapse_sensitivity(defaultNone– a static lapse) turns on a dynamic lapse driven by the account-value moneyness: the lapse decrement is scaled by the per-policy-year moneyness factor (lapse_sensitivitythe elasticity, clamped to[lapse_floor, lapse_cap]), which lifts surrenders when the GMAB is out-of-the-money and lowers them when the floor bites. The factor keys on the GMAB; both the closed-form variable-annuity path (the account value is the closed-form growth path) and the account-backed universal-life path (the value is read from the rolled account) are supported.- 매개변수:
- 반환 형식:
- fastcashflow.vfa.measure_aggregate(model_points, basis, *, chunk_size=200000)[소스]#
Portfolio-aggregate VFA measurement in bounded memory.
The VFA analogue of
fastcashflow.gmm.measure_aggregate(): BEL / RA / CSM / variable fee / time value are additive across contracts, so the portfolio’s run-off is the per-model-point trajectories summed over the model-point axis. Runsmeasure(..., full=True)over row-blocks ofchunk_sizemodel points and accumulates only the(n_time+1,)sums, so peak memory isO(chunk_size x n_time)regardless ofn_mp.Returns a
Aggregate(scalar totals + aggregatebel_path/ra_path/csm_path/lic_path).account_valuedoes not carry: it is a per-policy level whose closed-form growth never terminates at the boundary, so summing it is horizon-dependent (thegroupVFA result drops it for the same reason). The deterministic intrinsic value only – the guarantee time value over return scenarios is a per-contract analysis (vfa.tvog()), not aggregated here.basisis a singleBasis(mixed / routed portfolios go throughfastcashflow.portfolio.measure_aggregate()).- 매개변수:
model_points (ModelPoints)
basis (Basis)
chunk_size (int)
- 반환 형식:
Aggregate
- fastcashflow.vfa.measure_inforce(model_points, state, basis, *, period_months=None)[소스]#
In-force diagnostic / runoff valuation of a VFA book at a single date.
Unlike the PAA (no CSM) and like the GMM carry (
fastcashflow.gmm.measure_inforce()), the prior period’s closing CSM is carried forward; unlike the GMM, the VFA CSM accretes at the underlying-items return (not a locked-in rate), so the carry is_csm_kernel(state.prior_csm, coverage_units, r_m)– the GMM carry roll with the return substituted for the lock-in rate. The fulfilment cash flows (BEL / RA / variable fee / guarantee intrinsic value) are re-measured from the observed fund value at the valuation date (state.account_value): the account-value path is re-anchored at that observed value (_project()), the decrements come from the inception projection (they depend on policy duration, not the fund), and the result is sliced at each contract’selapsed_monthsand re-based bycount / inforce[elapsed](exact for cash flows linear in the in-force).state(anInforceState) supplies the period-closeelapsed_months/count(reconciled ontomodel_pointsbyapply_inforce_state()), the carriedprior_csm, and the observedaccount_value(required here – a VFA in-force needs the real fund value, not the modelled one).period_months(default 12) is the length of the period the prior CSM is rolled.v1 returns the as-of valuation-date headline (
bel/ra/csm/variable_fee/time_value/loss_component); the trajectory fields areNone. Deferred here: the paragraph-45 remeasurement (the change in the entity’s share of the underlying-items fair value and the guarantee future-service cash flows) and the loss-component movement – both live insettle(), the paragraph-45 settlement of the same state – plus the stochastic time value of the guarantee (time_valueis zero – the deterministic intrinsic value only) and the full movement trajectory.경고
The CSM is a carry-only approximation: it is the prior CSM accreted at the basis underlying-items return and released by the expected (inception-run) coverage units. Unlike the BEL / RA / variable fee, it does not respond to the observed account value – the paragraph-45 adjustment for the change in the entity’s share of the underlying-items fair value is deferred. So the fulfilment-cash-flow figures are observed-AV-consistent but the CSM is not yet a paragraph-45-compliant settlement CSM; for that, settle the same state with
settle().The result is tagged
csm_basis = 'carry_only'(seefastcashflow.vfa.CSM_BASES), and the accounting-output entry points –roll_forward(),report(),group(),group_of_contracts()andwrite_measurement()– reject it, so this figure cannot be silently consumed as a settlement CSM.- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
period_months (int | None)
- 반환 형식:
- fastcashflow.vfa.settle(model_points, state, basis, *, period_months=None, premium_experience_future_fraction=0.0)[소스]#
Paragraph-45 subsequent-measurement settlement of a VFA in-force book (period close).
The real IFRS 17 paragraph-45 opening -> closing movement, replacing the carry-only
vfa.measure_inforceheadline for settlement / disclosure: the CSM is adjusted for the change in the entity’s share of the fair value of the underlying items (45(b)) and for the changes in fulfilment cash flows relating to future service (45(c)), the loss component is reversed / recognised per paragraphs 48 and 50(b), and one paragraph-B119 coverage-unit release is taken on the post-adjustment balance. Returns aSettlementMovementwhose blocks reconcile exactly and whoseclosing_measurementis a settlement-grade closing balance sheet (csm_basis='paragraph_45_settlement').stateis the closing-datedInforceStateextended with the prior reporting date’s figures, all at monthelapsed_months - period_months:prior_csm(the opening CSM),prior_count,prior_account_valueand (optionally)prior_loss_component; plus the closing snapshot’scountand observedaccount_value. The same state feedsmeasure_inforce(the fast carry-only diagnostic) and this settlement.How it computes: two forward projections of the same book share one decrement run – the EXPECTED leg anchors the account value at the opening observation and advances it under the basis return; the OBSERVED leg anchors at the closing observation – and both are sliced at the closing date. The paragraph-45 future-service change is minus the observed-vs-expected difference of the engine’s own BEL and RA (exact with respect to every engine convention, including the end-of-month fee timing and a binding minimum-crediting-rate floor); the 45(b) / 45(c) split is a disclosure decomposition of that exact total, the 45(b) line being the fund-consistent (end-of-month-weighted) variable-fee PV difference. The CSM accretes by direct compounding at the underlying-items return over the period and is released once at period end over the coverage units provided in the period against those provided plus expected from the opening date. With no experience deviation this reproduces the
measure_inforcemonthly carry exactly (a telescoping identity of the release weights).A contract whose paragraph 34 boundary falls inside the period is a final settlement: allowed, provided its closing
countandaccount_valueare zero (validated) – its remaining CSM releases in full. The opening date must lie strictly within every contract’s boundary.An onerous book amortises its loss component through the paragraph-50(a)/51 incurred-service channel (
loss_component_finance/loss_component_amortised): the period’s guarantee-excess + expense release (the claims+expenses pool, which for VFA excludes the account-value investment component) is split on the loss-component ratio, running the loss component to zero by the end of coverage (52). The B96(c) investment- component experience (csm_investment_experience– the expected less the actual account value returned on exits) adjusts the CSM whenstate.actual_investment_componentis given.v1 scope (see
SettlementMovementfor the full statement): deterministic, single basis rate (the finance-not-adjusting-CSM split is an exact zero residual), intrinsic guarantee only, no assumption change, within-period experience assumed equal to expected (only the closing count and observed account value deviate), per-model-point floors. A basis with asettlement_patternis supported: the movement carries the liability for incurred claims (lic_opening/claims_incurred/claims_paid/lic_closing, paragraphs 40(b) / 42 / 103(b)) – benefit claims build it up as incurred and run it off over the pattern, undiscounted and at the expected scale, reconstructed from the projection each period (the same v1 cuts as gmm.settle: no 42(b)/(c)).- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
period_months (int | None)
premium_experience_future_fraction (float | FloatArray)
- 반환 형식:
SettlementMovement
- fastcashflow.vfa.settle_aggregate(model_points, state, basis, *, period_months=None, chunk_size=200000, premium_experience_future_fraction=0.0)[소스]#
Portfolio-total paragraph-45 settlement in bounded memory.
settle()materialises(n_mp, n_time)projection intermediates – two forward projections over the whole book – so a million-policy close would peak far beyond memory. Every line of the settlement movement is additive across contracts, so this runssettle()over row blocks ofchunk_sizemodel points and accumulates only the scalar line totals; peak memory isO(chunk_size x n_time)regardless ofn_mp.Returns a
SettlementAggregate: the movement’s lines summed, movement-positive (reconcileapplies the display negation and reproduces the per-MP movement’s table exactly). The aggregate cannot be chained –closing_inputs()raises; chain per-MP movements instead.statejoinsmodel_pointsby mp_id once, before chunking, so a period-close file in its own row order never pairs one contract’s rows with another’s prior balances.- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
period_months (int | None)
chunk_size (int)
premium_experience_future_fraction (float | FloatArray)
- 반환 형식:
SettlementAggregate
- fastcashflow.vfa.settle_stream(input_path, output_dir, basis, *, calculation_methods=None, state_path=None, period_months=None, chunk_size=200000, id_column=None, validate_unique_mp_id=True)[소스]#
Stream a paragraph-45 period close through a parquet file, chunk by chunk.
The out-of-core variant of
settle()and the VFA counterpart offastcashflow.gmm.settle_stream(). The VFA base is a single policies frame (account value + guarantee floors, no coverages), soinput_pathis read inchunk_sizeblocks, each block’s(ModelPoints, InforceState)pair is settled, and the per-MP settlement movements land as onepart-NNNNN.parquetper chunk underoutput_dir– every movement line plus themeasurement_basismarker. Returns the model points processed.Input layouts, as in the GMM variant: ONE combined file (policies spec plus the closing-state columns, including the observed
account_valueand theprior_count/prior_account_value/prior_loss_componentfiguressettle()needs) or TWO files (policies parquet +state_pathstate parquet, semi-joined per chunk onmp_idwith the global id sets validated bidirectionally).lock_in_ratemust be uniform across the book (v1 scalar; the VFA carries it as a state echo only).Chaining on disk: each part carries
count,lock_in_rate,elapsed_months,account_value_closingand the closing balances, so the next period’s state file is assembled from the parts alone (prior_csm <- csm_closing,prior_loss_component <- loss_component_closing,prior_count <- count,prior_account_value <- account_value_closing, then advance to the next observation) – the disk side ofSettlementMovement.closing_inputs().
- fastcashflow.vfa.recognition_schedule(model_points, state, basis, *, band_edges_months=(12, 36, 60), period_months=None)[소스]#
Paragraph-109 maturity-band disclosure for a settled VFA book.
The VFA counterpart of
recognition_schedule(): allocates thesettle()closing CSM to maturity bands by each contract’s forward coverage-unit (in-force) fraction, so the bands sum to the closing CSM – when, in maturity terms, the remaining CSM is expected to be recognised in profit or loss. Onerous contracts carry no CSM and contribute nothing.band_edges_monthsare the band boundaries in months from the valuation date (default 12 / 36 / 60);period_monthsis the settlement period (default 12).- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
period_months (int | None)
- class fastcashflow.vfa.Measurement(bel, ra, csm, variable_fee, time_value, loss_component, bel_path=None, ra_path=None, csm_path=None, account_value_path=None, csm_accretion=None, csm_release=None, lic_path=None, guarantee_excess_cf=None, benefit_cf=None, fee_cf=None, discount_factor_bom=None, cashflows=None, model_points=None, group_labels=None, group_sizes=None, csm_basis='projected_runoff')[소스]#
VFA measurement of a direct-participation (account-value) portfolio.
The headline
bel,ra,csm,variable_fee,time_valueandloss_componentare(n_mp,)as-of figures – at inception formeasure, at the valuation date formeasure_inforce(the RA a confidence-level margin for expense risk; the BEL net of the account value the entity holds;variable_feethe present value of the entity’s fee – its share of the underlying items). The full path adds the(n_mp, n_time+1)trajectoriesbel_path/ra_path/csm_path/account_value_path(column 0 the as-of figure),Noneon the headline-only path; a grouped result also leavesaccount_value_pathNone(the account value is a per-policy level, not a group quantity). The CSM is accreted at the underlying-items return and released by coverage units:csm_path[:, t+1] = csm_path[:, t] + csm_accretion[:, t] - csm_release[:, t]
The guarantee time value drives the CSM but is reported separately in
time_value, not folded intobel.- 매개변수:
bel (FloatArray)
ra (FloatArray)
csm (FloatArray)
variable_fee (FloatArray)
time_value (FloatArray)
loss_component (FloatArray)
bel_path (FloatArray | None)
ra_path (FloatArray | None)
csm_path (FloatArray | None)
account_value_path (FloatArray | None)
csm_accretion (FloatArray | None)
csm_release (FloatArray | None)
lic_path (FloatArray | None)
guarantee_excess_cf (FloatArray | None)
benefit_cf (FloatArray | None)
fee_cf (FloatArray | None)
discount_factor_bom (FloatArray | None)
cashflows (Cashflows | None)
model_points (ModelPoints | None)
group_labels (np.ndarray | None)
group_sizes (IntArray | None)
csm_basis (str)
- fastcashflow.vfa.CSM_BASES = ('initial_measurement', 'projected_runoff', 'carry_only', 'paragraph_45_settlement')#
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable’s items.
If the argument is a tuple, the return value is the same object.
- fastcashflow.vfa.tvog(model_points, basis, return_scenarios)#
Measure the time value of a VFA contract’s minimum-crediting-rate guarantee.
Values the crediting-rate floor only – the guarantee that the account is credited
max(return, minimum_crediting_rate)each month. The GMDB / GMAB account-value floors are NOT included here; their time value is folded intovfa.measure(..., return_scenarios).time_valueinstead. This function is the standalone crediting-rate analysis.return_scenariosis an(n_scenarios, n_time)array of monthly underlying-items returns – one path per scenario,n_timebeing the projection horizon. The model points must carry a crediting guarantee – aminimum_crediting_rateof0.0(a real 0% floor,max(return, 0)) or a positive rate. A contract with no crediting guarantee (NO_GUARANTEE_RATE) is rejected: it has no crediting-rate time value to measure (usevfa.measure(..., return_scenarios).time_valuefor the GMDB / GMAB floor time value instead). In v1 the rate is taken as a portfolio-wide scalar (per-MP varying rates with stochastic returns are a future extension), so the column is required to be uniform across rows.The guarantee cost is the present value of account-value benefits in excess of the no-guarantee benefits. Its mean over the scenarios is the total value; the cost in the central scenario (
investment_return) is the intrinsic value; the difference is the time value (TVOG).Maturity survivors are weighted at the matured term (time = term, one month past their term - 1 exit column), the same re-seat the folded credit-rate TVOG in
vfa.measure()uses, so the two agree on a mixed-term book.- 매개변수:
- 반환 형식:
- class fastcashflow.TVOGResult(guarantee_cost, intrinsic_value, time_value)[소스]#
The cost of a minimum guarantee, split into intrinsic and time value.
guarantee_costis the(n_scenarios,)present value of the guarantee under each scenario – its distribution.intrinsic_valueis the cost in the central (deterministic) scenario;time_valueis the TVOG, the mean cost in excess of the intrinsic value; andtotal_valueis their sum, the guarantee’s full economic cost.- 매개변수:
Portfolio (mixed-model orchestration)#
One heterogeneous portfolio – GMM, PAA and VFA contracts in a single routed
file – measured in one call. Each contract is routed to its segment’s
measurement model and each model’s native result is kept separate (a BEL and an
LRC are never summed into one array). measure returns the per-model-point
PortfolioMeasurement; measure_aggregate
returns the chunked, bounded-memory
PortfolioAggregate (a scalable sum of the
measured model-point results – not an IFRS group remeasurement and not a group of contracts
re-floor). loss_component is the lone quantity summed across models.
- fastcashflow.portfolio.measure(model_points, basis, *, full=True, backend='cpu', chunk_size=200000, return_scenarios=None)[소스]#
Measure a mixed-model portfolio in one call.
basismust be aBasisRouterwhose segments carry their IFRS 17 measurement model (read_basisreads it from themeasurement_modelcolumn of the segments sheet). Each row is routed to its segment’s model; the result keeps each model’s native measurement separate.fullmatchesfcf.gmm.measure()(the full trajectory vs the fused headline).With
full=Falsethe PAA / VFA partitions are measured headline-only inchunk_sizerow-blocks, so peak memory staysO(chunk_size x n_time)instead of materialising every contract’s trajectory at once (GMM’sfull=Falseis already a fused, no-trajectory kernel;chunk_sizedoes not affect it). The headline-only result still servessummary()/loss_component_total();group/roll_forward/reportneedfull=True.full=Truekeeps every trajectory (and is memory-bound).Each model’s rows are routed to its own kernel and kept in a separate slot of the result. A non-GMM row is never silently measured as GMM.
return_scenarios– an(n_scenarios, horizon)array of monthly underlying-items returns – prices the guarantee time value (TVOG) of the VFA partition, exactly asfcf.vfa.measure(..., return_scenarios=...)does for a single book; it is forwarded only to the VFA slot (GMM / PAA carry no return guarantee). It is an error to pass it for a portfolio with no VFA rows. When supplied, the VFA partition is measured without chunking and the scenario is sliced to each segment’s own horizon, sohorizonneed only cover the longest VFA contract (the per-model-point time value is additive), andchunk_sizedoes not bound the VFA scenario pass. Omitted (the default), the mixed path measures the VFA intrinsic value (deterministic) only.When the router declares a single measurement model the model partition is a no-op, so the whole book routes straight to that model – no per-row partition factorise, no subset copy. (A router that declares PAA / VFA segments but whose rows happen to be all GMM is not this case: it keeps the row partition, which is what makes the unused declared segment harmless.)
- 매개변수:
model_points (ModelPoints)
full (bool)
backend (str)
chunk_size (int)
- 반환 형식:
- fastcashflow.portfolio.measure_aggregate(model_points, basis, *, chunk_size=200000)[소스]#
Chunked aggregate measurement of a mixed-model portfolio.
A scalable sum of measured model-point results: each model’s inception totals and run-off trajectories summed over the model-point axis, computed in
chunk_sizerow-blocks so peak memory isO(chunk_size x n_time)– it works where the per-model-pointmeasure()full=Truewould OOM. Returns aPortfolioAggregatekeeping each model’s native figures separate (a BEL and an LRC are never pooled).Not an IFRS group remeasurement and not a group re-floor engine: every figure is the sum of the per-model-point results (CSM is the sum of each contract’s floored CSM – the
measure()headline aggregated, notgroup()’sCSM(sum FCF)). A per-group re-floor is a separate concern.- 매개변수:
model_points (ModelPoints)
chunk_size (int)
- 반환 형식:
- class fastcashflow.portfolio.PortfolioMeasurement(model_points, gmm=None, paa=None, vfa=None)[소스]#
Result of
measure(): oneModelMeasurementper model present (Nonewhen absent), keyed by model so a BEL and an LRC are never conflated.model_pointsis the full portfolio – the grouping axes for downstream per-segment / group of contracts analysis (e.g.group(pm.gmm.measurement, by=["product"])). The per-model indices must partition0..n_mp-1exactly; this is checked at construction.- 매개변수:
model_points (ModelPoints)
gmm (ModelMeasurement | None)
paa (ModelMeasurement | None)
vfa (ModelMeasurement | None)
- loss_component_total()[소스]#
The portfolio’s total onerous-contract loss at inception.
This is the only quantity summed across measurement models: the loss component is
max(0, fulfilment cash flows)at inception, defined and signed identically under GMM, PAA and VFA, so adding it across models is meaningful. A BEL, an LRC and a VFA BEL are not added – they measure different things; reach those through the per-model blocks ofsummary()(e.g.pm.gmm.measurement.bel).- 반환 형식:
- summary()[소스]#
Per-model headline totals, each model in its own block.
Returns
{"loss_component_total": float, <model>: {...}}with a block only for the models the portfolio carries. Each block sums that model’s own headline figures over its rows –gmm/vfagivebel/ra/csm/loss_component,paagiveslrc/loss_component. Figures of different meaning are never pooled into one number;loss_component_totalis the single cross-model sum (seeloss_component_total()).- 반환 형식:
- class fastcashflow.portfolio.ModelMeasurement(index, measurement)[소스]#
One measurement model’s slice of a portfolio: the original row positions (
index, sorted and unique) and the native result over exactly those rows.- 매개변수:
measurement (Measurement | Measurement | Measurement)
- class fastcashflow.portfolio.PortfolioAggregate(gmm=None, paa=None, vfa=None)[소스]#
Result of
measure_aggregate(): one aggregate per model present (Nonewhen absent), each holding that model’s inception totals and run-off trajectories summed over the model-point axis. A scalable sum of measured model-point results – no per-model-point row, so it works at a scale where the per-rowPortfolioMeasurementwould not fit in memory. Not an IFRS group remeasurement and not a group re-floor engine: every figure is the sum of the per-model-point results (CSM is the sum of each contract’s floored CSM, the headline aggregated – notgroup()’sCSM(sum FCF)). A BEL and an LRC are never pooled;loss_component_totalis the one cross-model sum.- 매개변수:
gmm (Aggregate | None)
paa (Aggregate | None)
vfa (Aggregate | None)
Per-group aggregate (scalable group of contracts)#
measure_group_of_contracts is the chunked, bounded-memory form of
fastcashflow.group_of_contracts(): the IFRS 17 unit of account
(portfolio x annual cohort x profitability) computed where holding the
per-model-point measure(full=True) would not fit in memory.
measure_group is the same machinery on any axis (the scalable
fastcashflow.group()). Both return a
PortfolioGroups holding each model’s native
grouped measurement – its rows the groups – so the group rows flow on into
fastcashflow.roll_forward(), fastcashflow.reconcile() and
fastcashflow.report().
The floor unit is what distinguishes this from measure_aggregate:
measure_aggregatefloors per model point, then sums –sum max(0, -FCF_i). It is a scalable sum of the already-floored per-contract results, never re-grouping them.measure_group_of_contracts/measure_groupre-floor per group, on the summed fulfilment cash flows –max(0, -sum FCF_in_group)per group, applied once on the fully-accumulated group (a group spans chunks, so it is never floored per chunk).
At initial recognition these agree: under any paragraph-16-compliant grouping a
group never mixes inception-FCF signs, so CSM(sum FCF) == sum CSM(FCF) and
measure_group_of_contracts and measure_aggregate report the same totals. The re-floor
changes the number only for a deliberately coarser, sign-mixing grouping (e.g.
measure_group(by="product") with no profitability axis – within-group
mutualisation) or in subsequent measurement (out of scope here). So at inception
measure_group_of_contracts’s value over measure_aggregate is the per-group rows
(disclosure, roll-forward, the paragraph-44 foundation), not a different number.
- fastcashflow.portfolio.measure_group_of_contracts(model_points, basis, *, portfolio='product', cohort='issue_year', profitability=None, chunk_size=200000)[소스]#
Scalable group-of-contracts aggregation – the IFRS 17 unit of account.
The chunked, memory-bounded form of
fcf.group_of_contracts(): the portfolio (paragraph 14) x annual cohort (22) x profitability (16) grouping, re-flooring the CSM / loss component on each group’s fulfilment cash flows (CSM(sum FCF), unlikemeasure_aggregate()which sums each contract’s already-floored CSM). Computed inchunk_sizerow-blocks so it works where holding the per-model-pointmeasure(full=True)would OOM.At initial recognition, under any paragraph-16-compliant grouping a group never mixes inception-FCF signs, so the re-floor equals the per-model-point floor sum –
measure_group_of_contractsandmeasure_aggregate()report the same totals;measure_group_of_contracts’s value is the per-group rows (disclosure / roll-forward / the paragraph-44 foundation).Arguments mirror
fcf.group_of_contracts():portfolio/cohortname the axis columns;profitabilityisNone(derive the onerous / remaining split per model point from the inception loss component, one rule across GMM / PAA / VFA), a column name (a locked classification, paragraph 24), or a precomputed(n_mp,)array (e.g. the three-way split).Unlike
fcf.group_of_contracts(), a missingissue_datewith the default cohort is rejected, not silently collapsed to one cohort: a silent collapse would mutualise across annual cohorts (paragraph 22) invisibly at settlement scale.issue_age/term_monthsare never a cohort substitute.- 매개변수:
model_points (ModelPoints)
portfolio (str)
cohort (str)
chunk_size (int)
- 반환 형식:
- fastcashflow.portfolio.settle_group_of_contracts(model_points, inforce_state, basis, period_months=None, *, portfolio='product', cohort='issue_year', coverage_units=None, profitability=None, premium_experience_future_fraction=0.0, chunk_size=200000)[소스]#
Group-of-contracts settlement for a routed CSM portfolio.
A pure GMM book returns a
GoCSettlement(paragraph 44); a pure VFA book returns a_vfa.GoCSettlement(paragraph 45).coverage_unitsandprofitabilityare required and explicit. PAA is rejected (no CSM / floor, so a per-GoC algebra is meaningless – usepaa.settleand sum by your own groupby), and a book mixing GMM and VFA is rejected whole: a group of contracts sits in one product, hence one measurement model. Thepremium_experience_future_fractionargument applies to both the GMM and VFA paths (paragraph B96(a); each routes the future leg into its own paragraph-45 algebra).- 매개변수:
model_points (ModelPoints)
inforce_state (InforceState)
period_months (int | None)
chunk_size (int)
- 반환 형식:
- fastcashflow.portfolio.measure_group(model_points, basis, by, *, chunk_size=200000)[소스]#
Scalable group aggregation of a mixed-model portfolio on any axis.
The chunked, memory-bounded form of
fcf.group(): each model’s rows are aggregated to thebyaxis (re-flooring the CSM / loss component on each group’s fulfilment cash flows,CSM(sum FCF)), computed inchunk_sizerow-blocks so peak memory isO(chunk_size x n_time)plus theO(n_groups x n_time)accumulator – it works where holding the per-model-pointmeasure(full=True)would OOM.byis one of a single axis name, a list of axis names and/or precomputed(n_mp,)label arrays, or a single(n_mp,)label array (asfcf.group()). For the IFRS 17 unit of account usemeasure_group_of_contracts(), the preset onportfolio x annual cohort x profitability.- 매개변수:
model_points (ModelPoints)
chunk_size (int)
- 반환 형식:
- class fastcashflow.portfolio.PortfolioGroups(gmm=None, paa=None, vfa=None)[소스]#
Result of
measure_group()/measure_group_of_contracts(): one native grouped measurement per model present (Nonewhen absent), its rows the groups (an IFRS 17 group of contracts formeasure_group_of_contracts()). The scalable form offcf.group_of_contracts()– it re-floors on each group’s fulfilment cash flows (CSM(sum FCF)), computed in bounded memory so it works where holding the per-model-pointmeasure(full=True)would OOM. A BEL and an LRC are never pooled;loss_component_totalis the one cross-model sum.Each slot is the same native type
fcf.group()returns (_gmm.Measurement/_paa.Measurement/_vfa.Measurementwithgroup_labels/group_sizesset), so the group rows flow straight intofcf.roll_forward()/fcf.reconcile()/fcf.report(). There is no cross-model group of contracts: a portfolio (product) carries one measurement model, so a group always sits inside one model’s slot.- 매개변수:
gmm (Measurement | None)
paa (Measurement | None)
vfa (Measurement | None)
- loss_component_total()[소스]#
The portfolio’s total onerous-group loss – the only quantity summed across measurement models (
max(0, group FCF), defined and signed identically under GMM / PAA / VFA). A BEL, an LRC and a VFA BEL are not added; reach those throughsummary().- 반환 형식:
- summary()[소스]#
Per-model headline totals, each model in its own block (a BEL and an LRC are never pooled);
loss_component_totalis the lone cross-model sum. Each block sums that model’s figures over its group rows –gmm/vfagivebel/ra/csm/loss_component,paagiveslrc/loss_component. A block appears only for a model the portfolio carries.- 반환 형식:
- class fastcashflow.portfolio.GoCSettlement(group_labels, group_sizes, period_months, bel_opening, bel_interest, bel_release, bel_experience, bel_closing, ra_opening, ra_interest, ra_release, ra_experience, ra_closing, finance_wedge, premium_experience_revenue, csm_opening, csm_accretion, csm_experience_unlocking, csm_premium_experience, csm_investment_experience, claims_experience, expense_experience, loss_component_opening, loss_component_finance, loss_component_amortised, lic_opening, claims_incurred, lic_finance, claims_paid, lic_closing, coverage_units_provided, coverage_units_future, csm_release, csm_closing, loss_component_reversed, loss_component_recognised, loss_component_closing, lock_in_rate, model_points=None, group_inverse=None, lock_in_rate_by_mp=0.0, profitability_by_mp=None, measurement_basis='settlement')[소스]#
Group-of-contracts paragraph-44 settlement movement.
Rows are IFRS 17 groups. Linear GMM settlement lines are group-summed; the paragraph-48/50(b) CSM/loss-component algebra and the B119 release are applied once at group grain.
closing_inputs()allocates group closing balances back to model points by closing-count pro-rata, or by an explicit per-row allocation weight.- 매개변수:
group_labels (ndarray)
period_months (int)
premium_experience_revenue (ndarray[tuple[Any, ...], dtype[float64]])
csm_experience_unlocking (ndarray[tuple[Any, ...], dtype[float64]])
csm_premium_experience (ndarray[tuple[Any, ...], dtype[float64]])
csm_investment_experience (ndarray[tuple[Any, ...], dtype[float64]])
claims_experience (ndarray[tuple[Any, ...], dtype[float64]])
expense_experience (ndarray[tuple[Any, ...], dtype[float64]])
loss_component_opening (ndarray[tuple[Any, ...], dtype[float64]])
loss_component_finance (ndarray[tuple[Any, ...], dtype[float64]])
loss_component_amortised (ndarray[tuple[Any, ...], dtype[float64]])
coverage_units_provided (ndarray[tuple[Any, ...], dtype[float64]])
coverage_units_future (ndarray[tuple[Any, ...], dtype[float64]])
loss_component_reversed (ndarray[tuple[Any, ...], dtype[float64]])
loss_component_recognised (ndarray[tuple[Any, ...], dtype[float64]])
loss_component_closing (ndarray[tuple[Any, ...], dtype[float64]])
model_points (ModelPoints | None)
group_inverse (ndarray[tuple[Any, ...], dtype[int64]] | None)
lock_in_rate_by_mp (ndarray[tuple[Any, ...], dtype[float64]] | float)
profitability_by_mp (ndarray | None)
measurement_basis (str)
- class fastcashflow.vfa.GoCSettlement(group_labels, group_sizes, period_months, bel_opening, bel_interest, bel_release, bel_experience, bel_closing, ra_opening, ra_interest, ra_release, ra_experience, ra_closing, csm_fv_share, csm_future_service, csm_premium_experience, premium_experience_revenue, csm_investment_experience, claims_experience, expense_experience, csm_opening, csm_accretion, variable_fee_closing, account_value_closing, loss_component_opening, loss_component_finance, loss_component_amortised, lic_opening, claims_incurred, lic_finance, claims_paid, lic_closing, coverage_units_provided, coverage_units_future, csm_release, csm_closing, loss_component_reversed, loss_component_recognised, loss_component_closing, lock_in_rate, model_points=None, group_inverse=None, lock_in_rate_by_mp=0.0, profitability_by_mp=None, account_value_by_mp=None, measurement_basis='settlement')[소스]#
Group-of-contracts paragraph-45 settlement movement (VFA).
The VFA mirror of
GoCSettlement. Rows are IFRS 17 groups. The LINEAR VFA settlement lines are group-summed – includingcsm_fv_share(45(b)) andcsm_future_service(45(c)), each carrying its ownv_half/k_obs, so the group fv_share is the SUM of the per-MP fv_shares (not a re-derivation from a re-summed group account value). The paragraph-48/50(b) algebra and the single B119 release are applied once at group grain on the group-summed inputs (the future-service change issum(csm_fv_share + csm_future_service)).closing_inputs()allocates the group closing CSM / loss component back to model points by closing- count pro-rata (or an explicit weight) and carries each contract’s observed account value forward.- 매개변수:
group_labels (np.ndarray)
group_sizes (IntArray)
period_months (int)
bel_opening (FloatArray)
bel_interest (FloatArray)
bel_release (FloatArray)
bel_experience (FloatArray)
bel_closing (FloatArray)
ra_opening (FloatArray)
ra_interest (FloatArray)
ra_release (FloatArray)
ra_experience (FloatArray)
ra_closing (FloatArray)
csm_fv_share (FloatArray)
csm_future_service (FloatArray)
csm_premium_experience (FloatArray)
premium_experience_revenue (FloatArray)
csm_investment_experience (FloatArray)
claims_experience (FloatArray)
expense_experience (FloatArray)
csm_opening (FloatArray)
csm_accretion (FloatArray)
variable_fee_closing (FloatArray)
account_value_closing (FloatArray)
loss_component_opening (FloatArray)
loss_component_finance (FloatArray)
loss_component_amortised (FloatArray)
lic_opening (FloatArray)
claims_incurred (FloatArray)
lic_finance (FloatArray)
claims_paid (FloatArray)
lic_closing (FloatArray)
coverage_units_provided (FloatArray)
coverage_units_future (FloatArray)
csm_release (FloatArray)
csm_closing (FloatArray)
loss_component_reversed (FloatArray)
loss_component_recognised (FloatArray)
loss_component_closing (FloatArray)
lock_in_rate (FloatArray)
model_points (ModelPoints | None)
group_inverse (IntArray | None)
lock_in_rate_by_mp (FloatArray | float)
profitability_by_mp (np.ndarray | None)
account_value_by_mp (FloatArray | None)
measurement_basis (str)
Tracing and validation#
Per-contract tracers that unfold a single model point’s measurement as an ASCII tree – which segment, table and rate feed each step, the year-by-year rates and cash flows, and the anchor-month discount / BEL / CSM. Used for hand-calculation validation, learning and debugging. Each measurement approach has its own tracer.
- fastcashflow.gmm.trace(mp_index, model_points, basis, *, file=None)[소스]#
Print a tree of how one model point’s BEL / RA / CSM is computed.
- 매개변수:
mp_index (int) – 0-based row index in
model_points.model_points (ModelPoints) – Portfolio
ModelPoints. The function slices to a single row before runningmeasure(), so a 1M-row portfolio does not pay for the trace of one contract.basis (Basis | dict) – A single
Basis, or theBasisRouterreturned byfastcashflow.io.read_basis()/fastcashflow.io.load_sample_basis(). With the router form the function looks up the segment via the model point’s(product, channel).file (IO | None) – Where to write.
Nonewrites tosys.stdout.calculation (Use it when an engine result disagrees with a hand)
segment (tree shows the)
tables
values (rate)
roll- (cash flows and)
step (forward step by)
glance. (so the diverging step is visible at a)
- 반환 형식:
None
- fastcashflow.gmm.trace_diff(mp_index, model_points, basis_a, basis_b, *, label_a='before', label_b='after', file=None)[소스]#
Print a tree of how the BEL / RA / CSM of one model point moves when basis change.
- 매개변수:
mp_index (int) – 0-based row index in
model_points.model_points (ModelPoints) – Portfolio
ModelPoints. Subset to the single row before eachmeasure()so the diff cost stays proportional to one MP.basis_a (Basis | dict) – Two basis to compare. Either a
Basisor theBasisRouterfromfastcashflow.io.read_basis(). With a router, each is routed independently by the model point’s(product, channel)– comparing two segments is also fine.basis_b (Basis | dict) – Two basis to compare. Either a
Basisor theBasisRouterfromfastcashflow.io.read_basis(). With a router, each is routed independently by the model point’s(product, channel)– comparing two segments is also fine.label_a (str) – Short labels for the two columns in the printed diff (e.g.
"baseline"vs"mortality+10%"). Default"before"/"after".label_b (str) – Short labels for the two columns in the printed diff (e.g.
"baseline"vs"mortality+10%"). Default"before"/"after".file (IO | None) – Where to write.
Nonewrites tosys.stdout.Sections
--------
identity) (1. Header (the model-point)
names) (2. Labels (the two basis)
differ (3. Assumption changes -- only the fields that)
side (4. Rate deltas -- year-by-year annual rates side by)
component (5. Cash flow deltas -- annual sum of each cash-flow)
months (7. BEL / CSM deltas at anchor)
months
loss_component (8. Final -- BEL / RA / FCF / CSM /) – percentage change
and (with absolute) – percentage change
so (Equal values are suppressed from the assumption-change section)
moved. (the eye lands on what actually)
- 반환 형식:
None
- fastcashflow.gmm.trace_bel_step(mp_index, model_points, basis, *, months=None, file=None)[소스]#
Print, term by term, how one model point’s BEL[t] is built.
The kernel runs the IFRS 17 backward recursion:
BEL[t] = annuity[t] - premium[t] + (mortality + morbidity + disability + expense + surrender)[t] * (1 + i[t])^(-1/2) + BEL[t+1] * (1 + i[t])^(-1)
seeded by
BEL[term] = maturity_benefit. This function unrolls the equation at chosen months: prints each cash-flow component att, the half-month and full-month discount factors, the mid-term piece (cash flows at mid-month) and the tail piece (carry from the next month), then the resultingBEL[t]against the engine’s value. When the printed identity holds the engine and a hand calculation are in agreement; when it does not, the offending term is right there in the row.- 매개변수:
mp_index (int) – 0-based row index in
model_points.model_points (ModelPoints) – Portfolio
ModelPoints. Subset to the single row before runningmeasure().basis (Basis | dict) – A
Basisor theBasisRouterfromfastcashflow.io.read_basis()(routed by the row’s(product, channel)liketrace()).months (list[int] | None) – Anchor months at which to unroll the recursion.
Noneuses{0, 12, term//2, term-1, term}– inception, end of year 1, the half-way point, the last recursion step, and the seed. Out-of-range entries are ignored.file (IO | None) – Where to write.
None->sys.stdout.
- 반환 형식:
None
- fastcashflow.gmm.trace_csm_step(mp_index, model_points, basis, *, months=None, file=None)[소스]#
Print, term by term, how one model point’s CSM[t] is built.
The kernel runs the forward recursion:
csm[0] = max(0, -(BEL[0] + RA[0])) csm[t] = csm[t-1] + accretion[t-1] - release[t-1] accretion[t-1] = csm[t-1] * i[t-1] release[t-1] = (csm[t-1] + accretion[t-1]) * coverage_units[t-1] / sum(coverage_units[t-1:])
coverage_unitsis the in-force survival series. This function unrolls the step at chosen months: prints the prior CSM, the monthly rate, the accretion, the coverage-unit share consumed in that month, the release amount, and the resulting CSM[t] against the engine’s value.For an onerous contract (
csm[0] == 0), every subsequent step is zero too – the printed trace then visibly says so, which is itself useful when checking that the engine is honouring the floor.- 매개변수:
mp_index (int) – 0-based row index in
model_points.model_points (ModelPoints) – Same shape as
trace_bel_step().basis (Basis | dict) – Same shape as
trace_bel_step().file (IO | None) – Same shape as
trace_bel_step().months (list[int] | None) – Months at which to unroll the step (each row shows the computation that produced
csm[t]fromcsm[t-1]).Noneuses{1, 12, term//2, term}. Out-of-range entries are ignored.t = 0is the seed and is always printed regardless ofmonths.
- 반환 형식:
None
- fastcashflow.vfa.trace(mp_index, model_points, basis, *, return_scenarios=None, file=None)[소스]#
Print a tree of how one VFA model point’s BEL / RA / CSM is computed.
The VFA (variable-fee, account-value) counterpart of
trace(). It slices to a single row, runs_vfa.measure(), and shows the account-value trajectory, the GMDB / GMAB floors (where the guarantee bites), the variable fee and the BEL / RA / CSM – plus the guarantee time value (TVOG) whenreturn_scenariosis supplied. Use it on direct-participation contracts;trace()traces the GMMmeasureand does not cover the account-value mechanic.
- fastcashflow.paa.trace(mp_index, model_points, basis, *, revenue_basis='time', file=None)[소스]#
Print a tree of how one PAA model point’s LRC / revenue / LIC is built.
The PAA (Premium Allocation Approach, the short-duration simplification) counterpart of
trace(). PAA has no CSM – the liability for remaining coverage (LRC) is an unearned-premium-style balance – so the tree shows the LRC roll-forward (premium in, revenue released), the insurance service result (revenue less service expense) and the liability for incurred claims (LIC). Use it on PAA contracts;trace()traces the GMMmeasure(BEL / RA / CSM).
Reinsurance#
- fastcashflow.reinsurance.measure(model_points, basis, *, treaty, underlying_loss_component=None, recovery_percentage=None, full=True)[소스]#
Measure a reinsurance contract held over a direct portfolio.
treatydescribes how the cover cedes the direct cash flows – e.g.QuotaShare(cession=0.5). The BEL is the present value of reinsurance premiums less recoveries; the RA is the margin on the ceded claims (the risk transferred). The CSM is-(BEL - RA)– the net cost or gain of the cover – and may be negative; it is accreted and released by coverage units like a direct contract’s CSM, but with no loss component (paragraph 65).basismust resolve to a singleBasis; multi-segment routers are not accepted.full=Falsereturns only the headline BEL / RA / CSM and leaves all trajectory and cash-flow fieldsNone.- 매개변수:
- 반환 형식:
- fastcashflow.reinsurance.settle(model_points, state, basis, *, treaty, period_months=None, underlying_loss_opening=None, underlying_loss_closing=None, recovery_percentage=None)[소스]#
Paragraph-66 subsequent-measurement settlement of a reinsurance contract held (the reinsurance counterpart of
settle()).The opening -> closing movement over one reporting period: BEL / RA re-measured at current rates, the CSM accreted at the locked-in rate (66(b)/B72(b)), adjusted for the future-service change measured at the locked-in rate (66(c)/B72(c) – the current-rate gap is the
finance_wedge), and released once at the period end over coverage units (66(e)/B119). The ONE difference fromgmm.settle: a reinsurance contract held cannot be onerous (paragraph 65), so the CSM is NOT floored and there is no loss component – the closing CSM may be negative (a net cost of cover). The BEL is PV(reinsurance premium) - PV(recovery), so the locked-in second leg re-prices the ceded cash flows at the locked-in rate.On-track experience makes every experience line zero and telescopes the closing CSM to the carry bridge (
measure_inforce()) exactly. A row whose closing date reaches the contract boundary withcount = 0is a final settlement (full B119 derecognition, paragraph 76).The loss-recovery component (paragraphs 66A-66B / B119F) is tracked when the cover is held over an onerous underlying group: pass
underlying_loss_opening/underlying_loss_closing(the underlying group’s loss component at the two dates, from the directgmm.settle) and, for a non-proportional treaty,recovery_percentage. The fourloss_recovery_*movement lines re-derive the component as the underlying loss x the claim recovery % and amortise it in lock-step with the underlying loss (the recovery reverses in P&L as the underlying runs off). The CSM is NOT re-adjusted here – the 66A CSM effect (csm_after = csm0 - loss_recovery) is a one-time inception event inreinsurance.measure. Absent the inputs => zero (byte-identical). B119C timing (the cover entered before/at the onerous underlying) is the caller’s responsibility.- 매개변수:
- 반환 형식:
SettlementMovement
- fastcashflow.reinsurance.settle_aggregate(model_points, state, basis, *, treaty, period_months=None, chunk_size=200000, underlying_loss_opening=None, underlying_loss_closing=None, recovery_percentage=None)[소스]#
Portfolio-total paragraph-66 reinsurance settlement in bounded memory.
Runs
settle()over row blocks ofchunk_sizemodel points and accumulates only the scalar line totals (every settlement line is additive across contracts), combined withmath.fsumso the total does not depend on the chunking. Replaces the carry-bridge aggregatemeasure_inforce_aggregate()with a true settlement.- 매개변수:
model_points (ModelPoints)
state (InforceState)
basis (Basis)
treaty (Treaty)
period_months (int | None)
chunk_size (int)
underlying_loss_opening (ndarray[tuple[Any, ...], dtype[float64]] | None)
underlying_loss_closing (ndarray[tuple[Any, ...], dtype[float64]] | None)
recovery_percentage (float | None)
- 반환 형식:
SettlementAggregate
- fastcashflow.reinsurance.settle_stream(input_path, output_dir, basis, *, treaty, coverages=None, calculation_methods=None, state_path=None, period_months=None, chunk_size=200000, id_column=None, validate_unique_mp_id=True)[소스]#
Stream a paragraph-66 reinsurance period close through a parquet file.
The out-of-core variant of
settle(): reads the direct policies + coverages parquet inchunk_sizeblocks, cedes withtreaty, settles each block, and writes the per-MP settlement movements (onepart-NNNNN.parquetper chunk). Same one-combined-file / two-file (state_path) layouts assettle_stream(); the reinsurance state carriesprior_csm(which may be negative – a net cost) andprior_count. Returns the number of model points processed. A ceded book is usually small enough forsettle()/settle_aggregate(); this exists for API symmetry with the other models.
Proportional reinsurance – cede a fixed fraction of claims and premiums.
cession(in[0, 1]) is the ceded fraction: the cedant recovers that fraction of its claims and pays the same fraction of its premiums as reinsurance premium.- 매개변수:
cession (float)
- class fastcashflow.reinsurance.Measurement(bel, ra, csm, loss_recovery_component=None, bel_path=None, ra_path=None, csm_path=None, csm_accretion=None, csm_release=None, recovery=None, reinsurance_premium=None, cashflows=None, discount_factor_bom=None, model_points=None, group_labels=None, group_sizes=None, measurement_basis='inception')[소스]#
Measurement of a reinsurance contract held.
Headline
bel,raandcsmare(n_mp,)inception figures –belis the present value of reinsurance premiums less recoveries (a net cost when positive),rais the risk transferred,csmis the inception net cost or gain (may be negative). Thebelsymbol is shared with the GMM result for a uniform surface, but for reinsurance held it is the present value of fulfilment cash flows of a reinsurance ASSET (IFRS 17 paragraph 63), not a liability – a negativebelis a net reinsurance asset. The trajectory fields are populated only on the full path;csm_pathreconciles ascsm_path[:, t+1] = csm_path[:, t] + csm_accretion[:, t] - csm_release[:, t].- 매개변수:
bel (FloatArray)
ra (FloatArray)
csm (FloatArray)
loss_recovery_component (FloatArray | None)
bel_path (FloatArray | None)
ra_path (FloatArray | None)
csm_path (FloatArray | None)
csm_accretion (FloatArray | None)
csm_release (FloatArray | None)
recovery (FloatArray | None)
reinsurance_premium (FloatArray | None)
cashflows (Cashflows | None)
discount_factor_bom (FloatArray | None)
model_points (ModelPoints | None)
group_labels (np.ndarray | None)
group_sizes (IntArray | None)
measurement_basis (str)
Pricing#
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.10for 10%); must satisfy0 <= margin < 1.csm– an absolute target CSM (profit) per model point.
Every product field of
model_pointsis used as given – onlypremiumis ignored, since it is the unknown being solved for. Returns the solved premium per model point, shape(n_mp,).
Profit testing#
Inception value (CSM + RA), profit margin and profit emergence over a GMM measurement, plus the traditional net-level-premium reserve and statutory profit test.
- fastcashflow.pricing.csm_plus_ra(measurement)[소스]#
CSM + RA per model point – the present value at issue of the profit the contract is expected to release:
CSM + RA - loss component(equivalently-BEL). Pre-tax and pre-required-capital. NOT the value of new business (vnb()), which nets the cost of capital.
- fastcashflow.pricing.profit_margin(measurement)[소스]#
Profit margin per model point –
csm_plus_raover the present value of premiums (the PVNBP margin). Zero-premium contracts return 0.
- fastcashflow.pricing.signature(measurement, period_months=12)[소스]#
The IFRS 17 profit signature – the per-period insurance service result (CSM release + RA release on a best-estimate run), summed over the book.
Built from
report(); the present value of the signature at the locked-in rate approximately reconciles to the portfoliocsm_plus_ra()total (the exact figure iscsm_plus_ra; the annual signature is an aggregated presentation that re-discounts a year’s profit from its mid-point).- 매개변수:
period_months (int)
- 반환 형식:
- class fastcashflow.ProfitSignature(period_months, month_end, profit)[소스]#
Period-by-period shareholder profit emergence of a book.
profitis(n_periods,)– the profit recognised in each reporting period ofperiod_monthsmonths (the portfolio total).month_endis the elapsed month at the end of each period.present_valuediscounts the stream;totalis its undiscounted sum.- 매개변수:
- present_value(annual_rate)[소스]#
Present value of the profit stream at a flat annual
annual_rate, each period discounted to issue from its mid-point (the standard mid-year convention – the period’s profit emerges over the period). This approximately reconciles tocsm_plus_ra(); the exact figure iscsm_plus_ra(CSM + RA), not this aggregated-and-re-discounted stream.
- fastcashflow.pricing.irr(cashflows, *, period_months=12, low=-0.99, high=10.0)[소스]#
Internal rate of return of a shareholder cash-flow stream (one entry per period of
period_monthsmonths, period 0 first).The rate
r(annual) at which the net present value is zero, found by bisection. The stream must change sign (a day-0 outgo / strain followed by profit), else there is no internal rate and aValueErroris raised – an all-positive IFRS 17 signature has none; pair it with the new-business strain (the statutory profit test) for a meaningful IRR.
- fastcashflow.pricing.break_even_year(cashflows, *, period_months=12)[소스]#
The first period (1-based, in
period_monthsunits) at which the cumulative shareholder cash flow turns non-negative – the payback point. Returns -1 if it never recovers.
- fastcashflow.pricing.statutory_reserve(model_points, statutory_basis)[소스]#
The net-level-premium (NLP) reserve trajectory on a locked statutory basis.
Computed by projection – the engine’s backward present value IS the prospective reserve, so no commutation functions (Dx / Nx / Mx) are needed: the net premium is the break-even premium (it funds the benefits with no margin), and the reserve at each month is the BEL carrying that net premium.
Returns
(reserve, net_premium).reserveis(n_mp, n_time+1)– the cohort prospective reserve, column 0 approximately zero (the net premium makes the issue value nil);net_premiumis(n_mp,).statutory_basisis the locked reserving basis (its mortality / interest / lapse). For a pure NLP reserve use a deterministic one (mortality_cv = 0, no expense loading); a gross-premium or expense-loaded reserve follows from putting those into the basis.
- fastcashflow.pricing.statutory_profit_signature(model_points, pricing_basis, statutory_basis, *, period_months=12, earned_rate=None)[소스]#
The traditional / statutory profit signature.
Holds the net-level-premium reserve
Vonstatutory_basisand lets the profit emerge on thepricing_basisexperience (the actual gross premium inmodel_pointsand the best-estimate decrements). Per month, matching the engine’s within-month discount convention (premium / annuity beginning of month, claims and expenses mid-month):- profit_t = (V_t + premium_t - annuity_t)(1 + i)
outgo_t (1 + i)^0.5 - V_{t+1}
with
ithe monthly earned rate (earned_rateif given, else the pricing discount). On a run where the pricing basis equals the statutory basis and the premium is the net premium this is identically zero (the reserve is self-financing); profit emerges from the premium loading (gross over net) and the interest spread (earned over the valuation rate). The result feedsirr()/break_even_year()once the day-0 strain is prepended.v1 assumes the pricing and statutory bases share decrements (mortality / lapse) – only the valuation interest differs; a reserve re-based onto different decrements is a follow-up. Pass a single
Basis(not a router) for each.- 매개변수:
model_points (ModelPoints)
pricing_basis (Basis)
statutory_basis (Basis)
period_months (int)
earned_rate (float | None)
- 반환 형식:
- fastcashflow.pricing.interest_tvog(model_points, statutory_basis, rate_scenarios, *, guaranteed_rate=None, central_rates=None, initial_prices=None)[소스]#
The cost of a traditional minimum interest-rate guarantee, split into intrinsic value and time value (TVOG).
A general-account (GMM) traditional / interest-sensitive contract credits the policy reserve at a minimum guaranteed rate
i_g. When the company’s earned investment raterfalls belowi_gthe company funds the shortfall on the reserve. Becausemax(i_g - r, 0)is convex, a deterministic projection at the central rate sees only the intrinsic value; the extra cost from rate volatility – the time value – appears only over many scenarios.This composes two pieces: the net-level-premium reserve
V_tfromstatutory_reserve()(which accrues ati_g), and the earned-rate scenariosrate_scenarios(e.g.fastcashflow.esg.simulate(...).rates). Per scenariosthe guarantee cost is the present value of the funded shortfall:cost_s = sum_t D_s(t) * max(i_g_m - r_m[s, t], 0) * V_t
with monthly rates
i_g_m = (1 + i_g) ** (1/12) - 1and likewiser_m,V_tthe portfolio reserve held at the start of montht, andD_s(t)the end-of-month stochastic discount along the scenario’s own short rate (the shortfall rides the reserve’s full-month interest credit). The scenario mean ofD_srepricesP(0, t+1), so the measure stays risk-neutral.- 매개변수:
rate_scenarios (ndarray[tuple[Any, ...], dtype[float64]]) –
(n_scenarios, n_time)annual earned rate per projection month, wheren_time = statutory_reserve(...)[0].shape[1] - 1(the contract-boundary horizon, NOT the term – the same horizon convention as the projection).guaranteed_rate (float | None) – The minimum guaranteed annual rate
i_g. Defaults tostatutory_basis.discount_annualwhen that is a scalar; pass it explicitly if the statutory basis uses a per-year discount curve (v1 takes a scalari_g).central_rates (ndarray[tuple[Any, ...], dtype[float64]] | None) –
(n_time,)annual central path for the intrinsic value. If omitted, the forward path implied byinitial_pricesis used; exactly one of the two must be given (no silent scenario-mean fallback).initial_prices (ndarray[tuple[Any, ...], dtype[float64]] | None) –
(n_time+1,)P(0, t)the scenarios were calibrated to (e.g.EconomicScenarios.initial_prices), used to derive the forward central path whencentral_ratesis omitted.model_points (ModelPoints)
statutory_basis (Basis)
- 반환:
guarantee_costis the(n_scenarios,)cost distribution;intrinsic_valuethe central-path cost;time_valuethe TVOG (mean(cost) - intrinsic);total_valuetheir sum. All are>= 0: the guarantee is a cost to the entity. Net it by ADDINGtotal_valueto the fulfilment cash flows / BEL (subtracting from CSM / csm_plus_ra);intrinsic_valueis the part a deterministic central-rate valuation already captures,time_valuethe extra only stochastic scenarios show.- 반환 형식:
- fastcashflow.vnb(profit_signature, *, reference_rate, discount_monthly=None, required_capital=None, reserve=None, frictional_spread=0.0, tvog=0.0)[소스]#
Value of new business from a profit signature and a cost of capital.
VNB = PVFP - CoC - TVOG(portfolio total). The function is basis-agnostic: pass the IFRS 17signature()or the traditionalstatutory_profit_signature()asprofit_signature– only its present value is used.- 매개변수:
reference_rate (float) – The annual rate discounting both the profit stream (via
ProfitSignature.present_value()) and the capital-cost stream.discount_monthly (ndarray[tuple[Any, ...], dtype[float64]] | None) –
(n_time,)per-month rate curve for the cost-of-capital present value (e.g.fastcashflow.curves.discount_monthly_curve()). Required when a non-zero capital charge is requested; otherwise the CoC is zero.required_capital (ndarray[tuple[Any, ...], dtype[float64]] | float | None) – The required-capital trajectory
RC_t, portfolio total. Either an explicit(n_time,)/(n_time+1,)array (the capital held at the start of each month – e.g. the confidence-levelmeasurement.ra_path.sum(0)as a risk-capital proxy, or a regulatory capital path), or a scalar capital factor applied toreserve.Nonegives a zero capital charge.reserve (ndarray[tuple[Any, ...], dtype[float64]] | None) –
(n_time+1,)reserve path, used only whenrequired_capitalis a scalar factor (the capital isrequired_capital * reserve); e.g.statutory_reserve(...)[0].sum(0)ormeasurement.bel_path.sum(0).frictional_spread (float) – The annual spread charged on the required capital (the cost of locking it up). Zero gives a zero capital charge.
tvog (float) – The time value of options and guarantees to deduct (e.g.
interest_tvog(...).total_value). Default 0.profit_signature (ProfitSignature)
- 반환:
pvfp,cost_of_capital,tvogand the derivedvalue.- 반환 형식:
참고
Double counting: pairing the traditional
statutory_profit_signature(whose profit carries no risk adjustment) with any required capital is clean. Pairing the IFRS 17signature(whose profit already includes the risk-adjustment release) with the confidence-level RA path as the capital is sound but mixes views – the RA release is value flowing into the PVFP, the CoC is the frictional drag on holding capital, two distinct quantities – so the traditional pairing is the cleaner default.The capital charge
(frictional_spread / 12) * sum_t RC_t * df_bom(t)is the same inception value the engine’s cost-of-capital risk adjustment produces for the same capital path and annual spread (the per-month-charged backward present value of the capital), so passing the confidence-levelra_path.sum(0)withfrictional_spread = cost_of_capital_ratereproduces that figure exactly.
- class fastcashflow.VNB(pvfp, cost_of_capital, tvog)[소스]#
The value of new business, split into its components (portfolio total).
pvfpis the present value of future shareholder profit;cost_of_capitalthe frictional cost of holding the required capital;tvogthe time value of options and guarantees.valueis the value of new businesspvfp - cost_of_capital - tvog(positive = value-creating).
Required capital (solvency)#
Regime-agnostic required capital (SCR) by shock-and-aggregate, with the Solvency II and K-ICS calibrations. Liability-side (insurance and interest risk); the asset side and available capital are caller-supplied.
- fastcashflow.gmm.required_capital(model_points, basis, *, regime, catastrophe=0.0, property_codes=(), interest_scenarios=None, measure_fn=None)[소스]#
Required capital (SCR) for a portfolio under
regime.Re-measures the liability under each sub-risk’s stress, takes
max(Delta BEL, 0)as the sub-risk capital, correlation-aggregates the insurance module, adds the interest-rate stress, and computes the regime risk margin. v1 is liability-side: the total isinsurance_scr + interest_capital(no inter-module diversification). PassSCRResulton tofastcashflow.pricing.vnb()via itsrequired_capitalargument.measure_fnis the liability measurement the stresses re-run (default the GMMmeasure()); passmeasure()to price a variable book’s sub-risks on its net BEL (seefastcashflow.vfa.required_capital()). It must accept(mp, basis, full=...)and return a result carryingbel(andra_path/bel_pathfor a cost-of-capital risk margin).Interest-rate capital comes from
interest_scenarioswhen supplied – aKICSInterest(the five K-ICS shock scenarios, aggregated by the handbook p.205 formula); its five scenario amounts also land insub_risk_capital(keysinterest_up…). Otherwise it is the worst-ofregime.interest_curves(the Solvency II maturity-relative up / down table), or zero when neither is present. The K-ICS shock spreads are supervisor- published, so they are supplied at call time rather than baked into the regime.Two EXTRA insurance sub-risks fold into the module through table 6 when the regime supports them:
property_codes(the long-term property / other coverages – a +16% rate shock, re-measured) viaproperty_correlation, andcatastrophe(the factor-based amount fromkics_catastrophe()) viacatastrophe_correlation. The risk margin EXCLUDES catastrophe (handbook: the margin is the insurance amount ex-catastrophe), but INCLUDES property.- 매개변수:
model_points (ModelPoints)
basis (Basis)
regime (RegimeSpec)
catastrophe (float)
interest_scenarios (KICSInterest | None)
measure_fn (Callable | None)
- 반환 형식:
- fastcashflow.solvency.kics_catastrophe(*, pandemic_death=0.0, accident_death=0.0, disability=0.0, property=0.0, prior_year_claims=None)[소스]#
The K-ICS catastrophe risk amount (handbook 2-8) – a factor on sum assured.
pandemic_deathis the sum assured of pandemic death-exposed coverages (charged 0.1%).accident_death/disability/propertyare the large-accident sum-assured buckets, each charged the zone-exposure factors againstmax(sum_assured x shock - prior_year_claims[bucket], 0). The result issqrt(pandemic^2 + large_accident^2)(the two are uncorrelated). The exposure buckets are caller-supplied (the catastrophe categorisation of a coverage is a mapping decision, not derivable from the engine type).
- class fastcashflow.solvency.SCRResult(regime, sub_risk_capital, insurance_scr, interest_capital, total_scr, risk_margin, base_bel, scr_path=None)[소스]#
The required-capital breakdown for a portfolio under one regime.
sub_risk_capitalis the per-sub-risk capital (pre-aggregation);insurance_scrthe correlation-aggregated insurance module;interest_capitalthe worst-of interest stress;total_scrtheir sum (v1: no inter-module diversification).scr_pathis the projected capital run-off used for a cost-of-capital risk margin (Nonefor percentile).
- class fastcashflow.solvency.RegimeSpec(name, sub_risks, correlation, interest_curves=None, risk_margin_method='percentile', risk_margin_factor=0.0, risk_margin_coc_rate=0.06, catastrophe_correlation=None, property_correlation=None)[소스]#
A solvency regime’s calibration (K-ICS, Solvency II).
sub_risksorder is locked to thecorrelationmatrix axes.interest_curvesis a tuple of interest-rateStress(worst taken), orNonewhen the regime’s interest scenarios are caller-supplied. The risk margin is either"percentile"(insurance_scr * risk_margin_factor) or"cost_of_capital"(risk_margin_coc_rateover the capital run-off).catastrophe_correlationandproperty_correlationare the catastrophe / long-term-property sub-risks’ correlation with each of thesub_risks(same order), used when a caller passes a catastrophe charge / property coverage codes torequired_capital();Nonemeans the regime does not fold that (extra) sub-risk into its insurance module.
- fastcashflow.solvency.ratio(scr, available_capital)[소스]#
The solvency ratio – available capital over the required capital (
available_capital / scr.total_scr).available_capitalis a CALLER INPUT: the market value of assets less the market value of liabilities (on the prudential balance sheet), tiered per the regime. fastcashflow is a liability engine with no asset model, so it cannot produce the available capital itself – supply it (e.g. from an asset system). The denominator is the liability-side required capital this module computes; asset-side market-risk modules are out of scope, so for a book with material asset risk the ratio is an upper bound on the regulatory one.
- fastcashflow.solvency.SII#
A solvency regime’s calibration (K-ICS, Solvency II).
sub_risksorder is locked to thecorrelationmatrix axes.interest_curvesis a tuple of interest-rateStress(worst taken), orNonewhen the regime’s interest scenarios are caller-supplied. The risk margin is either"percentile"(insurance_scr * risk_margin_factor) or"cost_of_capital"(risk_margin_coc_rateover the capital run-off).catastrophe_correlationandproperty_correlationare the catastrophe / long-term-property sub-risks’ correlation with each of thesub_risks(same order), used when a caller passes a catastrophe charge / property coverage codes torequired_capital();Nonemeans the regime does not fold that (extra) sub-risk into its insurance module.
- fastcashflow.solvency.KICS#
A solvency regime’s calibration (K-ICS, Solvency II).
sub_risksorder is locked to thecorrelationmatrix axes.interest_curvesis a tuple of interest-rateStress(worst taken), orNonewhen the regime’s interest scenarios are caller-supplied. The risk margin is either"percentile"(insurance_scr * risk_margin_factor) or"cost_of_capital"(risk_margin_coc_rateover the capital run-off).catastrophe_correlationandproperty_correlationare the catastrophe / long-term-property sub-risks’ correlation with each of thesub_risks(same order), used when a caller passes a catastrophe charge / property coverage codes torequired_capital();Nonemeans the regime does not fold that (extra) sub-risk into its insurance module.
Asset-liability management#
Deterministic interest-rate sensitivity of the liability and the assets backing it – duration, DV01, key-rate duration, and the asset-liability DV01 gap.
- class fastcashflow.alm.DurationResult(pv, macaulay, modified, dv01, convexity=nan)[소스]#
Interest-rate sensitivity of a present value.
pvis the present value (the BEL for a liability, the market value for a bond).macaulay/modifiedare durations in years (macaulayisnanwhere it is not well defined – a mixed-sign liability stream).dv01is the decrease inpvfor a +1bp parallel rise in the curve (positive for a normal positive-duration instrument).convexityis the second-order yield sensitivity(1/pv) d2pv/dy2in years^2 (the curvature that the linear duration misses for a large rate move:dpv/pv ~ -modified*dy + 0.5*convexity*dy^2);nanwhere it is not well defined (a near-zeropv).
- fastcashflow.alm.liability_duration(model_points, basis, *, bump=0.0001)[소스]#
The liability’s interest-rate sensitivity –
pv(BEL),dv01, an effectivemodifiedduration (= dv01 / (|pv| * 1bp)) and an effectiveconvexity(the second central difference of the BEL under a parallel curve shift,(BEL(+b) + BEL(-b) - 2 BEL(0)) / (|pv| * b^2)).macaulayisnan(the mixed-sign liability stream has no clean Macaulay time);modified/convexityarenanwhen|pv|is negligible (the ratios are then ill-conditioned – read thedv01instead).- 매개변수:
model_points (ModelPoints)
basis (Basis)
bump (float)
- 반환 형식:
- fastcashflow.alm.liability_dv01(model_points, basis, *, bump=0.0001)[소스]#
The liability DV01 – the decrease in BEL for a +1bp parallel rise in the discount curve, by central difference (re-measure at
+/-bump).Robust for any BEL (positive, negative, near zero).
bumpis the parallel rate shift used for the finite difference (default 1bp); the result is scaled to a per-1bp figure.- 매개변수:
model_points (ModelPoints)
basis (Basis)
bump (float)
- 반환 형식:
- fastcashflow.alm.key_rate_dv01s(model_points, basis, *, bump=0.0001)[소스]#
Key-rate DV01s – the liability DV01 attributed to each policy-year bucket of the curve, by bumping one year of the per-year discount curve at a time (central difference). Returns
(n_years,); the buckets sum to approximately the parallelliability_dv01()(the key-rate decomposition of it).
- fastcashflow.alm.gap(asset_dv01, liability_dv01)[소스]#
The asset-liability DV01 gap –
asset_dv01 - liability_dv01. Zero means the net value is immunised against a small parallel rate move (the asset and liability fall by the same amount per 1bp). Both inputs are DV01s on the same curve (e.g. summedbond_duration()DV01s andliability_dv01()).
Solvency balance sheet (assets)#
Static (t=0) asset valuation – the portfolio market value, available capital, the net interest-rate SCR, and the assembled solvency ratio.
- class fastcashflow.assets.Portfolio(holdings)[소스]#
An immutable set of holdings. Bonds are priced off the discount curve; equity / property / cash carry a given market value.
- 매개변수:
holdings (tuple)
- class fastcashflow.assets.Equity(market_value, risk_type='developed', currency='KRW', issuer='', credit_rating='AA')[소스]#
An equity holding carried at a given market value (asset-positive).
risk_typeselects the market-risk shock ("developed"or"emerging"market listed equity); the shock magnitude is the regime’s calibration.currency(ISO code, “KRW” for domestic) drives the FX SCR.issuer(counterparty) andcredit_ratinggroup exposures for the concentration SCR.
- class fastcashflow.assets.Property(market_value, currency='KRW')[소스]#
A property holding carried at a given market value.
currency(ISO code) drives the FX SCR; property contributes to the property concentration SCR.
- class fastcashflow.assets.Cash(market_value, currency='KRW', issuer='', credit_rating='AA')[소스]#
A cash holding, carried at face (curve-insensitive).
currency(ISO code) drives the FX SCR.issuer(the deposit counterparty) andcredit_ratinggroup exposures for the concentration SCR.
- fastcashflow.assets.portfolio_value(portfolio, discount_annual)[소스]#
Total market value of the portfolio at the given discount curve.
- fastcashflow.solvency.available_capital(asset_portfolio_value, bel, risk_margin)[소스]#
Available capital (own funds) – assets less liabilities on the prudential balance sheet:
asset_portfolio_value - (bel + risk_margin). The liability is the technical provision (best estimate plus risk margin). Positive = solvent surplus. (Other balance-sheet liabilities, if any, are the caller’s to net out of the portfolio value.)
- fastcashflow.solvency.net_interest_scr(portfolio, model_points, basis, *, interest_curves)[소스]#
The net interest-rate SCR – the worst loss in own funds (assets less liabilities) over the regime’s up / down curve shocks.
A rate rise lowers BOTH the asset value (bonds) and the BEL; the capital is the fall in net asset value (see
_nav_delta()). The worst of the up / down shocks is taken, floored at zero. A duration-matched book gives ~ 0 – the immunised gap. This is the Solvency II form; K-ICS uses the five-scenarionet_interest_kics_scr().interest_curvesis the regime’s tuple of interest-rate stresses (RegimeSpec.interest_curves); pass a non-empty tuple (the assembler handles a regime with no curves).- 매개변수:
portfolio (Portfolio)
model_points (ModelPoints)
basis (Basis)
interest_curves (tuple)
- 반환 형식:
- fastcashflow.solvency.equity_scr(portfolio, regime)[소스]#
The equity market-risk SCR – the per-type amounts (each type’s holdings’ market value times its price-fall shock) aggregated at the 0.75 inter-type correlation (handbook 4-3). Types: developed / emerging listed, infrastructure, long_term, other, preferred. Raises on an unknown type.
- fastcashflow.solvency.property_scr(portfolio, regime)[소스]#
The property market-risk SCR – property market value times the regime’s price-fall shock.
- fastcashflow.solvency.fx_scr(portfolio, regime, discount_annual)[소스]#
The FX-risk SCR on the net foreign-currency exposure (vs the won, the local currency here).
K-ICS: each currency’s table-22 shock, summing the net-asset-value losses of the declining currencies under a won-up and a won-down scenario through a 0.5 correlation, the worse of the two. Solvency II (Article 188): a flat 25% per currency, each currency’s larger of the up / down loss, SUMMED (no diversification). Returns 0 for an unknown regime.
- fastcashflow.solvency.concentration_scr(portfolio, regime, discount_annual, *, total_assets=None)[소스]#
The asset-concentration SCR –
sqrt(counterparty^2 + property^2).Counterparty: exposures are grouped by
issuer(deposits, equity and bonds); the amount above the limit (total_assetstimes the rating band’s percentage, table 23) is charged the band’s factor, and the per-issuer charges combine at correlation 0. Property: each holding above the individual limit (6% of total assets) and the whole book above the total limit (25%) are charged 20% (table 24), taking the worse of the two.total_assetsdefaults to the portfolio value. Solvency II (Articles 184-187) uses the single-name excessmax(0, exposure - threshold(CQS) x assets) x g(CQS)aggregated as a root-sum-of-squares. Returns 0 when a book has no tagged issuers and no property, or for an unknown regime.
- fastcashflow.solvency.market_scr(portfolio, model_points, basis, *, regime, interest_scenarios=None)[소스]#
The market-risk module SCR – the interest (net of liabilities), equity, property, FX and asset-concentration sub-risks aggregated through the regime’s market correlation matrix (
sqrt(c^T R c)). Interest is the K-ICS five- scenarionet_interest_kics_scr()wheninterest_scenariosis supplied (the supervisor-published shock set), else the worst-of-curvesnet_interest_scr()(Solvency II), else zero.- 매개변수:
portfolio (Portfolio)
model_points (ModelPoints)
basis (Basis)
interest_scenarios (KICSInterest | None)
- 반환 형식:
- fastcashflow.solvency.credit_scr(portfolio, regime, discount_annual)[소스]#
The credit-risk SCR – each bond’s market value times its credit factor.
The factor is read off the K-ICS (rating x effective-maturity) grid for the bond’s
exposure_class(handbook tables 29 / 30 / 31); it is a percent of market value and already embeds the default + downgrade charge, so the SCR issum(market_value x factor)– no re-measure.Cashis risk-free and equity / property carry market (not credit) risk, so only bonds contribute. Solvency II uses the Art-176 spread stress (piecewise-linear in modified duration by credit quality step).
- fastcashflow.solvency.operational_scr(model_points, basis, regime, *, bscr=None, measure_fn=None)[소스]#
The operational-risk SCR – a factor on the liability (premiums and BEL).
K-ICS:
max(premium x 3.5%, BEL x 0.4%). Solvency II:min(0.3 x bscr, max(0.04 x premium, 0.0045 x BEL)) + 0.25 x unit-linked expenses– passbscr(the basic SCR) for the cap; unit-linked expenses are 0 in v1. The premium exposure is the first projection year’s earned premium; the BEL exposure is floored at zero.measure_fnis the liability measurement (default the GMMmeasure()); passmeasure()for a variable book’s BEL / premium exposure.- 매개변수:
model_points (ModelPoints)
basis (Basis)
bscr (float | None)
- 반환 형식:
- fastcashflow.solvency.basic_scr(insurance, market, credit, *, regime, operational=0.0, general_insurance=0.0)[소스]#
The basic required capital from disclosed module amounts – the top-level aggregate of the (life) insurance, market and credit modules plus the operational charge (added OUTSIDE the aggregate).
K-ICS uses the table-3 correlation; Solvency II uses the Annex IV BSCR matrix. For (life, market, credit) the two coincide (all pairs 0.25), so the 3-module aggregate is the same; with
general_insurance(a fourth P&C module) they differ only in general-vs-credit (K-ICS 0.25, Solvency II 0.5) and share life-vs-general 0. The discloseddiversification effectis the simple module sum minus this aggregate. Use it to reproduce a disclosed basic required capital from the published module risk amounts, or for a what-if on the module mix without re-running a book.
- fastcashflow.gmm.assess(portfolio, model_points, basis, *, regime, tax_rate=0.0, tax_recoverability_limit=None, catastrophe=0.0, property_codes=(), general_insurance_scr=0.0, interest_scenarios=None, relief=None)[소스]#
Assemble the t=0 solvency ratio from the assets and the liability SCR.
Runs
required_capital()for the liability (insurance) SCR, values the portfolio, forms available capital (assets less the technical provision), and builds the market-risk module (net interest, equity, property, FX, concentration) aggregated through the market correlation.interest_scenarios(aKICSInterest) makes the net interest sub-risk the K-ICS five-scenario amount on net asset value; without it the net interest is the regime’s worst-of curves (Solvency II) or zero (K-ICS supplies no curves). The interest risk sits in the market module (net of assets and liabilities), NOT in the insurance module –required_capitalis run without interest here. The BSCR aggregates the insurance, market and credit modules at the top level: K-ICS uses the table-3 correlation, Solvency II the Annex IV BSCR matrix; for the (life, market, credit) modules the two coincide (all pairwise 0.25). The operational-risk SCR is added on top to form the basic required capital.tax_adjustment(K-ICS chapter 7 – the loss-absorbing capacity of deferred taxes) is then subtracted to give the total required capital, the ratio denominator:min(basic_required_capital x tax_rate, tax_recoverability_limit).tax_rateis the company’s average effective rate (over its recent pre-tax profits) and defaults to 0 (no tax relief – conservative); supplytax_recoverability_limitfor the regulatory recoverability cap (else the relief is uncapped atbasic x tax_rate).catastrophe(the K-ICS catastrophe amount fromkics_catastrophe()) andproperty_codes(the long-term property / other coverages, a +16% rate shock) fold into the insurance module (table-6 correlation); both default to off.general_insurance_scr(a caller-supplied P&C amount for a life + general book) enters the BSCR as a fourth top-level module (table 3: life-vs-general 0, else 0.25); the life-only engine leaves it at 0.relief(aCedantSolvencyRelieffromcedant_solvency_relief()) folds a mass-lapse reinsurance treaty into the ratio: the insurance module drops byrelief.insurance_relief(the diversified life-module lapse relief), the counterparty-default chargerelief.counterparty_defaultis added into the credit module, and the risk margin falls byrelief.risk_margin_relief(raising available capital). Compute the relief under the SAMEregime(its ownrequired_capitalrun must match). Both default off (None). Because the counterparty-default charge here enters the BSCR through the top-level correlation (it diversifies with the life and market modules), the ratio benefit can differ from the undiversifiedrelief.net_scr_benefit; the insurance / credit modules and the risk margin reported are the post-treaty (net) figures. Pricing the relief on acatastrophe/property_codesinsurance module is a small approximation – the relief delta is built on the base life module (those default off, the common case). A relief that exceeds the module it offsets (almost always a regime / basis mismatch) raises aValueErrorrather than flooring to zero and understating the SCR.Notes: K-ICS supplies no interest curves (its scenarios are caller-supplied), so the net interest component is zero here – equity and property still apply. Credit, FX and concentration risk are calibrated for both regimes (K-ICS handbook / Solvency II Delegated Regulation). A non-positive total required capital (a risk-free book) gives an unbounded ratio.
- 매개변수:
portfolio (Portfolio)
model_points (ModelPoints)
basis (Basis)
regime (RegimeSpec)
tax_rate (float)
tax_recoverability_limit (float | None)
catastrophe (float)
general_insurance_scr (float)
interest_scenarios (KICSInterest | None)
relief (CedantSolvencyRelief | None)
- 반환 형식:
- class fastcashflow.solvency.Assessment(asset_portfolio_value, bel, risk_margin, available_capital, insurance_scr, general_insurance_scr, net_interest_scr, equity_scr, property_scr, fx_scr, concentration_scr, market_scr, credit_scr, operational_scr, basic_scr, basic_required_capital, tax_adjustment, total_scr, ratio)[소스]#
The asset-inclusive solvency picture at t=0 – the full ratio and its parts.
available_capitalisasset_portfolio_value - (bel + risk_margin). The market module aggregates thenet_interest_scr(assets and liabilities), theequity_scr, theproperty_scr, thefx_scrand theconcentration_scrthrough the market correlation; thebasic_scr(basic SCR) aggregates theinsurance_scr, the (optional)general_insurance_scr, themarket_scrand thecredit_scrat the top level;basic_required_capitaladds theoperational_scron top of the BSCR.total_scr(the ratio denominator) subtracts thetax_adjustment(loss-absorbing capacity of deferred taxes) from the basic required capital.ratioisavailable_capital / total_scr.- 매개변수:
asset_portfolio_value (float)
bel (float)
risk_margin (float)
available_capital (float)
insurance_scr (float)
general_insurance_scr (float)
net_interest_scr (float)
equity_scr (float)
property_scr (float)
fx_scr (float)
concentration_scr (float)
market_scr (float)
credit_scr (float)
operational_scr (float)
basic_scr (float)
basic_required_capital (float)
tax_adjustment (float)
total_scr (float)
ratio (float)
Reporting#
- fastcashflow.report(measurement)[소스]#
- fastcashflow.report(measurement)
- fastcashflow.report(measurement)
- fastcashflow.report(measurement)
- fastcashflow.report(measurement)
- fastcashflow.report(result)
- fastcashflow.report(measurement)
- fastcashflow.report(measurement)
Assemble the IFRS 17 report from a GMM, PAA, VFA or reinsurance measurement.
See the module docstring for the basis (IFRS 17 paragraphs B120-B124). Dispatches on the measurement type; a new model registers its own report with
@report.register. A reinsurance-held measurement returns a_reinsurance.Report(the mirror layout, IFRS 17 paragraphs 82 + 86), not aReport. A mixed-portfolio container (PortfolioMeasurementorPortfolioGroups) is also accepted: each model slot is reported on its own measurement and aPortfolioReportis returned (a GMM, PAA and VFA report are never merged).- 반환 형식:
- class fastcashflow.Report(insurance_revenue, insurance_service_expense, insurance_service_result, insurance_finance_expense, bel_finance_expense, ra_finance_expense, csm_finance_expense, loss_component, csm_opening, csm_accretion, csm_release, csm_closing)[소스]#
IFRS 17 reporting figures, period by period.
Each flow array is shaped
(n_mp, n_time)– one row per model point, one column per month;loss_componentis(n_mp,)– the onerous loss at inception.insurance_service_resultis revenue less service expense;insurance_finance_expenseis signed (positive an expense). The CSM analysis of change reconciles ascsm_opening + csm_accretion - csm_release = csm_closing(the CSM columns are zero for a PAA measurement, which has no CSM).insurance_finance_expenseis also disaggregated by source (IFRS 17 B130-B136) intobel_finance_expense(finance on the estimates of future cash flows),ra_finance_expense(finance on the risk adjustment) andcsm_finance_expense(the CSM interest accreted at the locked-in rate, B72). The three sum toinsurance_finance_expenseup to floating-point rounding (the aggregate is kept as its own expression, so the parts may differ from it by a rounding step rather than re-deriving it). The split is the structural basis for a later P&L / OCI allocation.- 매개변수:
insurance_revenue (ndarray[tuple[Any, ...], dtype[float64]])
insurance_service_expense (ndarray[tuple[Any, ...], dtype[float64]])
insurance_service_result (ndarray[tuple[Any, ...], dtype[float64]])
insurance_finance_expense (ndarray[tuple[Any, ...], dtype[float64]])
bel_finance_expense (ndarray[tuple[Any, ...], dtype[float64]])
ra_finance_expense (ndarray[tuple[Any, ...], dtype[float64]])
csm_finance_expense (ndarray[tuple[Any, ...], dtype[float64]])
- annual()[소스]#
Portfolio totals aggregated to policy years.
Each per-period line item is summed across model points and then across the twelve months of each policy year.
- by_period(period_months=12, *, basis='elapsed', inception_month=None)[소스]#
Portfolio totals bucketed into reporting periods of
period_months.The general form of
annual()(which isby_period(12)on the elapsed basis for its six lines): every flow line of the report – revenue, service expense, service result, the finance expense and its B130-B136 split, and the CSM accretion / release – summed across model points into each reporting period, plusloss_componentplaced in the period of each cohort’s inception.basis='elapsed'(default) buckets by elapsed policy time;basis='calendar'shifts each cohort by itsinception_monthso flows fall in the calendar period they occur in (see_period_offsets()). Returnsdict[str, FloatArray], each array one entry per period (period 0 first).
report also accepts a mixed-portfolio container
(PortfolioMeasurement or
PortfolioGroups) and returns a
PortfolioReport – one Report per model,
never merged.
- class fastcashflow.portfolio.PortfolioReport(gmm=None, paa=None, vfa=None)[소스]#
Result of
fcf.report()on a portfolio container: oneReportper model present (Nonewhen absent), keyed by model. Accepts aPortfolioMeasurement(per-model-point) or aPortfolioGroups(grouped); a BEL and an LRC report are never pooled.
Period-close analysis of change#
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)[소스]#
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)
- fastcashflow.roll_forward(measurement, period_months=12, *, revised=None, revised_at=None, actual_inforce=None, experience_at=None)
Slice a measurement into reporting-period movements.
Returns one movement per reporting period of
period_monthsmonths, reconciling the opening and closing figures; consecutive periods chain and a partial final period is allowed. Dispatches on the measurement type – a new model registers with@roll_forward.register.For a GMM measurement, an assumption revision is recognised by passing
revised(a second measurement of the same book under updated basis) andrevised_at(the month it takes effect); in-force experience byactual_inforce(the(n_mp,)in-force remaining at the period end, or a 2-D(n_periods, n_mp)array to roll experience through every period) andexperience_at. Either change adjusts the CSM by the resulting change in fulfilment cash flows (floored at zero, any excess falling into the loss component); v1 recognises one or the other, not both in a single call. A PAA or VFA measurement is also accepted – the movement is then the roll of the LRC or of the CSM, to which the revision and experience options do not apply.A mixed-portfolio container (
PortfolioMeasurementorPortfolioGroups) is also accepted: each model slot is rolled forward on its own measurement and aPortfolioMovementsis returned (the revision / experience options, being single-GMM-measurement features, are rejected on the container).- 매개변수:
period_months (int)
- fastcashflow.reconcile(movements)[소스]#
- fastcashflow.reconcile(aggregate)
- fastcashflow.reconcile(aggregate)
- fastcashflow.reconcile(aggregate)
- fastcashflow.reconcile(aggregate)
- fastcashflow.reconcile(settlement)
- fastcashflow.reconcile(settlement)
- fastcashflow.reconcile(movements)
Aggregate period movements into IFRS 17 reconciliation tables.
Each
_gmm.PeriodMovement– per model point – becomes one portfolio-total_gmm.Reconciliationin the layout of IFRS 17 paragraph 101. Run-off rows are shown negative, so opening plus every row equals closing. A list of_paa.PeriodMovementor_vfa.PeriodMovementis reconciled instead into the PAA liability-for-remaining-coverage or VFA contractual-service-margin tables.The base implementation takes a list of movements (dispatch falls through to it for any list); a mixed-portfolio
PortfolioMovementsregisters its own arm (returning aPortfolioReconciliation).- 매개변수:
movements (list[PeriodMovement] | list[PeriodMovement] | list[PeriodMovement])
- 반환 형식:
list[Reconciliation] | list[Reconciliation] | list[Reconciliation]
- class fastcashflow.gmm.PeriodMovement(month_start, month_end, bel_opening, bel_assumption_change, bel_experience, bel_interest, bel_release, bel_closing, ra_opening, ra_assumption_change, ra_experience, ra_interest, ra_release, ra_closing, csm_opening, csm_assumption_change, csm_experience, csm_accretion, csm_release, csm_closing, loss_component_recognised)[소스]#
One reporting period’s analysis of change.
The period covers months
[month_start, month_end). Every array is(n_mp,), and each block reconciles exactly:bel_opening + bel_assumption_change + bel_experience + bel_interest - bel_release == bel_closing
and likewise for RA and CSM (with
csm_accretionin place of*_interest).*_interest/csm_accretionis the unwind of discount at the locked-in rate;*_releaseis the expected run-off over the period.*_assumption_changeand*_experienceare the effect of an assumption revision and of in-force experience – non-zero only in the period the change is recognised. Both relate to future service and so adjust the CSM.loss_component_recognisedis the part of an unfavourable change beyond the CSM, which falls into the loss component.- 매개변수:
- class fastcashflow.gmm.Reconciliation(month_start, month_end, bel_opening, bel_future_service, bel_finance, bel_release, bel_closing, ra_opening, ra_future_service, ra_finance, ra_release, ra_closing, csm_opening, csm_future_service, csm_finance, csm_release, csm_closing, loss_component_recognised)[소스]#
An IFRS 17 reconciliation of the insurance contract liability.
Portfolio totals for one reporting period, in the layout of IFRS 17 paragraph 101: the estimates of the present value of future cash flows (
bel), the risk adjustment (ra) and the CSM each reconcile from opening to closing.*_future_serviceis the assumption and experience effect;*_financeis the interest unwind;*_releaseis the run-off, shown negative – so opening plus every row equals closing.- 매개변수:
month_start (int)
month_end (int)
bel_opening (float)
bel_future_service (float)
bel_finance (float)
bel_release (float)
bel_closing (float)
ra_opening (float)
ra_future_service (float)
ra_finance (float)
ra_release (float)
ra_closing (float)
csm_opening (float)
csm_future_service (float)
csm_finance (float)
csm_release (float)
csm_closing (float)
loss_component_recognised (float)
roll_forward and reconcile also accept the mixed-portfolio containers:
roll_forward on a PortfolioMeasurement /
PortfolioGroups returns a
PortfolioMovements (one movement list per model),
which reconcile() turns into a
PortfolioReconciliation – a GMM CSM movement and
a PAA LRC movement are never merged.
- class fastcashflow.portfolio.PortfolioMovements(gmm=None, paa=None, vfa=None)[소스]#
Result of
fcf.roll_forward()on a portfolio container: one list of period movements per model present (Nonewhen absent), keyed by model. Each slot is the model’s own movement list (list[gmm.PeriodMovement]/list[paa.PeriodMovement]/list[vfa.PeriodMovement]) – a GMM CSM movement and a PAA LRC movement are never merged. Feed it tofcf.reconcile()for aPortfolioReconciliation.
- class fastcashflow.portfolio.PortfolioReconciliation(gmm=None, paa=None, vfa=None)[소스]#
Result of
fcf.reconcile()on aPortfolioMovements: one list of reconciliation tables per model present (Nonewhen absent), keyed by model (list[gmm.Reconciliation]/list[paa.Reconciliation]/list[vfa.Reconciliation]).
Aggregation and transition#
- fastcashflow.group(measurement, by)[소스]#
- fastcashflow.group(measurement, by)
- fastcashflow.group(measurement, by)
- fastcashflow.group(measurement, by)
- fastcashflow.group(measurement, by)
- fastcashflow.group(measurement, by)
Aggregate a per-model-point measurement to any axis.
A general aggregation primitive – not IFRS 17-specific.
byis one of:a single axis name (e.g.
"product");a list of axis names and/or precomputed
(n_mp,)label arrays (e.g.["product", "issue_year"], or["product", onerous_array]), joined into one composite label;a single precomputed
(n_mp,)array of group labels.
Names are resolved per model point via
ModelPoints.axis()against the model points the measure stamped on the result, so no re-passing is needed; a computed axis with no source column (e.g. an onerous flag fromloss_component) is passed as an array instead – annp.ndarray, since a Python list is read as a list of axes, not a single label vector.BEL and RA are summed within each group; the CSM and the loss component are re-derived on the group aggregate, so the
max(0, ...)floor nets the contracts within a group but not across groups. The IFRS 17 unit of account (portfolio x annual cohort x profitability) is one choice of axes –group_of_contracts()is the preset for it; management-accounting, profitability and validation views are other choices ofby.Dispatches on the measurement type (
_gmm.Measurement,_vfa.Measurement,_reinsurance.Measurement,_paa.Measurement). APortfolioMeasurement(the mixed-model container) is also accepted: each model slot is grouped on its own native measurement and aPortfolioGroupsis returned (a precomputed arraybyis subset to each slot’s rows). Returns a measurement of the same type whose rows are the groups, in ascending label order – usable in turn byroll_forward(),reconcile()andreport(). Itsgroup_labelsattribute carries the composite label of each row, so a caller can map a group back to its key (e.g."|"-split agroup_of_contracts()label into portfolio / cohort / profitability) without rebuilding the keys;group_sizescarries the number of model points in each group (model-point rows, not the policy count – they differ when a model point’scountstands for several policies).
- fastcashflow.group_of_contracts(measurement, *, portfolio='product', cohort='issue_year', profitability=None)[소스]#
- fastcashflow.group_of_contracts(measurement, *, portfolio='product', cohort='issue_year', profitability=None)
- fastcashflow.group_of_contracts(measurement, *, portfolio='product', cohort='issue_year', profitability=None)
- fastcashflow.group_of_contracts(measurement, *, portfolio='product', cohort='issue_year', profitability=None)
- fastcashflow.group_of_contracts(measurement, *, portfolio='product', cohort='issue_year', profitability=None)
- fastcashflow.group_of_contracts(measurement, *, portfolio='product', cohort='issue_year', profitability=None)
Aggregate a measurement to the IFRS 17 group of insurance contracts.
The unit of account (paragraphs 14-24) is a portfolio (14) x annual cohort (22) x profitability (16). This preset builds that grouping from the model points
measure()stamped on the measurement and runsgroup(), so the CSM floor nets within a group but not across.Dispatches on the measurement type; the profitability axis differs by type (a new measurement registers with
@group_of_contracts.register):_gmm.Measurement/_vfa.Measurement/_paa.Measurement– insurance contracts issued, direct-participating, and short-coverage (PAA) contracts; profitability is the onerous / remaining split (paragraph 16, and 57 for the PAA). The per-type re-derivation differs (VFA accretes the CSM at the underlying-items return; the PAA has no CSM, only the LRC and the onerous loss), handled bygroup()’s own dispatch._reinsurance.Measurement– reinsurance contracts held; profitability is the net-gain split (paragraph 61,csm > 0), and there is no loss component or floor (paragraph 65), so the grouped CSM is the sum of the contract CSMs.PortfolioMeasurement– the mixed-model container; each model slot is grouped on its own native measurement and aPortfolioGroupsis returned. For a book too large to hold the full per-model-point measurement, use the chunkedfastcashflow.portfolio.measure_group_of_contracts()instead.
Arguments (keyword-only):
portfolio– the column naming the portfolio axis (default"product": paragraph 14’s product line). Pass another column name to group on a different portfolio definition.cohort– the column naming the annual-cohort axis (default"issue_year", derived fromissue_date: paragraph 22). Pass another column (e.g."issue_quarter"carried in the data) for a finer cohort; paragraph 22 caps the span at one year, so a cohort may be finer than annual but not coarser.profitability– the profitability classification.None(default) derives it from the measurement, since it is an output, not a known input (paragraph 16 / 47’s net-outflow test). Pass a precomputed(n_mp,)array for a custom split (e.g. the paragraph-16 three-way split using a CSM-vs-RA threshold), or a column name for a locked classification carried in the data (paragraph 24: the group is fixed at inception).
Requires a
full=Truemeasurement.- 매개변수:
- 반환 형식:
- fastcashflow.transition(measurement, fair_value)[소스]#
Re-set the CSM on the IFRS 17 fair value transition basis.
measurementis a measurement of the in-force book at the transition date – its inception column being that date.fair_valueis the(n_mp,)fair value of each contract or group. The CSM becomesmax(0, fair_value - fulfilment cash flows), any excess of the fulfilment cash flows over the fair value falling into the loss component, and is rolled forward from there.Returns a measurement with the re-set CSM and loss component; the BEL and RA are unchanged. It flows on into
roll_forward(),reconcile()andreport().- 매개변수:
measurement (Measurement)
- 반환 형식:
Period close and disclosure#
The reporting-period close assembles the settlement reconciliations of a period (one per group of contracts) into the aggregate IFRS 17 statements – the statement of financial position, the insurance finance statement and the insurance service result – and serialises them to a disclosure artifact. The tidy-frame helpers expose the same content as audit-traceable rows.
- fastcashflow.close(reconciliations, *, reports=None, group_ids=None)[소스]#
Assemble the close pack from a reporting period’s settlement reconciliations.
reconciliationsis the GMM / VFA / PAA / reinsurance settlement reconciliations of one reporting period (whatfastcashflow.reconcile()returns, one per model / group) – the source of the SoFP, the finance statement and the reconciliation detail.reports, if given, is the list ofReport/Reportthat adds the insurance service result statement (sourced from the report, not the settlement – seeassemble_service_result()).group_ids, if given, names the group of contracts each reconciliation belongs to (parallel toreconciliations); it stamps the reconciliation detail so per-group lines stay identifiable.All reconciliations must share the same
period_months– a close pack is one reporting period.- 반환 형식:
- class fastcashflow.ClosePackage(period_months, sofp, finance, reconciliation, service_result=None)[소스]#
The assembled IFRS 17 close pack for one reporting period.
sofpis the statement of financial position (assemble_sofp());financeis the insurance finance statement (assemble_finance());service_resultis the insurance service result statement (assemble_service_result()), present only whencloseis given the reports;reconciliationis the stacked per-model settlement detail (the lean tidy frame ofreconciliation_to_frame(), one block of rows per reconciliation, stamped withgroup_id). The disclosure emitter materialises these into the multi-sheet close-pack artifact.- 매개변수:
period_months (int)
sofp (DataFrame)
finance (DataFrame)
reconciliation (DataFrame)
service_result (DataFrame | None)
- fastcashflow.assemble_sofp(reconciliations)[소스]#
The statement of financial position (IFRS 17 paragraphs 78, 99-101).
The closing carrying amount of insurance contracts, split into LRC excluding the loss component / loss component / liability for incurred claims, for contracts issued, reinsurance contracts held, and the net – each with the opening balance, the period change and the closing balance. Per row,
opening + change == closing; per kind, the Total row is the sum of the three components (the carrying amount); the Net kind sums issued and reinsurance held in the one signed liability frame (a reinsurance recoverable is a negative carrying amount, so it reduces the net).- 반환 형식:
DataFrame
- fastcashflow.assemble_finance(reconciliations)[소스]#
The insurance finance statement (IFRS 17 paragraphs 87-89, B130-B136).
The period’s insurance finance income or expenses disaggregated by source – finance on the BEL, the RA, the CSM (accretion at the locked-in rate, B72), the liability for incurred claims (42(c)), and the B97(a) locked-in rate adjustment (the current-vs-locked-in rate gap on the experience adjustment) – for contracts issued, reinsurance contracts held, and the net. The five sources sum to the
Insurance finance income or expensestotal line.Loss component financeis a memo: the loss component’s share of the BEL finance (51(c)), already inside the BEL finance line, not an additional amount.- 반환 형식:
DataFrame
- fastcashflow.assemble_service_result(reports, *, period_months=12)[소스]#
The insurance service result statement (IFRS 17 paragraphs 83, B120-B124).
The period-by-period insurance revenue, service expense and service result for contracts issued, and the premiums / recoveries / net / service result for reinsurance contracts held (presented separately, paragraph 82), summed across the reports of each kind.
reportsis a list ofReport(issued) and / orReport(held).The service result is sourced from
Report.by_period(), NOT the settlement reconciliation: insurance revenue (B120-B124) needs the gross expected claims and expenses, which the settlement table does not carry (its BEL release is net of premiums). It is therefore the EARNED / projected P&L of the measurement – for a new-business group’s first reporting period it ties to that period’s settlement; for a later in-force period it is the projection, with experience variances carried in the reconciliation and finance memos. v1 buckets on the elapsed basis (the calendar basis needs a per-report inception offset – useReport.by_period()directly for it).- 매개변수:
period_months (int)
- 반환 형식:
DataFrame
- fastcashflow.reconciliation_to_frame(recon)[소스]#
- fastcashflow.reconciliation_to_frame(recon)
- fastcashflow.reconciliation_to_frame(recon)
- fastcashflow.reconciliation_to_frame(recon)
- fastcashflow.reconciliation_to_frame(recon)
Return the lean canonical tidy frame for a settlement reconciliation.
- 반환 형식:
DataFrame
- fastcashflow.line_metadata()[소스]#
The disclosure line registry as a frame – (model, block, line, line_code, ifrs17_paragraph, is_memo, sort_order) – the single source the lean
reconciliation_to_frame()and the emitter both read. Exposed so a user can join the reference columns onto a lean frame themselves.- 반환 형식:
DataFrame
- fastcashflow.write_reconciliation(reconciliation, path)[소스]#
Serialize a settlement reconciliation (or a list of them, one per reporting period) to a tidy file – parquet / csv / xlsx – with the rich audit columns materialised. A list is stacked with a 0-based
period_indexso a multi-period close schedule round-trips as one long frame.Mirrors
write_measurement()(which serializes the per-MP movements); this is the disclosure-shaped (reconciliation / close) serialization path.- 반환 형식:
None
- fastcashflow.write_close_pack(package, path, *, movements=None)[소스]#
Write a close pack (a
ClosePackage) to a multi-sheet.xlsx– the aggregate IFRS 17 statements an entity reads – and, whenmovementsis given, a per-model-point parquet sidecar.The workbook carries an index cover, the statement of financial position, the service result (if it was assembled), the finance statement, and the reconciliation detail with the rich audit columns (line_code, the IFRS 17 paragraph anchor, the memo flag, the deterministic order) materialised by joining
line_metadata()– so the artifact is self-contained.The per-model-point settlement movement does NOT go in the workbook (a sheet caps at ~1,048,576 rows);
movements– one settlement movement or a list – is written to<path>_per_mp[_i].parquetbeside the workbook viawrite_measurement(), and the index sheet names the file(s).- 반환 형식:
None
State models#
The in-force state machine driving multi-state products (waiver, paid-up,
disability income, reincidence, long-term care), under the fcf.multistate
namespace. A Model is a tuple of
State objects, each carrying its
Transition edges; Model.from_preset holds
the bundled models.
- class fastcashflow.multistate.Model(states, seating=(0,))[소스]#
A product’s in-force state machine, declared as data.
statesare the transient states; position fixes the kernel state index, and state 0 is the issue state.seatingmaps a model point’s input contract state – theModelPoints.statecode (STATE_ACTIVE,STATE_WAIVER,STATE_PAIDUP) – to the index of the state its in-force is seated on at the valuation date:seating[code]is that index. It defaults to seating every model point on state 0.The occupancy recursion treats every state identically, so an arbitrary Model runs on the existing kernels with no per-product code – see the module docstring and
compile_model().- property transitions: tuple[TransitionRecord, ...]#
The model’s transition structure as an ordered tuple of records.
The transMat analogue: every transition the topology declares, in a stable order, derived from the state / transition declaration alone (a transition is listed even if a particular book sets its rate to zero). The order groups all death exits, then all lapse exits, then all inter-state transfers – each group in state-index order, and within a state in declaration order:
a death record for each state that declares a mortality decrement (a transition with
rate == "mortality");a lapse record for each state that declares a lapse decrement (a rate-driven
to=Noneexit that is not the mortality one);a transfer record for each declared inter-state transition (
toset to a different state), theModelPointsoccupancy moving fromfrom_statetoto_state.
The per-transition sum at risk reads this for its axis order and its descriptors; it emits a row for the transitions a given book actually exercises (so a book with a zero decrement carries no row for it), keeping this list a structural superset of that book’s axis.
- classmethod from_preset(name)[소스]#
Return the bundled model registered under
name.A non-programmer actuary can pick a topology by name – in the
segmentssheet’sstate_machinecolumn, or in Python viaModel.from_preset("ACTIVE_WAIVER"). The preset key lists the transient states in state-index order (ACTIVE_WAIVER= active + waiver;ACTIVE_WAIVER_PAIDUP= active + waiver + paid-up). Users with a topology outside the registry build their ownModel.
- classmethod presets()[소스]#
The available
from_preset()names, in sorted order.
- class fastcashflow.multistate.State(name, pays_premium=False, pays_periodic_benefit=False, transitions=(), sojourn_tracking_months=0, periodic_benefit_term_months=0, mortality_rate='mortality', death_benefit_factor=1.0)[소스]#
One transient state of the in-force model.
pays_premiumflags a premium-paying state – the level and single premium accrue on the occupancy of the states so flagged.pays_periodic_benefitflags a benefit-paying state – theModelPoints.disability_incomeamount is paid each month its occupancy is held (disability income on a disabled state).transitionsare the transitions out of the state, held in application order: the competing-decrement convention (see the module docstring) applies each in turn to the survivors of the previous.sojourn_tracking_monthsswitches the state to a semi-Markov model. When set toD > 0, the engine tracksDmonthly cohorts of in-force in this state (cohort 0 entered this month, cohort 1 entered last month, and cohortD - 1absorbs everyone who has been hereD - 1months or longer). Transitions withsojourn_dependent=Truethen receive a cohort index and may carry different rates per cohort – the natural way to express recovery, reincidence, exclusion periods, and other duration-since-entry effects. The default0keeps the state Markov (a single cohort, identical to the pre-Phase-(c) behaviour).mortality_rateroutes this state’s in-force death decrement to a named rate (default"mortality", the global decrement). A post-diagnosis state can carry an elevated death rate by naming a different rate, supplied viaBasis.state_mortality_annual.periodic_benefit_term_monthscaps how many months abenefitstate pays (0= unbounded); see the field comment in__post_init__.death_benefit_factorscales the death-coverage benefit paid for the lives residing in this state (default1.0= no change). The aggregate death claim is occupancy-weighted:claim = (sum_s occ[s]*factor[s]) * claim_rate. It multiplies the benefit AMOUNT, not the decrement, so the death count is unchanged. A post-diagnosis state paying a richer death benefit (e.g. 2x after a cancer diagnosis) setsdeath_benefit_factor=2.0. Supported on the full path only (measure(full=True)); the fast path and the VFA path reject a non-default factor.A cover that ends after a fixed sojourn (or a guaranteed conversion to another state at a fixed sojourn) is a deterministic
Transition(after_sojourn_months=K, to=...)–to=Noneends the cover,to="active"converts. It is distinct fromperiodic_benefit_term_months(which stops the payment but keeps the lives in force): a guaranteed-payout state that pays a fixed term then lapses setsperiodic_benefit_term_months(pay window) and aTransition(after_sojourn_months=K, to=None)(cover end) together.
- class fastcashflow.multistate.Transition(rate=None, to=None, pays_lump_sum=False, sojourn_dependent=False, after_sojourn_months=0, at_premium_term=False)[소스]#
One transition out of a state.
ratenames an assumption rate –"mortality","lapse","waiver_incidence"and so on – evaluated by the engine and supplied tocompile_model().tois the destination state’s name when the transition moves occupancy to another transient state (waiver inception, recovery, reincidence), orNonewhen it removes occupancy from the in-force set entirely (death, lapse).pays_lump_sumflags a transition that pays a one-off benefit when it fires – theModelPoints.disability_benefitamount times the transitioning occupancy. It applies only to a transition with a destination; death and diagnosis lump sums stay on the coverage list.sojourn_dependentflags a semi-Markov transition: the rate depends on the sojourn time in the source state (time since entering it), not just on the policy duration. The source state must havesojourn_tracking_months > 0– the engine tracks per-cohort occupancy there. The rate function for a duration-dependent transition takes a fourth argumentstate_duration(months in source state).after_sojourn_monthsmakes the transition deterministic (probability one) at a fixed sojourn: when a cohort’s sojourn in the source state reaches this many months, all of it moves toto(or leaves the in-force set whento is None). It carries norate(the move is certain). This expresses a cover that ends after a fixed term (to=None), or a guaranteed conversion to another state (to="active"). At most one deterministic transition per state.
- class fastcashflow.multistate.TransitionRecord(from_state, to_state, kind, from_name, to_name)[소스]#
One transition in a model’s transition structure (its transMat analogue).
Enumerated by
Model.transitions– the model-level list of every transition the topology declares, in a stable order, independent of any particular book’s rates (a transition is listed if declared, even where a book sets its rate to zero). It is the descriptor axis the per-transition sum at risk reads for itsn_transitionorder and labels.from_state/to_stateare transient-state indices;to_stateisNonefor an absorbing exit (death / lapse leave the in-force set).kindis"death","lapse"or"transfer"(an inter-state edge).from_name/to_namelabel them for display (to_nameis the destination state name, or"death"/"lapse").
- fastcashflow.STATE_ACTIVE = 0#
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4
- fastcashflow.STATE_WAIVER = 1#
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4
- fastcashflow.STATE_PAIDUP = 2#
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4
Stochastic valuation#
- fastcashflow.gmm.stochastic(model_points, basis, rate_scenarios)#
Value a portfolio under each economic scenario – the liability distribution.
rate_scenariosis eithera 1-D
(n_scenarios,)array – one flat annual discount rate per scenario; ora 2-D
(n_scenarios, n_time)array – one discount-rate curve per scenario, an annual rate for each projection month.
The portfolio total of every figure is recorded under each scenario, so the distribution – mean, percentiles – can be read from the result. The projection runs once and a single parallel kernel sweeps the scenario axis (see module docstring); the settlement-pattern and cost-of-capital paths fall back to a per-scenario
measureloop (full=Falsefor the confidence-level RA,full=Truefor cost-of-capital, which the fast path does not compute). Cost-of-capital supports flat (1-D) rate_scenarios only.- 매개변수:
- 반환 형식:
- class fastcashflow.StochasticResult(bel, ra, csm, loss_component)[소스]#
Per-scenario portfolio totals from a stochastic valuation.
Each array is
(n_scenarios,)– the portfolio total of that figure under each scenario. Read the distribution off withmean()andpercentile(), or from the arrays directly.- 매개변수:
Input and output#
- fastcashflow.read_model_points(path, coverages=None, calculation_methods=None)[소스]#
Read model points from a parquet, CSV, Excel or feather file.
Reads the portfolio without any basis – the model points and the actuarial basis are separate inputs. The basis enters only at the engine call (
measure), which aligns its coverages to the portfolio’s coverage order.The portfolio is two frames – a policies frame plus a coverages frame:
a policies frame (
mp_id,issue_age,term_months, optionalsex/count/state/issue_class/issue_date/premium/premium_term_months/premium_frequency_months/annuity_frequency_months/contract_boundary_months/product/channel), one row per policy. Any other column is read as a grouping attribute (portfolio_id,profitability_group,risk_class,region, …) intoModelPoints.attributes, forgroup()/group_of_contracts();a coverages frame (
mp_id,coverage,amount, optionalpremium/waiting/reduction_end/reduction_factorand the benefit step-up / escalation columnsstep_month/step_factor(a benefit step at a duration) /escalation_annual/escalation_cap(annual compounding growth, capped – the escalating-benefits recipe in the cookbook)), one row per policy x coverage – so per-coverage rules (waiting, reduction and escalation) ride along, which a flat one-row-per-policy file cannot carry.
Pass them as
read_model_points(policies, coverages=coverages_path, calculation_methods=...), or as a single.xlsxcarryingpoliciesandcoveragessheets. The normalised two-frame shape mirrors a policy table joined to a coverage table – the form data arrives in from a policy system.calculation_methodsis the company taxonomy file (CSV / parquet / feather / xlsx) – the third side of the split between portfolio (policies + coverages), basis (basis.xlsx) and catalogue (calculation_methods.csv).The policies frame is the inception-time static spec – issue_age, term, sex, and so on. The in-force closing state (elapsed_months, prior_csm, lock_in_rate) belongs in a separate file read by
read_inforce_state(). Anelapsed_monthscolumn on the policies side is ignored and aUserWarningis emitted; do not encode the as-of date by mixing it into the static spec.- 매개변수:
- 반환 형식:
- fastcashflow.read_vfa_model_points(path, *, calculation_methods=None)[소스]#
Read the account-value base of variable (VFA) contracts from a policies file.
This reads the part measured under the VFA model: the account value and its guarantee floors (GMDB / GMAB), all named policy columns (
account_value,minimum_death_benefit,minimum_accumulation_benefit,minimum_crediting_rate). That base carries no coverage-code coverages, so it is a single policies frame.issue_ageandterm_monthsare required; the named policy / account columns are read if present.Protection riders attached to a variable product (death / cancer / hospitalisation rider) are separate coverages, read and measured on their own – a policies + coverages book through
read_model_points()(GMM). So a<coverage>_benefitcolumn is rejected here: a coverage encoded as a column is the lossy wide form, and coverages belong in their own frame which can hold the per-coverage waiting / reduction rules a flat column cannot.- 매개변수:
- 반환 형식:
- fastcashflow.read_basis(path)[소스]#
Read the basis workbook into a per-segment
BasisRouter.pathis a singlebasis.xlsxworkbook holding both the rate tables and the segment mapping (see the module header for the sheet layout). Thesegmentssheet maps each (product, channel) to which tables it uses plus scalar parameters, with a_DEFAULTSrow whose values blank cells inherit; thecoveragessheet attaches rate-driven coverages to products.Returns a
BasisRouterkeyed by the segment axes –(product, channel)by default, or whatever non-assumption columns the segments sheet declares (one axis, or three);.segment_axesrecords the axis names someasure()routes without asegment_byargument.v1: the discount and inflation tables are read but used flat (their first entry); the per-segment BasisRouter is returned for the caller to value segment by segment.
- 매개변수:
- 반환 형식:
- fastcashflow.read_inforce_policies(path, coverages=None, calculation_methods=None)[소스]#
Read a single combined policies + in-force state file.
A self-contained snapshot at a settlement date – one file per valuation date, one row per surviving contract. Columns combine the permanent contract spec (
issue_age,sex,term_months, premiums, benefits, …) and the closing state from the prior period (elapsed_months,count,prior_csm,lock_in_rate). This matches the Korean industry period-close file pattern – one self-contained snapshot per period, no separate state file to keep in sync.Returns a
(ModelPoints, InforceState)tuple. TheModelPointshas the state’selapsed_monthsandcountalready folded in; theInforceStatecarriesprior_csmandlock_in_ratefor the period-close settlement call:mp, state = fcf.read_inforce_policies( "inforce_2026Q1.csv", coverages="coverages.csv", calculation_methods="calculation_methods.csv", ) movement = fcf.gmm.settle(mp, state, basis, period_months=3)
(For a diagnostics / run-off view without the movement lines, the same inputs feed
fastcashflow.gmm.measure_inforce().)For the two-file equivalent (separate
policies.csv+inforce_state.csv), seeread_model_points()+read_inforce_state()+apply_inforce_state(). Both workflows produce valuation-ready inputs that give the same valuation:settleandmeasure_inforcere-align the state by mp_id internally, so the answer does not depend on the state file’s row order. The returnedInforceStateis itself row-aligned to the model points here; in the two-file path the state object keeps its file order (only the model points are reordered byapply_inforce_state()), so callalign_inforce_state()before slicing or readingstate.prior_csmdirectly. Pick the form that fits the company’s extract pipeline.Required columns:
mp_id,elapsed_months,count,prior_csm,lock_in_rate, plus whatever the spec side ofread_model_points()needs (issue_age,term_months, optionalsex, premiums,<code>_benefitcolumns for wide form). Optional settlement columns:prior_count(the in-force count at the opening date – bothfastcashflow.gmm.settle()andfastcashflow.vfa.settle()need it) andprior_loss_component(the prior period’s closing loss component). Optional VFA state columns:account_value(the observed fund value at the valuation date – it rides on the returnedInforceState; the snapshot has no separate inception fund column) andprior_account_value(the prior reporting date’s observed fund value, whichvfa.settleneeds). Variance / movement analysis (roll_forward(),reconcile()) is unaffected – mp_id-based matching across periods works the same regardless of which reader built each snapshot.- 매개변수:
- 반환 형식:
- fastcashflow.read_inforce_state(path)[소스]#
Read an in-force state file – the per-MP closing state from the prior reporting period.
The file has one row per model point with columns
mp_id,elapsed_months,count,prior_csmandlock_in_rate, plus the optional settlement columnsprior_countandprior_loss_component(the prior reporting date’s figuresfastcashflow.gmm.settle()/fastcashflow.vfa.settle()need) and the VFA columnsaccount_value(observed fund value at the valuation date) andprior_account_value. Reads.parquet,.csv,.xlsxor.feather/.arrowvia_read_frame().Pair with
apply_inforce_state()to join the state onto aModelPointsbuilt from the static policies file, then settle the period withfastcashflow.gmm.settle()and the returnedInforceState(or value the book diagnostically withfastcashflow.gmm.measure_inforce()).lock_in_rateis required to be uniform across rows in v1 – the engine takes a scalar locked-in rate. Cohort-aware per-MP rates are a future extension; for now the reader errors out if the column is not constant rather than silently dropping the per-row detail.- 매개변수:
path (Path | str)
- 반환 형식:
- fastcashflow.apply_inforce_state(model_points, state)[소스]#
Return a
ModelPointswith the state’selapsed_monthsandcountsubstituted in, joined onmp_id(seealign_inforce_state()for the join rules).Note this substitutes only
elapsed_months/countonto the model points; the state’sprior_csmrides on the (separately passed)InforceState.measure_inforce()re-aligns that state by mp_id internally, so prior_csm cannot drift out of order.- 매개변수:
model_points (ModelPoints)
state (InforceState)
- 반환 형식:
- fastcashflow.align_inforce_state(model_points, state)[소스]#
Return
statereordered so its rows line up withmodel_points.Every per-MP field of the returned state (
elapsed_months,count,prior_csm,mp_id) is row-for-row aligned with the model points. When both carrymp_idthe match is by mp_id – reordered when the two files are in different orders, and rejected when their id sets differ – so a misaligned period-close file cannot silently assign one contract’s state (including its prior CSM) to another. When the model points have nomp_id(a hand-built set), the rows are taken positionally after a length check; align them yourself in that case.- 매개변수:
model_points (ModelPoints)
state (InforceState)
- 반환 형식:
- class fastcashflow.InforceState(mp_id, elapsed_months, count, prior_csm, lock_in_rate, account_value=None, prior_count=None, prior_account_value=None, prior_loss_component=None, profitability=None, actual_premium=None, actual_investment_component=None, actual_claims=None, actual_expenses=None, prior_lic=None)[소스]#
Per-MP closing state from the prior reporting period.
The input layer for in-force / subsequent-measurement workflows. A fresh
inforce_state.csvis produced at each period close from the company’s policy administration system and joined onto the staticpolicies.csvto value the in-force at the next reporting date.Fields:
mp_id– join key, matches themp_idcolumn on the policies file.elapsed_months– months since each contract’s inception as of the valuation date (= valuation date - inception date).count– in-force at the valuation date (the user has already scaled it down for past lapses); seats the projection.prior_csm– closing CSM at monthelapsed_months - period_months, the prior reporting date’s result carried into this period.lock_in_rate– annual locked-in discount rate (paragraph B72(b)). Usually scalar; per-MP cohort-aware rates are accepted for GoC-grain settlement, where the portfolio entry validates uniformity inside each group before calling the scalar GMM kernel.account_value– observed per-MP fund value at the valuation date (Nonefor non-VFA states). VFA subsequent measurement (vfa.measure_inforce) re-anchors the account-value path at this observed value; GMM / PAA ignore it. It stays on the state – it does not overwrite the model point’s inceptionaccount_value.prior_count– in-force at monthelapsed_months - period_months, the prior reporting date (Noneunless the state feedsvfa.settle). Mirrorsprior_csm: a prior-date figure carried on the closing-dated state.prior_account_value– observed per-MP fund value at the prior reporting date (Noneunless the state feedsvfa.settle).prior_loss_component– closing loss component at the prior reporting date (Nonemeans zero). Read byvfa.settle; the paragraph-48/50(b) algebra reverses it on favourable changes before rebuilding the CSM.profitability– optional inception-frozen profitability class used as an explicit group-of-contracts axis at settlement.actual_premium– observed per-MP premium cash actually received over the reporting period (Nonemeans as expected).gmm.settlesplits the experience adjustmentactual_premium - expected_premiumbetween future service (CSM, paragraph B96(a)) and current/past service (P&L, paragraph B97(c)). May be negative (a net refund period, TRG 2018-09 Example B).actual_investment_component– observed per-MP investment component actually paid over the period (surrender values, annuity / maturity repayments – the amounts repaid regardless of an insured event;Nonemeans as expected).gmm.settleroutes the whole differenceexpected - actualinto the CSM (paragraph B96(c)); investment components do not affect insurance revenue.actual_claims/actual_expenses– observed per-MP claims incurred / expenses incurred over the period (Nonemeans as expected). The difference from expected is an experience adjustment relating to past / current service (paragraph B97(b)/(c)): it is recognised in the insurance service result (P&L) and does NOT adjust the CSM. Reported ongmm.settleasclaims_experience/expense_experience.prior_lic– closing liability for incurred claims at the prior reporting date (Nonemeans reconstruct from the in-force). Required to settle a PAA pure-LIC-runoff period (the opening date at or past the contract boundary): once coverage has ended there is no in-force to scale the LIC by, so the carried balance seeds the run-off.paa.settle’sclosing_inputs()carries the period’slic_closinghere.
- 매개변수:
mp_id (ndarray)
lock_in_rate (float | ndarray[tuple[Any, ...], dtype[float64]])
account_value (ndarray[tuple[Any, ...], dtype[float64]] | None)
prior_count (ndarray[tuple[Any, ...], dtype[float64]] | None)
prior_account_value (ndarray[tuple[Any, ...], dtype[float64]] | None)
prior_loss_component (ndarray[tuple[Any, ...], dtype[float64]] | None)
profitability (ndarray | None)
actual_premium (ndarray[tuple[Any, ...], dtype[float64]] | None)
actual_investment_component (ndarray[tuple[Any, ...], dtype[float64]] | None)
actual_claims (ndarray[tuple[Any, ...], dtype[float64]] | None)
actual_expenses (ndarray[tuple[Any, ...], dtype[float64]] | None)
- fastcashflow.read_scenarios(path)[소스]#
Read a stochastic scenario set from a file.
The file is a 2-D table – one row per scenario, one column per projection month, every cell a rate or return. Reads
.parquet,.csv,.xlsxor.feather/.arrowvia_read_frame().Returns a numpy
float64array of shape(n_scenarios, n_time), or(n_scenarios,)when the file has a single column (flat-rate scenarios). The result is whatmeasure_stochastic()andmeasure_tvog()accept as theirscenarios/return_scenariosinput.Calibration – Hull-White, Vasicek, regime-switching, climate paths, etc. – is left to a separate scenario-generator step; this reader is just the storage / handover layer. For large scenario sets (thousands of paths) prefer
.parquetor.featherover.xlsx.
- fastcashflow.describe_basis(obj, *, file=None)[소스]#
Print the tree structure of a Basis (or read_basis BasisRouter).
Groups the fields by role – rates, economic / expense, risk adjustment, coverages / coverage types, state machine, other – so a reader can see what is inside the object without scanning every dataclass field.
Pass a single
Basisto see one segment, or pass theBasisRouterreturned byfastcashflow.io.read_basis()/fastcashflow.io.load_sample_basis()to also see the(product, channel)keys.- 반환 형식:
None
- fastcashflow.samples.model_points(template='gmm')[소스]#
Bundled sample model points (
template="gmm"default,"vfa"for the variable account-value contracts,"ul"for the account-backed universal-life contracts,"ul-annuity"for the universal-life-annuity 2-phase accumulation -> income contracts).- 매개변수:
template (str)
- fastcashflow.samples.basis(template='gmm')[소스]#
Bundled sample basis.
template="gmm"(default) returns the per-segmentBasisRouter(a(product, channel)->Basismapping);template="vfa"returns the single variable-contractBasis;template="ul"returns the single account-backed universal-lifeBasis;template="ul-annuity"returns the universal-life-annuity (2-phase accumulation -> income)Basis.- 매개변수:
template (str)
- fastcashflow.samples.inforce_state()[소스]#
Bundled sample in-force state (elapsed_months / count / prior_csm / …).
- fastcashflow.samples.calculation_methods()[소스]#
Bundled sample coverage-code -> calculation-method taxonomy.
- fastcashflow.samples.treaty(cession=0.3)[소스]#
Bundled sample reinsurance treaty – a quota share ceding
cessionof the direct book (default 30%).A treaty is a parameter object, not a data file, so this is the one reinsurance-specific sample object: the underlying ceded contracts are the same
model_points()/basis()portfolio. Pass it tomeasure()orsettle()over a segment of the sample book (reinsurance is measured on a singleBasis).- 매개변수:
cession (float)
- fastcashflow.samples.export(output_dir, template='gmm', format='csv', *, quiet=False)[소스]#
Write a starter set of input template files to
output_dir.template="gmm"writesbasis.xlsxpluspolicies/coverages/calculation_methods/inforce_stateand the combinedinforce_policies(the period-close one-file form);template="vfa"writes the variable-contractbasis.xlsxandpolicies. Edit them and read back withread_model_points()/read_basis().formatpicks the data-file extension –"csv"(default),"parquet","feather"or"xlsx". The basis is always a multi-sheet.xlsxworkbook (it cannot be a flat table), soformatapplies only to the policies / coverages / state files. Use"parquet"for a portfolio large enough to stream withmeasure_stream().Prints a tree of the files written – expanding
basis.xlsxinto its sheets – so it is clear what landed where. Passquiet=Trueto suppress (e.g. in scripts). Returns the destination directory.
- fastcashflow.samples.templates()[소스]#
The available
export()/ load template names (["gmm", "vfa", "paa", "ul", "ul-annuity", "ul-cost-deduct", "ul-var-annuity"]).
- fastcashflow.write_measurement(measurement, path, *, ids=None)[소스]#
- fastcashflow.write_measurement(measurement, path, *, ids=None)
- fastcashflow.write_measurement(measurement, path, *, ids=None)
- fastcashflow.write_measurement(measurement, path, *, ids=None)
- fastcashflow.write_measurement(measurement, path, *, ids=None)
- fastcashflow.write_measurement(movement, path, *, ids=None)
- fastcashflow.write_measurement(movement, path, *, ids=None)
- fastcashflow.write_measurement(movement, path, *, ids=None)
- fastcashflow.write_measurement(movement, path, *, ids=None)
- fastcashflow.write_measurement(measurement, path, *, ids=None)
- fastcashflow.write_measurement(settlement, path, *, ids=None)
- fastcashflow.write_measurement(settlement, path, *, ids=None)
Write a measurement’s per-model-point headline results to parquet / CSV.
One row per model point, in model-point order. Pass
idsfor a leadingidcolumn so the results join back to policies. Dispatches on the measurement type – GMM writesbel/ra/csm/loss_component, PAA writeslrc/loss_component, VFA addsvariable_fee/time_value, reinsurance held writesbel/ra/csm. A mixed-portfolioPortfolioMeasurementwrites one file per model present (results.parquetbecomesresults-gmm.parquet/results-paa.parquet/ …), each with anidcolumn joining its rows back to the portfolio. A new model registers its columns with@write_measurement.registerin the module that defines its measurement type (so io.py stays free of the engine import).
- fastcashflow.gmm.measure_stream(input_path, output_dir, basis, *, coverages=None, calculation_methods=None, chunk_size=20000000, backend='cpu', id_column=None, validate_unique_mp_id=True)[소스]#
Stream a valuation through a parquet file one chunk at a time.
Reads the input in chunks of
chunk_sizemodel points, values each chunk with the fused fast path (measure(..., full=False)), and writes the results as a parquet dataset – onepart-NNNNN.parquetfile per chunk – underoutput_dir. Peak memory is a single chunk, so this scales past what an in-memory run could hold.The input is a policies + coverages pair, mirroring
read_model_points():input_pathis the policies parquet andcoveragesthe coverages parquet. Each chunk of policies pulls its coverage rows bymp_id, so sorting the coverages file bymp_idlets the parquet reader prune row groups. A flat one-row-per-policy (wide) file is not accepted – it cannot carry the per-coverage waiting and reduction rules.basismay be a singleBasis(uniform portfolio) or a{(product, channel): Basis}dict, exactly asmeasure. With a dict each chunk routes its model points to their segment’s basis, so the policies parquet must carryproduct/channelcolumns.id_columnnames the policies column written as the resultid(so the output parquet joins back to a business key); it defaults tomp_id. The coverages are always joined onmp_idregardless.validate_unique_mp_id(defaultTrue) scans the whole policies file once up front and rejects a duplicatemp_id– the same data errorread_model_points()raises, which a chunk-by-chunk read would otherwise miss when the same id falls in different chunks. Set itFalseto skip the scan when the upstream extract already guarantees uniqueness.Returns the total number of model points processed.
- fastcashflow.sample_data_dir()[소스]#
Return the on-disk path of the bundled sample data directory.
The directory contains
sample_basis.xlsx,sample_policies.csvandsample_coverages.csv– the inputs behindload_sample_basis()andload_sample_model_points(). Use this to open the workbook in Excel and see what a complete fastcashflow input looks like before preparing your own.- 반환 형식:
- fastcashflow.clear_codegen_cache(*, prune_older_than_days=None)[소스]#
Remove generated kernel sources from the on-disk codegen cache.
Each unique state-machine topology persists a
fast_kernel_*.py(orfast_kernel_sm_*.py) source file plus its__pycache__sidecars. Over a session that explores many topologies these accumulate. This helper sweeps them.- 매개변수:
prune_older_than_days (float | None) – Only drop files whose mtime is older than this many days.
None(the default) drops everything.- 반환:
Number of files removed (sources and
__pycache__artefacts).- 반환 형식:
참고
Disk-only: modules already imported in the current process continue to work via
sys.modules. Codegen for a still-needed topology will transparently regenerate on the next call.
Visualisation#
The plotting helpers use matplotlib, which is included in the standard install. The measurement and reconciliation charts dispatch on the result type – GMM, PAA, VFA and reinsurance held each draw their own model’s quantities (a PAA result draws its LRC and LIC; a reinsurance-held result draws the ceded streams and its possibly-negative CSM). A portfolio container is refused – plot one model slot’s native result instead.
- fastcashflow.plot_liability(measurement, *, ax=None, title='Liability components over time')[소스]#
- fastcashflow.plot_liability(measurement, *, ax=None, title='Liability components over time')
- fastcashflow.plot_liability(measurement, *, ax=None, title='Liability components over time')
- fastcashflow.plot_liability(measurement, *, ax=None, title='Reinsurance-held components over time')
- fastcashflow.plot_liability(measurement, *, ax=None, title='Liability components over time')
Plot the liability components over the contract’s life.
Dispatches on the measurement type: a GMM, VFA or reinsurance-held measurement draws the BEL, RA and CSM trajectories; a PAA measurement draws the LRC and the LIC (its liability has no BEL / RA / CSM split – and the LRC line excludes the loss component, whose run-off
plot_analysis_of_change()shows withcomponent="loss_component"). Each line is the portfolio total of that component at each month. Needs the trajectories, so measure withfull=True.- 매개변수:
ax (Axes | None)
title (str)
- 반환 형식:
Axes
- fastcashflow.plot_cashflows(measurement, *, period_months=12, ax=None, title='Projected cash flows')[소스]#
- fastcashflow.plot_cashflows(measurement, *, period_months=12, ax=None, title='Projected cash flows')
- fastcashflow.plot_cashflows(measurement, *, period_months=12, ax=None, title='Projected cash flows')
- fastcashflow.plot_cashflows(measurement, *, period_months=12, ax=None, title='Projected cash flows')
- fastcashflow.plot_cashflows(measurement, *, period_months=12, ax=None, title='Projected ceded cash flows')
Plot the projected money in against the money out.
Dispatches on the measurement type. A GMM, PAA or VFA measurement draws premium income against claim and expense outgo; a reinsurance-held measurement draws the ceded streams – recoveries in against reinsurance premiums out. The monthly cash flows are aggregated into buckets of
period_monthsmonths – a policy year by default. Money in is drawn upward, money out downward, and the marked line is the net cash flow each period. Bucketing keeps a front-loaded month from dominating the chart while the cash-flow shape stays visible.
- fastcashflow.plot_csm_runoff(measurement, *, ax=None, title='CSM run-off')[소스]#
- fastcashflow.plot_csm_runoff(measurement, *, ax=None, title='CSM run-off')
- fastcashflow.plot_csm_runoff(measurement, *, ax=None, title='CSM run-off')
- fastcashflow.plot_csm_runoff(measurement, *, ax=None, title='Reinsurance CSM run-off')
- fastcashflow.plot_csm_runoff(measurement, *, ax=None, title='CSM run-off')
Plot the contractual service margin running off to zero.
Dispatches on the measurement type. A GMM or VFA measurement draws the unearned profit emerging into the income statement as service is provided; a reinsurance-held measurement draws its net cost or gain amortising – that CSM may be negative, so its axis is not clamped at zero. A PAA measurement is rejected: the PAA carries no CSM.
- 매개변수:
ax (Axes | None)
title (str)
- 반환 형식:
Axes
- fastcashflow.plot_risk_adjustment(measurement, basis, *, bands=(0.75, 0.85), ax=None, title='The risk adjustment as a confidence level')[소스]#
- fastcashflow.plot_risk_adjustment(measurement, basis, *, bands=(0.75, 0.85), ax=None, title='The risk adjustment as a confidence level')
- fastcashflow.plot_risk_adjustment(measurement, basis, *, bands=(0.75, 0.85), ax=None, title='The risk adjustment as a confidence level')
- fastcashflow.plot_risk_adjustment(measurement, basis, *, bands=(0.75, 0.85), ax=None, title='The risk adjustment as a confidence level')
- fastcashflow.plot_risk_adjustment(measurement, basis, *, bands=(0.75, 0.85), ax=None, title='The risk adjustment as a confidence level')
Plot the risk adjustment as a percentile of the liability distribution.
The confidence-level method models the value arising from non-financial risk as a normal distribution centred on the best estimate; the risk adjustment is the margin from that mean out to a chosen percentile. This chart draws that normal distribution and shades the margin up to each confidence level in
bands. Dispatches on the measurement type: a GMM measurement requires a confidence-level basis; a VFA measurement’s RA is always this construct (a confidence-level margin for expense risk), as is a reinsurance-held measurement’s (the margin on the ceded claims – the risk transferred, which reduces the net cost, so its margin shades to the left of the best estimate). A PAA measurement is rejected – the PAA carries no explicit risk adjustment.
- fastcashflow.plot_analysis_of_change(reconciliation, *, component='csm', ax=None, title=None)[소스]#
- fastcashflow.plot_analysis_of_change(reconciliation, *, component='csm', ax=None, title=None)
- fastcashflow.plot_analysis_of_change(reconciliation, *, component='csm', ax=None, title=None)
- fastcashflow.plot_analysis_of_change(reconciliation, *, component='csm', ax=None, title=None)
- fastcashflow.plot_analysis_of_change(reconciliation, *, component='lrc', ax=None, title=None)
Plot one reporting period’s analysis of change as a waterfall.
Dispatches on the reconciliation type. A GMM reconciliation bridges
component–"bel","ra"or"csm"– from the opening balance to the closing balance through the future-service, finance and release drivers; a VFA or reinsurance reconciliation through finance and release. A PAA reconciliation selects one of its paragraph-100 blocks:componentis"lrc"(the default there),"loss_component"or"lic_path".A settlement reconciliation (from
gmm.settle/vfa.settleviareconcile) has no waterfall arm in v1 and is rejected here; itsstr()form prints the full paragraph-44 / paragraph-45 table.
- fastcashflow.plot_stochastic(result, *, line='bel', ax=None, bins=30, kde=True, title=None)[소스]#
Plot the distribution of a figure across the stochastic scenarios.
lineselects"bel","ra","csm"or"loss_component". A smooth Gaussian kernel density estimate is drawn over the histogram unlesskdeisFalse; the dashed line marks the mean.- 매개변수:
result (StochasticResult)
line (str)
ax (Axes | None)
bins (int)
kde (bool)
title (str | None)
- 반환 형식:
Axes