Learn
Build an MT5 EA with AI: From Prompt to Backtest
At a glance
To build an MT5 Expert Advisor (EA) with AI, write the trading rule down, give it to the assistant as one bounded prompt and test what comes back. Our range-breakout EA came from a single prompt, compiled unchanged with zero errors and placed 646 trades in a Strategy Tester run on EURUSD M15 from January 2024 to September 2026. We recomputed every trade from exported bars, and the EA followed the rule. The backtest still lost 15.6% after commission: working code is not a profitable strategy.
On this page

You can build an MT5 Expert Advisor with AI in one afternoon. This guide shows a complete run: one prompt, the unchanged answer, the compiler output, a Strategy Tester backtest and a check of every trade against the written rule.
The result has two parts. The code worked: it compiled without errors and followed the rule in the backtest. The strategy did not: it lost 15.6% after commission. Both parts are documented below, and the complete material is in the download.
A program like this is often called an AI trading bot or a trading robot. Here that means a bot whose code was written with AI. The EA is a deterministic MQL5 program. It compares prices with a time range and sends orders. It does not ask an AI service for trading decisions. The AI wrote the source code, and that is all it did here. Our MT5 AI Assistant guide explains the other case, an assistant with access to the platform. In our MT5 MCP server test an agent wrote, compiled and backtested an EA through the platform’s own tools.
Write the rule before the prompt
“Make a profitable trading robot” cannot be tested. A rule can. We chose a time-range breakout because every part of it is visible on a chart. The table holds the complete rule; everything the EA does later has to trace back to one of these lines.
Scroll horizontally if needed
| Part | Rule for this example |
|---|---|
| Range | Highest high and lowest low of all bars that open from 00:00 to 08:00 broker server time |
| Entry | A bar that opens between 08:00 and 16:00 closes above the range high: buy. Below the range low: sell |
| Timing | Decide once per closed bar, never on the unfinished bar |
| Trades | At most one per server day |
| Stop loss | The opposite side of the range |
| Take profit | 1.5 times the stop distance, measured from the entry price |
| Volume | Risk 1% of equity if the stop is hit |
| Filters | No entry above 30 points spread or with stops closer than the broker allows |
| Time exit | Close the position at 21:00 if neither stop nor target was hit |
| Ownership | Only positions with this EA’s magic number on this symbol |
The hours and numbers are illustrative inputs. We fixed them in the prompt before the first test and did not optimise them afterwards.
The prompt: one bounded task for the AI
This is the prompt, word for word. It names the platform language, forbids what the program must not do and asks the assistant to state its assumptions before writing code.
Write a complete MT5 Expert Advisor in MQL5 (not MQL4) for a time-range
breakout. Name it "RX Range Breakout". Use the standard library class CTrade.
No DLLs, no file access, no network access.
Rules. All times are broker server time. Use the symbol and timeframe of the
chart the EA is attached to.
1. Range: the highest high and lowest low of all bars that open from
RangeStartHour (default 0) up to, but not including, RangeEndHour
(default 8) of the current server day.
2. Entry window: bars that open from RangeEndHour up to, but not including,
LastEntryHour (default 16). Decide once per closed bar, never on the
unfinished bar. Buy at market when a bar closes above the range high.
Sell at market when a bar closes below the range low.
3. At most one trade per server day. After a restart, find out from the
account history whether today's trade already happened and do not trade
again.
4. Stop loss at the opposite side of the range. Take profit at RewardRisk
(default 1.5) times the stop distance, measured from the entry price.
5. Volume: risk RiskPercent (default 1.0) of account equity if the stop is
hit. Compute it from the symbol's tick value and tick size and round down
to the volume step. Below the minimum volume: skip the trade and log why.
Above the maximum volume: use the maximum.
6. Skip the entry when the spread exceeds MaxSpreadPoints (default 30), when
no bar exists inside the range window, or when stop or target is closer
than the symbol's minimum stop distance.
7. If neither stop nor target was hit, close the position at CloseHour
(default 21).
8. Only touch positions and deals with this EA's magic number (an input) on
this symbol. It must work on netting and on hedging accounts.
9. Check the result of every trade request and log the return code. Do not
resend a failed request in a loop.
10. Draw each day's range as a rectangle on the chart.
11. Log one line per decision: the range values, the signal, a skipped entry
with its reason, the order result and the time exit.
First restate the rules in your own words and list everything that is
ambiguous, with the assumption you chose. Then give the complete source in
one file. Do not claim that you compiled or tested the code.
We sent it once, on 2 October 2026, to Claude Opus 5.5 in a fresh session. The assistant was told to answer without files, a compiler or web search. The session ran inside our project workspace, so it could see the project’s general working instructions. They contain no EA code, but they explain the RoboXpert copyright line in the source.
Using ChatGPT, Gemini or another assistant
The prompt names no assistant and needs nothing that only one of them offers. You can paste it into ChatGPT, Gemini or a coding agent such as Claude Code. We ran it with Claude Opus 5.5 only and have not tested other models for this guide. Expect different code, different assumptions and possibly compiler errors. What you check stays the same: the list of assumptions, the compiler output, the tester journal and the trade list. Keep the prompt, the answer and every later change together, whichever assistant you use. In a separate test we gave Claude and GPT the same indicator prompt and checked both results tick by tick.
What the AI answered
The answer had three parts: the rules in the assistant’s own words, a list of 25 assumptions and one MQL5 file with 724 lines. It closed with the sentence “I have not compiled or run this code” and six points to check first.
The list of assumptions is the most useful part. Our prompt looked complete, and the assistant still found gaps. Five of them changed how the EA behaves:
Scroll horizontally if needed
| Gap in our prompt | Assumption the AI chose |
|---|---|
| Which bar may trigger the entry? | Any closed bar outside the range, as long as no trade happened that day. It need not be the first breakout bar |
| Does a skipped entry use up the day? | No. The next closed bar outside the range is a new decision |
| When exactly is the decision made? | On the first tick of the next bar, at the ask for a buy and at the bid for a sell |
| Where does a sell stop sit? | Exactly on the range high, with no spread buffer. The ask triggers it |
| What if the close at 21:00 fails? | Try again, at most once per 60 seconds. This is the one request that is repeated |
Read this list before you read the code. Each line is a decision about your money that the prompt left open. The first two come back in the backtest below.
The volume calculation shows how the assistant turned rule 5 into code:
double riskMoney =equity*RiskPercent/100.0;
double lossPerLot=stopDistance/tickSize*tickValue;
// ...
double raw =riskMoney/lossPerLot;
double vol =NormalizeDouble(MathFloor(raw/step+1e-9)*step,digits);
The first trade of the backtest lets you check it by hand. Equity was 10,000 USD, so the risk was 100 USD. The stop was 282 points away, which costs 282 USD per lot on EURUSD in a USD account. 100 divided by 282 is 0.3546 lots, rounded down to 0.35. The tester journal shows Order SELL 0.35 lots.
Compile the unchanged answer
We saved the MQL5 block as RX_Range_Breakout.mq5 without editing a character and compiled it in MetaEditor build 6230:
Result: 0 errors, 0 warnings, 965 ms elapsed, cpu='X64 Regular'
That is a good start and proves little. A compiler checks that the code is valid MQL5. It cannot know whether stopDistance holds the right price difference or whether the EA trades twice on one day. For that, the program has to run.
If your answer does not compile, give the assistant the exact compiler message and the line it points to, and ask for a fix of that error only. Do not ask for a rewrite.
Run the MT5 Expert Advisor in the Strategy Tester
We ran the EA once with these settings:
Scroll horizontally if needed
| Setting | Value |
|---|---|
| Platform | MetaTrader 5 build 6230 on macOS (Wine 10.0), 2 October 2026 |
| Account | Demo account, server CapitalPointTrading-Demo (Capital Point Trading Ltd), hedging, USD |
| Symbol and timeframe | EURUSD, M15 |
| Period | 1 January 2024 to 30 September 2026 |
| Model | Every tick based on real ticks, no execution delay |
| Deposit and leverage | 10,000 USD, 1:100 |
| Inputs | Defaults from the prompt, not optimised |
| Commission | As charged by the tester for this account: 7 USD per lot round turn |
| Server time | GMT+3 on the test day |
The Strategy Tester simulates. It sent no orders to the demo account. The broker appears here as the source of the price history. It is not a recommendation, and this guide contains no broker link.

