Technology

Codebase MQL4 — เขียน Expert Advisor และ Automated Trading บน MetaTrader 4

codebase mql4
Codebase MQL4 — คู่มือเทรด Forex ฉบับสมบูรณ์ 2026 | SiamCafe Blog
2025-07-18· อ. บอม — SiamCafe.net· 1,195 คำ

MQL4 คืออะไรและใช้ทำอะไร

MQL4 (MetaQuotes Language 4) เป็นภาษาโปรแกรมที่พัฒนาโดย MetaQuotes สำหรับเขียน automated trading programs บน MetaTrader 4 (MT4) platform ซึ่งเป็น trading platform ที่นิยมที่สุดสำหรับ Forex trading ทั่วโลก

MQL4 ใช้สำหรับเขียน Expert Advisors (EAs) โปรแกรม automated trading ที่ซื้อขายอัตโนมัติ, Custom Indicators สร้าง technical indicators เฉพาะทาง, Scripts โปรแกรมที่รันครั้งเดียวสำหรับ specific tasks, Libraries ฟังก์ชันที่ reuse ได้ข้าม programs

MQL4 syntax คล้าย C/C++ มี built-in functions สำหรับ market data access, order management, technical analysis, file operations และ network communication เหมาะสำหรับ traders ที่ต้องการ automate trading strategies, สร้าง custom indicators ที่ไม่มีใน standard MT4 และ backtest strategies ก่อนใช้จริง

เริ่มต้นเขียน MQL4

ขั้นตอนเริ่มต้นเขียนโปรแกรม MQL4

//+------------------------------------------------------------------+
//| MQL4 Basics — Getting Started                                      |
//+------------------------------------------------------------------+

// === 1. ติดตั้ง MetaTrader 4 ===
// ดาวน์โหลดจาก broker ที่ใช้ (เช่น XM, FXCM, IC Markets)
// เปิด MetaEditor: Tools > MetaQuotes Language Editor (F4)

// === 2. โครงสร้างไฟล์ MQL4 ===
// Expert Advisors: MQL4/Experts/
// Indicators:      MQL4/Indicators/
// Scripts:         MQL4/Scripts/
// Libraries:       MQL4/Libraries/
// Include:         MQL4/Include/

// === 3. MQL4 Data Types ===
// int     — จำนวนเต็ม
// double  — ทศนิยม (ราคา, lots)
// string  — ข้อความ
// bool    — true/false
// datetime — วันเวลา
// color   — สี

// === 4. Built-in Variables ===
// Ask    — ราคา Ask ปัจจุบัน
// Bid    — ราคา Bid ปัจจุบัน
// Point  — ค่า point เล็กที่สุด (0.0001 สำหรับ 4 digits)
// Digits — จำนวนทศนิยมของราคา
// Bars   — จำนวน bars บน chart
// Volume — volume ของ bar ปัจจุบัน

