Credit Early Warning Platform

Covenant

Messy SEC filings in, explainable 12-month distress alerts out. Covenant rebuilds every borrower's financials as they were known on any date, and flags trouble early, with the evidence attached.

Distress score, quarter by quarterillustration
flagged event T−8qT−4q
risk scorealert threshold

The goal: cross the threshold quarters before the filing, not the week of it.

The problem

Public filings hold the warning signs, buried in messy data

Credit teams monitoring hundreds of borrowers can't read every filing. Automating it means solving data problems that quietly break most models.

Tag chaos

One concept, many labels. Revenue alone appears under 8+ XBRL tags across companies and years.

Restatements

The same quarter is reported several times, sometimes with different numbers. Overwrite it and history is lost.

Year-to-date math

10-Qs report cash flow as year-to-date; 10-Ks rarely report Q4 at all. Quarters must be derived.

Future leakage

Using a number before it was public makes back-tests look brilliant and live alerts useless.

Messy borrower lists

Typos, old names, missing or wrong tickers, and subsidiaries that look like their parents.

How it works

From raw filings to an alert an analyst can trust

Batch Python jobs pull public data, clean it into a point-in-time model, and serve it through Supabase with per-tenant security.

SEC EDGAR + FREDfilings, XBRL facts, macro series
→
Ingestrate-limited, idempotent raw landing
→
Pipelinetag mapping, quarters, versions, quarantine
→
Modelsfeatures, labels, risk scores, explanations
→
Analyst consolewatchlist, evidence, alerts
01 · Match

Find each borrower

Names and tickers are resolved to SEC companies; anything uncertain goes to a reviewer, never a guess.

02 · Rebuild

Clean the financials

25 standard metrics per company, every value dated to when it became public and traced to its filing.

03 · Score

Rank distress risk

A calibrated LightGBM model scores 12-month bankruptcy risk from ratios, trends, filing behavior and macro conditions, with plain-English drivers.

04 · Act

Review and respond

A watchlist you can rewind to any past quarter, risk signals Claude read from the filings (each with its quote), and “Ask the filings” with citations. Agree/dismiss feedback comes later.

Python 3.12PolarsPostgreSQL / SupabaseRow-level securityrapidfuzzPanderaLightGBMClaude APINext.jsGitHub Actions

System design

The whole system, in one picture

Six simple steps take public filings and turn them into a risk score you can check. The diagram walks through them one at a time. Pick a step to jump to it.

How Covenant works Public data from the SEC and FRED is collected weekly by a GitHub Actions robot, cleaned and scored, stored in Supabase behind a security guard, and shown on a website. The model is trained once on a laptop and saved to cloud storage. Trainingonce, on a laptop Public data Weekly robotGitHub Actions, every Monday Supabase (cloud) On the web SEC EDGARcompany filings FREDeconomy numbers Collectdownload Cleanfix + date Scorerisk + why Databasenumbers and scores File storageraw files, the model Security guardwho can see what WebsiteNext.js on Vercel Youany browser, no sign-up SEC bulk files11,000 companies Learnfrom 1,400 bankruptcies ModelLightGBM

How it fits together

Data moves left to right. Training happens once, at the top.

Everything uses public data only. Swipe sideways to see the whole diagram on a phone.

Point-in-time financials

Every number, as it was known on the day

A worked example from real data: Hertz's 2020 revenue, the year it filed for Chapter 11.

QuarterSourceRevenue ($M)
Q4 201910-K, reported2,326
Q1 202010-Q, reported1,923
Q2 202010-Q, reported832
Q3 202010-Q, reported1,268
Q4 2020 derivedFY − 9 months1,235
FY 202010-K, reported5,258

The 10-K never states Q4. Covenant derives it as full year minus the first nine months: 5,258 − (1,923 + 832 + 1,268) = 1,235.

It is dated honestly. The derived value only exists from the day the 10-K was filed (2021-02-26), so no back-test can see it earlier.

  • 2020-11-09 · Q3 10-Q filed: Q1–Q3 known, Q4 unknown
  • 2021-02-26 · 10-K filed: FY reported, Q4 derived
  • Later filings · each re-report or restatement is a new version; nothing is overwritten

Ordered tag fallback

Per company, period and filing, the best available tag wins, and the chosen tag is stored with the number.

Formula fallbacks

44 active companies report no total-liabilities figure; it is computed from their balance sheet, checked exact in 89% of 16,393 comparable periods.

Quarantine, not silent drops

Non-USD values, impossible dates and 1000× scale slips are set aside with the rule they broke.

Borrower matching

A cascade that knows when to ask a human

Each uploaded row walks down the cascade. The first decisive step sets a confidence score; anything below 0.90 goes to review with the top candidates.

0
Prior decisionYour team already resolved this exact input before (kept per tenant, never shared)
1.00
1
Ticker + agreeing nameExact ticker, and the name matches that company
0.99
2
Exact nameNormalized name equals the company's current name (or a former name: 0.93)
0.97
3
Fuzzy nameTypos, word order and spacing ("Delta Airlines" → "DELTA AIR LINES")
score / 100
!
Conflict or near-tieTicker points at one company, the name at another, or two companies equally close
≤ 0.75
Why it matters: "Hertz Corporation" with ticker HTZ is a subsidiary loan, not Hertz Global Holdings. The cascade sends it to a reviewer instead of silently attaching the wrong company's risk score.

Results so far

Measured, not promised

On real SEC data for a 205-company sample. Each number links to the committed report behind it.

100% precision

Borrower matching

Auto-matches on a held-out, deliberately messy 100-row file, with 82% of rows matched automatically and every uncertain row sent to review.

