← Patrick Taylor
hmda-audit  |  Personal project  |  2026

Who gets a mortgage, and is it fair?

US lenders have to show that their approvals are fair across race, ethnicity and sex, and banks have to validate the models behind credit decisions. This project runs both kinds of check on public mortgage data.

32.6M
mortgage applications checked, 2023 to 2025
614
of 5,329 lenders flagged on at least one race, ethnicity or sex group (100+ applications)
66%
of the Black applicant denial gap left after income, loan and debt controls
86%
of denied vs. approved pairs ranked right by a model with race left out

A screening signal, not a legal finding. Public HMDA data has no credit scores. A gap or a flag is a reason to look closer. It does not prove discrimination.

The data

In the US, every lender has to publish every mortgage application and whether they approved it. I pulled three years, 2023 to 2025: 36.7 million HMDA records from 5,329 lenders. Taking out purchased loans, which nobody applied for, leaves 32.6 million applications. I split the approvals by race, ethnicity and sex.

The screen

Black applicants were denied 28% of the time, joint applicants 15%
Share of mortgage applications denied, by race, 2023 to 2025, all lenders combined
Joint applicants15.5%
Asian16.5%
White17.4%
Black28.2%
American Indian or Alaska Native28.2%
Two or more minority races29.6%
Native Hawaiian or Pacific Islander29.7%
How to read this: a longer bar means that group was turned down more often. Joint applicants (a mixed-race applicant pair) are the best-approved group, so the audit compares everyone to them.
Note: Every record except purchased loans, 32.6M applications, so a withdrawn or incomplete file counts as not denied. Not shown: "Race Not Available", 26.7% of applications (20.2% denied), and "Free Form Text Only", 9,032 applications (41.9% denied). Hover a bar for its count.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor
Click to enlarge

First I screened every lender with the four-fifths rule, a first-pass test regulators use. If a group gets approved less than 80% as often as the reference group, that lender gets flagged for a closer look.

614 of 5,329 lenders got flagged. The screen counts every record except purchased loans, so a withdrawn or incomplete application counts as not denied. On that definition, with all lenders added together, Black applicants are approved at 0.85 of the joint-applicant rate, just above the line; the only group below it is "Free Form Text Only", 9,032 filings where race was only written in as free text.

The definition matters. Counting only the files a lender actually approved or denied, which is how the model below is trained, Black applicants come in at 0.79 of the joint-applicant rate, below the line, and three other named race groups fall below it too. Both versions are in the results files.

The fair comparison

66% of the gap for Black applicants is still there after matching income, loan size and debt
Denial-rate gap, Black vs. joint applicants, in percentage points
4.2 ptsaccounted for by income, loan size and debt
8.2 ptsstill unexplained
How to read this: the whole bar is the raw 12.4-point gap. The orange part is what is left when applicants with the same income, loan size and debt are compared.
Note: Controls are income, how much of the home's value is being borrowed, debt measured against income, what the loan is for, and whether it is a first or a second mortgage. Run on a 200,000-row stratified sample, where the raw gap is 12.4 points (12.7 on the full file). Public HMDA has no credit scores, so the unexplained part is a reason to look closer, not proof of discrimination.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor
Click to enlarge

Raw gaps can mislead, because groups differ in income and debt. So I compared applicants with the same income, loan size and debt load.

For Black applicants the gap went from about 12 points to about 8, so roughly two thirds of it was still there.

The model

Shown one denied and one approved file, the model picks the denied one 86 times in 100
Denial model with race left out, trained on 2023 and 2024, tested on 2025
86 / 100pairs in the right orderA coin flip gets about 50, down to the black line.
How to read this: each square is one pair of applications. Orange squares are pairs the model put in the right order. Grey squares are pairs it got the wrong way round.
Note: AUC 0.86 (Gini 0.72) for gradient-boosted trees and AUC 0.78 (Gini 0.57) for logistic regression. Tested on the 2025 holdout, about 380,000 decisions per seed, from a 1,500,000-row sample; three random seeds agreed. Ranking well is not the same as being accurate for one applicant.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor
Click to enlarge

Next I trained a model that predicts which applications get denied, with race kept out of the inputs. It learned from 2023 and 2024 and was scored on 2025, a year it never saw.

