SiamCafe · Blog
Martingale Code MQL4 — วิธีเขียน Expert Advisor
บทความ

Martingale Code MQL4 — วิธีเขียน Expert Advisor

เผยแพร่ 28 พฤษภาคม 2569

Martingale Strategy คืออะไร

Martingale เป็นกลยุทธ์การจัดการเงิน (Money Management) ที่มาจากทฤษฎีความน่าจะเป็น หลักการคือเพิ่ม Lot Size เป็น 2 เท่าหลังจากขาดทุนแต่ละครั้ง เมื่อชนะครั้งเดียวจะได้กำไรคืนทั้งหมดที่ขาดทุนไปบวกกำไรเท่ากับ Lot แรก

ตัวอย่าง เริ่มที่ 0.01 Lot ขาดทุน → เพิ่มเป็น 0.02 Lot ขาดทุนอีก → เพิ่มเป็น 0.04 Lot ชนะ → ได้กำไรคืนทั้งหมด กลับไปเริ่มที่ 0.01 Lot ข้อเสียคือถ้าขาดทุนติดกันหลายครั้ง Lot Size จะใหญ่มากจนอาจ Margin Call ดังนั้นต้องมี Risk Control ที่ดี

Martingale Expert Advisor — MQL4 Code

//+------------------------------------------------------------------+
//| MartingaleEA.mq4 — Martingale Expert Advisor                     |
//| พร้อม Risk Management และ Safety Features                        |
//+------------------------------------------------------------------+
#property copyright "SiamCafe Blog"
#property version   "2.0"
#property strict

//--- Input Parameters
input double   BaseLot          = 0.01;    // Lot เริ่มต้น
input double   Multiplier       = 2.0;     // ตัวคูณ Lot (Martingale)
input int      MaxLevel         = 6;       // จำนวนครั้งสูงสุดที่เพิ่ม Lot
input int      TakeProfit       = 30;      // Take Profit (pips)
input int      StopLoss         = 30;      // Stop Loss (pips)
input int      MagicNumber      = 12345;   // Magic Number
input int      MaxSpread        = 3;       // Max Spread ที่ยอมรับ (pips)
input double   MaxDrawdownPct   = 30.0;    // Max Drawdown % ก่อนหยุดเทรด
input bool     UseTimeFilter    = true;    // ใช้ Time Filter
input int      StartHour        = 8;       // เริ่มเทรด (ชั่วโมง Server)
input int      EndHour          = 20;      // หยุดเทรด (ชั่วโมง Server)
input int      SignalPeriod     = 14;      // RSI Period สำหรับ Signal

//--- Global Variables
int      currentLevel = 0;
double   currentLot;
datetime lastTradeTime;
double   initialBalance;

