Consumer Lending Analyst Path · Mission 24 of 30Expert

Charge-off rate + principal recovery by vintage year

Multi-CTE with LEFT JOIN aggregation, then nested conditional aggregates with NULLIF/CASE guards for safe ratio math

The Brief

Marcus HollandChief Credit Officerconsumer-lending

Vintage charge-off review for the credit committee. By origination vintage year, report: total vintage size, charge-off count, charge-off rate, total unrecovered principal at time of CO, and principal-recovered rate (principal paid pre-CO ÷ funded amount across the CO cohort). Skip the 2019 ARM cohort — filter `funded_date >= '2024-11-01'`. Six columns: `vintage_year`, `vintage_size`, `charge_offs`, `co_rate` (4 decimals), `unrecovered_principal` (rounded 2 decimals), `principal_recovered_rate` (4 decimals). Sort by vintage_year ascending.

You'll practice

Multi-CTEConditional aggregationNULLIF guard

Tables & columns available

lending_originationsfact17 columns
ColumnTypeKey
origination_idINTPK
app_idINTFK → lending_applications
account_idINTFK → retail_accounts
funded_dateTEXT
funded_amountREAL
term_monthsINT
interest_rateREAL
rate_typeTEXT
arm_reset_dateTEXT
fico_at_origINT
dti_at_origREAL
ltv_at_origREAL
property_valueREAL
lo_nameTEXT
channelTEXT
current_statusTEXT
closed_dateTEXT
lending_paymentsfact10 columns
ColumnTypeKey
payment_idINTPK
origination_idINTFK → lending_originations
due_dateTEXT
paid_dateTEXT
scheduled_amountREAL
principal_paidREAL
interest_paidREAL
escrow_paidREAL
days_lateINT
statusTEXT

Hints (3)

Each hint you reveal reduces the XP you can earn. Try the query first.

Hint 1

First CTE rolls per-loan: LEFT JOIN payments and aggregate `principal_paid` for the non-CO statuses (paid plus late) so each origination row carries its total principal-paid-pre-CO. Use COALESCE inside the SUM so loans with no payments yet contribute zero rather than propagating NULL through the math.

Hint 2

Outer SELECT aggregates per vintage_year. The unrecovered-principal column is the loss amount per CO loan — funded minus principal-paid-pre-CO — summed only across charge-offs. Conditional SUM on a status filter is the canonical shape; the recovery-rate column is the inverse ratio (paid-pre-CO ÷ funded) summed across the CO cohort.

Hint 3

Guard the recovery-rate divide with a CASE on the denominator — a vintage with zero charge-offs has a zero denominator and would otherwise throw. Cast inner numeric expressions to NUMERIC before each ROUND; Postgres won't ROUND `double precision` to a fixed decimal count.