learn-finance-with-phoebe / Practitioner session 9 of 10
Learn Finance with Phoebe · Practitioner session 9 of 10

The finding that changed at 48 months

The first pass of the Marchmont bench was run to 24 months. It showed a company profitable in every month, below zero in the bank from month 10, and still under water when the run stopped. The sentence being drafted was "a profitable company runs out of cash and dies". Before it was written down the same model was run again with one argument changed, to 48 months, and the balance comes back above zero on its own in month 28 after eighteen months under. The headline was wrong. What was wrong with it was not the model, not the arithmetic and not a key: it was the window.

🟢 Hands on Analysts about to publish a finding Anyone who has to retract one 45 min
0-4 · Welcome 4-22 · The claim, and the run that killed it 22-42 · Killing your own first 42-60 · What survives a correction
Part 0

A true story, and the thing it is evidence for

Session 6 named the horizon as an assumption and showed the two windows side by side. This is what happened when that was not done in time. The interesting part is not that a draft was wrong, which is ordinary. It is that every figure in the wrong draft was correct, the balance sheet tied in the run that produced it, and no check in the engine would ever have flagged it.

Live = run together in the session Self-study = the full depth, after class 🔍 Every diagram zooms

★ What you walk out with today. The horizon treated as an input with a value, reported next to the answer rather than left in a function signature. Four tests you can run on your own claim before anybody else sees it, each of them one line. The difference between a finding and a reading of a window, which is that one of them survives an extension and the other does not. A method for retracting half a claim and keeping the other half. And the opposite error, which is available from exactly the same mistake pointed the other way.

The uncomfortable part. The draft that died passed every automated check there was. MARCHMONT.ties() returns ok on a 24 month run and on a 48 month run, with a worst error of 2.24e-8 across the whole canon set. Profitability was real. The cash figures were real. There is no assertion that could have been added to the bench to catch this, because nothing was broken. The failure was a conclusion drawn from a window that did not contain the event, and only reading catches that.

LiveThe horizon is an input, and it is the one with no default worth trusting4 min▶

Fifteen assumption keys get argued over before a run. The horizon gets typed once, usually as whatever the function does when you leave the second argument off, and then never appears in the output. Marchmont's engine defaults to 24 months. Nobody chose 24 months; it was chosen in the source file by whoever wrote the signature, for no reason connected to the question being asked.

Treat it the way the growth rate is treated. It has a value, the value changes the answer, and the value belongs next to the answer. A figure quoted without its horizon cannot be reproduced by the person reading it, which means it cannot be disagreed with, which means it is not yet a finding.

Real world

"Marchmont troughs at -£115,501 and does not recover" and "Marchmont troughs at -£115,501 and recovers in month 28" are both accurate reports of the same model. The first one is missing four words: over a 24 month run.

Part 1 · covers the claim and its death

Three stages, and one changed argument

14 min live

No assumption key was touched between the draft and the correction. The growth rate, the margin, the collection days and the opening balances were identical. The second argument to run went from 24 to 48, and a company that died became a company with a financing requirement.

One claim, three stages, and the only difference is a horizon. Stage 1 · as drafted Run to 24 months Profitable in every month Below zero from month 10 Never comes back in view Headline: the company dies Stage 2 · the run that killed it Same model, same keys Run to 48 months Trough -£115,501, month 19 Back above zero in month 28 Nothing else was changed Stage 3 · what was left Kept: profit and cash diverge Kept: the gap needs funding Dropped: the company dies Reported: the run that did it A finding with a correction A result that flips when you extend the window was never a finding. The model was right in both runs. The claim was a statement about the window, written as though it were a statement about the company.
🔍 Click to zoom - stage 2 is a single character of difference in the call. That is the entire distance between the two conclusions.
LiveThe two runs, side by side, in the console5 min▶

One reader, two horizons. The variable that matters is the last one, and its name is doing real work: back returns zero when the balance does not come back inside this window, which is not the same statement as never.

