Beyond the Backtest: I Want to Break My EA Before the Market Does
Beyond the Backtest: I Want to Break My EA Before the Market Does
There is a thought that has been bothering me for quite a while.
I can test an Expert Advisor on ten years of ticks. I can optimize it, run forward tests, use different symbols and execution delays, and compare hundreds of parameter sets.
But what happens if the Internet connection disappears exactly after the trade request has been sent?
What happens if Windows freezes the process for half a second?
What if 100 strategies wake up at almost the same moment?
What if the terminal restarts with several open positions?
And my favorite question: what if everything goes wrong at once?
This is where a normal backtest stops being enough.
Backtesting and software testing are not the same thing
As MQL developers we are lucky to have the MetaTrader 5 Strategy Tester. It is an exceptionally powerful tool. We have real ticks, multi-currency testing, optimization agents, remote agents, MQL5 Cloud Network, forward testing and execution delay simulation.
I use these tools and value them.
But they mostly answer questions about a trading strategy.
Would the strategy have traded correctly on this history? How sensitive is it to parameters? What happens if execution is slower? Does it survive another market period?
Software engineers ask another class of questions.
What happens if a function receives an impossible value?
Will the program recover after a failed operation?
Can the same request be processed twice?
Can I reproduce a bug after it has happened once?
Will a small change in one module silently break another module?
That is why the software world has unit tests, integration tests, regression tests, load tests, fuzz testing and fault injection.
Java developers have JUnit. Python developers have pytest. C++ developers use GoogleTest and Catch2. There are systems such as JMeter, k6 and Gatling for load testing. CI platforms such as Jenkins, GitHub Actions, GitLab CI, TeamCity and Azure Pipelines automatically build and test software after changes.
The names are not important. The philosophy is.
Do not wait for users to discover your failure modes. Try to discover them yourself.
For trading software, I find this idea especially attractive.
I would love an MQL reliability laboratory
Imagine pressing one button and not getting another equity curve.
Instead, a server starts attacking your EA.
It slows execution. Disconnects things. Restarts terminals. Changes symbol properties. Produces bursts of ticks. Denies file access. Returns invalid data. Introduces latency at inconvenient moments.
And while doing all this, it checks one thing:
Did the EA remain in a valid state?
That would be much more interesting to me than another optimization with 50,000 parameter combinations.
Some pieces already exist in MetaTrader. The Strategy Tester can emulate execution delay. We can use OnTester() , OnTesterInit() , OnTesterPass() and frames. MetaEditor can be started from the command line. MetaTrader itself can be launched using a configuration file and start testing automatically.
So the foundation is there.
What I miss is the layer above it: a framework that thinks in terms of test cases, assertions, failures, baselines and reproducible scenarios.
And I would really like to test things like these:
-
Kill the terminal while positions are open. Restart it and verify that the EA reconstructs its state instead of opening the same logical trade again.
-
Reboot Windows unexpectedly. Not a polite OnDeinit() , but a real ugly interruption.
-
Disconnect the network at an arbitrary moment. Especially around trade requests, where uncertainty is much more interesting than a clean failure.
-
Replay real slippage distributions. Collect execution statistics from live operation and later reproduce similar conditions in a test environment.
-
Slow the CPU deliberately. I want to know how the EA behaves when calculations that normally take 2 ms suddenly take 200 ms.
-
Inject operating-system scheduling pauses. Freeze processing for short random intervals and watch for stale decisions.
-
Run 100 strategies simultaneously. Not 100 optimization passes — 100 strategies competing for CPU, memory, disk and event-processing time.
-
Generate a multi-symbol tick storm. Let many instruments become active at the same time and measure processing latency.
-
Remove random ticks from a recorded stream. The EA should not collapse because reality did not deliver the perfect sequence expected by the developer.
-
Duplicate selected events. This is a good way to discover logic that accidentally performs the same action twice.
-
Create extreme spread spikes. Five times normal spread, twenty times normal spread, then back to normal.
-
Generate large price gaps. Stops and expected execution prices should never be treated as guarantees.
-
Return repeated trade errors. The important question is not whether an error happens, but whether retry logic has a sensible end.
-
Simulate a delayed confirmation after a timeout. This is exactly the kind of situation that can reveal duplicate-order bugs.
-
Change symbol specifications. Tick size, volume step, minimum lot, stops level or margin parameters may not remain what the developer once cached.
-
Reduce available margin sharply. The EA should fail safely rather than entering an endless loop of rejected operations.
-
Deny filesystem writes. What happens when a log, state file or configuration file cannot be written?
-
Feed damaged or incompatible configuration data. A broken .set or state file should result in a clear error, not mysterious behavior.
-
Run the EA for a very long simulated period. I would watch memory use, counters, object creation, handle leaks and gradual latency growth.
-
Test incorrect external data. Truncated JSON, an empty reply, old data, duplicated data, malformed fields, wrong timestamps and extremely slow responses should all be normal test cases.
The last point has an interesting limitation. WebRequest() is not available inside the Strategy Tester, and MQL code does not give us low-level control over MetaTrader's internal encrypted connection to the broker.
So some experiments cannot be implemented purely inside an EA.
And that is fine.
A serious test laboratory would probably have to live partly outside MetaTrader.
For external APIs we could place a mock server between the EA and the real service. For network failures we could use an OS-level proxy or another controlled network layer. For CPU and memory pressure we would need the operating system, virtual machines or containers. Multiple terminal installations could be started in isolated environments.
MQL would be one part of the laboratory, not necessarily the whole laboratory.
That, to me, makes the idea more interesting rather than less.
First make the EA testable
There is a catch.
You cannot easily test code in which everything is mixed together.
If strategy logic directly calls trading functions, reads system time, accesses files and contacts external services from everywhere in the program, introducing artificial failures becomes painful.
I increasingly like the opposite approach: keep important logic as independent as possible.
For example, even a tiny function can be tested without running several years of EURUSD history:
double NormalizeVolume(double requested, double min_volume, double max_volume, double step) { if(step <= 0.0) return 0.0; double volume = MathFloor(requested / step + 1e-9) * step; volume = MathMax(min_volume, MathMin(max_volume, volume)); return NormalizeDouble(volume, 8); } bool CheckNear(string name, double actual, double expected, double epsilon = 1e-8) { bool passed = (MathAbs(actual - expected) <= epsilon); PrintFormat("%s: %s | actual=%.8f expected=%.8f", name, passed ? "PASS" : "FAIL", actual, expected); return passed; } void OnStart() { int failed = 0; if(!CheckNear("Normal volume", NormalizeVolume(0.107, 0.01, 10.0, 0.01), 0.10)) failed++; if(!CheckNear("Minimum volume", NormalizeVolume(0.001, 0.01, 10.0, 0.01), 0.01)) failed++; if(!CheckNear("Maximum volume", NormalizeVolume(15.0, 0.01, 10.0, 0.01), 10.0)) failed++; PrintFormat("Tests finished. Failed: %d", failed); }
Nothing revolutionary is happening here.
That is exactly the point.
We do not need a Strategy Tester pass to discover that a volume-normalization function is broken. A tiny deterministic test can answer that question in milliseconds.
The same principle becomes much more powerful when trading, time, storage and external communication are placed behind small wrapper classes.
Instead of strategy logic directly sending an order, it could call our own trade interface.
The real implementation talks to MetaTrader.
The test implementation can answer: rejected, delayed, partially executed, timeout.
Now failure becomes something we can deliberately create.
Profit should not be the only PASS condition
This is another habit I would like to change.
A test finishing with profit does not mean that the software behaved correctly.
Suppose the final balance is excellent, but one retry mechanism accidentally opened two positions once. For me, the test failed.
I would rather define invariants: risk must never exceed a limit; one logical signal must never create two unintended trades; retry loops must terminate; calculated volume must respect symbol limits; after a restart internal state must agree with real positions.
If any of these conditions is violated, the result should be red.
Even if the equity curve looks beautiful.
Then automate the build
Once tests exist, relying on the developer to run them manually is a bad plan.
I know how this story ends.
There is a small fix. It looks harmless. The release is needed today. Someone says, “I changed only three lines.”
And that is exactly when the full test suite should run automatically.
MetaEditor already supports command-line compilation with /compile and /log . MetaTrader can be launched with /config , and its [Tester] settings can start a test automatically.
That means a normal CI system can orchestrate the process.
A commit arrives. A clean build machine compiles the source. Compiler errors or forbidden warnings stop the build. Fast tests run. Then regression tests start in Strategy Tester. Release candidates receive a larger matrix of symbols and settings. Nightly builds can run slow stress scenarios. A dedicated server can perform long load tests.
Only after all mandatory stages pass do we produce the release EX5.
I like this model because suddenly the released file has a history.
We know which Git revision produced it. We know which MetaTrader build tested it. We know which .set files were used. We know which tests passed. We can save the logs and reports together with the release.
Six months later, when somebody asks, “What exactly did you test in version 4.12?”, there is an answer.
Not a memory.
An answer.
Every bug can become permanent knowledge
This may be my favorite part of automated testing.
We developers forget things.
Tests do not.
A customer finds a strange bug. We reproduce it. We fix it. Then we add a test that recreates exactly that situation.
Three years later nobody remembers the original bug anymore, but the test is still there, quietly preventing it from coming back.
That is a beautiful idea.
A bug stops being merely an unpleasant event and becomes another piece of knowledge accumulated by the project.
I would also make every randomized failure reproducible. If a chaos test uses randomness, save its seed.
Instead of receiving a report saying, “Something went wrong after six hours,” I want:
EA 4.12, scenario 18427, seed 78342901, connection interrupted after trade request, duplicate retry detected.
Run the same scenario again and you have the same failure.
Now we can work.
Could we build this for MQL?
I think we could build a surprisingly large part of it today.
Not entirely in MQL5, and probably not as one magical library.
I imagine a small MQL testing framework for assertions and mocks, Strategy Tester integration through OnTester* events and frames, MetaEditor command-line compilation, MetaTrader configuration files for automated test runs, plus an external orchestrator written in PowerShell or Python.
Then Jenkins, GitHub Actions, GitLab CI, TeamCity or any similar CI system could simply launch the whole process.
The difficult part is not compiling an EA automatically.
That already works.
The interesting part is creating a clean abstraction around failures: network loss, restarts, delayed confirmations, damaged external data, CPU pressure, resource exhaustion and state recovery.
I keep thinking about such a wrapper because I would genuinely like to use it myself.
Not to prove that an EA can never fail. No serious engineer can promise that.
I want something more practical.
I want failures to become less surprising.
I want them reproducible.
And I want to break my EA in the laboratory before the market, broker, VPS or operating system gets the opportunity to do it for me.
If you develop EAs, I would be very interested to know how you solve this today.
Do you have automated builds? Do you run regression tests outside the Strategy Tester? Do you use Jenkins, GitHub Actions, GitLab CI, TeamCity, custom PowerShell or Python scripts? Have you built mocks for trading operations or external services?
And most importantly: do you think a practical automated testing wrapper around MQL5 and MetaTrader can be built without making it so complicated that nobody will actually use it?
I have some ideas.
I suspect other MQL developers have better ones.
That discussion may be the most interesting test of the idea.