// === 5. Hello World EA ===
//+------------------------------------------------------------------+
//| Expert initialization function                                     |
//+------------------------------------------------------------------+
int OnInit()
{
    Print("EA initialized on ", Symbol(), " ", Period(), " timeframe");
    Print("Current Ask: ", Ask);
    Print("Current Bid: ", Bid);
    Print("Spread: ", MarketInfo(Symbol(), MODE_SPREAD), " points");
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                    |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    Print("EA removed. Reason: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function — called on every price tick                   |
//+------------------------------------------------------------------+
void OnTick()
{
    // แสดงราคาทุก tick
    static datetime lastBar = 0;
    datetime currentBar = iTime(Symbol(), Period(), 0);
    
    // ทำงานเฉพาะเมื่อมี bar ใหม่
    if(currentBar != lastBar)
    {
        lastBar = currentBar;
        double close1 = iClose(Symbol(), Period(), 1);
        double open1  = iOpen(Symbol(), Period(), 1);
        
        if(close1 > open1)
            Print("Previous bar: BULLISH | Close=", close1);
        else
            Print("Previous bar: BEARISH | Close=", close1);
    }
}

// === 6. Compile และ Test ===
// กด F7 ใน MetaEditor เพื่อ compile
// ลาก EA ไปวางบน chart ใน MT4
// เปิด Strategy Tester (Ctrl+R) เพื่อ backtest

สร้าง Expert Advisor (EA)

ตัวอย่าง EA สำหรับ automated trading

//+------------------------------------------------------------------+
//| Moving Average Crossover EA                                        |
//| Strategy: Buy when fast MA crosses above slow MA                   |
//|           Sell when fast MA crosses below slow MA                   |
//+------------------------------------------------------------------+
#property copyright "SiamCafe"
#property version   "1.00"
#property strict

// === Input Parameters ===
input int    FastMA_Period = 10;       // Fast MA Period
input int    SlowMA_Period = 30;       // Slow MA Period
input int    MA_Method     = MODE_EMA; // MA Method (0=SMA, 1=EMA)
input double LotSize       = 0.01;     // Lot Size
input int    StopLoss      = 50;       // Stop Loss (points)
input int    TakeProfit    = 100;      // Take Profit (points)
input int    MagicNumber   = 12345;    // Magic Number
input int    MaxSpread     = 30;       // Maximum Spread (points)
input int    Slippage      = 3;        // Maximum Slippage

// === Global Variables ===
double fastMA_current, fastMA_prev;
double slowMA_current, slowMA_prev;

//+------------------------------------------------------------------+
int OnInit()
{
    if(FastMA_Period >= SlowMA_Period)
    {
        Alert("Fast MA must be less than Slow MA!");
        return(INIT_PARAMETERS_INCORRECT);
    }
    
    Print("MA Crossover EA started: Fast=", FastMA_Period, " Slow=", SlowMA_Period);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnTick()
{
    // Check new bar
    static datetime lastBar = 0;
    if(iTime(Symbol(), Period(), 0) == lastBar) return;
    lastBar = iTime(Symbol(), Period(), 0);
    
    // Check spread
    int spread = (int)MarketInfo(Symbol(), MODE_SPREAD);
    if(spread > MaxSpread)
    {
        Print("Spread too high: ", spread);
        return;
    }
    
    // Calculate MAs
    fastMA_current = iMA(Symbol(), Period(), FastMA_Period, 0, MA_Method, PRICE_CLOSE, 1);
    fastMA_prev    = iMA(Symbol(), Period(), FastMA_Period, 0, MA_Method, PRICE_CLOSE, 2);
    slowMA_current = iMA(Symbol(), Period(), SlowMA_Period, 0, MA_Method, PRICE_CLOSE, 1);
    slowMA_prev    = iMA(Symbol(), Period(), SlowMA_Period, 0, MA_Method, PRICE_CLOSE, 2);
    
    // Check for crossover signals
    bool buySignal  = (fastMA_prev <= slowMA_prev) && (fastMA_current > slowMA_current);
    bool sellSignal = (fastMA_prev >= slowMA_prev) && (fastMA_current < slowMA_current);
    
    // Count open orders
    int buyOrders = 0, sellOrders = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                if(OrderType() == OP_BUY)  buyOrders++;
                if(OrderType() == OP_SELL) sellOrders++;
            }
        }
    }
    
    // Execute trades
    if(buySignal && buyOrders == 0)
    {
        // Close sell orders first
        CloseOrders(OP_SELL);
        
        double sl = Ask - StopLoss * Point;
        double tp = Ask + TakeProfit * Point;
        
        int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage,
                               sl, tp, "MA Cross Buy", MagicNumber, 0, clrGreen);
        
        if(ticket > 0)
            Print("BUY order opened: #", ticket, " at ", Ask);
        else
            Print("BUY error: ", GetLastError());
    }
    
    if(sellSignal && sellOrders == 0)
    {
        CloseOrders(OP_BUY);
        
        double sl = Bid + StopLoss * Point;
        double tp = Bid - TakeProfit * Point;
        
        int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage,
                               sl, tp, "MA Cross Sell", MagicNumber, 0, clrRed);
        
        if(ticket > 0)
            Print("SELL order opened: #", ticket, " at ", Bid);
        else
            Print("SELL error: ", GetLastError());
    }
}

