The one rule
Language models are pattern machines, not calculators. Ask one for a return or a moving average and it will happily emit something that looks like an answer — sometimes right, sometimes off by a decimal, always delivered with the same even tone. In finance that tone is the danger.
Code computes every number deterministically. The model is only ever allowed to reason on top of numbers it was handed — it never gets to make one up.
Everything about the system is arranged to enforce that one sentence. Reasoning is welcome; invention is designed out.
The pipeline
The numbers are produced and locked before the model sees anything. Only the last step is an LLM, and it receives a finished scorecard, not raw data to "interpret".
collector → quant → state.diff → analyze → notify
(prices) (metrics, (what moved (claude -p (Telegram)
all in since last reasons on
code) run) the numbers)
The quant step owns every figure — returns, ratios, volatility, the lot — in plain, tested Python. state.diff decides what actually changed since the previous run, so the commentary is about movement, not noise. Only then does claude -p get invited, and its job is narrow: explain the numbers in front of it, in words, without ever restating a figure it wasn't given.
Tests are the safety rail
Because the quant layer is the source of truth, it's the part that's guarded hardest: a suite of tests is the gate every change has to pass before it can ship or self-correct. If a metric's math drifts, a test goes red — long before a wrong number reaches a human reading it over morning coffee.
Why this generalizes
This isn't really a stocks trick. It's the same line I keep drawing across every project in this log: let deterministic code own the facts, and let the model do the one thing it's genuinely good at — turning facts into language. The moment you let it own the facts too, you've built something that lies fluently. Keep the two jobs apart and you get a system you can actually trust in production.