Skip to content

Vintage Analysis Tutorial

This guide shows the baseline vintage workflow for comparing smoothing methods, validating them, and optionally projecting incomplete vintage tails.

Front door: the single deep entry point is vintage.run() in cranalytics.vintage._session, which runs triangle construction, smoothing comparison, ranking, and validation in one call. Reach for the lower-level cranalytics.vintage helpers only when you need a narrower step. The cranalytics.vintage package __init__.py is a compatibility re-export surface, not a place where logic lives.

Use this workflow when

  • you have cohort-level loss history by month on book
  • you want a fast cumulative-loss workflow without building a transition model
  • you need a ranked smoothing comparison as the first success metric

Do not start here if

  • your starting object is a portfolio snapshot plus a transition matrix — use Lifetime Loss Forecasting
  • your starting object is monthly aggregated rollforward data — use the Rollforward workflow
  • your main question is time-to-default or prepayment timing — use Survival Analysis

Inputs

Minimum columns:

  • vintage_date
  • months_on_book
  • cumulative_loss_rate

Useful segmentation columns:

  • vintage_name if you plan to override vintage_col
  • segment

If your data uses older labels such as vintage_name and mob, pass them through vintage.run(..., vintage_col=..., mob_col=...).

Tip

Start with the session boundary before choosing one smoothing method. It compares methods, validates them, and can project incomplete tails.

For the full contract, see Input Data Contracts.

Code

import numpy as np
import pandas as pd

from cranalytics import vintage

rows = []
for vintage_name, max_mob in [("2022-Q1", 12), ("2022-Q2", 9)]:
    curve = np.maximum.accumulate(
        0.055 * (1.0 - np.exp(-np.arange(1, max_mob + 1) / 6.0))
    )
    for mob, value in enumerate(curve, start=1):
        rows.append(
            {
                "vintage_date": vintage_name,
                "months_on_book": mob,
                "cumulative_loss_rate": float(value),
            }
        )

session = vintage.run(
    pd.DataFrame(rows),
    min_maturity_months=12,
    extrapolate_tails=True,
)
print(session.rankings[["method_name", "score"]])
print(session.validation_summary[["method_name", "train_mse", "test_mse"]])
print(session.tail_projections.tail())

Expected output / first win

Your first win is a vintage analysis session result with method rankings, validation output, and optional projected tails for incomplete vintages.

You should expect:

  • a ranked smoothing comparison table
  • a validation summary across smoothing methods
  • optional tail_projections rows when incomplete vintages are present and extrapolate_tails=True

Visualization

Visualize your vintage curves using the plotting utilities.

from cranalytics.viz import plot_vintage_triangle
import pandas as pd

triangle = pd.DataFrame(
    {
        1: [0.01, 0.008],
        2: [0.02, 0.018],
        3: [0.03, 0.027],
    },
    index=["2022-Q1", "2022-Q2"],
)

fig = plot_vintage_triangle(triangle)
fig.show()

Common mistakes

  • Passing a wide pivoted table without first reshaping it into long vintage observations.
  • Feeding a non-cumulative loss series into a cumulative-loss workflow.
  • Expecting this workflow to model cashflows, transitions, or dynamic status migration.
  • Jumping straight to a single fitter before checking whether the session boundary already covers comparison, validation, and tail projection.
  • Using Vintage when your real business question is about feature ranking or ML backtesting.

Next step

Run the packaged demo end-to-end with:

python -m cranalytics.examples.core_vintage

If you only need one direct parametric curve fit instead of the full analysis session, use CurveFitter from cranalytics.vintage.

If you need reserve-style portfolio forecasting instead of cohort curve fitting, continue to the Lifetime Loss Forecasting Tutorial.