The picture shows two days. On 24 September the bar that opened at 09:15 closed above the range high. The EA bought at 09:30, and the stop on the range low was hit at 13:15. On 25 September the same sequence ended at the target at 14:09. The journal of that run holds one line per decision, as rule 11 asked:
2026.09.25 08:00:00 Range 2026.09.25 00:00-08:00: high 1.13824, low 1.13684, height 140 points, 32 bars
2026.09.25 09:30:00 Signal BUY: bar 2026.09.25 09:15 closed at 1.13845, above the range high 1.13824
2026.09.25 09:30:00 Order BUY 0.62 lots at 1.13845, SL 1.13684, TP 1.14086: accepted, retcode 10009 (done at 1.13845), order #10, deal #10, filled 0.62 lots at 1.13845, last error 0
One limit of the data matters for everything that follows. The report states “History Quality: 18% real ticks”. The demo server supplied real ticks from 1 April 2026 only. For the 27 months before that, the tester generated ticks from minute bars. MetaQuotes documents this fallback and notes that the ask price is then the bid plus the spread stored in the minute bar. Source: real and generated ticks.
Check every trade against the rule
A backtest report tells you what the EA earned. It does not tell you whether the EA did what you asked. We checked that separately.
A small MQL5 script exported the EURUSD M15 bars of the test period from the chart. A Python script then rebuilt each day’s range and signals from those bars. It follows the written rule and never reads the EA source. Finally it compared its own result with the orders and deals in the tester report. The table shows how many of the 646 trades passed each comparison: all of them on direction, entry and exit, and all but one on stop, target and volume.
Scroll horizontally if needed
| Check | Result |
|---|---|
| Days with a trade | 646, never more than one per server day |
| Direction equals the recomputed signal | 646 of 646 |
| Entry at the open of the next bar, within the spread limit | 646 of 646 |
| Stop on the opposite range boundary | 645 of 646 |
| Target 1.5 times the stop distance from the entry | 645 of 646 |
| Volume from 1% of the balance before the trade | 645 of 646 |
| Exit by stop or target before 21:00, otherwise at 21:00 | 646 of 646 |
The one deviating day is 1 April 2026, the first day with real ticks. Our exported chart history for that night starts at 01:45. The tester built two more bars from ticks and logged a lower range low. The EA’s stop, target and volume match the range in its own journal line. The difference lies in the two data sets.
The check found something the report hides. On 28 days the rule showed a signal and the EA placed no trade. On 12 more days it entered later than the first signal. The journal explains both with 497 lines like this one:
2025.03.18 09:30:00 Signal SELL: bar 2025.03.18 09:15 closed at 1.09040, below the range low 1.09063
2025.03.18 09:30:00 Entry skipped: spread 34 points exceeds MaxSpreadPoints 30
On that day the EA skipped a sell signal and then 16 buy signals in a row. It bought at 14:45, four hours after the first close above the range. That is exactly what the first two assumptions in the table above allow. It is probably not what you pictured when you read “buy when a bar closes above the range high”.
All but six of the 497 skipped entries fall into the period with generated ticks, where the spread comes from the stored minute bar. We cannot tell from here whether those spreads were real. The filter worked as written. The data it reacted to is the weak point.
The backtest result
The EA followed the rule, and the rule lost money. The table shows the Strategy Tester report for EURUSD M15 from 1 January 2024 to 30 September 2026 with a 10,000 USD deposit: a net loss of 1,562.18 USD, nearly all of it commission.
Scroll horizontally if needed
| Measure | Value |
|---|---|
| Trades | 646 (286 with a profit, 44.27%) |
| Total net profit | −1,562.18 USD (−15.6%) |
| Profit before commission | −7.88 USD |
| Commission | −1,554.30 USD |
| Profit factor | 0.94 |
| Maximal equity drawdown | 3,202.08 USD (31.21%) |
| Exits | 154 by target, 262 by stop, 230 by time |