//+------------------------------------------------------------------+
void CloseOrders(int orderType)
{
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber
               && OrderType() == orderType)
            {
                double price = (orderType == OP_BUY) ? Bid : Ask;
                bool closed = OrderClose(OrderTicket(), OrderLots(), price, Slippage, clrYellow);
                
                if(!closed)
                    Print("Close error: ", GetLastError());
            }
        }
    }
}

void OnDeinit(const int reason)
{
    Print("EA removed. Open P/L: ", AccountProfit());
}

Custom Indicators และ Scripts

สร้าง custom indicator

//+------------------------------------------------------------------+
//| Custom RSI Divergence Indicator                                    |
//+------------------------------------------------------------------+
#property copyright "SiamCafe"
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 3
#property indicator_color1 DodgerBlue
#property indicator_color2 Red
#property indicator_color3 Lime
#property indicator_width1 2
#property strict

input int RSI_Period = 14;
input int Overbought = 70;
input int Oversold = 30;

double RSIBuffer[];
double OverboughtBuffer[];
double OversoldBuffer[];

int OnInit()
{
    SetIndexBuffer(0, RSIBuffer);
    SetIndexBuffer(1, OverboughtBuffer);
    SetIndexBuffer(2, OversoldBuffer);
    
    SetIndexStyle(0, DRAW_LINE);
    SetIndexStyle(1, DRAW_LINE, STYLE_DOT);
    SetIndexStyle(2, DRAW_LINE, STYLE_DOT);
    
    SetIndexLabel(0, "RSI(" + IntegerToString(RSI_Period) + ")");
    SetIndexLabel(1, "Overbought");
    SetIndexLabel(2, "Oversold");
    
    IndicatorShortName("Custom RSI(" + IntegerToString(RSI_Period) + ")");
    
    return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    int start = MathMax(RSI_Period + 1, prev_calculated - 1);
    
    for(int i = start; i < rates_total; i++)
    {
        RSIBuffer[i] = iRSI(Symbol(), Period(), RSI_Period, PRICE_CLOSE,
                             rates_total - 1 - i);
        OverboughtBuffer[i] = Overbought;
        OversoldBuffer[i] = Oversold;
    }
    
    return(rates_total);
}

//+------------------------------------------------------------------+
//| Utility Script: Account Summary                                    |
//+------------------------------------------------------------------+
// Save as Scripts/AccountSummary.mq4

// void OnStart()
// {
//     double balance    = AccountBalance();
//     double equity     = AccountEquity();
//     double margin     = AccountMargin();
//     double freeMargin = AccountFreeMargin();
//     double profit     = AccountProfit();
//     int    leverage   = AccountLeverage();
//     
//     string report = StringFormat(
//         "=== Account Summary ===\n"
//         "Balance:     %.2f %s\n"
//         "Equity:      %.2f\n"
//         "Margin:      %.2f\n"
//         "Free Margin: %.2f\n"
//         "Profit:      %.2f\n"
//         "Leverage:    1:%d\n"
//         "Open Orders: %d\n"
//         "Server:      %s\n",
//         balance, AccountCurrency(),
//         equity, margin, freeMargin, profit,
//         leverage, OrdersTotal(),
//         AccountServer()
//     );
//     
//     // Count orders by type
//     int buys = 0, sells = 0;
//     double buyProfit = 0, sellProfit = 0;
//     
//     for(int i = 0; i < OrdersTotal(); i++)
//     {
//         if(OrderSelect(i, SELECT_BY_POS))
//         {
//             if(OrderType() == OP_BUY)
//             {
//                 buys++;
//                 buyProfit += OrderProfit() + OrderSwap() + OrderCommission();
//             }
//             else if(OrderType() == OP_SELL)
//             {
//                 sells++;
//                 sellProfit += OrderProfit() + OrderSwap() + OrderCommission();
//             }
//         }
//     }
//     
//     report += StringFormat(
//         "\nBuy Orders:  %d (P/L: %.2f)\n"
//         "Sell Orders: %d (P/L: %.2f)\n",
//         buys, buyProfit, sells, sellProfit
//     );
//     
//     Alert(report);
//     
//     // Save to file
//     int handle = FileOpen("AccountReport.txt", FILE_WRITE|FILE_TXT);
//     if(handle != INVALID_HANDLE)
//     {
//         FileWriteString(handle, report);
//         FileClose(handle);
//         Print("Report saved to AccountReport.txt");
//     }
// }

