The map · Part One · The method · Chapter 4 of 23
The screener: two thousand to a handful
A coarse mechanical pass that removes what can be faulted on public figures, so that a person's reading time goes only to what survives. It finds nothing. It only removes. About 10 minutes.
Two thousand names, one reader. The only way through is to remove most of them without reading them, using questions coarse enough that a machine can ask them of public figures. That pass is the screener, and the most important thing to understand about it is what it does not do: it does not find good companies. It removes companies that fail a small number of coarse tests, and hands whatever is left to a person.
Where the two thousand come from
The list is fixed and dated: the members of the main American, European and Nordic indices for large and mid-sized companies, written to files on a stated day and kept under version control. The list is never fetched live, so a run on any day can be repeated later on exactly the same names.11The universe files under config/universe/ carry their date in the filename; index membership is never fetched at run time (vss/universe.py). 2,011 instruments on 2026-09-10. Funds, trusts, blank-cheque companies and foreign-listing wrappers are removed on the source list's own type field, never by guessing from the name.
The steps, in order, and why each question is asked
Step 0: names already owned or already decided
A small exclusion list of companies the owner already holds or has already ruled on. There is no point screening a name whose answer is known. An entry on that list that matches nothing is reported as inert, so the list cannot quietly rot.
Filter 1: has the price fallen enough, but not too much?
The method is looking for businesses the market has temporarily lost interest in. So the first question is whether the price sits between 15 and 50 percent below its highest point of the last year. Less than 15 and nothing has happened. More than 50 and the working assumption is that something is actually broken, which is a different kind of company and out of scope.22Framework §3 gate 1; the band is implemented once in vss/rules.py and reused by both the screener and the nightly run.
Two more questions ride with this one. Has the company been listed for at least five years? A younger listing does not have enough reported history to test, and a company spun out of another counts as new.33Ruling E52 and E52.1: a listing younger than five years is rejected on missing data, and a spin-off is a new listing. And is the business one the method can judge at all? Chapter 5 covers that boundary.
Filter 2: can the business be faulted on three coarse figures?
For the survivors, and only the survivors, the tool fetches accounts and asks three questions. Did the business generate spare cash over the last twelve months? Is its debt, net of cash, no more than two and a half times its yearly operating earnings before interest, tax, depreciation and amortisation, a figure abbreviated as EBITDA? And has revenue avoided falling, against the same quarter a year earlier, two quarters in a row?44Thresholds live in config/screener_filter2.yaml, each citing its framework section: free cash flow positive (a coarsening of gate 3's six-of-eight quarters, because quarterly cash-flow history is unavailable for much of the list); net debt to EBITDA at most 2.5×, ruling E44; two consecutive year-on-year quarterly revenue declines kill, ruling E45.
The debt cap has its own history. The first version used a looser limit, three and a half times, on the argument that a coarse filter must not lose a name the careful reading would keep. Four days later a test run showed 72 names passing the loose limit and then failing the method's own stricter test later on. Letting them through bought nothing but reading time. The cap was tightened by a dated ruling that records the count.55Rulings E2 (2026-08-22, 3.5×) and E44 (2026-08-26, 2.5×), with the 72-name measurement in the ruling text.
Ranking: which survivors get read first
What is left is ordered on two figures. How profitable is the business relative to everything it owns, measured as operating profit over total assets. And how cheap is it relative to what the whole business would cost to buy, measured as operating profit over that cost. Each survivor is ranked on both, the two rank positions are added, and the list is sorted. No weights, no score, no threshold.66Ruling E6 as amended by E43. An earlier version used a profitability measure whose denominator went to zero for businesses that own little, so the top thirteen placings went to the thirteen names nearest that pole; it was replaced. Banks, insurers and property companies cannot be ranked on the first figure and are listed separately on the second alone, never mixed in.
What comes out
The top twenty are watched for price movements. Three to five are proposed for reading. None of them carries a value, a buy price or a confidence level. The screener's output is a reading list, and a person reads it.
What a screen cannot see
- Why the price fell. A 30 percent fall on a rumour and a 30 percent fall on a fraud look identical to filter 1. The reason is the first thing the reader has to find out, and it is the subject of the next chapter.
- Anything the vendor's figures get wrong. The accounts come from a data vendor at this stage, not from the filings. A wrongly labelled line passes through unnoticed. One such line was found only because it failed to match any filed figure for any of five companies checked by hand.77Ruling E43: the vendor's 'gross profit' line did not reconcile to any filed line for any of five test names, and was replaced by operating profit in the ranking.
- A name it never fetched accounts for. Accounts are fetched only for filter 1's survivors. A test that reads an industry label cannot see a company that never got that far. That silence is a fact about the fetch, not about the company, and the tool says so.88Ruling E96: 'The string limb is blind where the fetch has not reached.'
- Whether a company is any good. Nothing in this chapter is a judgement. It is a list of things that were not faulted on a coarse test.
One more honesty. The screener replays past dates using today's accounts against that day's prices, because the vendor serves only the latest accounts. A replay is therefore not evidence of what the screen would have found on that day, and every replay report says so in a banner at the top.
The exact rules
Framework §3 (gates 1 to 5) and the screener rulings E2, E6, E43, E44, E45, E46, E48, E52, E93, E96, E97 in reference/FRAMEWORK-EDITS.md. Thresholds: config/screener_filter2.yaml. Code: vss/screen.py, vss/filters.py, vss/universe.py, vss/ranking.py. Two technical figures, a momentum indicator and a moving average, are computed and printed but never used as filters, because on a real past purchase either direction of such a filter would have discarded one of the two relevant days.
Sources for this chapter
- Screener design and rulings — read from this repository: reference/FRAMEWORK-EDITS.md E2, E6, E43, E44, E45, E52, E93, E96, E97; config/screener_filter2.yaml; vss/screen.py; vss/filters.py
The slow way, made survivable. Built 2026-09-13 from the repository as it stood that day. NAME A and NAME B are fictional; no company, price or holding appears on this site. Not financial advice: one private investor's method, shared as it is.