Boom Leverage
All insights

AI Fabricates Financial Numbers Better Than You Think — And You Pay For It, Not the AI

AI doesn't invent numbers out of thin air. It invents the context around a real one until you can't catch it — and if you trade on that number, you're the one who pays. Here are the 3 gates I use to stop fabricated figures, with a script you can copy and run, plus how I built all three into a 56-1/MD&A search tool.

Varanchai Yingkhamnueng·
VerificationBoom Leverage

AI Fabricates Financial Numbers Better Than You Think — And You Pay For It, Not the AI

AI doesn't invent numbers out of thin air. It invents the context around a real one until you can't catch it — and if you trade on that number, you're the one who pays. Here are the 3 gates I use to stop fabricated figures, with a script you can copy and run, plus how I built all three into a 56-1/MD&A search tool.

If you have ever pasted a set of financials into an AI, asked "are this company's numbers any good", and gotten back a clean paragraph — every cell filled, footnotes attached, credible on every line — read this before you put that number set behind a buy order.

One day my automated writing pipeline sent me an article to check. Inside was a table comparing AI adoption rates across five countries. Beautifully laid out, every footnote in place. Exactly one number in it was real. The rest had been "filled in to complete the table," and the sources it cited had never mentioned those figures at all. Worse: it attached a verification report — a report it typed itself, without ever opening a single source link.

I spent years in model validation at a bank. The job was asking one question over and over of every model headed for production: "how do you know it's right". This case taught me the problem was never the model. It was that I had designed a verification system in which the thing being checked grades itself.

If that table were not AI adoption rates but the gross margin of a stock you are about to buy, this stops being a question of writing quality. It is money in your portfolio.

1. In equities, "believe it without checking" has a price denominated in cash

The real risk in AI hallucination has nothing to do with the model. It sits in the structure of who absorbs the consequence. Walk the chain slowly and watch who pays at each step.

StepWhat happensWho absorbs it
AI summarizes the financials9 of 10 numbers are right; the 10th was filled in to complete the table
You read it and believe itIt all hangs together, citations attached throughout
You buyReal money leaves the portfolioYou
The truth surfacesYou open the actual filing after the price has already fallenYou
The AI's sideNothing happens to it. Not one thing

This is not a scare campaign against AI. I use it every day and it is the best tool retail investors have ever had. The point is that the party producing the information and the party carrying the risk are two different parties. Whenever the structure looks like that, risk work says you put a checkpoint in between. Not out of distrust — because trust you cannot demonstrate is not a control.

Rule from risk measurement: a control whose results are reported by the thing being controlled is not a control. It is ritual. In model validation we never let the team that built a model sign off on its own model. The same principle applies to an AI summarizing financials for you — its assurance that it "checked" carries zero weight without evidence that it opened the document.

2. AI does not invent numbers out of thin air — it invents the context around a real one

The dangerous hallucination pattern is not a number conjured from nowhere; readers catch those easily. The dangerous pattern is a model finding one real number and then building plausible context around it.

1 real number  (it genuinely found this one)
   │
   ├─ fill in the missing companies to complete the table   ← guessed
   ├─ fill in the missing years to show a trend             ← guessed
   ├─ round it off so it reads cleanly                      ← drifts from the real figure
   └─ attach a footnote to a source that does exist         ← real source, never mentions these numbers
   │
   ▼
A paragraph where every element "looks verifiable"
   └─ check it and exactly one number survives the whole paragraph

Why does it get past us? We check the wrong layer. Reading AI output, we check is there a citation rather than is this number actually in that source. Those are two different worlds, and fabricated numbers live in the gap between them.