Backtesting และ Optimization

ทดสอบและ optimize EA

//+------------------------------------------------------------------+
//| Backtesting Guide for MQL4                                         |
//+------------------------------------------------------------------+

// === 1. Strategy Tester Setup ===
// เปิด Strategy Tester: Ctrl+R ใน MT4
// Settings:
// - Expert Advisor: เลือก EA ที่ต้องการ test
// - Symbol: เลือกคู่เงิน (เช่น EURUSD)
// - Period: เลือก timeframe (เช่น H1)
// - Model: "Every tick" (แม่นที่สุด)
// - Date: เลือกช่วงเวลา test (เช่น 1 ปี)
// - Spread: Current หรือ fixed

// === 2. EA with Reporting ===
//+------------------------------------------------------------------+
// #property strict
// 
// // Performance tracking
// int totalTrades = 0;
// int winTrades = 0;
// int loseTrades = 0;
// double totalProfit = 0;
// double totalLoss = 0;
// double maxDrawdown = 0;
// double peakEquity = 0;
// 
// void TrackPerformance()
// {
//     double equity = AccountEquity();
//     
//     if(equity > peakEquity)
//         peakEquity = equity;
//     
//     double drawdown = (peakEquity - equity) / peakEquity * 100;
//     if(drawdown > maxDrawdown)
//         maxDrawdown = drawdown;
// }
// 
// void OnTrade()
// {
//     // Called when order status changes
//     for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
//     {
//         if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
//         {
//             if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
//             {
//                 double profit = OrderProfit() + OrderSwap() + OrderCommission();
//                 
//                 if(profit > 0)
//                 {
//                     winTrades++;
//                     totalProfit += profit;
//                 }
//                 else
//                 {
//                     loseTrades++;
//                     totalLoss += MathAbs(profit);
//                 }
//                 totalTrades++;
//             }
//         }
//     }
// }
// 
// void PrintReport()
// {
//     double winRate = (totalTrades > 0) ?
//         (double)winTrades / totalTrades * 100 : 0;
//     double profitFactor = (totalLoss > 0) ?
//         totalProfit / totalLoss : 0;
//     double avgWin = (winTrades > 0) ?
//         totalProfit / winTrades : 0;
//     double avgLoss = (loseTrades > 0) ?
//         totalLoss / loseTrades : 0;
//     
//     Print("=== Performance Report ===");
//     Print("Total Trades: ", totalTrades);
//     Print("Win Rate: ", DoubleToStr(winRate, 1), "%");
//     Print("Profit Factor: ", DoubleToStr(profitFactor, 2));
//     Print("Avg Win: ", DoubleToStr(avgWin, 2));
//     Print("Avg Loss: ", DoubleToStr(avgLoss, 2));
//     Print("Max Drawdown: ", DoubleToStr(maxDrawdown, 1), "%");
//     Print("Net Profit: ", DoubleToStr(totalProfit - totalLoss, 2));
// }

// === 3. Optimization Tips ===
// Strategy Tester > Expert properties > Inputs tab
// เลือก parameters ที่ต้องการ optimize:
// - Start: ค่าเริ่มต้น
// - Step: ค่าที่เพิ่มแต่ละรอบ
// - Stop: ค่าสุดท้าย

