Skip to content
← Back
Information extraction · Python

A map of who buys from whom, and why it stops short

Companies name each other in their annual filings. I extracted 5,461 of those relationships with a model running on my laptop, built the graph, and then found it was too thin to answer the question I built it for. The reason turned out to be the interesting part.

The idea

“Intel is one of our most significant customers.” “We purchase substrates from Ibiden and Unimicron.” Sentences like these sit in thousands of 10-K filings, and together they describe a network of who depends on whom.

There's a known effect that makes this worth mapping: when a company's major customer has a good month, the supplier tends to follow about a month later, because investors don't track relationships no database makes visible.

You can buy supply-chain data, but not as a historical series that knows what was public on each past date. Without that, any backtest is quietly using tomorrow's knowledge. So I built one: every edge stamped with the day the filing appeared, and every claim required to quote the sentence it came from word-for-word.

What I built

1,989 filings from S&P 500 companies, run through a 30-billion-parameter model on a laptop over about 22 hours. No API spend. Each extracted relationship survives only if its supporting quote appears literally in the filing, which throws out anything the model invented.

Before running it I wrote down what would make me stop. If the finished graph couldn't cover enough companies to test the return effect, the study ends at the coverage report rather than proceeding to a weaker test that might produce a publishable-looking number anyway.

The gate fired

27 covered~420 tradable6.4%
Companies the graph can say something about on a typical day, against every company trading that day. A return test needs roughly 145 lit dots to say anything. This is 27.

1,185 relationships across 125 companies — which sounds like a lot until you ask how many companies it covers on any given day. The answer is 27, out of roughly 420 trading. The test I built this for needs about 145.

At 27, the statistical bar the test would have to clear is roughly double the size of the effect published research reports. It would run. It just wouldn't mean anything. So I didn't run it.

Four explanations for the shortfall occurred to me, and I tested each against data I already had before changing anything: that the name-matching had broken, that valid claims were being silently discarded, that the cheaper model had collapsed, and that coverage saturates so more filings wouldn't help. All four were wrong. Coverage grows in a straight line with the number of filings — ten times the filings gives almost exactly ten times the coverage.

What was actually going on

customer
668
partner
276
supplier
241
Relationships extracted, by type. Companies name their customers nearly three times as often as their suppliers — not by preference, but because Regulation S-K compels one disclosure and not the other.

US securities regulation requires a company to disclose any customer worth more than 10% of its revenue. It requires nothing equivalent about suppliers.

So disclosure is lopsided by law, and extracting the S&P 500 means extracting the side of every relationship that isn't obliged to speak. Large companies rarely name who they buy from. That also explains why roughly 45% of the filings name nobody at all — a rate that held steady across two different models, so it's a property of the filings rather than of the extractor.

The design lesson follows directly: this graph is built from the supplier side. Small companies naming their large customers is what the law compels. 2,261 filings from 181 such companies are already downloaded and screened in the repository, waiting.

Why it matters

The graph is real and released, with per-edge quotes and its coverage ceiling attached. Commercial supply-chain datasets publish neither their point-in-time behaviour nor how much of the market they actually cover, and both decide whether a result built on them is trustworthy.

The rest is a negative result reported at full strength. I built the thing, it wasn't enough, and the reason is a fact about disclosure law that anyone planning the same project can use before spending the compute.