TradingTeaching Press · Dubai, UAE Trading is high-risk. Check local laws, your residency rules and your finances. Risk warning
🤖 Trading Automation

Creating Your First Expert Advisor in MQL5

Build your first Expert Advisor from scratch. Step-by-step tutorial to create a working trading bot in MQL5.

7 min read TradingTeaching Team

This tutorial guides you through creating a complete, working Expert Advisor. We'll build a simple Moving Average Crossover EA that can actually place trades.

What We'll Build

A simple but functional EA that:

  • Uses two Moving Averages (fast and slow)
  • Buys when fast MA crosses above slow MA
  • Sells when fast MA crosses below slow MA
  • Includes basic risk management
  • Has proper position management

Step 1: Create the EA File

  1. Open MetaEditor (F4 in MT5)
  2. File → New → Expert Advisor
  3. Name it "MACrossoverEA"
  4. Click Finish

Step 2: Define Input Parameters

Input parameters let users customize the EA:

``mql5 //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ input group "=== Moving Average Settings ===" input int FastMAPeriod = 10; // Fast MA Period input int SlowMAPeriod = 20; // Slow MA Period input ENUM_MA_METHOD MAMethod = MODE_SMA; // MA Method

input group "=== Trading Settings ===" input double LotSize = 0.1; // Lot Size input int StopLossPips = 50; // Stop Loss (pips) input int TakeProfitPips = 100; // Take Profit (pips) input int MagicNumber = 12345; // Magic Number `

Step 3: Declare Global Variables

`mql5 //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ int fastMAHandle; // Handle for fast MA indicator int slowMAHandle; // Handle for slow MA indicator double fastMA[]; // Array to store fast MA values double slowMA[]; // Array to store slow MA values `

Step 4: Initialize in OnInit()

`mql5 //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create MA indicator handles fastMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MAMethod, PRICE_CLOSE); slowMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MAMethod, PRICE_CLOSE);

// Check if handles are valid if(fastMAHandle == INVALID_HANDLE || slowMAHandle == INVALID_HANDLE) { Print("Error creating MA indicators"); return(INIT_FAILED); }

// Set arrays as series (index 0 = most recent) ArraySetAsSeries(fastMA, true); ArraySetAsSeries(slowMA, true);

Print("EA initialized successfully"); Print("Fast MA: ", FastMAPeriod, " | Slow MA: ", SlowMAPeriod);

return(INIT_SUCCEEDED); } `

Step 5: Clean Up in OnDeinit()

`mql5 //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release indicator handles IndicatorRelease(fastMAHandle); IndicatorRelease(slowMAHandle);

Print("EA stopped. Reason: ", reason); } `

Step 6: Main Logic in OnTick()

`mql5 //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Only trade at the start of a new bar if(!IsNewBar()) return;

// Copy MA values if(CopyBuffer(fastMAHandle, 0, 0, 3, fastMA) < 3) return; if(CopyBuffer(slowMAHandle, 0, 0, 3, slowMA) < 3) return;

// Check for crossover signals bool buySignal = (fastMA[1] > slowMA[1]) && (fastMA[2] <= slowMA[2]); bool sellSignal = (fastMA[1] < slowMA[1]) && (fastMA[2] >= slowMA[2]);

// Check current positions bool hasPosition = HasOpenPosition();

// Execute trades if(buySignal && !hasPosition) { CloseAllPositions(); // Close any opposite positions OpenBuyTrade(); } else if(sellSignal && !hasPosition) { CloseAllPositions(); OpenSellTrade(); } } `

Step 7: Helper Functions

Check for New Bar

`mql5 //+------------------------------------------------------------------+ //| Check if new bar has formed | //+------------------------------------------------------------------+ bool IsNewBar() { static datetime lastBarTime = 0; datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);

if(currentBarTime != lastBarTime) { lastBarTime = currentBarTime; return true; } return false; } `

Check for Open Positions

`mql5 //+------------------------------------------------------------------+ //| Check if we have open position | //+------------------------------------------------------------------+ bool HasOpenPosition() { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket > 0) { if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return true; } } } return false; } `

Open Buy Trade

`mql5 //+------------------------------------------------------------------+ //| Open a buy trade | //+------------------------------------------------------------------+ void OpenBuyTrade() { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

double sl = NormalizeDouble(ask - StopLossPips point 10, digits); double tp = NormalizeDouble(ask + TakeProfitPips point 10, digits);

MqlTradeRequest request = {}; MqlTradeResult result = {};

request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = LotSize; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = 10; request.magic = MagicNumber; request.comment = "MA Crossover Buy";

if(OrderSend(request, result)) { Print("Buy order placed. Ticket: ", result.order); } else { Print("Buy order failed. Error: ", GetLastError()); } } `