// ตัวอย่าง optimize MA periods:
// FastMA: Start=5, Step=5, Stop=30
// SlowMA: Start=20, Step=10, Stop=100
// StopLoss: Start=30, Step=10, Stop=100

// Optimization criteria:
// - Balance (สูงสุด)
// - Profit Factor (> 1.5)
// - Expected Payoff (สูงสุด)
// - Maximal Drawdown (ต่ำสุด)
// - Custom (ใช้ OnTester() function)

// === 4. Walk-Forward Analysis ===
// แบ่งข้อมูลเป็น:
// - In-sample (70%): สำหรับ optimization
// - Out-of-sample (30%): สำหรับ validation
// ทำซ้ำหลาย periods เพื่อตรวจสอบ robustness

Risk Management และ Best Practices

การจัดการความเสี่ยงและแนวปฏิบัติที่ดี

//+------------------------------------------------------------------+
//| Risk Management Functions for MQL4                                 |
//+------------------------------------------------------------------+
// #property strict
// 
// input double RiskPercent = 2.0;     // Risk per trade (%)
// input double MaxDailyLoss = 5.0;    // Max daily loss (%)
// input int    MaxOpenOrders = 3;     // Max concurrent orders
// 
// //--- Calculate lot size based on risk percentage
// double CalculateLotSize(double stopLossPoints)
// {
//     double balance = AccountBalance();
//     double riskAmount = balance * RiskPercent / 100;
//     
//     double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
//     double tickSize  = MarketInfo(Symbol(), MODE_TICKSIZE);
//     double minLot    = MarketInfo(Symbol(), MODE_MINLOT);
//     double maxLot    = MarketInfo(Symbol(), MODE_MAXLOT);
//     double lotStep   = MarketInfo(Symbol(), MODE_LOTSTEP);
//     
//     if(tickValue == 0 || stopLossPoints == 0) return minLot;
//     
//     double lotSize = riskAmount / (stopLossPoints * tickValue / tickSize);
//     
//     // Round to lot step
//     lotSize = MathFloor(lotSize / lotStep) * lotStep;
//     
//     // Clamp to min/max
//     lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
//     
//     return NormalizeDouble(lotSize, 2);
// }
// 
// //--- Check if daily loss limit reached
// bool IsDailyLossExceeded()
// {
//     double dailyLoss = 0;
//     datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
//     
//     for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
//     {
//         if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
//         {
//             if(OrderCloseTime() >= today && OrderSymbol() == Symbol())
//             {
//                 dailyLoss += OrderProfit() + OrderSwap() + OrderCommission();
//             }
//         }
//     }
//     
//     // Add floating P/L
//     for(int i = OrdersTotal() - 1; i >= 0; i--)
//     {
//         if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
//         {
//             if(OrderSymbol() == Symbol())
//                 dailyLoss += OrderProfit() + OrderSwap() + OrderCommission();
//         }
//     }
//     
//     double maxLoss = AccountBalance() * MaxDailyLoss / 100;
//     
//     if(dailyLoss < -maxLoss)
//     {
//         Print("Daily loss limit reached: ", dailyLoss);
//         return true;
//     }
//     
//     return false;
// }
// 
// //--- Count open orders for this EA
// int CountOpenOrders()
// {
//     int count = 0;
//     for(int i = OrdersTotal() - 1; i >= 0; i--)
//     {
//         if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
//         {
//             if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
//                 count++;
//         }
//     }
//     return count;
// }
// 
// //--- Pre-trade checks
// bool CanTrade()
// {
//     if(!IsTradeAllowed())
//     {
//         Print("Trading not allowed");
//         return false;
//     }
//     
//     if(IsDailyLossExceeded())
//         return false;
//     
//     if(CountOpenOrders() >= MaxOpenOrders)
//     {
//         Print("Max orders reached: ", MaxOpenOrders);
//         return false;
//     }
//     
//     if(AccountFreeMargin() < 100)
//     {
//         Print("Insufficient free margin: ", AccountFreeMargin());
//         return false;
//     }
//     
//     return true;
// }