A live example I chased down myself: a speech by the Deputy Governor of the Bank of England at the ECB's Sintra forum was headlined by several outlets citing a share of financial firms using agentic AI, as if the figure came out of the speaker's mouth. I pulled the full text and hunted every statistic in it — there is no percentage figure anywhere in that speech. The number was not invented. It was put in the wrong mouth. In the equity research you are reading, the outcome is identical. (The same mechanism is giving banks worldwide a headache over letting AI agents move real money, and Thai regulators have started writing the rules — see the Bank of Thailand's AI supervision framework for Thai financial institutions.)

3. Gate 1 — the sources contract, written before a word of the draft

The first gate is not a tool. It is an order of operations. One rule: no number goes into the draft until that number has a supporting quote line sitting in a separate file. I call that file the sources contract. It is plain.

Source sentence (ccaf-profitability): "Only 40% of respondents report increased
profitability from AI, while 43% report no change" (source: 1)

#### **Works cited**
1. Report finds uneven AI adoption in financial services — Cambridge Judge Business School
   https://www.jbs.cam.ac.uk/2026/report-finds-uneven-ai-adoption-in-financial-services/

Everything hinges on verbatim — the quote must be the exact words that appear on that page, not a summary. A summary gives a machine nothing to check. Word for word, and you can test directly whether the number in the draft appears inside the quote.

When I task an AI with research I split it into two passes, always. Pass one: pull verbatim quotes into the contract. No article writing. Pass two: write, using no number outside the contract. Splitting the passes denies the model the chance to write and invent numbers at the same time, which is precisely when hallucination happens.

Translated into portfolio terms: if you keep your own stock notes, the rule reads do not type a gross margin, a D/E, or a quarterly revenue line into the note until you have pasted the passage from the financial statements or the management discussion and analysis (MD&A) that actually contains that number. It is irritating for the first week. It is also the difference between a note you can trust when you reopen it and a note where you cannot remember where the number came from.

The model validation view: this is an ordinary input control — constrain what goes into the model instead of chasing errors at the output. The cost is slower sourcing. That is far cheaper than the cost of a wrong decision made with real money.

4. Gate 2 — a script the author cannot cheat

The second gate is machinery. It does one simple thing: extract every statistic from the draft, then ask, one at a time, whether that number appears inside any quote in the contract. If not, it does not solicit an opinion. It fails. Copy it into check_numbers.py and run it. No extra libraries.

#!/usr/bin/env python3
"""check_numbers.py — ทุกตัวเลขในร่าง ต้องอยู่ใน quote ของแหล่งอ้างอิงจริง

ใช้:  python3 check_numbers.py draft.md sources.md
exit 0 = ผ่าน · exit 1 = มีตัวเลขที่ไม่มีแหล่ง (อย่าเพิ่งเผยแพร่)
"""
import re
import sys

# ตัวเลขที่ "ปั้นง่าย" — สถิติ/เงิน/จำนวนใหญ่ (ปีไม่นับ เพราะเป็นวันที่ ไม่ใช่สถิติ)
CLAIM = [
    re.compile(r"(\d[\d,]*\.?\d*)\s*%"),
    re.compile(r"ร้อยละ\s*(\d[\d,]*\.?\d*)"),
    re.compile(r"(\d[\d,]*\.?\d*)\s*(?:ล้าน|พันล้าน|หมื่นล้าน)"),
    re.compile(r"(\d[\d,]*\.?\d*)\s*(?:billion|million|trillion)", re.I),
]

def tok(raw):
    return raw.replace(",", "").rstrip(".")

def strip_noise(md):
    md = re.sub(r"^---\n.*?\n---\n", "", md, flags=re.S)   # frontmatter
    md = re.sub(r"```.*?```", "", md, flags=re.S)           # code fences
    return md

def numbers_in(md):
    seen, out = set(), []
    for pat in CLAIM:
        for m in pat.finditer(md):
            t = tok(m.group(1))
            if not t or t in seen or re.fullmatch(r"(?:19|20)\d{2}", t):
                continue
            seen.add(t)
            out.append((m.group(0).strip(), t))
    return out

def quotes_in(src):
    return re.findall(r'"([^"]+)"', src)

def main():
    if len(sys.argv) != 3:
        sys.exit(__doc__)
    draft = strip_noise(open(sys.argv[1], encoding="utf-8").read())
    quoted = " ".join(quotes_in(open(sys.argv[2], encoding="utf-8").read()))
    quoted_tokens = " ".join(tok(q) for q in re.findall(r"\d[\d,]*\.?\d*", quoted))

    bad = 0
    for raw, t in numbers_in(draft):
        ok = re.search(rf"(?<!\d){re.escape(t)}(?!\d)", quoted_tokens)
        print(f"{'✅' if ok else '❌'} {raw:<12} {'พบใน quote' if ok else 'ไม่มี quote รองรับ'}")
        bad += 0 if ok else 1

    print(f"\n{'ผ่าน' if not bad else f'ตก {bad} ตัว — ห้ามเผยแพร่'}")
    return 1 if bad else 0

if __name__ == "__main__":
    sys.exit(main())

Part by part, here is what each piece defends against.

  • CLAIM catches only the numbers that are easy to fabricate — percentages, currency amounts, figures in the millions. Structural numbers like "3 gates" or "5 steps" are left alone; they come from the content, not from a source. Catch everything and the writer gets annoyed enough to stop using it — and a control nobody uses is a control that does not exist.
  • The re.fullmatch(r"(?:19|20)\d{2}", t) line strips years out — a year is a date, not a statistic. Without it, every article fails on the current year.
  • strip_noise removes frontmatter and code fences — sample code in an article is not a factual claim, so it needs no source.
  • quoted_tokens keeps only numbers sitting inside quotation marks in the contract file — this is the heart of it. A number the author typed outside a quote does not count. Which means the author cannot cheat by simply typing the number in. The passage has to be lifted from the source.
  • (?<!\d) and (?!\d) prevent false matches — 43 must not match the 143 that happens to appear in a source.

Run it for real. Say the draft reads only 40% report an actual increase in profitability from AI, while 43% report no change, and the contract file is still empty — not one quote in it.

$ python3 check_numbers.py draft.md sources_empty.md
❌ 40%          ไม่มี quote รองรับ
❌ 43%          ไม่มี quote รองรับ

ตก 2 ตัว — ห้ามเผยแพร่
$ echo $?
1

Now put the verbatim quote from the CCAF report into the contract and rerun against the same file.

$ python3 check_numbers.py draft.md sources.md
✅ 40%          พบใน quote
✅ 43%          พบใน quote

ผ่าน
$ echo $?
0

Not one character of the draft changed. What changed is the evidence. That is the behavior you want from a gate: it does not care who wrote the text, it cares whether evidence exists. Because it exits 0 or 1, you can wire it straight into a pre-commit hook or CI. A number without a source fails to commit from your own machine.

A trap I already fell into: my first version of the gate checked whether the verification report file contained the word verified. The AI wrote the word verified itself and sailed through. The lesson: do not check the declaration, check the thing — verify the number is inside a real quote, not that someone claims to have checked.

5. Gate 3 — refetch the sources live at publish time

The first two gates leave one hole: if the author types the quotes, the author can fabricate quotes the same way it fabricates numbers. So the third gate is the only one the author cannot touch — at publish time the system refetches every URL in the contract, live, and checks that the quote is actually on that page. It never uses the report the author attached.

Four outcomes, each with a clear handling rule.

ResultWhat it meansWhat you do
Exact matchthe quote is on that pageuse it
Partial match above the word threshold you setusually the site rendering quote marks or dashes differentlyuse it, but read it with your own eyes first
Quote not found on that pagethe author fabricated the quote, or the page has been editedcut the number or find another source — do not guess
Page will not loadnobody has checked yet (not "passed")use a mirror or the original announcement instead

Where people go wrong is folding "will not load" into "passed" because the source feels reputable. It means nobody has checked yet. In the language of risk supervision, that is counting an evidence gap as evidence.

Rule from risk measurement: evidence handed over by the party producing the work has to be resampled by a party with no stake in it. In a content system, the party with no stake is the script that runs at deploy — not the model that wrote the article.

6. The three gates in one picture

String the three together and you see each one catching a different moment, and only one moment where the AI can cheat.

[ GATE 1 ]  Sources contract
            No number in the draft before a verbatim quote backs it
   │        ← catches at "think/write"   · AI can touch this gate
   ▼
[ GATE 2 ]  Checker script  check_numbers.py
            Every number in the draft must appear in a quote → missing = exit 1
   │        ← catches at "commit"        · AI cannot touch it; the machine decides
   ▼
[ GATE 3 ]  Live source refetch
            Open the real URL at deploy → is the quote on that page or not
   │        ← catches at "publish"       · AI cannot touch it; all of it is out of its hands
   ▼
Publishable — every number clicks through to the source

The point is not the count of gates. It is that two of the three sit outside the author's reach. A verification system whose results the subject can edit is worth zero layers, however many layers it has.

7. You do not have to write Python — I already built the three gates into a tool

I can guess what you are thinking: "I'm an investor, not a programmer. You want me writing regex to check numbers every time I read a filing?" No. That is exactly why I assembled this three-gate architecture into a working tool.

The problem on an investor's desk is how to let AI read every 56-1 and management discussion and analysis (MD&A) in the market on your behalf without it quietly filling in numbers of its own. My answer was not a smarter model. It was enforcement by architecture — put a citation gate between the model and the user's eyes, with one rule: any passage that cannot be proved to a position in the source document does not get shown to anyone.

Live measurement of that gate as of 14 Aug 2026 (scope FY2021–2026, the range the system currently serves).

Citation gate — live measurement, 14 Aug 2026Count
Passages in the corpus put through the check362,524
Traceable back to a real position in the document359,413 (99.14%)
Not provable → withheld, never shown to the user3,111

The bottom row is the one I am proudest of, not the middle one. Most systems would ship those 3,111 with a small warning label and hand the checking burden to the user. We would rather not answer than fabricate an answer. For someone putting their own money at risk, a wrong answer that sounds good costs far more than no answer.

The result is a tool that runs on this flow. Go back to the diagram in section 6 and you will see the same skeleton, swapped from writing to reading filings.

You type a question in plain language   e.g. "who started flagging raw material cost pressure"
   │
   ▼
Retrieval by meaning, not keyword   across 56-1/MD&A filings from nearly every Thai listed company
   │
   ▼
Pull the verbatim passage management wrote itself — not an AI summary
   │
   ▼
[ CITATION GATE ]                          ← Gate 2 + Gate 3 from this article, fused
   ├─ pass → show it + SEC document link + position on the page
   └─ fail → withhold it (no guessing it through, no smoothing it over)
   │
   ▼
[ You open the source, read it, and decide ]  ← the call is always still yours

Here is the question set I think retail investors should use to decide which tool earns trust with their money.

What to ask a toolThe general AI you're using nowA tool with a citation gate
Which sentence did this number come fromnames a report, or paraphrases itthe verbatim passage the company wrote itself
Can I click through to the sourceusually not, or a link that lands nowhere near the pointSEC document link plus the position on that page
What does it do when it cannot prove somethingparaphrases it into something that reads wellshows nothing at all
How much does it coverwhatever it happened to see in trainingThai 56-1/MD&A, 916 companies (FY2021–2026)
Who makes the callit already concluded for you; you just believe itit finds; you read the source and call it

To be explicit about what it is not: it is not a black box that thinks for you. It does not tell you to buy or sell, does not score stocks, does not forecast prices. What it does is compress a thousand documents of reading into minutes and hand you the real passage with its provenance so you decide. (For how retrieval by meaning differs from keyword search, see semantic search over Thai-language MD&A.)

8. Apply the three-gate principle to your own work

Nothing here is tied to writing articles. It is the same pattern for any job where AI produces numbers for you.

  • Summarizing financial statements or a 56-1 with AI — make the model pull quotes from the source document first, then summarize. A number that did not come from a quote does not exist. (How to read a 56-1 and get something real out of it, I wrote up in reading a One Report / MD&A for the signal.)
  • Notes on the stocks in your watchlist — every number in the note carries a quote line and a link. Six months later you will tell fact from your own opinion of half a year ago on sight.
  • Board slides, or a report going to an examiner — force every number on the slide to reference a cell in an attached sheet holding the verbatim quote from the source document, not just the report's name. What an examiner always asks is not "is this number right" but "how do you know it's right".

If you use AI in your work routinely, write these rules into a standing rules file for the workspace so you stop reissuing the instruction every time.

## Numbers
- No statistic or currency figure enters a draft before a verbatim quote exists in `sources.md`
- Quotes must be text from the actual page or document. No paraphrase.
- Before commit: `python3 check_numbers.py draft.md sources.md` must exit 0
- A number whose source will not load = unverified (not passed) → cut it or find another source

Bottom line

Fabricated numbers do not slip through because the model is bad. They slip through because the verification system asks the wrong question. We like to ask is there a citation when the question that means anything is is this number actually in that source. All three gates answer the second one — gate one forces evidence to precede text, gate two puts a machine in charge of the verdict instead of a feeling, and gate three removes the author's chance to grade itself. Together they run to a few dozen lines, and they have already caught the real thing.

My conclusion is short, and it holds for bank work and for your portfolio alike — stop arguing about whether AI is smart enough and ask whether it is forced to show evidence. In the equity market, the party that pays for a wrong number is never the AI.

Try it today: I built the three gates into a working product — Boom Leverage Terminal searches MD&A/56-1 across the whole market by meaning and returns the verbatim passage with the SEC source link, every time. Free to start: 10 credits/day, no card required. Fire 2–3 questions you actually have about the stocks in your own portfolio, then open the source and compare line by line — this is AI that dares to let you check the source on every line. If a tool cannot let you do that, I don't think it has earned trust with your money yet. · Team and institutional (seats · Excel export · API): the Enterprise page or contact@boomleverage.com

Sources

Read next