If you pick one denied and one approved application, it ranks the denied one as riskier about 86% of the time. That is an AUC of 0.86, or a Gini of 0.72 in credit-scoring terms. Logistic regression gets AUC 0.78 (Gini 0.57). A constant score can't order anyone, so it gets AUC 0.5.

The race probe

Race was never an input, yet the inputs partly give it away: 0.68, where a guess scores 0.50
Linear probe telling Black from White applicants, AUC, mean over 5 seeds
0.50pure guess (shuffled labels)
0.68Read from the allowed inputs
1.00 = never wrong
How to read this: 0.50 means the probe is guessing, 1.00 means it is never wrong. The further right the orange dot, the more the allowed fields give race away.
Note: The strongest single proxies are property value (0.599), income (0.595) and debt-to-income band (0.594). Inside the network the probe scores 0.673 and 0.659 on hidden layers 1 and 2 (random-weights control 0.642), so training does not make race easier to read. This measures information a probe can extract, not discrimination or intent. 500,000-row national sample; the network's own denial AUC is 0.838.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor
Click to enlarge

I also trained a small neural net on the same allowed inputs. Then I checked whether a simple probe could still tell Black and White applicants apart from its inputs and hidden layers. It could, well above chance.

The controls

37 of 38 model-risk controls are enforced by an automated test
Each square is one control, mapped to SR 11-7 (US) and OSFI E-23 (Canada)
has a testno test yet (C-38)
How to read this: a filled square is a rule the code checks on every build, so breaking it turns the build red. Hover a square to read the rule.
Note: C-01 fails the build if a race, ethnicity, sex or age column reaches the model.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor
Click to enlarge

I also wrote 38 model-risk controls, mapped to the US and Canadian guidance (SR 11-7 and OSFI E-23).

37 of them have an automated test behind them, including one that fails if race ever sneaks into the model.

The watch list

1,018 of 1,400 raw lender flags do not survive a small-sample correction
Lenders flagged for the Black vs. White denial-rate gap, all years pooled
382still on the watch list
1,018drop out after the correction
How to read this: the whole bar is every lender the raw four-fifths screen flags. A lender with a handful of applications can fail it by luck, so the correction pulls small lenders toward the typical gap.
Note: The typical Black vs. White denial-rate gap is 9.8 points, and it varies by about 5.9 points from lender to lender. With a 100-application floor the raw screen flags 368; the full watch list has 404 lenders, 97 of them missed by that screen. A flag is a prompt for review, not a finding.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor
Click to enlarge

The four-fifths screen treats every lender the same, so a lender with only a handful of applications can fail it by chance. I also built an empirical-Bayes watch list: it compares each lender's Black vs. White denial-rate gap, pooled across all years, and shrinks small lenders' estimates toward the typical gap before ranking them.

1,018 of the 1,400 lenders flagged by the raw four-fifths screen do not survive that correction. For the top watch-list lenders, ranked by shrunk gap, I also checked the raw gap against a version controlled for loan and applicant differences: it could be estimated for 21 of them and stayed positive for all 21, and it could not be estimated for 4 more. No lender identity appears on this page.

Checked on Databricks too

Spark on Databricks and DuckDB agree on every row: 0 mismatches
Lender-by-race application and denial counts, Spark SQL vs. DuckDB, full national file
RowsApplicationsDenials
Spark SQL, Databricks31,79332,620,7896,166,654
DuckDB, local parquet31,79332,620,7896,166,654
Note: 0 value mismatches across every (lender, group) row, checked on 2026-09-28.
Source: public HMDA filings, 2023 to 2025. hmda-audit analysis.
Patrick Taylor

To make sure the DuckDB numbers above hold up on a different engine, I reran the lender-by-race counts as Spark SQL against the full national file loaded into a Databricks Delta table, and compared every row against the DuckDB result.

Every row matched: 0 mismatches across 31,793 lender-by-race rows, 32,620,789 applications and 6,166,654 denials.

The limit

Public HMDA has no credit scores, and I can't add them, so every controlled gap leaves out the biggest factor in real underwriting. A flag means look closer, not proof of discrimination.

Stack
Python, DuckDB, scikit-learn, LightGBM, Databricks (Spark SQL, PySpark, MLflow), Streamlit
Speed
185x faster than pandas on the same tables, 0.13 s against 24.36 s
Back to everything else