CDFI and MDI peer benchmarking tool using FDIC call report data — NIM, efficiency ratio, ROAA, CET1, and more
Project description
cdfi-benchmark 📊
CDFI and MDI peer benchmarking tool using FDIC call report data.
Pull call report financials for any FDIC-insured CDFI or MDI, compute key performance metrics, build a peer group of similar institutions, and generate a benchmarking report — using the free FDIC BankFind Suite API, no API key required.
Why cdfi-benchmark?
CDFI banks and MDIs benchmark their performance against peers manually — pulling call report data from FFIEC, computing ratios in Excel, and building comparison tables by hand. cdfi-benchmark automates the entire workflow in Python.
Installation
pip install cdfi-benchmark
Quickstart
from cdfibenchmark import (
get_financials, build_peer_group,
generate_report, summary_table,
)
# Pull call report data for Broadway Federal Bank (CERT 57542)
institution = get_financials(cert=57542)
# Build peer group — similar asset size, no API key needed
peers = build_peer_group(institution, same_state=True)
# Generate benchmarking report
report = generate_report(institution, peers)
print(report)
# Get results as DataFrame
df = summary_table(institution, peers)
Sample Data (No API Required)
from cdfibenchmark import build_sample_peer_group
from cdfibenchmark.data.schema import InstitutionProfile
institution = InstitutionProfile(
cert=57542,
name="Broadway Federal Bank",
city="Los Angeles",
state="CA",
report_date="20241231",
total_assets=655_000,
total_deposits=520_000,
net_loans=380_000,
net_income=1_950,
interest_income=28_000,
interest_expense=8_000,
non_interest_income=3_500,
non_interest_expense=22_000,
total_equity=48_000,
tier1_ratio=12.2,
)
peers = build_sample_peer_group(institution)
report = generate_report(institution, peers)
print(report)
Metrics Computed
| Metric | Description | Benchmark (Strong) |
|---|---|---|
| NIM | Net Interest Margin | >= 3.5% |
| Efficiency Ratio | Non-interest expense / Revenue | <= 60% |
| ROAA | Return on Average Assets | >= 1.0% |
| ROAE | Return on Average Equity | >= 10% |
| Tier 1 Capital Ratio | Regulatory capital ratio | >= 12% |
| Loans-to-Deposits | Loan utilization | <= 80% |
| NPL Ratio | Non-performing loans / Gross loans | <= 1.0% |
| Reserve Coverage | Loan loss reserve / NPLs | >= 100% |
Asset Size Buckets
- micro — Under $50MM
- small — $50MM to $250MM
- medium — $250MM to $1B
- large — $1B to $5B
- mega — Over $5B
Data Source
FDIC BankFind Suite API — free public API, no authentication required. Data covers all FDIC-insured institutions with quarterly call report data since 1934.
https://banks.data.fdic.gov/api
Error handling
The data layer fails loud. In an early-warning / anomaly-detection pipeline a silently-empty or fabricated result reads as "nothing anomalous" and masks the real problem, so the FDIC fetchers raise typed errors instead of swallowing failures:
FDICAPIError— a transport problem: the request never produced a usable response body. Network/timeout errors, non-2xx HTTP status, and JSON decode failures all raise this.FDICResponseError— the response decoded but its structure is wrong, either at the envelope level (the top-leveldatakey absent,null, or not a list) or at the field level inside a record: a record missing itsCERTidentity, aCERTthat isn't int-coercible, or any core/optional financial field that is present but not numeric. A bad record is never coerced into a phantomcert=0bank or a fabricated0.0.- Legitimately empty is not an error. A successful request that returns zero rows
(
{"data": []}) returns the empty value for that fetcher —None, an emptyDataFrame, or[]— and does not raise.
Missing-but-not-garbage fields inside an otherwise valid record are kept, not dropped:
an absent core financial (e.g. ASSET) becomes NaN (unknown — it propagates to
any metric computed from it rather than fabricating 0.0), and an absent optional
ratio (e.g. RBCT1J) becomes None. A real present 0.0 is preserved as 0.0.
Both error types subclass CDFIBenchmarkError, so callers can catch the contract
broadly or distinguish "the API is unreachable" (FDICAPIError) from "the API changed
its shape" (FDICResponseError):
from cdfibenchmark import FDICAPIError, FDICResponseError
try:
institution = get_financials(cert=57542)
except FDICAPIError:
... # transport/HTTP/decode failure — retry or alert
except FDICResponseError:
... # wrong-shape or present-but-garbage field — contract problem
Running Tests
PYTHONPATH=. pytest tests/ -v
79 tests across all modules.
Who This Is For
- CDFI banks and credit unions benchmarking against peers
- MDI management teams preparing board reports
- CDFI Fund analysts reviewing institution performance
- Impact investors evaluating CDFI bank investments
- Researchers studying community banking performance trends
License
MIT 2026 Jaypatel1511
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cdfi_benchmark-0.2.0.tar.gz.
File metadata
- Download URL: cdfi_benchmark-0.2.0.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c1432bc1639990403b77788d3f36b0195fd28120a43ea472dc3bfdfd365a3bc
|
|
| MD5 |
b53ffdaf61282254f50bd3070fbd8852
|
|
| BLAKE2b-256 |
96883de747ba60b2c825dc4b931c66779df734bb6bdb490c78c458a7b35ba6d6
|
Provenance
The following attestation bundles were made for cdfi_benchmark-0.2.0.tar.gz:
Publisher:
release.yml on Jaypatel1511/cdfi-benchmark
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cdfi_benchmark-0.2.0.tar.gz -
Subject digest:
3c1432bc1639990403b77788d3f36b0195fd28120a43ea472dc3bfdfd365a3bc - Sigstore transparency entry: 1973221607
- Sigstore integration time:
-
Permalink:
Jaypatel1511/cdfi-benchmark@73509b11633e20a75aa7e8285cfbbf09cc20f8fb -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Jaypatel1511
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@73509b11633e20a75aa7e8285cfbbf09cc20f8fb -
Trigger Event:
push
-
Statement type:
File details
Details for the file cdfi_benchmark-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cdfi_benchmark-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
562e6b63d6275a82bb32958b76cb19764c48c2f1e9e40e450007ca7c7d8c20b0
|
|
| MD5 |
63c52b7e8b489cee5077c2aafa94daa1
|
|
| BLAKE2b-256 |
c905769289de30bf056d46564f435efc396d73af813efcc61c147b0324fc4032
|
Provenance
The following attestation bundles were made for cdfi_benchmark-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Jaypatel1511/cdfi-benchmark
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cdfi_benchmark-0.2.0-py3-none-any.whl -
Subject digest:
562e6b63d6275a82bb32958b76cb19764c48c2f1e9e40e450007ca7c7d8c20b0 - Sigstore transparency entry: 1973221671
- Sigstore integration time:
-
Permalink:
Jaypatel1511/cdfi-benchmark@73509b11633e20a75aa7e8285cfbbf09cc20f8fb -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Jaypatel1511
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@73509b11633e20a75aa7e8285cfbbf09cc20f8fb -
Trigger Event:
push
-
Statement type: