The short version: we set a hard daily loss limit of $50. For at least the 42 trading days we can measure, the number actually running was about $142. Nothing crashed. No alarm fired. The logs were clean. We found it by hand, weeks later, by comparing what we had written down with what the machine had actually done.
"Daily loss limit" in plain words: a ceiling on how much the account may lose in one day. When the ceiling is hit, trading stops. It is the one rule that is not supposed to be negotiable. That was the entire idea. It did not work.
Step one: the number we set
Our bot keeps its risk rules in a small file it reads at startup. One entry said, in effect, stop trading for the day once the loss reaches $50. We wrote that line ourselves. We believed it was running. Everyone who has ever configured a safety limit believes it is running — that is exactly why this kind of bug survives so long.
Step two: what the code actually did
Every tick, the bot computes a day cap from the account balance: 1.5% of the balance, which on a roughly $9,500 account is about $142. Then it assigned that value to the variable that holds the cap.
Here is the part that matters. That assignment was unconditional. It did not compare the two numbers. It did not ask which one was stricter. It just wrote over whatever was there — including the $50 that our own rule file had just supplied.
So the rule file was read. The rule file was then discarded, silently, before anything could act on it. We had two caps and the code kept the looser one.
Step three: why nothing complained
Think about what a working limit looks like from the inside. When the loss approaches the cap, the bot stops trading and says so. When the cap is never reached, the bot also does nothing and says nothing.
Those two states — "nothing to do yet" and "the guard is disabled" — produce the same output. A silence that means fine and a silence that means broken are indistinguishable if the only thing you watch is whether the bot complained.
That is the whole trap. The guard was healthy in the only sense the logs could express.
And here is the number that finishes the story: our worst single day over that period was −$100.26. That is a big loss, and it is still less than $142. The cap was never within reach, so it never fired once.
Step four: how we actually found it
Not from the logs. From a mismatch.
We sat down and asked a boring question: what did we configure, and what actually happened? We took the limit we believed in — $50 — and asked the fill history how many days had closed worse than that. The answer was 5 days out of 42.
Five days below a limit that was supposedly live is not a subtle discrepancy. It is a contradiction, and it had been sitting in our own data the whole time. We just had never put the config and the fills side by side.
The dates: 08-27 (−$81.45), 09-02 (−$62.92), 09-10 (−$70.25), 09-22 (−$100.26), 10-08 (−$77.68). Day boundaries are the bot's local day (UTC+8), which is worth stating because a different day boundary moves the numbers around.
We do not have a clean start date for this bug. That is not a detail we are brushing past — it is a second symptom. We could not date the failure because nothing recorded when the configured value stopped being used.
Step five: the fix
The change is two lines long and the principle is one sentence: when a rule and a default disagree, take the stricter one.
- Before:
cap = balance * 1.5%(overwrite, unconditionally) - After:
cap = min(balance * 1.5%, rule_value)(the tightest one wins)
The rule can now only tighten the automatic cap, never loosen it. If we set $50 and the balance formula says $142, the cap is $50.
Step six: what we changed so this cannot hide again
A fixed line is not a fixed system. Three things had to change beyond the line itself:
- Compare config against outcomes, on a schedule. Not "read the logs" — logs showed nothing. The test is: does the behaviour match the stated setting? Five violations of a stated limit is a finding, not noise.
- Count how often each rule fires. Every risk rule now carries a hit counter. A rule that has fired zero times across dozens of trading days is either unnecessary or broken, and both of those deserve a look. Ours read zero, and that was the clue we had been ignoring.
- When a safety value is discarded, log it. Silent discards are how a protection becomes decoration.
The honest part
For completeness: our own replay estimated that enforcing the $50 limit would have refused 12 trades across 3 days, and would have moved the total by roughly +$11. We are reporting that because we report things — not because it is a reason to buy anything. A loss limit is not a profit feature. Its job is to bound the damage on the day it is needed, and on that job it was simply absent.
We should also be clear about the account this came from, because "we caught a risk bug" is easy to read as a good-news story. This is a $10,000 deposit that is down to $9,493.35 — a −5.07% loss overall, measured from MT5 server records on 2026-10-08. Finding this bug did not make the account profitable, and we are not claiming it did. The account was losing money while the guard was off, which is precisely why the guard being off mattered.
What to take away if you run automation
- A limit you set is a hypothesis until you have seen it fire. Both a limit that is too loose and a limit that is broken produce the same calm.
- Config and code will disagree eventually. Decide in advance which one wins. "The stricter one" is usually the right answer for anything that exists to stop you.
- Zero is a suspicious number. Zero triggers, zero rejections, zero halts — those are readings, not reassurance.
- Test by outcome, not by log level. The question is not "did anything complain", it is "does the behaviour match what we said we wanted".
VigilDesk is a safety layer for MT5 accounts: a kill switch, hard risk caps and a restart-proof audit log, running locally. We do not sell signals, we do not give advice, we do not manage accounts, and we make no profit claims — any tool that promised you that would be lying. Algorithmic trading carries real risk and no tool removes the possibility of loss. The numbers above are from our own account and our own records; they are a report of a failure, not a track record.
Written by the person who configured the $50 and then spent weeks trusting it.
(We build VigilDesk - a local safety layer for MT5. Risk control only.)


Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.