//+------------------------------------------------------------------+
//| Expert initialization function                                     |
//+------------------------------------------------------------------+
int OnInit()
{
   initialBalance = AccountBalance();
   currentLot = BaseLot;
   currentLevel = 0;
   
   Print("Martingale EA initialized. Balance: ", initialBalance);
   Print("Base Lot: ", BaseLot, " | Max Level: ", MaxLevel);
   Print("TP: ", TakeProfit, " | SL: ", StopLoss);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                               |
//+------------------------------------------------------------------+
void OnTick()
{
   // ตรวจสอบ Drawdown
   if(CheckDrawdown())
   {
      Comment("STOPPED: Max Drawdown reached (",
              MaxDrawdownPct, "%)");
      return;
   }
   
   // ตรวจสอบเวลาเทรด
   if(UseTimeFilter && !IsTradeTime())
      return;
   
   // ตรวจสอบ Spread
   double spread = MarketInfo(Symbol(), MODE_SPREAD);
   if(spread > MaxSpread * 10)
      return;
   
   // ตรวจสอบว่ามี Order เปิดอยู่หรือไม่
   if(CountOpenOrders() > 0)
      return;
   
   // ตรวจสอบ Order ล่าสุดว่าชนะหรือแพ้
   CheckLastOrder();
   
   // คำนวณ Signal
   int signal = GetSignal();
   
   if(signal == 1)      // Buy Signal
      OpenOrder(OP_BUY);
   else if(signal == -1) // Sell Signal
      OpenOrder(OP_SELL);
}

//+------------------------------------------------------------------+
//| คำนวณ Trading Signal ด้วย RSI                                     |
//+------------------------------------------------------------------+
int GetSignal()
{
   double rsi = iRSI(Symbol(), PERIOD_CURRENT, SignalPeriod, PRICE_CLOSE, 0);
   double rsi_prev = iRSI(Symbol(), PERIOD_CURRENT, SignalPeriod, PRICE_CLOSE, 1);
   
   // RSI Oversold → Buy
   if(rsi_prev < 30 && rsi >= 30)
      return 1;
   
   // RSI Overbought → Sell
   if(rsi_prev > 70 && rsi <= 70)
      return -1;
   
   return 0;  // No Signal
}

//+------------------------------------------------------------------+
//| เปิด Order                                                        |
//+------------------------------------------------------------------+
void OpenOrder(int orderType)
{
   double price, sl, tp;
   double point = MarketInfo(Symbol(), MODE_POINT);
   int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
   
   // ปรับ Point สำหรับ 5-digit broker
   double pip = point;
   if(digits == 3 || digits == 5)
      pip = point * 10;
   
   if(orderType == OP_BUY)
   {
      price = Ask;
      sl = NormalizeDouble(price - StopLoss * pip, digits);
      tp = NormalizeDouble(price + TakeProfit * pip, digits);
   }
   else
   {
      price = Bid;
      sl = NormalizeDouble(price + StopLoss * pip, digits);
      tp = NormalizeDouble(price - TakeProfit * pip, digits);
   }
   
   // ตรวจสอบ Lot Size
   double lotSize = CalculateLotSize();
   if(lotSize <= 0) return;
   
   int ticket = OrderSend(Symbol(), orderType, lotSize, price,
                          3, sl, tp, "Martingale EA",
                          MagicNumber, 0, clrGreen);
   
   if(ticket > 0)
   {
      Print("Order opened: ", orderType == OP_BUY ? "BUY" : "SELL",
            " | Lot: ", lotSize, " | Level: ", currentLevel,
            " | Price: ", price);
      lastTradeTime = TimeCurrent();
   }
   else
   {
      Print("Order failed: Error ", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| คำนวณ Lot Size ตาม Martingale Level                               |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
   double lot = BaseLot * MathPow(Multiplier, currentLevel);
   
   // ตรวจสอบ Max Lot
   double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
   double minLot = MarketInfo(Symbol(), MODE_MINLOT);
   double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
   
   lot = MathMin(lot, maxLot);
   lot = MathMax(lot, minLot);
   lot = NormalizeDouble(lot / lotStep, 0) * lotStep;
   
   // ตรวจสอบ Margin
   double marginRequired = MarketInfo(Symbol(), MODE_MARGINREQUIRED) * lot;
   if(marginRequired > AccountFreeMargin() * 0.8)
   {
      Print("Not enough margin. Required: ", marginRequired,
            " Free: ", AccountFreeMargin());
      return 0;
   }
   
   return lot;
}

//+------------------------------------------------------------------+
//| ตรวจสอบ Order ล่าสุด                                              |
//+------------------------------------------------------------------+
void CheckLastOrder()
{
   for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
         {
            if(OrderProfit() >= 0)
            {
               // ชนะ — Reset กลับ Level 0
               currentLevel = 0;
               Print("WIN: Reset to Level 0");
            }
            else
            {
               // แพ้ — เพิ่ม Level (ถ้าไม่เกิน Max)
               if(currentLevel < MaxLevel)
               {
                  currentLevel++;
                  Print("LOSS: Increase to Level ", currentLevel);
               }
               else
               {
                  // เกิน Max Level — Reset
                  currentLevel = 0;
                  Print("MAX LEVEL reached. Reset to Level 0");
               }
            }
            break;
         }
      }
   }
}

//+------------------------------------------------------------------+
//| นับ Open Orders                                                    |
//+------------------------------------------------------------------+
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;
}

//+------------------------------------------------------------------+
//| ตรวจสอบ Drawdown                                                   |
//+------------------------------------------------------------------+
bool CheckDrawdown()
{
   double currentBalance = AccountBalance();
   double drawdown = (initialBalance - currentBalance) / initialBalance * 100;
   return (drawdown >= MaxDrawdownPct);
}

//+------------------------------------------------------------------+
//| ตรวจสอบเวลาเทรด                                                   |
//+------------------------------------------------------------------+
bool IsTradeTime()
{
   int hour = Hour();
   return (hour >= StartHour && hour < EndHour);
}

Risk Management Table