// === Best Practices Summary ===
// 1. ใช้ risk-based lot sizing เสมอ (ไม่เกิน 2% per trade)
// 2. ตั้ง Stop Loss ทุก order (ห้าม trade โดยไม่มี SL)
// 3. ใช้ Magic Number แยก orders ของแต่ละ EA
// 4. Check IsTradeAllowed() ก่อน trade
// 5. Handle errors จาก OrderSend() ทุกครั้ง
// 6. ใช้ NormalizeDouble() สำหรับราคาและ lots
// 7. Backtest อย่างน้อย 2 ปีก่อนใช้จริง
// 8. เริ่ม live ด้วย demo account ก่อน
// 9. ตั้ง daily loss limit
// 10. Log ทุก trade สำหรับ review

FAQ คำถามที่พบบ่อย

Q: MQL4 กับ MQL5 ต่างกันอย่างไร?

A: MQL5 เป็น version ใหม่กว่า syntax คล้าย C++ มากขึ้น รองรับ OOP เต็มรูปแบบ multi-threading, netting position system (MQL4 ใช้ hedging), Strategy Tester ที่เร็วกว่าและรองรับ multi-currency testing MQL4 ยังคง popular กว่าเพราะ MT4 มี brokers รองรับมากกว่า, EAs และ indicators มีให้เลือกมากกว่า, community ใหญ่กว่า สำหรับ Forex trading แนะนำเริ่มจาก MQL4 ถ้าต้องการ stocks/futures ใช้ MQL5

Q: EA ทำกำไรจริงไหม?

A: EA ที่ดีสามารถทำกำไรได้ แต่ไม่มี EA ที่ทำกำไรตลอดเวลา ปัจจัยสำคัญ strategy ต้อง robust (ทดสอบหลาย market conditions), risk management ต้องเข้มงวด (ไม่เสี่ยงเกิน 2% per trade), ต้อง adapt ตาม market changes (re-optimize periodically), execution quality ขึ้นกับ broker (spread, slippage, uptime) EA ที่ขายตาม internet ส่วนใหญ่ overfitted กับ historical data ไม่ทำกำไรจริง ควรเขียนเองและ backtest อย่างถูกวิธี

Q: ต้องรู้ programming มาก่อนไหม?

A: รู้พื้นฐาน programming จะช่วยมาก เพราะ MQL4 syntax คล้าย C ถ้าไม่เคยเขียน code เริ่มเรียน basic concepts (variables, loops, conditions, functions) ก่อน 2-4 สัปดาห์ จากนั้นเรียน MQL4 specific functions (order management, indicator functions) อีก 2-4 สัปดาห์ resources ที่ดี MQL4 Reference ใน MetaEditor (F1), MQL4 Community Forums, YouTube tutorials และ books เช่น Expert Advisor Programming for MetaTrader 4

Q: Backtesting ไว้ใจได้แค่ไหน?

A: Backtesting เป็น necessary step แต่ไม่ guarantee ผลในอนาคต ข้อจำกัด tick data quality ใน MT4 ไม่สมบูรณ์ (ใช้ Tick Data Suite สำหรับ accurate ticks), spread ใน backtest อาจไม่ตรงกับ reality (ใช้ variable spread), slippage ไม่จำลองได้สมบูรณ์, market conditions เปลี่ยน (regime change) ทำ walk-forward analysis, test หลาย time periods, หลายคู่เงิน และเริ่ม live ด้วย demo account อย่างน้อย 3 เดือนก่อนใช้เงินจริง

📖 บทความที่เกี่ยวข้อง

codebase mql5อ่านบทความ → mql4 programming courseอ่านบทความ → คู่มือการเขียนโปรแกรม mql4อ่านบทความ → mql4 volume indicatorอ่านบทความ → mql4 ordersend without stop lossอ่านบทความ →

📚 ดูบทความทั้งหมด →