consolefunction verdict(months) { const r = MARCHMONT.run({}, months); if (!MARCHMONT.ties(r).ok) throw new Error("does not tie at " + months); let t = r.rows[0]; for (const row of r.rows) if (row.cash < t.cash) t = row; let back = 0; // 0 means "not inside this window" for (const row of r.rows) { if (row.month > t.month && row.cash >= 0) { back = row.month; break; } } return { horizon: months, out: MARCHMONT.cashOutMonth(r), at: t.month, back: back }; } verdict(24); // out 10, trough at 19, back 0 verdict(48); // out 10, trough at 19, back 28

Both rows are correct. The trough is the same month and the same figure in both, because extending a horizon cannot change a month the engine has already computed. Only the last field moves, and the whole headline hung off it.

LiveZero is not a value, it is a missing value with a number on it4 min▶

The recovery reader returns 0 when it finds nothing. That is a convenient thing for a function to do and a dangerous thing for a reader to receive, because 0 is a perfectly good month number in a context where months are integers, and because "no recovery found" reads at a glance as "no recovery".

The fix is not clever. Return something that cannot be mistaken for an answer, or return the horizon alongside it so the reader has to look at both. A field called back with a value of 0 and no horizon next to it is a fact that has been stripped of the only context that makes it readable, and that stripping is where the draft went wrong rather than anywhere in the arithmetic.

console// the same information, shaped so the missing case cannot be read as a result function recovery(r, horizon) { let t = r.rows[0]; for (const row of r.rows) if (row.cash < t.cash) t = row; for (const row of r.rows) { if (row.month > t.month && row.cash >= 0) return { month: row.month, seen: true }; } return { month: null, seen: false, searchedTo: horizon }; }
Self-studyWhy months 1 to 24 are identical in both runs3 min▶

The engine steps forward month by month from a solved opening sheet, and no month reads a later one. So months 1 to 24 of the 48 month run are the same values as months 1 to 24 of the 24 month run, and the trough at month 19 is the same trough in both. The horizon is a pure reporting choice: it changes what you can see and nothing about what happened.

That is worth confirming once by hand rather than believing, because it is the property that makes extending a window a cheap check. If extending the horizon changed earlier months, comparing two windows would not be a test of the reading, it would be a test of the model, and a much more expensive conversation.

Part 2 · covers testing your own claim

Kill it yourself, before somebody else does it for free

16 min live

Four tests, one line each, run before the sentence leaves your machine. The draft that died would have failed the first two and had never been given the last two.

Four tests, run before anybody else reads the sentence. Test The draft How it is run Extend the window by half again Failed Rerun at 36 and 48 months and compare the conclusion, not the figures Look for extremes on the edge Failed A trough in the final month of a window is an artefact until a longer run agrees Write the opposite claim first Not done Draft the sentence you would publish if this were wrong, then look for its run Sweep the ladder, not one setting Not done Five growth rates, two horizons, and read all ten rather than the one you began in All four are one line each, and all four are cheaper than a retraction. None of them is a check the tie can perform. The sheet ties in both runs and both runs are arithmetically correct. Report the horizon next to the answer, in the same sentence, every time.
🔍 Click to zoom - the two "failed" rows are the two that were available at the time and were not run.
LiveTest one: extend by half again and compare the conclusion4 min▶

Run it, run it again at one and a half times the length, and compare the sentence rather than the figures. The figures are supposed to change when the window gets longer, because there is more of them. The conclusion is not.

This is the cheapest test available and it is the one that would have caught the draft on its own. It also has a useful failure mode: if the conclusion is stable from 24 to 36 to 48 months you have learned something worth putting in the write-up, which is that the claim does not depend on where the window ends.

console[24, 36, 48].map(verdict); // [ {horizon:24, out:10, at:19, back:0}, // {horizon:36, out:10, at:19, back:28}, // {horizon:48, out:10, at:19, back:28} ] // // the conclusion changes between the first row and the second. that is the finding.
LiveTest two: an extreme on the edge is an artefact until proved otherwise4 min▶

If the deepest month in a run is the last month of the run, the model has reported where you stopped looking. Marchmont's trough at 4% growth sits at month 19 inside a 24 month window, which is why the depth in the dead draft was right. At 8% growth the trough is -£7,885,358 in month 48 on a 48 month run, which is the same shape of unanswered question one order of magnitude further along.

The check is a comparison, not a judgement, so it can be an assertion in the bench rather than something a reader has to remember. Any run whose extreme month equals its horizon gets flagged and does not get quoted until a longer run agrees with it.

consolefunction edgeFlag(over, months) { const r = MARCHMONT.run(over, months); if (!MARCHMONT.ties(r).ok) throw new Error("does not tie"); let t = r.rows[0]; for (const row of r.rows) if (row.cash < t.cash) t = row; return t.month === months; // true means: do not quote this trough yet } edgeFlag({}, 12); // true edgeFlag({}, 24); // false edgeFlag({ growth: 0.08 }, 48);// true
LiveTest three: write the opposite claim before you look for it4 min▶

Draft the sentence you would publish if your claim were false, in full, before running anything else. For the dead headline the opposite is "a profitable company with a working capital gap comes back on its own if you wait long enough". Having written that down, the next question asks itself: what run would produce it, and has anybody done that run.

The value of writing it out is that a vague opposite is easy to dismiss and a specific one is not. "It might recover" invites a shrug. "It recovers in month 28 after eighteen months under" is a claim with a shape, and a shape is a thing you can go and test in one call.

Self-studyTest four: the sweep, and what it catches that a longer window does not4 min▶

Extending the window fixes a claim about time. It does nothing for a claim that was read off a single setting, and the canon set exists for that reason: five growth rates crossed with five collection settings, each run to both 24 and 48 months, fifty runs with the worst tie error at 2.24e-8.

consolefor (const g of [0, 0.02, 0.04, 0.06, 0.08]) { for (const h of [24, 48]) { const r = MARCHMONT.run({ growth: g }, h); if (!MARCHMONT.ties(r).ok) throw new Error("does not tie: " + g + " " + h); console.log(g, h, MARCHMONT.cashOutMonth(r)); } } // ten rows. the gap opens in three of the five growth settings // and closes inside 48 months in exactly one of them.

Reading all ten rows is what stops the correction becoming the next error. The 48 month run at 4% says Marchmont recovers. It does not say that companies like Marchmont recover, and the rows underneath it say plainly that most of them do not.

Part 3 · covers the correction

What to keep when the headline dies

14 min live

A dead headline usually takes less with it than it feels like at the time. The robust part of the Marchmont work survived the correction untouched, and the two errors available here point in opposite directions and come from the same habit.

Two opposite errors, one habit. Error one · read one window Run: base case, 24 months Reading: the line never comes back Claim: a profitable company dies Killed by: the same run at 48 months Error two · read one setting Run: base case at 4%, 48 months Reading: it recovers in month 28 Claim: a growing company always recovers Killed by: 6%, -£897,962, no recovery by 48 Both errors are produced the same way: one run, generalised. The fix is not a longer window. It is more than one run, with the conditions reported beside the answer. What survived the correction is the part that held in every run. Profit and cash diverge hard and for a long time, and the gap is real and has to be funded. True at 24 months and at 48.
🔍 Click to zoom - the right hand box is the error nobody made here, and it was one careless sentence away at every point after the correction.
LiveSeparating the robust part from the dead part5 min▶

Go through the draft claim by claim and mark each one with the run that supports it. The Marchmont draft had four claims in it, and two of them were never in danger.

Profit and cash diverge, hard, for a long time. Twelve month net income of £445,631 against free cash flow of -£297,261, a gap of £742,892, of which £664,607 is the working capital build. Unchanged by the horizon, because it is a twelve month total inside both windows.

The gap is real and has to be funded. The balance goes under in month 10 and stays under for eighteen months. Recovering in month 28 does not help anybody in month 19, so the funding requirement survives the correction completely.

The company does not recover. Dead. The correction run produced month 28.

Therefore borrowing is only a deferral. Dead, and it was the most damaging of the four, because it is the one that would have led somewhere. It was a conclusion built on the claim above it rather than on any run of its own, which is the pattern worth looking for: a claim with no run attached, sitting downstream of one that has.

consoleconst long = MARCHMONT.run({}, 48); MARCHMONT.profitableThrough(long, 48); // true Math.round(MARCHMONT.cumulative(long, "netIncome", 12)); // 445631 Math.round(MARCHMONT.cumulative(long, "freeCF", 12)); // -297261 // the surviving half of the claim, checked against the run that killed the other half
LiveWhere practitioners genuinely disagree about publishing the correction5 min▶

Everyone agrees the corrected number goes out. What to do with the dead one is an argument, and it is not a soft argument about tone.

One position: publish the trail, including the claim that died and the run that killed it. A correction is the strongest available evidence that the work is being checked, and the near miss teaches more than the result does, because the result looks obvious once you have it and the miss shows how it nearly did not happen. Suppressing it means the next person runs 24 months and reaches the same place, and the audience learns nothing about what a window can do to a conclusion. On this view an analysis with no visible corrections is either lucky or not being checked.

The other position: a document carrying every abandoned claim is unreadable, and the abandoned claim is the part that gets quoted. "A profitable company runs out of cash and dies" is a better sentence than anything that replaced it, and printing it next to its retraction guarantees it travels on its own, stripped of the correction, into somebody else's deck. Readers act on documents, not on their footnotes. On this view the retraction belongs in a change log where the people who need it will look, and the published page carries the number people are meant to use.

Both sides have watched their own failure happen: one has seen the same mistake repeat because nobody recorded it, the other has seen a dead headline outlive its correction by years. There is no run that settles it, and the honest move is to say which risk you are choosing and why, rather than presenting a house style as a principle.

Self-studyWriting the correction so it cannot be half quoted4 min▶

Whichever side of the argument above you land on, a correction has a shape that travels better than prose. Name the claim, name the run that supported it including its horizon, name the run that killed it, and name what is left. Four lines, each of them checkable by somebody who has the engine.

The detail that does most of the work is putting the horizon inside the dead claim rather than in a note about it. "Over a 24 month run, Marchmont never recovers" is a sentence that carries its own limitation wherever it goes, and it is much harder to quote out of context than "Marchmont never recovers, see footnote". The condition is not an apology for the claim, it is part of what the claim says.

Part 4 · build-along

Reproduce the death, then build the check

14 min hands on

Five steps in the console and one on paper. The first step is to get the wrong answer deliberately, because it is much harder to respect a trap you have never fallen into.

Produce the dead claim on purpose

Run the base case to 24 months and read out the four facts the draft was built on: profitable in every month, below zero from month 10, the trough at month 19, and no recovery in view. Write the headline sentence out in full. Every word of it is supported by that run.

Kill it with one character

Change 24 to 48 and rerun the same reader. Confirm that the crossing month and the trough month are identical and that only the recovery field moved. Then check that months 1 to 24 match value for value across the two runs.

Make the missing case impossible to misread

Rewrite the recovery reader so that "not found" cannot be received as a month. Return a null and the horizon searched, or throw. Then rerun the 24 month case and see what your new return value forces the caller to do.

Add the edge flag to the bench

Write the check that compares the trough month against the horizon and returns true when they are equal. Run it over the base case at 12, 24 and 48 months and over 8% growth at 48 months. Decide whether a flagged run should warn or refuse.

Sweep ten rows and look for the opposite error

Five growth rates crossed with 24 and 48 months, tie checked on every run. Find the rows that would support "it always recovers" and the rows that contradict it, then write the one sentence that is true of all ten.

Write the four line correction

The claim, the run that supported it with its horizon, the run that killed it, and what is left standing. No console for this one. Then decide, and say out loud, whether you would publish it next to the number or in a log.

The habit to keep. Before a claim goes out, ask what run would make it false and whether anybody has done that run. If the answer is that no such run exists, the claim is stronger than you thought. If the answer is that nobody has looked, it is not a finding yet, however many times the balance sheet tied on the way to it.

Check yourself

Three questions

The 24 month run and the 48 month run use identical assumptions and the balance sheet ties in both. What was wrong with the draft?

Extending a horizon cannot change a month the engine has already computed, so months 1 to 24 are identical in both. Every figure in the draft was correct. The failure was a reading, and no assertion in the bench would have caught it.

Which part of the original claim survived the correction?

Twelve month net income of £445,631 against free cash flow of -£297,261 is inside both windows and never moved. The balance is under from month 10 for eighteen months, so the funding requirement stands. What died was the ending, and the recommendation that had been built on the ending.

After the correction, somebody writes "a growing company that sells on credit recovers on its own if you wait". What is wrong with that?

One run generalised produced the first error and would produce this one. The ladder is measured across five settings: the gap opens in three of them and closes inside 48 months in exactly one. A longer window fixes a claim about time and does nothing for a claim read off a single setting.

Before session 10

Homework

Thirty minutes, and the second half is not about Marchmont.

Find a second claim that depends on its window

Pick any statement in the measured set that involves a month number and test it at three horizons. Write down which ones are stable and which ones move. A claim that survives all three is worth more than a claim nobody tried to break.

Extend the window on something of your own

Take a conclusion you have drawn from real data over some period and ask what a period half again as long would show. If you cannot get the longer period, write down what the claim would have to say about its own window to be honest, and see whether it still reads as a finding.

Covered today

What this session covers

✓The claim that died and the run that killed it24 months said it never comes back; 48 months returns month 28.
✓The horizon as an input with a valueReported next to the answer, not left in a function signature.
✓Why no check in the engine could catch itThe sheet ties in both runs; worst error 2.24e-8 across the canon set.
✓A missing value shaped so it cannot be read as a resultZero from a recovery reader means not in view, not never.
✓Four tests to run before publishingExtend by half, flag edges, write the opposite, sweep the ladder.
✓Keeping the robust half of a dead claim£445,631 against -£297,261 survived; the death and its recommendation did not.
✓The opposite error, from the same habitAt 6% the trough is -£897,962 and has not closed by month 48.
◐Choosing a horizon on purposeSession 6, which set up the two windows this one takes apart.
◐Turning the corrected figure into a requestSession 8 sizes it; leader session 6 presents it.
○Statistical inference and confidence intervalsThe model is deterministic; there is no sampling here to be uncertain about.
○Whether any of this is a reason to buy or sell anythingNothing here is advice about any investment.
Take this with you

Cheat sheet

The story. 24 months said the company dies. 48 months, nothing else changed, returns month 28.
The rule. A result that flips when you extend the window was never a finding.
The horizon. An input with a value. It goes in the sentence, not in a footnote.
Zero is missing. A recovery reader returning 0 means not in view. Return null and the horizon instead.
Four tests. Extend by half again. Flag extremes on the edge. Write the opposite. Sweep the ladder.
What survived. £445,631 against -£297,261, and eighteen months under water that still need funding.
What died. The ending, and the recommendation that had been built on the ending.
The other error. "It always recovers" is the same mistake reversed: 6% troughs at -£897,962 and does not come back by month 48.