Before commission the 646 trades ended almost exactly at zero. The commission of 7 USD per lot turned that into a loss of 15.6%. Spread is already inside the first number, because the tester fills buys at the ask and sells at the bid.
The years differ widely. Split by calendar year, the same backtest lost money in 2024, ended flat in 2025 and gained in 2026:
Scroll horizontally if needed
| Period | Trades | Before commission | Commission | Net |
|---|---|---|---|---|
| 2024 | 244 | −1,768.36 | −721.88 | −2,490.24 |
| 2025 | 218 | +332.75 | −386.84 | −54.09 |
| 2026 to 29 September | 184 | +1,427.73 | −445.58 | +982.15 |
The 129 trades on real ticks, from 1 April 2026, made 810.75 USD after commission. Do not read that as an edge. It is six months of one symbol with inputs nobody tested beforehand, and the 27 months before it lost 2,372.93 USD. A rule that is negative, flat and positive in three consecutive years has shown no stable behaviour.
This is a historical backtest. It is not an out-of-sample test, a demo-forward run or a live result. Our backtest versus live guide explains what each of those stages adds, and the cost-sensitivity study shows how quickly costs decide a result like this one.
What we did not test
The run above covers one path through the program. Several rules never executed:
- Rule 3 after a restart. The tester does not restart an EA. The search in the account history ran once, at the start.
- A netting account. The test account was a hedging account. MT5 hedging vs netting shows why that difference matters for an EA that closes its own position.
- Rejected or partly filled orders. The tester filled every request at the requested price. A real server can reject, requote or fill a different volume. MetaQuotes requires checking the return code of every request; a sent order is not a completed fill. Source: OrderSend.
- A closed market at 21:00, any symbol other than EURUSD and any timeframe other than M15.
The assistant’s own list adds more, among them stops that a broker does not accept together with a market order. If the EA stays silent on your chart, our EA-not-trading guide separates signal, permission, request and result.
Change one thing with a follow-up prompt
The backtest gives you a concrete case, and a concrete case is what a good follow-up prompt needs. To make the EA trade the first breakout only, do not ask for a “better strategy”. Describe one change and hand over the journal lines that show the old behaviour:
Change one behaviour in RX Range Breakout v1.00 and nothing else.
New rule: only the first closed bar outside the range may trigger an entry.
If that entry is skipped or fails, there is no trade on that day. Log one
line "first breakout not traded: <reason>".
Case that must change: on 2025.03.18 the first signal (SELL, bar 09:15) was
skipped for spread. v1.00 then bought at 14:45. The new version must not
trade on that day.
Return only the changed functions and name the lines you changed.
Then compile, repeat the same backtest and run the check again. The day you named must now show no trade, and all other days must still pass. We have not run this change. It alters the rule after we saw the result, so its backtest would belong to a new version with its own record.
Start smaller: test one function first
A trading EA is a large first step. Our first project for this guide was smaller: an EA that only logs a crossing of two moving averages and sends no orders. Its comparison sits in one pure function, which six fixed cases can test without market data:
int CrossDirection(double fast_old, double slow_old,
double fast_new, double slow_new)
{
if(fast_old <= slow_old && fast_new > slow_new) return 1;
if(fast_old >= slow_old && fast_new < slow_new) return -1;
return 0;
}
On 26 September 2026 the six cases passed in an account-free MT5 build 6230 terminal. We also swapped the older and the newer values on purpose. The code still compiled, and the test returned −1 instead of 1. A compiler cannot know which point in time you meant, and a test case can.
The read that feeds this function is where generated code often goes wrong. In MQL5, iMA returns a handle, and the values come from CopyBuffer, which puts the older value first in a plain array. Source: CopyBuffer. The signal-logger project contains the source, the test harness, the compiler logs and a second, timer-driven run against native indicator buffers. That project was developed with a different assistant, which its README names.
Download and reproduce
Download the MQL5 project (ZIP). The prompt, the unchanged AI answer, MQL5 source, compile log, tester journal, trade list and the Python check. No executable, no account data.
The ZIP contains the prompt, the complete unchanged answer, the MQL5 source, the compile log, the tester report summary, all 3,230 journal lines of the EA, the trade list, the export script and the Python check. It contains no executable and no account data.
Use a demo account or a separate test installation. Copy the source into a subfolder of MQL5/Experts, compile it in MetaEditor and read the Errors tab. For the folder and compile route, see our MT5 EA installation guide, and for macOS the MetaTrader on Mac walkthrough.
Run the Strategy Tester with your own broker’s history. Your numbers will differ from ours, because prices, spreads, commission and server time differ. The useful comparison is your trade list against the rule. Read the “History Quality” line of your report before you read the profit. Our guide on how to read an MT5 Strategy Tester report goes through the report of this run line by line.


