A map of who buys from whom, and why it stops short
Companies name each other in their annual filings. I extracted 10,382 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, and acting on it nearly doubled the graph without closing the gap.
- What I proposed
- A dated map of who buys from whom, extracted from 10-K text by a model on a laptop, every edge quoting the sentence it came from, built so that a backtest on it never sees tomorrow's knowledge.
- What it produced
- 2,572 edges over 220 firms, covering 47 names on a typical day. The return test needs 97, so the study stopped at the coverage report. The shortfall traces to disclosure law: a company must name customers above 10% of revenue and never its suppliers, so the S&P 500 side of the graph is the silent side. Building from the supplier side nearly doubled coverage, as predicted, and still fell short.
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
3,298 filings, run through a 30-billion-parameter model on a laptop over about 27 hours across two passes. 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
10,382 validated claims become 2,572 dated relationships between listed companies, across 220 source firms. Only about a third of the claims name a counterparty that resolves to a traded ticker; the rest name private companies, subsidiaries or governments. 2,572 sounds like a lot until you ask how many companies it covers on any given day. The answer is 47, out of roughly 403 trading. The test I built this for needs about 97 to see effects the size the literature actually reports.
At 47, the bar the test would have to clear is IC 0.024, and published effects sit near 0.010 to 0.020. It would run. It still 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. One explanation I did not test: the two-thirds of claims lost at name resolution. That is a larger loss than the disclosure asymmetry below, and whether a better resolver would move coverage is an open question. Coverage grows in a straight line with the number of filings, which I have now measured twice — ten times the filings gave almost exactly ten times the coverage the first time, and running a second corpus 1.7× the size moved it 1.7× again.
What was actually going on
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 half of the filings name nobody at all (45% in the full corpus, 54 to 55% in the model comparison), 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 should be built from the supplier side, because small companies naming their large customers is what the law compels. So I ran it.
First a check that decided whether it was worth doing at all: none of those 181 companies were in the price panel, because the panel is built from index membership. A company with no share price contributes a relationship but never a testable observation. Fetching prices and applying the same screens left 105 that are genuinely tradable.
Extracting their 1,322 filings confirmed the prediction. They yield 5.9 relationships per productive filing against 5.0 for the large caps, at an unchanged resolution rate, and coverage went from 27 names per day to 47.
And it still wasn't enough. That is the honest end of this study: the reason for the shortfall was right, acting on it nearly doubled the graph, and the bar was further away than the gain. Getting to 97 would need roughly 6,800 filings against the 3,298 that exist, from companies not yet downloaded. That is a different project, not a continuation of this one.
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.