credit_tools.rating.assign_rating maps borrowers to rating buckets given:
a
borrower_id,ml_score, and observeddefaultedoutcome per borrower, anda
rating_scale: a{rating: expected_default_rate}dictionary.
It fits a monotonic realized-default-rate curve against ml_score (higher score = higher risk), then maps each borrower’s calibrated default probability to the rating with the closest expected default rate. The scale itself is just data — Moody’s idealized default rates ship as a bundled resource, but any {rating: edr} mapping works.
import random
from credit_tools.rating import assign_rating
from credit_tools.resources import load_rating_scale
random.seed(0)Load a rating scale¶
load_rating_scale reads any bundled resources/<name>.json file.
scale = load_rating_scale("moody")
dict(list(scale.items())[:5]){'Aaa': 0.0001, 'Aa1': 0.0002, 'Aa2': 0.0003, 'Aa3': 0.0004, 'A1': 0.0006}Simulate a borrower portfolio¶
In practice ml_scores and defaulted come from your model’s predictions and observed outcomes. Here we simulate them so the notebook is self-contained.
n = 2000
borrower_ids = [f"b{i}" for i in range(n)]
ml_scores = [random.random() for _ in range(n)]
defaulted = [random.random() < score for score in ml_scores]Assign ratings¶
ratings = assign_rating(borrower_ids, ml_scores, defaulted, scale)
{borrower_ids[i]: ratings[borrower_ids[i]] for i in range(5)}{'b0': 'C', 'b1': 'C', 'b2': 'Ca', 'b3': 'Ca', 'b4': 'C'}Check calibration¶
For each rating, the realized default rate among the borrowers assigned to it should track the target expected default rate from the scale.
from collections import defaultdict
realized = defaultdict(list)
for i, borrower_id in enumerate(borrower_ids):
realized[ratings[borrower_id]].append(defaulted[i])
for rating in sorted(realized, key=lambda r: scale[r]):
outcomes = realized[rating]
print(
f"{rating:5s} target_edr={scale[rating]:.4f} n={len(outcomes):4d} "
f"realized_dr={sum(outcomes) / len(outcomes):.4f}"
)Aaa target_edr=0.0001 n= 48 realized_dr=0.0000
B1 target_edr=0.0270 n= 88 realized_dr=0.0227
Caa1 target_edr=0.0900 n= 56 realized_dr=0.0893
Caa2 target_edr=0.1300 n= 216 realized_dr=0.1389
Ca target_edr=0.3000 n= 465 realized_dr=0.3032
C target_edr=0.5000 n=1127 realized_dr=0.7098