Open Sell Trade

`mql5 //+------------------------------------------------------------------+ //| Open a sell trade | //+------------------------------------------------------------------+ void OpenSellTrade() { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

double sl = NormalizeDouble(bid + StopLossPips point 10, digits); double tp = NormalizeDouble(bid - TakeProfitPips point 10, digits);

MqlTradeRequest request = {}; MqlTradeResult result = {};

request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = LotSize; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = 10; request.magic = MagicNumber; request.comment = "MA Crossover Sell";

if(OrderSend(request, result)) { Print("Sell order placed. Ticket: ", result.order); } else { Print("Sell order failed. Error: ", GetLastError()); } } `

Close All Positions

`mql5 //+------------------------------------------------------------------+ //| Close all positions for this EA | //+------------------------------------------------------------------+ void CloseAllPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket > 0) { if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { MqlTradeRequest request = {}; MqlTradeResult result = {};

request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = PositionGetDouble(POSITION_VOLUME); request.deviation = 10; request.magic = MagicNumber;

if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { request.type = ORDER_TYPE_SELL; request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID); } else { request.type = ORDER_TYPE_BUY; request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); }

OrderSend(request, result); } } } } `

Complete EA Code

Here's the complete EA in one block:

`mql5 //+------------------------------------------------------------------+ //| MACrossoverEA.mq5 | //| TradingTeaching.com | //+------------------------------------------------------------------+ #property copyright "TradingTeaching.com" #property version "1.00"

//--- Input Parameters input group "=== Moving Average Settings ===" input int FastMAPeriod = 10; input int SlowMAPeriod = 20; input ENUM_MA_METHOD MAMethod = MODE_SMA;

input group "=== Trading Settings ===" input double LotSize = 0.1; input int StopLossPips = 50; input int TakeProfitPips = 100; input int MagicNumber = 12345;

//--- Global Variables int fastMAHandle, slowMAHandle; double fastMA[], slowMA[];

//+------------------------------------------------------------------+ int OnInit() { fastMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MAMethod, PRICE_CLOSE); slowMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MAMethod, PRICE_CLOSE);

if(fastMAHandle == INVALID_HANDLE || slowMAHandle == INVALID_HANDLE) return(INIT_FAILED);

ArraySetAsSeries(fastMA, true); ArraySetAsSeries(slowMA, true);

return(INIT_SUCCEEDED); }

//+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(fastMAHandle); IndicatorRelease(slowMAHandle); }

//+------------------------------------------------------------------+ void OnTick() { if(!IsNewBar()) return;

if(CopyBuffer(fastMAHandle, 0, 0, 3, fastMA) < 3) return; if(CopyBuffer(slowMAHandle, 0, 0, 3, slowMA) < 3) return;

bool buySignal = (fastMA[1] > slowMA[1]) && (fastMA[2] <= slowMA[2]); bool sellSignal = (fastMA[1] < slowMA[1]) && (fastMA[2] >= slowMA[2]);

if(buySignal && !HasOpenPosition()) { CloseAllPositions(); OpenBuyTrade(); } else if(sellSignal && !HasOpenPosition()) { CloseAllPositions(); OpenSellTrade(); } }

// Include all helper functions from above... ``

Testing Your EA

Step 1: Compile

  1. Press F7 in MetaEditor
  2. Fix any errors
  3. Ensure "0 errors, 0 warnings"

Step 2: Strategy Tester

  1. In MT5: View → Strategy Tester
  2. Select your EA
  3. Set symbol and timeframe
  4. Choose date range
  5. Click "Start"

Step 3: Analyze Results

Check the results tab for:

  • Total profit/loss
  • Win rate
  • Maximum drawdown
  • Profit factor

Key Takeaways

  • Input parameters make EAs customizable
  • Indicator handles provide efficient access to indicator values
  • Trade on new bars to avoid overtrading
  • Always use Magic Numbers to identify your trades
  • Include proper error handling
  • Test thoroughly in Strategy Tester first

What's Next?

Learn how to create custom indicators in MQL5 to enhance your trading systems.


This article is for educational purposes only and does not constitute financial advice.

Risk warning. This is education only, not financial, investment, tax or legal advice, and not a recommendation to buy or sell anything. Trading forex, CFDs, stocks, commodities, futures, options and crypto is high-risk; with leverage you can lose more than you deposit, and most retail traders lose money. Before you trade or invest in any form, check the local laws where you are and the rules of your country of residence, and consider your own financial situation; if in doubt, ask a licensed adviser. Past and tested results, and all examples, are for illustration only and do not guarantee future results.

Get the strategy right before you automate it

An automated system is only as good as the rules behind it. The book series covers building and testing those rules before a line of code is written.

Explore the books