Evaluation →
4.0M XBRL facts

Financials rebuilt

Into 25 standard metrics from 50+ tags; core metrics complete for 139 of 179 active companies.

Coverage report →
25/25 found

Bankruptcy event scan

Every known Chapter 11 filer in the sample detected in its petition year from 8-K filings.

Spot-check →
0 wrong matches

Adversarial traps

Subsidiaries (Hertz Corp, Ford Motor Credit), namesakes and old brands: none auto-matched to the wrong company.

Test cases →

The distress model

Trained on 11,000 SEC filers since 2011; tested on 2022+ data it never saw (78,000 company-quarters, 1.1% default rate).

5.1x Altman Z''

Better ranking than the classic

PR-AUC 0.098 for LightGBM vs 0.019 for an Altman Z'' baseline (random ≈ 0.011), and ahead in every backtest year 2015–2024.

Model card →
65% caught

Of bankruptcies in the riskiest 10%

Ranked within each quarter, the top tenth of companies held 65% of the next year's bankruptcies (45% in the top 5%).

Results →
14 months early

Median warning time

151 of 211 test-period bankruptcies entered the high-risk tier before filing, a median 14 months ahead.

Lead times →
0 leaks

Leakage test

Features recomputed with every later filing, restatement and macro release deleted come out identical; a planted leak is caught in CI.

The test →

What the data taught us

Real findings, documented instead of silently patched.

A label you can't take at face value

SEC's bankruptcy tag (8-K Item 1.03) includes mis-tags (AT&T, 2012), sister-company filings and subsidiary-only cases. Each filing's text is now classified before it becomes a label (≈88% precision on a fresh hand-checked sample).

Manual review →

A disappearing data series

FRED now serves only 3 years of the high-yield credit spread, too short to train on. It was replaced by Moody's Baa spread, which goes back to 1986.

Thousands vs. units

Early-XBRL filings (e.g. TrueBlue, TTEC) reported values 1,000× too small. They're caught by comparing to stable neighboring quarters.

…but not every jump is an error

BioCardia's reverse merger looked like a scale slip and was a genuine restatement. The rule was tightened so real history isn't thrown away.

Analyst console live

A watchlist you can rewind in time

The app a credit analyst would use. It is live: try the demo, no sign-up needed. It opens on June 30, 2024, five months before Spirit Airlines went bankrupt.

The watchlist on June 30, 2024: 50 companies ranked by risk, with Spirit Airlines highlighted in the high-risk group
Watchlist. 50 real companies ranked by risk. Slide back to any past quarter and see only what was public on that day.
Spirit Airlines on June 30, 2024: 4.4% risk, high tier, with score history and the top reasons
Company page. Spirit Airlines on June 30, 2024: already high risk, 4.6 months before it filed for bankruptcy, with the reasons why.

Go back in time

Pick any quarter since 2011. Every score uses only the filings that existed on that day, so you see what an analyst could really have known.

See the reasons

Each score shows its top 5 reasons in plain words, like "cash fell" or "debt rose", next to a chart of the score over time.

AI reads the filings

Claude reads each company’s risk factors, management discussion and auditor’s report and flags five warning signs, like going-concern doubt or a covenant waiver. Every flag shows the exact sentence; if that sentence isn’t really in the filing, the flag is thrown out.

Ask the filings

Ask a question about a company and get an answer built only from its filings up to the date you’re viewing, with quotes and links. If the filings don’t say, it tells you so instead of guessing.

Check every number

Key numbers link to the exact SEC filing they came from. Numbers that were worked out or later corrected are marked.

Open, but read-only

Anyone can try the demo without signing up. Visitors can look but never change anything or see another lender's data, and 19 tests check that.

Stays up to date

Every Monday a robot downloads the latest filings for the 50 companies and scores them again.

Honest demo list

25 large companies that went bankrupt since 2022, picked by size, not by score, so the misses show too. Plus 25 healthy ones.

Open the live demo →

Engineering

Built so the numbers can be trusted

The guarantees that matter for a risk product are enforced by the database and checked on every change.

No future leakage

Features read data only through an as-of query; a database check rejects any snapshot built from later filings.

Tenant isolation

Postgres row-level security on every customer table, with tests proving one lender can never read another's data.

History is append-only

Metrics, predictions, feedback and audit logs reject updates, deletes and truncation at the database level.

Safe to re-run

Every job is idempotent; a full rebuild followed by a second run adds zero rows.

281 tests in CI

Lint, strict type checks and tests on every pull request, a full build of the web app, plus migrations applied to a real Supabase database.

CI runs →

Config, not code

Tag mappings, event codes, sector exclusions and model names live in versioned config.

Roadmap

Seven phases shipped, and the demo is live

Each phase ships with acceptance tests before the next one starts.

PhaseScopeStatus
0 · FoundationsMonorepo, migrations, row-level security, CIDone
1 · IngestionSEC + FRED clients, dev sample, bankruptcy scanDone
2 · Canonical modelPoint-in-time financials, quarters, restatementsDone
3 · Entity resolutionUpload, matching cascade, review queueDone
4 · ModelsFeatures, labels, Altman / logistic / LightGBM, SHAP, model cardDone
6a · Console + demoWatchlist with rewind, company pages, model card, read-only public demo, weekly refreshDone
5 · AI signalsClaude reads filings for risk signals with quotes; “Ask the filings” with citationsDone
6b · Full appAPI, borrower upload and review, alerts and feedbackNext
7 · HostingSupabase, scheduled jobs, drift monitoring, alertsPlanned

Follow the build

The source, specs and every evaluation report are public.