LevelLot Sizeถ้าชนะ (30 pips)สะสมขาดทุนMargin ที่ต้องใช้ (EURUSD)
00.01$3.00$0~$11
10.02$6.00-$3.00~$22
20.04$12.00-$9.00~$44
30.08$24.00-$21.00~$88
40.16$48.00-$45.00~$176
50.32$96.00-$93.00~$352
60.64$192.00-$189.00~$704

Python Script วิเคราะห์ Martingale Risk

# martingale_analysis.py — วิเคราะห์ความเสี่ยง Martingale
import random
import statistics

def simulate_martingale(base_lot=0.01, multiplier=2.0, max_level=6,
                        tp_pips=30, sl_pips=30, win_rate=0.55,
                        initial_balance=1000, num_trades=1000,
                        pip_value=10):
    """จำลอง Martingale Strategy"""
    balance = initial_balance
    level = 0
    max_drawdown = 0
    peak_balance = balance
    trades = []
    consecutive_losses = 0
    max_consecutive = 0

    for i in range(num_trades):
        lot = base_lot * (multiplier ** level)

        # Random win/loss based on win_rate
        if random.random() < win_rate:
            profit = lot * tp_pips * pip_value
            balance += profit
            trades.append({"trade": i, "result": "win", "lot": lot,
                           "profit": profit, "balance": balance,
                           "level": level})
            level = 0  # Reset
            consecutive_losses = 0
        else:
            loss = lot * sl_pips * pip_value
            balance -= loss
            trades.append({"trade": i, "result": "loss", "lot": lot,
                           "profit": -loss, "balance": balance,
                           "level": level})
            consecutive_losses += 1
            max_consecutive = max(max_consecutive, consecutive_losses)

            if level < max_level:
                level += 1
            else:
                level = 0  # Reset at max level

        # Track drawdown
        peak_balance = max(peak_balance, balance)
        drawdown = (peak_balance - balance) / peak_balance * 100
        max_drawdown = max(max_drawdown, drawdown)

        # Margin Call check
        if balance <= 0:
            print(f"MARGIN CALL at trade {i}!")
            break

    profits = [t["profit"] for t in trades]
    wins = [t for t in trades if t["result"] == "win"]

    print(f"=== Martingale Simulation ===")
    print(f"Trades: {len(trades)}")
    print(f"Win Rate: {len(wins)/len(trades)*100:.1f}%")
    print(f"Final Balance: ")
    print(f"Net Profit: ")
    print(f"Max Drawdown: {max_drawdown:.1f}%")
    print(f"Max Consecutive Losses: {max_consecutive}")
    print(f"Avg Profit/Trade: ")

    return trades, balance

# จำลอง 10 ครั้ง
results = []
for run in range(10):
    _, final = simulate_martingale(win_rate=0.55, num_trades=500)
    results.append(final)

print(f"\n=== 10 Simulations Summary ===")
print(f"Avg Final Balance: ")
print(f"Min:  | Max: ")
print(f"Profitable: {sum(1 for r in results if r > 1000)}/10")

ข้อควรระวังและ Best Practices

  • กำหนด Max Level: จำกัดจำนวนครั้งที่เพิ่ม Lot เช่น 5-7 ครั้ง เกินนี้ Reset กลับ Lot เริ่มต้น
  • ใช้ Stop Loss เสมอ: ทุก Order ต้องมี Stop Loss ไม่ปล่อยให้ขาดทุนไม่จำกัด
  • Max Drawdown: ตั้งขีดจำกัด Drawdown เช่น 30% เมื่อถึงให้หยุดเทรดอัตโนมัติ
  • Backtest อย่างละเอียด: ทดสอบหลาย Period หลาย Symbol หลายสภาวะตลาด
  • Demo ก่อน Live: ทดสอบบน Demo Account อย่างน้อย 3 เดือนก่อนใช้เงินจริง
  • เงินทุนเพียงพอ: ต้องมีเงินทุนรองรับ Worst Case เช่น ขาดทุน Max Level ติดกัน 2 รอบ
  • ไม่เหมาะกับทุกตลาด: Martingale ทำงานดีในตลาด Ranging ไม่ดีในตลาด Trending แรง

Martingale Strategy คืออะไร

Martingale เพิ่ม Lot Size 2 เท่าหลังขาดทุน ชนะครั้งเดียวได้กำไรคืนทั้งหมด ข้อเสียคือ Lot Size ใหญ่มากเมื่อขาดทุนติดกันหลายครั้ง อาจ Margin Call ต้องมี Risk Control ที่ดี