Skip to main content

LLM ignoring its prompt? The prompt fix bought 10 points, the model swap bought the rest

Published: August 28, 20267 min read
#llms#algo-trading#prompt-engineering#testing#build-in-public
Dashboard card on deep navy. Kicker: TRADER-7 · SIGNAL MODEL AUDIT · 26 AUGUST 2026. Label RULE COMPLIANCE above a large gold figure: 37%. Below: a reward-to-risk floor stated four separate times in the prompt, met on 37 of every 100 proposals; 17 came back at exactly one to one. A row of proposal ticks, most slate and hollow, a minority green. Title: LLM ignoring its prompt? The prompt fix bought 10 points, the model swap bought the rest.

Eight of 23 trade signals came back with the stop and the target set to exactly the same distance.

Those signals come out of a language model. It's the part of a crypto trading system I've been building that decides what to trade, where to get out if the trade goes against me, and where to take the profit.

Stop and target the same distance apart means risking a pound to make a pound. The rule says the reward has to be at least twice the risk before a trade is allowed. Not a guideline. A hard floor, and one the model is told about four separate times in the prompt it receives.

It was doing it on roughly two-thirds of everything it produced. Across 107 logged proposals the average was 1.7 against a 2.0 minimum, and 17 sat at exactly 1.0.

None of them ever became trades. That is the part that took me weeks to see.

The gate was working perfectly

There's a check that rejects any proposal below the floor. It caught all of them. Every single one. From the outside the system looked appropriately fussy: lots of analysis, few trades, quiet weeks.

I had been explaining those quiet weeks with market conditions. Sideways price action, low volatility, nothing worth taking. Reasonable explanations, and I had one ready for every stretch.

The gate was the whole story. 342 of 523 signals over the period could never have opened a position, no matter what the market did. In range-bound conditions it was 84%. The system wasn't being selective. It was being fed rubbish and filtering it, and the filtering looked like judgement.

So I fixed the prompt

I went and read the thing properly, and there was a genuine contradiction in it. One section gave a table of minimum ratios by market type. Another section, in prose, told the model to reject anything below 1.5. Two different numbers, both stated as the rule.

There was a second one I nearly missed, in a different part of the prompt, suggesting a target multiple that didn't match the table either.

Found both, fixed both, made the table the only authority. Satisfying afternoon.

It barely moved the needle.

The bit I'd have got wrong

I could have shipped that fix, watched the numbers improve slightly, and told myself the job was done. What stopped me was that I'd built something two weeks earlier for a different reason: a bit of tooling that saves the exact prompt sent to the model on every live call, so it can be replayed later against other models offline.

I had 288 real production prompts sitting on disk. So instead of guessing, I ran all of them through five models, twice each — once with the old prompt, once with the fixed one. Eight arms. Total cost $15.77.

The old model, on the old prompt, broke the floor 76% of the time. On the fixed prompt, 66%.

Ten points. That's what fixing a real, genuine, definitely-a-bug contradiction bought.

Meanwhile Gemini 3.7 Flash scored zero percent below the floor. Not on the fixed prompt. On both of them. It was immune to the contradiction, because it was actually doing the arithmetic rather than pattern-matching something that looked like an answer.

The prompt was never the problem. The model couldn't multiply.

Two models replayed against the same 288 captured production prompts, measured on how often signals fell below the reward-to-risk floor. DeepSeek v3.2 broke the floor 76.3% of the time on the original prompt and 66.2% on the fixed one. Gemini 3.7 Flash scored 0.0% on both. The whole comparison cost $15.77

What happened next

I swapped the model. Production, in the 30 hours after: 76 signals, every one above the floor, none at 1:1, ratios spread between 2.00 and 2.25. The gate that used to kill everything now rejects nothing, because there's nothing to reject.

Trades started happening again.

There's a caveat I missed the first time round, and it's the more interesting half.

Compliance isn't the same as thinking. The model sets its target at exactly twice the stop distance, every time. Stop 0.90%, target 1.80%. Stop 1.40%, target 2.80%. The stop moves with volatility and the target is arithmetic on it, so the ratio clears the check because it was built to clear the check. A couple of days later I counted again: 45 of 60 proposals came back at precisely 2.00.

Then I found that the system replaces the model's target with its own before the order ever reaches the exchange. So the number the gate checks describes a trade that never gets placed.

The one that would have been worse

While that was running I went looking at a different control, and found the mirror image of it.

There's a check that halts trading if market data goes stale. In testing mode the tolerance is 25 minutes. In live mode, which switches on the day real money goes in, it's 15 seconds.

The system fetches fresh prices every 15 minutes.

So on the first day of real capital, the data would have been stale by that definition within 15 seconds of every refresh, forever. The halt would have fired constantly and correctly and blocked everything. And the log would have said "stale data", which reads like a feed problem rather than a number nobody had ever reconciled with the architecture.

Nothing was broken. Two files, written months apart by me, each internally sensible, describing systems that don't exist together.

I found that five weeks before the money goes in rather than on the morning it does. That's luck with a bit of process attached to it, and I'll take it.

The law I keep rediscovering

Last month I audited 26 risk controls in this thing and found seven that could never fire. I wrote then that a control you cannot observe firing is not a control, it is a belief.

This month is the sequel, and it's less comfortable. Every control in this story worked. The ratio gate fired correctly on every bad proposal. The staleness halt would have fired correctly on every stale reading. Both were doing precisely what they were built to do, and neither was protecting me from anything.

A control being correct tells you nothing about whether the system is healthy. It only tells you about the control. When the ratio gate rejected 65% of proposals, the honest reading was "something upstream is producing garbage", and the reading I actually took for weeks was "the market is quiet". Same data. The gate had no opinion about which.

The thing I'd been missing is that a filter's rejection rate is a measurement of its input, and I was treating it as a measurement of the market.

What I'd do with this

If you've got an LLM in production with rules in its prompt, go and measure how often it follows them. Not whether the prompt is clear. Whether the output complies. Those are different questions and only one of them has an answer you can count.

Mine was at 37% on a rule stated four times, and I found out by accident.

Then check what your gates reject and how often. If one of them is killing most of what reaches it, the problem is very unlikely to be at the gate.

28 issues closed since the last post. 31 still open, none of them high severity, which is the first time that's been true. Real money still goes in on 1 October.

No paywall, no sponsors. If this saved you some time, you can buy me a coffee.

☕ Buy me a coffee

Get the next one in your inbox

I build with AI in the open and write up what held and what didn't. Real numbers, the failures before the wins.

Share this post