MT5 EA Source Code: Build and Test a Working Example
By FXNook Lab team

MT5 EA source code is an editable .mq5 program; MetaTrader compiles it into an .ex5 file that the terminal can run. The example below is a complete EMA-crossover EA for MetaTrader 5. It uses indicator handles and CopyBuffer to read completed bars, checks for a new bar before acting, and sends trades through CTrade.
This is a coding example, not a profitable strategy recommendation. It has no stop-loss or take-profit, and it assumes the EA has exclusive use of its symbol: do not run it alongside other strategies or manual trades on that symbol without adapting its position handling. Test it in Strategy Tester and add your own exit and risk rules before considering any live use. Past or tested results do not guarantee future ones.
What MT5 EA source code includes
An Expert Advisor is an MQL5 program that runs in the MetaTrader terminal. Its source normally defines configurable inputs, initialization in OnInit(), cleanup in OnDeinit(), and trade or signal logic that runs from OnTick() or another event handler. The .mq5 file is the readable source you can edit; the compiled .ex5 is the executable output.
For this example, the inputs set the fast and slow EMA periods, the signal timeframe, trade volume, a magic number and the allowed deviation. Initialization creates the two moving-average indicator handles. On each new bar, the EA copies the last two completed values from each indicator, looks for a crossover, and uses CTrade to open or reverse a position.
A working MQL5 EMA-crossover EA
Save the following as EMA_Cross_Example.mq5 under MQL5/Experts. It trades the chart symbol. The default timeframe is the chart timeframe, and the crossover is evaluated using the two most recently completed bars, not the still-forming bar.
#property strict
#include <Trade/Trade.mqh>
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT;
input int InpFastPeriod = 12;
input int InpSlowPeriod = 26;
input double InpLots = 0.10;
input ulong InpMagic = 26031801;
input ulong InpDeviationPoints = 20;
CTrade trade;
int fastHandle = INVALID_HANDLE;
int slowHandle = INVALID_HANDLE;
datetime lastBarTime = 0;
datetime lastReadErrorBar = 0;
bool TradeResultAccepted()
{
uint code = trade.ResultRetcode();
return (code == TRADE_RETCODE_DONE ||
code == TRADE_RETCODE_DONE_PARTIAL ||
code == TRADE_RETCODE_PLACED);
}
int OnInit()
{
if(InpFastPeriod <= 0 || InpSlowPeriod <= 0 ||
InpFastPeriod >= InpSlowPeriod || InpLots <= 0.0)
{
Print("Invalid inputs: use positive periods, fast < slow, and positive volume.");
return INIT_PARAMETERS_INCORRECT;
}
fastHandle = iMA(_Symbol, InpTimeframe, InpFastPeriod, 0, MODE_EMA, PRICE_CLOSE);
slowHandle = iMA(_Symbol, InpTimeframe, InpSlowPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
{
Print("Could not create EMA indicator handles. Error: ", GetLastError());
return INIT_FAILED;
}
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpDeviationPoints);
if(!trade.SetTypeFillingBySymbol(_Symbol))
Print("Could not set filling mode from symbol settings.");
lastBarTime = iTime(_Symbol, InpTimeframe, 0);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(fastHandle != INVALID_HANDLE)
IndicatorRelease(fastHandle);
if(slowHandle != INVALID_HANDLE)
IndicatorRelease(slowHandle);
}
void OnTick()
{
datetime currentBar = iTime(_Symbol, InpTimeframe, 0);
if(currentBar == 0 || currentBar == lastBarTime)
return;
double fastValues[2];
double slowValues[2];
int fastCopied = CopyBuffer(fastHandle, 0, 1, 2, fastValues);
int slowCopied = CopyBuffer(slowHandle, 0, 1, 2, slowValues);
if(fastCopied != 2 || slowCopied != 2)
{
if(lastReadErrorBar != currentBar)
{
Print("EMA data is not ready yet. CopyBuffer error: ", GetLastError());
lastReadErrorBar = currentBar;
}
return;
}
lastBarTime = currentBar;
lastReadErrorBar = 0;
// CopyBuffer stores the older copied value first: index 0 is bar 2,
// and index 1 is the most recently completed bar, bar 1.
bool crossUp = (fastValues[0] <= slowValues[0] &&
fastValues[1] > slowValues[1]);
bool crossDown = (fastValues[0] >= slowValues[0] &&
fastValues[1] < slowValues[1]);
if(crossUp)
ExecuteSignal(true);
else if(crossDown)
ExecuteSignal(false);
}
void ExecuteSignal(const bool buySignal)
{
if(PositionSelect(_Symbol))
{
long positionType = PositionGetInteger(POSITION_TYPE);
if((buySignal && positionType == POSITION_TYPE_BUY) ||
(!buySignal && positionType == POSITION_TYPE_SELL))
return;
if(!trade.PositionClose(_Symbol) || !TradeResultAccepted())
{
Print("Could not close the existing position. Retcode: ",
trade.ResultRetcode(), " ", trade.ResultRetcodeDescription());
return;
}
}
bool sent = buySignal ? trade.Buy(InpLots, _Symbol)
: trade.Sell(InpLots, _Symbol);
if(!sent || !TradeResultAccepted())
{
Print("Trade request failed. Retcode: ", trade.ResultRetcode(),
" ", trade.ResultRetcodeDescription());
}
else
{
Print(buySignal ? "Buy signal sent." : "Sell signal sent.",
" Retcode: ", trade.ResultRetcodeDescription());
}
}
The EA checks for a position by symbol, not by magic number. In a netting account, positions for a symbol are combined; in a hedging account, multiple positions can exist for one symbol. This compact example is intended for a netting account or a setup with only one position on the symbol. If you need multiple EAs, manual trades or multiple hedged positions on the same symbol, adapt position selection and closing to identify the exact position the EA owns.
How the example makes a decision
Initialization creates indicator handles
In MQL5, iMA() returns an indicator handle, not the current moving-average value. The EA stores one handle for each EMA and releases both in OnDeinit(). If the periods or timeframe change, MetaTrader reinitializes the EA, so OnInit() creates handles using the new inputs.
This differs from the usual MQL4 pattern, where iMA() can return an indicator value for a specified shift. In MQL5, you request calculated values from an indicator handle with CopyBuffer(). See the official documentation for iMA and CopyBuffer.
New-bar check and closed-bar values
iTime() reads the opening time of the current bar on the selected timeframe. Comparing it with lastBarTime makes the signal code run once per new bar, rather than on every tick. The EA initializes that timestamp in OnInit(), so it waits for the next bar after being attached or restarted.
The CopyBuffer() calls start at shift 1 and request two values. Shift 1 is the last completed bar and shift 2 is the bar before it. Although the call requests from shift 1, copied data is placed oldest-first in the target array: index 0 is shift 2 and index 1 is shift 1. A bullish crossover means the fast EMA was at or below the slow EMA on the older bar and is above it on the latest completed bar; a bearish crossover is the reverse.
Position handling and order submission
If a crossover points in the same direction as the current position, the EA does nothing. If it points the other way, the EA closes the existing symbol position first and sends a market order in the new direction only if the close request reports an accepted result. With no position, it submits the crossover direction directly.
CTrade makes common trading operations easier to read, but its Boolean return value alone is not proof that a trade completed. The code also checks ResultRetcode() and prints the broker or terminal response. For a market request, retcodes such as TRADE_RETCODE_DONE indicate completion; TRADE_RETCODE_REJECT indicates rejection. The CTrade documentation describes the class methods and result information.
Compile the source in MetaEditor
- In MetaTrader 5, choose File > Open Data Folder, then open
MQL5/Experts. Save the code there asEMA_Cross_Example.mq5. - Open the file in MetaEditor. You can also open MetaEditor from the terminal with F4, then locate the file in the Navigator.
- Press F7 or choose Compile. Read the Errors tab for compiler messages and the Journal tab for compiler and program messages.
- Return to the terminal and refresh the Navigator if needed. The compiled EA should appear under Expert Advisors. Attach it to a chart only after checking its inputs and permissions.
A successful compile creates the .ex5 output, but it only proves that MetaEditor accepted the source syntax and types. It does not establish that the signal is reached, that the broker will accept an order, or that the EA behaves as intended.
| Compile problem | Common cause | What to do |
|---|---|---|
iMA or CopyBuffer gives an undeclared identifier | The file is being compiled as MQL4, or the source is outside the MT5 editor context. | Use MetaEditor opened from MetaTrader 5 and save the file with the .mq5 extension in MQL5/Experts. |
| Wrong parameter count or incompatible argument type | A function call uses a signature from another MQL version or has a mistyped input. | Compare the call with the MQL5 function documentation and read the compiler's line number and parameter details. |
| Cannot open include file | An include path is misspelled or the required library is unavailable. | For this example, use the standard #include <Trade/Trade.mqh> file supplied with MetaTrader 5. |
| There are errors after pasting the code | HTML entities or formatting were copied into the source instead of MQL characters. | In MetaEditor, use the actual operators such as <, && and quotes, not displayed HTML entities. |
Change inputs without changing the logic
Use the EA's Inputs tab to change the EMA periods, timeframe, volume, magic number or deviation. InpTimeframe = PERIOD_CURRENT means the chart timeframe; another timeframe constant makes the EA read bars from that timeframe while it remains attached to the chart symbol. The symbol itself is the chart symbol in this example, so changing the chart changes the traded symbol.
Keep the fast period lower than the slow period. The initialization check rejects zero or negative periods, a fast period that is not lower than the slow period, and non-positive volume. The EA does not round volume to a broker's allowed minimum, maximum or step, so use a volume that the symbol permits. Broker symbol settings and the account's available margin also affect whether an order can be accepted.
Changing the timeframe or periods requires new indicator handles because the indicator calculation has changed. MetaTrader reinitializes the EA when you change its inputs, and this code creates the handles again in OnInit(). If you later add a separate symbol input, create handles for that symbol and use it consistently in bar-time reads, position selection and trade requests; changing only the indicator symbol would leave the rest of the logic inconsistent.
Test the EA in Strategy Tester
- Open Strategy Tester in MetaTrader 5 and select
EMA_Cross_Exampleas the Expert Advisor. - Select the symbol and timeframe you want to test. For this example, the traded symbol is the tester's selected symbol, and
PERIOD_CURRENTuses the tested timeframe. - Set a date range with enough available price history for both moving averages to calculate. Choose a tick model appropriate to the question you are testing; more detailed tick data can matter when testing intrabar rules, although this EA acts on new bars.
- Set the inputs, deposit and account settings, then run a non-visual test. Review the trade list, balance graph, Journal and Experts output for order results and errors.
- Run a visual test to see when bars form and when the EA sends orders. Compare the chart's completed-bar EMA crossover with the logged decisions.
- Repeat with other dates and conditions before drawing conclusions. Strategy Tester results are historical simulations, and past or tested results do not guarantee future ones.
The Strategy Tester can show whether this code detects crossovers and how the terminal handles its trade requests under the selected test conditions. It cannot make an incomplete strategy specification complete: this example deliberately has no stop, target, spread filter or session rule. Add and test those rules if they are part of your intended system.
If it compiles but does not trade
First distinguish a code path that never produces a signal from a trade request that reaches the broker and is rejected. The Journal and Experts tabs, plus the retcode printed by this example, help separate those cases. A quiet test with no crossover is not the same as an order error.
- History or indicator data is missing. If
CopyBuffer()cannot return two values, the EA logs that its EMA data is not ready and tries again on later ticks. Load sufficient history for the selected symbol and timeframe, then rerun the test. - The inputs do not pass validation. The EA stops during initialization if a period is invalid, the fast period is not less than the slow period, or volume is not positive. Read the tester's initialization messages and correct the inputs.
- No crossover occurred in the date range. This example waits for a crossover between the two most recently completed bars. Extend or change the test period to include a signal rather than expecting an order on every bar.
- Trading is disabled. In a terminal or chart run, ensure Algo Trading is enabled and the EA's common settings allow algorithmic trading. Account restrictions and market availability can also prevent execution.
- The broker rejected the request. Inspect the printed result code and description. Volume outside the symbol's allowed range or step, insufficient margin, an unavailable market, or trade restrictions can prevent an order; symbol trading properties are available through functions such as
SymbolInfoDouble(). - Another position is interfering. Because this example selects and closes a position by symbol, a manual trade or another EA on that symbol can affect its behavior. Test it in isolation or change the position-management logic to identify and manage only the intended position.
For a longer diagnostic sequence, see MT5 EA Not Taking Trades: Checks, Causes, and Fixes. If the source compiles but its entry path or order handling is unclear, the FXNook EA debugger can inspect the program's logic and report what stops it from trading.
Build an EA from your own rules
Before adapting this example, write down what should make the EA enter, exit and stay out of the market. Specify whether signals use the current or a completed bar, what happens on an opposite signal, and whether simultaneous or repeated positions are allowed. Include the symbol and timeframe behavior, trading hours and any spread or volatility filters.
Define position sizing and risk controls separately from the entry signal. State how volume is calculated, whether there is a stop-loss or take-profit, what happens after a stop or target, and whether the EA should limit positions, daily losses or total exposure. Also specify broker-facing requirements such as volume constraints, stop distances, filling mode and the expected account type. Test requirements should name the symbols, timeframes, date ranges and execution assumptions you want checked.
If you want a custom MT5 EA built from a written specification, the FXNook AI EA builder turns described rules into a confirmed specification before building, compiling and checking the program. For source you already have, the EA debugger is a practical next step when compilation succeeds but trading behavior does not match the rules.
Frequently asked questions
Where can I find MT5 EA source code?
MT5 EA source code is normally an editable .mq5 file stored in the terminal's MQL5/Experts folder. You can write one in MetaEditor, adapt an existing source file, or have an EA built from clearly specified rules. A compiled .ex5 file can run in MetaTrader 5, but it is not the editable source.
How do I compile an EA in MetaTrader 5?
Open the .mq5 file in MetaEditor and press F7 or choose Compile. Read the Errors tab and fix any reported issues; a successful compile produces an .ex5 file. Then select the EA in MetaTrader 5 and test it in Strategy Tester before relying on its behavior.
What is the difference between an .mq5 file and an .ex5 file?
The .mq5 file contains the human-readable MQL5 source code that you can edit and compile. The .ex5 file is the compiled program that MetaTrader 5 loads to run the EA. Having the compiled file does not by itself give you the original editable source.
Why does my MT5 EA compile but not open trades?
Compilation checks code syntax and types, not whether a signal occurs or a broker accepts an order. Missing history, filters that never pass, disabled algorithmic trading, invalid volume, account restrictions or broker rejection can all stop trading. Read the Experts and Journal messages and inspect the trade result code to identify which stage failed.
Questions
Where can I find MT5 EA source code?
MT5 EA source is an editable .mq5 file, usually saved in the terminal's MQL5/Experts folder. You can write or adapt the source in MetaEditor, or build an EA from explicit trading rules with a code-generation tool.
How do I compile an EA in MetaTrader 5?
Open the .mq5 file in MetaEditor and press F7 or select Compile. Resolve any messages in the Errors tab; a successful compile creates an .ex5 file, which you can then select and test in Strategy Tester.
What is the difference between an .mq5 file and an .ex5 file?
An .mq5 file is the editable MQL5 source code. An .ex5 file is the compiled program MetaTrader 5 runs, and it does not serve as the original source for ordinary editing.
Why does my MT5 EA compile but not open trades?
A compile only confirms that the source passes compiler checks; it does not prove a signal will occur or that an order is valid. Look for missing history, entry filters that block the signal, disabled trading permissions, invalid volume and broker rejection messages in the Experts and Journal logs.