MQL5 Tutorial — คู่มือเทรด Forex ฉบับสมบูรณ์ 2026
MQL5 (MetaQuotes Language 5) เป็นภาษา programming สำหรับเขียน Expert Advisors (EA), indicators และ scripts บน MetaTrader 5 ซึ่งเป็น trading platform ยอดนิยมสำหรับ Forex, CFDs, futures และ stocks MQL5 เป็นภาษาที่คล้าย C++ รองรับ OOP (Object-Oriented Programming) มี built-in functions สำหรับ technical analysis, order management และ chart operations บทความนี้เป็นคู่มือ MQL5 ฉบับสมบูรณ์ ตั้งแต่พื้นฐานจนถึง advanced EA development
MQL5 Basics
//+------------------------------------------------------------------+
//| mql5_basics.mq5 — MQL5 fundamentals |
//+------------------------------------------------------------------+
// 1. Data Types
void DataTypes()
{
int count = 10; // Integer
double price = 1.08523; // Double (price)
string symbol = "EURUSD"; // String
bool isActive = true; // Boolean
datetime now = TimeCurrent(); // Date/time
// Arrays
double prices[];
ArrayResize(prices, 100);
// Enumerations
ENUM_TIMEFRAMES tf = PERIOD_H1; // 1-hour timeframe
ENUM_ORDER_TYPE orderType = ORDER_TYPE_BUY;
}
// 2. Getting Market Data
void MarketData()
{
// Current price
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
// OHLC data
double open = iOpen(_Symbol, PERIOD_H1, 0); // Current bar open
double high = iHigh(_Symbol, PERIOD_H1, 0); // Current bar high
double low = iLow(_Symbol, PERIOD_H1, 0); // Current bar low
double close = iClose(_Symbol, PERIOD_H1, 1); // Previous bar close
// Volume
long volume = iVolume(_Symbol, PERIOD_H1, 0);
// Account info
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_MARGIN);
PrintFormat("Ask: %.5f | Bid: %.5f | Spread: %.1f pips",
ask, bid, spread / _Point / 10);
}
// 3. Technical Indicators
void Indicators()
{
// Moving Average
int ma_handle = iMA(_Symbol, PERIOD_H1, 20, 0, MODE_SMA, PRICE_CLOSE);
double ma_buffer[];
CopyBuffer(ma_handle, 0, 0, 3, ma_buffer);
// RSI
int rsi_handle = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE);
double rsi_buffer[];
CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer);
// MACD
int macd_handle = iMACD(_Symbol, PERIOD_H1, 12, 26, 9, PRICE_CLOSE);
double macd_main[], macd_signal[];
CopyBuffer(macd_handle, 0, 0, 3, macd_main);
CopyBuffer(macd_handle, 1, 0, 3, macd_signal);
PrintFormat("MA(20): %.5f | RSI(14): %.1f | MACD: %.5f",
ma_buffer[0], rsi_buffer[0], macd_main[0]);
}
Expert Advisor (EA) Development
//+------------------------------------------------------------------+
//| simple_ea.mq5 — Simple Moving Average Crossover EA |
//+------------------------------------------------------------------+
#property copyright "MQL5 Tutorial"
#property version "1.00"
// Input parameters
input int FastMA_Period = 10; // Fast MA Period
input int SlowMA_Period = 20; // Slow MA Period
input double LotSize = 0.01; // Lot Size
input int StopLoss = 100; // Stop Loss (points)
input int TakeProfit = 200; // Take Profit (points)
input int MagicNumber = 12345; // Magic Number
// Global variables
int fast_ma_handle, slow_ma_handle;
double fast_ma[], slow_ma[];
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
fast_ma_handle = iMA(_Symbol, PERIOD_CURRENT, FastMA_Period, 0, MODE_SMA, PRICE_CLOSE);
slow_ma_handle = iMA(_Symbol, PERIOD_CURRENT, SlowMA_Period, 0, MODE_SMA, PRICE_CLOSE);
if(fast_ma_handle == INVALID_HANDLE || slow_ma_handle == INVALID_HANDLE)
{
Print("Error creating MA indicators");
return INIT_FAILED;
}
ArraySetAsSeries(fast_ma, true);
ArraySetAsSeries(slow_ma, true);
Print("EA initialized successfully");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Copy indicator values
if(CopyBuffer(fast_ma_handle, 0, 0, 3, fast_ma) < 3) return;
if(CopyBuffer(slow_ma_handle, 0, 0, 3, slow_ma) < 3) return;
// Check for crossover (using previous completed bars)
bool bullish_cross = fast_ma[1] > slow_ma[1] && fast_ma[2] <= slow_ma[2];
bool bearish_cross = fast_ma[1] < slow_ma[1] && fast_ma[2] >= slow_ma[2];
// Check if we have open positions
bool has_buy = HasPosition(POSITION_TYPE_BUY);
bool has_sell = HasPosition(POSITION_TYPE_SELL);
// Trading logic
if(bullish_cross && !has_buy)
{
if(has_sell) CloseAllPositions(POSITION_TYPE_SELL);
OpenPosition(ORDER_TYPE_BUY);
}
else if(bearish_cross && !has_sell)
{
if(has_buy) CloseAllPositions(POSITION_TYPE_BUY);
OpenPosition(ORDER_TYPE_SELL);
}
}
//+------------------------------------------------------------------+
//| Open a position |
//+------------------------------------------------------------------+
bool OpenPosition(ENUM_ORDER_TYPE type)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price = (type == ORDER_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = (type == ORDER_TYPE_BUY) ?
price - StopLoss * _Point :
price + StopLoss * _Point;
double tp = (type == ORDER_TYPE_BUY) ?
price + TakeProfit * _Point :
price - TakeProfit * _Point;
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = type;
request.price = price;
request.sl = sl;
request.tp = tp;
request.magic = MagicNumber;
request.deviation = 10;
request.comment = "MA Crossover EA";
if(!OrderSend(request, result))
{
PrintFormat("OrderSend error: %d", GetLastError());
return false;
}
PrintFormat("Position opened: %s at %.5f, SL: %.5f, TP: %.5f",
EnumToString(type), price, sl, tp);
return true;
}
//+------------------------------------------------------------------+
//| Check if position exists |
//+------------------------------------------------------------------+
bool HasPosition(ENUM_POSITION_TYPE type)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
PositionGetInteger(POSITION_TYPE) == type)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Close all positions of given type |
//+------------------------------------------------------------------+
void CloseAllPositions(ENUM_POSITION_TYPE type)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
PositionGetInteger(POSITION_TYPE) == type)
{
ulong ticket = PositionGetInteger(POSITION_TICKET);
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = PositionGetDouble(POSITION_VOLUME);
request.type = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
request.price = (type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.position = ticket;
request.deviation = 10;
OrderSend(request, result);
}
}
}
Risk Management
//+------------------------------------------------------------------+
//| risk_management.mq5 — Position sizing and risk management |
//+------------------------------------------------------------------+
class CRiskManager
{
private:
double m_riskPercent;
int m_maxPositions;
double m_maxDrawdown;
public:
CRiskManager(double riskPct=1.0, int maxPos=3, double maxDD=10.0)
{
m_riskPercent = riskPct;
m_maxPositions = maxPos;
m_maxDrawdown = maxDD;
}
// Calculate lot size based on risk percentage
double CalculateLotSize(string symbol, double slPoints)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * m_riskPercent / 100.0;
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double pointValue = tickValue / tickSize * _Point;
double lotSize = riskAmount / (slPoints * pointValue);
// Normalize lot size
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
lotSize = MathFloor(lotSize / lotStep) * lotStep;
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
return lotSize;
}
// Check if we can open new position
bool CanOpenPosition()
{
// Max positions check
if(PositionsTotal() >= m_maxPositions)
{
Print("Max positions reached");
return false;
}
// Drawdown check
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double dd = (balance - equity) / balance * 100;
if(dd >= m_maxDrawdown)
{
PrintFormat("Max drawdown reached: %.1f%%", dd);
return false;
}
// Margin check
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
if(freeMargin < 100)
{
Print("Insufficient free margin");
return false;
}
return true;
}
// Calculate trailing stop
double TrailingStop(ulong ticket, double trailPoints)
{
if(!PositionSelectByTicket(ticket)) return 0;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentPrice;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double newSL = currentPrice - trailPoints * _Point;
if(newSL > currentSL && newSL > openPrice)
return newSL;
}
else
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double newSL = currentPrice + trailPoints * _Point;
if((newSL < currentSL || currentSL == 0) && newSL < openPrice)
return newSL;
}
return 0; // No update needed
}
};
Backtesting & Optimization
# backtesting.py — Python analysis of MQL5 backtest results
import json
import random
class BacktestAnalysis:
CODE = """
# analyze_backtest.py — Analyze MT5 backtest results
import pandas as pd
import numpy as np
from datetime import datetime
class BacktestAnalyzer:
def __init__(self, trades_csv):
self.df = pd.read_csv(trades_csv)
self.df['time'] = pd.to_datetime(self.df['time'])
def calculate_metrics(self):
profits = self.df['profit']
total_trades = len(profits)
winners = profits[profits > 0]
losers = profits[profits < 0]
win_rate = len(winners) / total_trades * 100
avg_win = winners.mean() if len(winners) > 0 else 0
avg_loss = abs(losers.mean()) if len(losers) > 0 else 0
profit_factor = winners.sum() / abs(losers.sum()) if losers.sum() != 0 else float('inf')
# Maximum drawdown
cumulative = profits.cumsum()
peak = cumulative.cummax()
drawdown = (cumulative - peak)
max_dd = drawdown.min()
# Sharpe ratio (annualized)
daily_returns = profits.resample('D', on=self.df['time']).sum() if 'time' in self.df else profits
sharpe = np.sqrt(252) * daily_returns.mean() / daily_returns.std() if daily_returns.std() > 0 else 0
return {
'total_trades': total_trades,
'win_rate': round(win_rate, 1),
'profit_factor': round(profit_factor, 2),
'avg_win': round(avg_win, 2),
'avg_loss': round(avg_loss, 2),
'max_drawdown': round(max_dd, 2),
'net_profit': round(profits.sum(), 2),
'sharpe_ratio': round(sharpe, 2),
}
# analyzer = BacktestAnalyzer('backtest_results.csv')
# metrics = analyzer.calculate_metrics()
# print(json.dumps(metrics, indent=2))
"""
def show_code(self):
print("=== Backtest Analyzer ===")
print(self.CODE[:600])
def sample_results(self):
print(f"\n=== Sample Backtest Results (MA Crossover EA) ===")
print(f" Period: 2024-01-01 to 2024-12-31")
print(f" Symbol: EURUSD H1")
print(f" Total trades: {random.randint(80, 200)}")
print(f" Win rate: {random.uniform(45, 65):.1f}%")
print(f" Profit factor: {random.uniform(1.1, 2.5):.2f}")
print(f" Net profit: ")
print(f" Max drawdown: ")
print(f" Sharpe ratio: {random.uniform(0.5, 2.0):.2f}")
bt = BacktestAnalysis()
bt.show_code()
bt.sample_results()
Advanced Topics
# advanced.py — Advanced MQL5 topics
import json
class AdvancedMQL5:
TOPICS = {
"multi_tf": {
"name": "Multi-Timeframe Analysis",
"description": "วิเคราะห์หลาย timeframes — trend จาก Daily, entry จาก H1, timing จาก ",
"tip": "ใช้ iMA/iRSI กับ timeframe ต่างกัน → confirm ก่อน entry",
},
"news_filter": {
"name": "News Event Filter",
"description": "หยุดเทรดก่อน/หลัง high-impact news — ป้องกัน volatility spike",
"tip": "ใช้ MQL5 Calendar functions: CalendarValueHistory()",
},
"portfolio": {
"name": "Portfolio EA (Multi-Symbol)",
"description": "เทรดหลายคู่เงินพร้อมกัน — diversification ลด risk",
"tip": "ใช้ OnTimer() + loop ผ่าน symbols array",
},
"ml_integration": {
"name": "Machine Learning Integration",
"description": "ส่ง data จาก MT5 ไป Python → ML predict → ส่ง signal กลับ",
"tip": "ใช้ MetaTrader5 Python package: pip install MetaTrader5",
},
"custom_indicator": {
"name": "Custom Indicators",
"description": "สร้าง indicator เอง — plot บน chart, ใช้ใน EA",
"tip": "ใช้ OnCalculate() + SetIndexBuffer() สำหรับ custom indicator",
},
}
PYTHON_MT5 = """
# python_mt5.py — Python integration with MetaTrader 5
import MetaTrader5 as mt5
import pandas as pd
# Initialize MT5
mt5.initialize()
# Get account info
account = mt5.account_info()
print(f"Balance: {account.balance}, Equity: {account.equity}")
# Get OHLCV data
rates = mt5.copy_rates_from_pos("EURUSD", mt5.TIMEFRAME_H1, 0, 1000)
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
# Send order
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": "EURUSD",
"volume": 0.01,
"type": mt5.ORDER_TYPE_BUY,
"price": mt5.symbol_info_tick("EURUSD").ask,
"sl": mt5.symbol_info_tick("EURUSD").ask - 0.0050,
"tp": mt5.symbol_info_tick("EURUSD").ask + 0.0100,
"comment": "Python EA",
}
result = mt5.order_send(request)
print(f"Order: {result.comment}")
mt5.shutdown()
"""
def show_topics(self):
print("=== Advanced Topics ===\n")
for key, topic in self.TOPICS.items():
print(f"[{topic['name']}]")
print(f" {topic['description']}")
print()
def show_python(self):
print("=== Python + MT5 ===")
print(self.PYTHON_MT5[:500])
adv = AdvancedMQL5()
adv.show_topics()
adv.show_python()
FAQ - คำถามที่พบบ่อย
Q: MQL5 เรียนยากไหม?
A: ปานกลาง — ถ้ามีพื้นฐาน C/C++/Java จะเรียนเร็ว ถ้าเป็นมือใหม่: ใช้เวลา 2-4 สัปดาห์สำหรับ basics, 2-3 เดือนสำหรับ EA development แหล่งเรียน: mql5.com/docs (official), YouTube tutorials, MQL5 forum เริ่มจาก: Script ง่ายๆ → Custom Indicator → Simple EA → Advanced EA
Q: EA ทำกำไรได้จริงไหม?
A: ได้ แต่ไม่ง่าย: EA ส่วนใหญ่ที่ขายตาม market = ไม่ทำกำไรระยะยาว สิ่งสำคัญ: backtest ดี ≠ forward test ดี (overfitting) ต้อง: risk management ดี, walk-forward optimization, realistic expectations คำแนะนำ: อย่าหวังรวยเร็ว, เริ่มจาก demo account, ทดสอบอย่างน้อย 6 เดือน
Q: MQL5 กับ Pine Script อันไหนดี?
A: MQL5: full programming language, OOP, เขียน EA auto-trade ได้, backtesting ครบ Pine Script (TradingView): ง่ายกว่า, visualization ดี, community ใหญ่, แต่ auto-trade จำกัด เลือก MQL5: ถ้าต้องการ automated trading จริงจัง เลือก Pine Script: ถ้าต้องการ indicator + manual trading + charting
Q: ใช้ Python แทน MQL5 ได้ไหม?
A: ได้บางส่วน: Python + MetaTrader5 package: ดึงข้อมูล, ส่ง orders, backtest ด้วย Python libraries ข้อดี Python: ML/AI libraries (scikit-learn, TensorFlow), data analysis (pandas) ข้อจำกัด: ต้องรัน Python script แยก, latency สูงกว่า native MQL5 แนะนำ: ใช้ร่วมกัน — Python สำหรับ analysis/ML, MQL5 สำหรับ execution
