EA Watchdog: tự viết lớp canh chừng thứ hai cho bot MT5 bằng MQL5
Hướng dẫn13/09/2026 · 17 phút đọc

EA Watchdog: tự viết lớp canh chừng thứ hai cho bot MT5 bằng MQL5

Có một câu hỏi kiểm tra rất nhanh để biết hệ thống giám sát của bạn có thật sự hoạt động hay không: "Nếu MT5 bị treo — cửa sổ vẫn sáng, dữ liệu đứng im — thì hệ thống của bạn có phát hiện ra không?"

Nếu câu trả lời là "không chắc" hoặc "chắc là không", bạn đang giám sát sai đối tượng. Phần lớn trader tự dựng cơ chế kiểm tra theo kiểu "máy còn sống không" — ping VPS, xem tiến trình terminal64.exe có tồn tại, hoặc kiểm tra RDP có kết nối được. Cả ba cách này đều trả lời đúng câu hỏi "máy còn sống không", nhưng câu hỏi bạn thật sự cần trả lời là "bot còn làm việc không". Hai câu hỏi đó khác nhau, và khoảng cách giữa chúng chính là chỗ sự cố xảy ra.

Bài này hướng dẫn tự viết EA Watchdog — lớp canh chừng thứ hai, chạy song song với EA chính, bằng MQL5. Bạn sẽ có code đầy đủ cho cả ba phần: EA chính phát tín hiệu sống, Watchdog đọc tín hiệu đó và quyết định, và phần hành động khi phát hiện bất thường. Cuối bài có bảng chọn cơ chế, năm cạm bẫy, và một quy trình kiểm thử 5 phút để chứng minh watchdog của bạn thật sự hoạt động.

Điều cần nói trước: watchdog tự viết không thay thế được giám sát từ bên ngoài VPS. Mục 10 giải thích vì sao, và đó là giới hạn kỹ thuật, không phải vấn đề cài đặt.


1. Vì sao "máy còn sống" không có nghĩa là "bot đang làm việc"

Hãy tưởng tượng ba tình huống thật, cả ba đều xảy ra thường xuyên:

Tình huống 1 — MT5 treo nhưng tiến trình vẫn sống. Terminal mất kết nối tới server dữ liệu nhưng vẫn giữ kết nối tới server giao dịch, hoặc một chỉ báo lỗi chặn luồng xử lý. Cửa sổ vẫn kéo được, chuột vẫn click được, terminal64.exe vẫn nằm trong Task Manager với CPU gần 0%. Mọi phép kiểm tra "tiến trình còn sống" đều báo xanh. Bot thì đã ngừng làm việc từ ba tiếng trước.

Tình huống 2 — EA bị gỡ khỏi chart. Chart tự tải lại sau khi mất kết nối, và EA không được gắn lại. Hoặc bạn đổi symbol của chart và EA viết cho symbol cũ không khởi tạo được. Terminal chạy bình thường, dữ liệu chảy về bình thường, chỉ có EA là không còn ở đó. Log của terminal không có lỗi nào — vì không có gì sai cả.

Tình huống 3 — AutoTrading bị tắt. Sau một lần khởi động lại, nút Algo Trading trở về trạng thái tắt. Mọi thứ trông hoàn hảo: MT5 chạy, chart có dữ liệu, EA nằm trên chart với biểu tượng đúng. Chỉ có một chi tiết nhỏ: mọi lệnh gửi đi đều bị từ chối với mã 4109, và không có gì nổi bật để bạn nhận ra.

Điểm chung của cả ba: tiến trình còn sống, nhưng công việc đã dừng. Vì thế định nghĩa đúng của "bot còn sống" không phải là "có tiến trình", mà là "có hoạt động mới trong khoảng thời gian gần đây". Trong bài này, chúng ta dùng heartbeat — nhịp tim do chính EA phát ra — để đo hoạt động đó.

2. Watchdog là gì — và không phải là gì

Watchdog là một EA thứ hai, chạy trên một chart riêng (hoặc ít nhất là một tiến trình riêng), có nhiệm vụ duy nhất: kiểm tra xem EA chính còn phát tín hiệu sống hay không, và làm gì đó khi tín hiệu ngừng.

:

  • Một lớp canh chừng độc lập về logic — nếu EA chính có bug làm ngừng OnTick, watchdog không bị ảnh hưởng.
  • Một nguồn thông tin thứ hai: watchdog có thể đọc trạng thái tài khoản, số lệnh, tick cuối và ghi lại.
  • Một cơ chế chống im lặng: mục tiêu chính của nó là báo cho bạn, không phải giao dịch.

không phải:

  • Một cách để "chữa" mọi sự cố. Watchdog phát hiện; việc xử lý có thể nằm ngoài tầm với của MQL5 (ví dụ khởi động lại terminal).
  • Một lưới an toàn tuyệt đối. Nếu VPS chết, watchdog chết cùng. Nếu terminal treo, watchdog treo cùng.
  • Một thay thế cho stop-loss phía server. Bảo vệ lệnh phải nằm ở phía sàn, không phải ở phía phần mềm của bạn.

Nói ngắn: watchdog là còi báo cháy, không phải bình chữa cháy. Nhưng một cái còi báo cháy hoạt động tốt có giá trị hơn nhiều so với một bình chữa cháy bạn không biết là đã hết bột.

3. Kiến trúc hai lớp: EA chính phát tín hiệu, Watchdog kiểm tra

Thiết kế đơn giản nhất và cũng chắc chắn nhất gồm hai phần:

Phần phát (EA chính). Mỗi 5 giây, EA chính ghi lại một "dấu thời gian sống" vào hai nơi: một biến toàn cục của terminal (Global Variable) và một file văn bản. Ghi hai nơi vì chúng bổ sung cho nhau: biến toàn cục cực nhanh và không đụng ổ đĩa, còn file thì đọc được từ bên ngoài MT5 — bằng PowerShell, bằng Agent, hoặc bằng bất kỳ tiến trình nào khác. Đây là điểm quan trọng: file heartbeat là cầu nối giữa thế giới MQL5 và thế giới bên ngoài.

Phần kiểm tra (Watchdog). Mỗi 5 giây, watchdog đọc dấu thời gian đó, tính khoảng cách tới giờ hiện tại, và nếu vượt ngưỡng (ví dụ 60 giây) thì báo động.

Có một chi tiết kỹ thuật quyết định thành bại của toàn bộ thiết kế: dùng giờ máy (`TimeLocal`) hay giờ server (`TimeCurrent`)?

  • TimeCurrent() là giờ của tick cuối cùng từ server. Khi mất kết nối, nó đứng im — nên không thể dùng nó để đo "đã bao lâu kể từ lần cuối tôi thấy hoạt động".
  • TimeLocal() là giờ hệ điều hành, luôn chạy. Dùng nó làm mốc so sánh.

Nhưng có một cạm bẫy tinh vi: nếu EA chỉ phát heartbeat trong OnTick, thì khi mất kết nối dữ liệu, OnTick không được gọi, heartbeat ngừng, và watchdog báo động. Điều đó nghe có vẻ đúng — và thật ra nó đúng, vì mất kết nối dữ liệu là sự cố thật cần biết. Nhưng nếu bạn muốn phân biệt "EA dừng" với "mất dữ liệu", hãy phát heartbeat trong OnTimer (chạy theo đồng hồ hệ thống, không phụ thuộc tick) và ghi kèm TimeCurrent() vào nội dung heartbeat. Khi đó:

  • Heartbeat ngừng hoàn toàn → EA hoặc terminal đã dừng.
  • Heartbeat vẫn chạy nhưng TimeCurrent() trong đó đứng im → EA sống, nhưng mất dữ liệu.

Sự phân biệt này quan trọng vì hai sự cố cần hai cách xử lý khác nhau.

Hai lớp canh chừng và vì sao cần lớp ngoài VPS
Hai lớp canh chừng và vì sao cần lớp ngoài VPS

4. Sáu cơ chế canh chừng — và điểm mù của từng cái

Trước khi viết code, hãy chọn đúng cơ chế. Bảng dưới đây là kết quả của việc thử từng cách và ghi lại cái gì nó không bắt được — phần quan trọng hơn.

Cơ chếPhát hiện đượcKHÔNG phát hiện được
1. Ping VPS (ICMP)VPS tắt, mất mạng hoàn toànMT5 treo, EA dừng, AutoTrading tắt
2. Tiến trình terminal64.exeMT5 bị kill, thiếu RAMMT5 treo, AutoTrading tắt, EA rớt chart
3. File heartbeat do EA ghiEA dừng, chart bị đóngMT5 mất dữ liệu mà EA vẫn ghi log
4. Global Variable (MQL5)EA dừng trong cùng terminalTerminal bị kill, chạy ở terminal khác
5. So tick cuối với giờ hiện tạiMất kết nối dữ liệu, MT5 treoĐiều kiện vào lệnh không bao giờ tới
6. Gửi lệnh thử khối lượng nhỏAutoTrading tắt, read-only, hết marginKhông nên chạy dày: tốn phí, thêm rủi ro
Sáu cơ chế canh chừng — cái nào bắt được gì
Sáu cơ chế canh chừng — cái nào bắt được gì

Cách dùng đúng là kết hợp 2–3 cơ chế bổ trợ, không chọn một cái rồi tin tuyệt đối. Tổ hợp mình khuyên dùng trong thực tế:

  • Cơ chế 3 + 5 (file heartbeat kèm tick cuối) cho góc nhìn "bot còn làm việc không".
  • Cơ chế 2 chỉ để phân biệt "terminal còn tồn tại" — hữu ích khi chẩn đoán, không dùng làm cảnh báo chính.
  • Cơ chế 6 chạy thưa, một lần mỗi ngày vào giờ có thanh khoản, để xác nhận đường vào lệnh còn thông.

Điểm cần nhấn: cơ chế 1 (ping) và 2 (tiến trình) là hai cơ chế mọi người chọn đầu tiên và cũng là hai cơ chế cho cảm giác an toàn sai. Chúng không sai — chúng chỉ trả lời một câu hỏi khác với câu hỏi bạn cần.

5. Bước 1 — EA chính phát heartbeat

Đây là phần code bạn thêm vào EA đang chạy. Không cần sửa logic giao dịch, chỉ cần thêm một hàm và gọi nó.

mql5
// ===== THÊM VÀO EA CHÍNH =====
#define HB_FILE   "findme_hb.txt"     // file trong thư mục MQL5/Files
#define HB_GVAR    "FM_HB_"           // tiền tố biến toàn cục
input string TenEa = "chinh";          // khoá để watchdog biết đang canh ai

// Gọi hàm này trong OnInit() sau khi EA khởi tạo xong
void BatDauHeartbeat()
{
   EventSetTimer(5);                  // phát nhịp mỗi 5 giây
   PhatHeartbeat();                   // phát ngay lần đầu, đừng chờ 5 giây
}

// Gọi trong OnTimer() — ĐỪNG chỉ gọi trong OnTick()
void OnTimer()
{
   PhatHeartbeat();
}

void PhatHeartbeat()
{
   datetime gioMay  = TimeLocal();              // mốc thời gian SỐNG của máy
   datetime gioTick = TimeCurrent();            // tick cuối từ server (đứng im khi mất dữ liệu)

   // (1) Biến toàn cục — nhanh, không đụng ổ đĩa, watchdog trong cùng terminal đọc được
   GlobalVariableSet(HB_GVAR + TenEa, (double)gioMay);

   // (2) File — để đọc được TỪ BÊN NGOÀI MT5 (PowerShell, Agent, dịch vụ cloud)
   int h = FileOpen(HB_FILE, FILE_WRITE | FILE_TXT | FILE_ANSI);
   if(h == INVALID_HANDLE)
   {
      PrintFormat("Không mở được file heartbeat, lỗi %d", GetLastError());
      return;                                   // KHÔNG return im lặng: đã in log
   }
   FileWriteString(h, StringFormat("%s|%d|%d|%.2f|%d",
      TenEa,
      (int)gioMay,
      (int)gioTick,
      AccountInfoDouble(ACCOUNT_EQUITY),
      PositionsTotal()));                        // số lệnh đang mở
   FileClose(h);
}

// Gọi trong OnDeinit() để dọn dẹp
void KetThucHeartbeat()
{
   EventKillTimer();
}

Vài điểm cần chú ý trong đoạn code trên:

  • `EventSetTimer(5)` chạy theo đồng hồ hệ thống, không phụ thuộc tick. Đây là lý do heartbeat vẫn phát khi thị trường đóng hoặc mất dữ liệu — và cũng là lý do watchdog phân biệt được hai loại sự cố.
  • `FileWriteString` cần quyền ghi file. MT5 có thể chặn ghi file nếu bạn bật sandbox ở mức chặt. Nếu file không xuất hiện, kiểm tra MQL5/Files và đặt lại quyền trong Tools → Options → Expert Advisors.
  • Ghi thêm `PositionsTotal()` và `equity` không phải để trang trí. Khi sự cố xảy ra, hai con số này giúp bạn biết ngay mình đang ở tình trạng nào: bot chết khi không có lệnh (thiệt hại nhẹ) hay chết khi đang mở ba lệnh (thiệt hại nặng).
  • Không gọi `PhatHeartbeat()` trong `OnTick` nếu bạn dùng nó để đo "EA còn sống". OnTick không được gọi khi thị trường đóng — bạn sẽ nhận cảnh báo giả mỗi cuối tuần.

6. Bước 2 — Watchdog đọc heartbeat và quyết định

Watchdog là một EA riêng. Gắn nó lên một chart khác với EA chính (ví dụ chart M1 của một symbol ít dùng), bật AutoTrading cho nó. Nó không giao dịch, chỉ đọc và báo.

mql5
// ===== EA WATCHDOG (file riêng, gắn trên chart riêng) =====
#property strict

input string TenEaCanh      = "chinh";   // khoá heartbeat của EA cần canh
input int    NguongGiay     = 60;        // im lặng quá bao lâu thì coi là chết
input int    GianCachBao    = 10;        // phút, chống báo động trùng lặp
input int    NguongTickGiay = 120;       // tick cuối cũ hơn bao lâu thì coi là mất dữ liệu

datetime lanBaoCuoi = 0;

int OnInit()
{
   EventSetTimer(5);
   PrintFormat("Watchdog khởi động · canh '%s' · ngưỡng %d giây", TenEaCanh, NguongGiay);
   return(INIT_SUCCEEDED);
}

void OnTimer()
{
   string khoa = "FM_HB_" + TenEaCanh;

   if(!GlobalVariableCheck(khoa))
   {
      // Chưa từng có heartbeat. Có thể EA chính chưa chạy lần nào —
      // đừng báo động ngay, nhưng phải GHI LOG để bạn biết.
      PrintFormat("Chưa thấy heartbeat '%s' — EA chính đã chạy chưa?", TenEaCanh);
      return;
   }

   datetime lanSongCuoi = (datetime)GlobalVariableGet(khoa);
   int treGiay = (int)(TimeLocal() - lanSongCuoi);

   if(treGiay > NguongGiay)
      BaoDong(treGiay);
   else
      XoaTrangThaiBaoDong();
}

void OnDeinit(const int lyDo) { EventKillTimer(); }

Phần đọc file heartbeat (thay vì biến toàn cục) có thêm một lợi ích: bạn kiểm tra được TimeCurrent() mà EA chính ghi vào, tức là phát hiện được trường hợp EA sống nhưng dữ liệu đứng — trường hợp mà biến toàn cục không thấy được.

mql5
// Đọc file heartbeat do EA chính ghi. Trả về true nếu đọc được.
bool DocFileHeartbeat(datetime &gioMay, datetime &gioTick, double &equity, int &soLenh)
{
   int h = FileOpen("findme_hb.txt", FILE_READ | FILE_TXT | FILE_ANSI);
   if(h == INVALID_HANDLE) return(false);

   string dong = FileReadString(h);
   FileClose(h);
   if(StringLen(dong) == 0) return(false);

   string phan[];
   if(StringSplit(dong, '|', phan) < 5) return(false);

   gioMay  = (datetime)StringToInteger(phan[1]);
   gioTick = (datetime)StringToInteger(phan[2]);
   equity  = StringToDouble(phan[3]);
   soLenh  = (int)StringToInteger(phan[4]);
   return(true);
}

// Kiểm tra riêng: EA sống nhưng dữ liệu đứng (mất kết nối broker)
bool MatDuLieu()
{
   datetime gm, gt; double eq; int sl;
   if(!DocFileHeartbeat(gm, gt, eq, sl)) return(false);
   return((TimeLocal() - gt) > NguongTickGiay);
}

Hai hàm này đưa bạn tới bốn trạng thái rõ ràng thay vì một trạng thái mơ hồ "có gì đó sai":

Trạng tháiHeartbeatTick cuốiNghĩa làViệc cần làm
Bình thườngMớiMớiBot đang làm việcKhông làm gì
EA/terminal dừngCũ/không cóEA rớt chart, terminal treo, hoặc bị killKiểm tra terminal, nạp lại EA
Mất dữ liệuMớiEA sống, kết nối broker có vấn đềKiểm tra mạng, server broker
Thị trường đóngMớiCuối tuần / ngoài giờKhông báo động (cần lọc theo lịch)

Dòng cuối cùng là dòng quan trọng nhất để tránh báo động giả: cuối tuần, giá không đổi, tick cuối sẽ luôn cũ — đó là trạng thái bình thường. Watchdog của bạn phải biết điều này, nếu không bạn sẽ nhận hai ngày tin nhắn rác mỗi tuần và bắt đầu bỏ qua cảnh báo. Đó là khởi đầu của mọi thảm họa im lặng.

7. Bước 3 — Hành động khi phát hiện

Có năm mức hành động, từ nhẹ tới nặng. Đừng nhảy thẳng lên mức cao nhất: mỗi mức tăng đều thêm rủi ro làm hỏng thứ đang chạy tốt.

MứcHành độngLàm từ đâuRủi ro cần biết
1Gửi thông báo (Telegram, push)Trong MQL5Không có — luôn nên bật
2Nhắc lại cách nhau, leo thang nếu kéo dàiTrong MQL5Báo nhiều quá gây nhờn cảnh báo
3Nạp lại EA lên chartTrong MQL5Nạp sai chart → chạy hai EA
4Khởi động lại MT5Phải làm từ ngoài MQL5Mất state; cần lưu trạng thái trước
5Khởi động lại VPSTừ ngoài VPSNặng tay nhất, chỉ dùng cuối cùng

Điểm kỹ thuật cần biết: MQL5 không có API để đóng hoặc khởi động lại terminal. Bạn có thể gỡ EA khỏi chart (ExpertRemove()), nạp template (ChartApplyTemplate()), hoặc tắt AutoTrading (TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) chỉ đọc) — nhưng muốn kill và mở lại terminal64.exe thì phải làm từ bên ngoài, bằng Task Scheduler, PowerShell, hoặc một Agent.

Vì thế watchdog trong MQL5 nên tập trung vào mức 1–3, và để mức 4–5 cho lớp ngoài.

Ví dụ phần báo động với chống trùng lặp và có ngữ cảnh:

mql5
void BaoDong(int treGiay)
{
   // (1) Chống báo trùng: cùng một sự cố chỉ báo lại sau GianCachBao phút
   if(TimeLocal() - lanBaoCuoi < GianCachBao * 60) return;
   lanBaoCuoi = TimeLocal();

   // (2) Ngữ cảnh: không có mấy số này thì bạn phải dậy, mở RDP và điều tra lại từ đầu
   datetime gm, gt; double eq; int sl;
   DocFileHeartbeat(gm, gt, eq, sl);

   string tin = StringFormat(
      "⚠ Bot im lặng %d giây\n"
      "Heartbeat cuối : %s\n"
      "Tick cuối      : %s\n"
      "Equity         : %.2f\n"
      "Lệnh đang mở   : %d\n"
      "Máy            : %s",
      treGiay,
      TimeToString(gm, TIME_DATE | TIME_MINUTES | TIME_SECONDS),
      TimeToString(gt, TIME_DATE | TIME_MINUTES | TIME_SECONDS),
      eq, sl, TerminalInfoString(TERMINAL_NAME));

   Print(tin);            // ghi vào Journal của MT5
   SendNotification(tin); // đẩy tới app MT5 trên điện thoại (cần MetaQuotes ID)
   GhiLogFile(tin);       // ghi file để còn tra cứu về sau
}

void GhiLogFile(const string tin)
{
   int h = FileOpen("findme_watchdog.log", FILE_READ | FILE_WRITE | FILE_TXT | FILE_ANSI);
   if(h == INVALID_HANDLE) return;
   FileSeek(h, 0, SEEK_END);
   FileWriteString(h, TimeToString(TimeLocal(), TIME_DATE | TIME_MINUTES | TIME_SECONDS) + " " + tin + "\n");
   FileClose(h);
}

void XoaTrangThaiBaoDong()
{
   // Bot sống lại: nếu trước đó có báo động, ghi lại một dòng để đóng sự cố
   if(lanBaoCuoi > 0 && TimeLocal() - lanBaoCuoi < 3600)
      PrintFormat("Bot đã phát heartbeat trở lại sau %d giây.", (int)(TimeLocal() - lanBaoCuoi));
   lanBaoCuoi = 0;
}

SendNotification là cách nhanh nhất và rẻ nhất để đưa cảnh báo lên điện thoại: không cần server, không cần bot Telegram, chỉ cần MetaQuotes ID trong Tools → Options → Notifications. Hạn chế của nó là phụ thuộc vào hạ tầng MetaQuotes và không có nút hành động — nên trong thực tế, mình dùng SendNotification cho cảnh báo nhanh và một kênh thứ hai (Telegram qua HTTP) cho cảnh báo có ngữ cảnh đầy đủ.

Vòng lặp của Watchdog — 5 bước, chạy mỗi 5 giây
Vòng lặp của Watchdog — 5 bước, chạy mỗi 5 giây

8. Năm cạm bẫy và cách tránh

Sau khi viết xong watchdog, phần khó không phải là code chạy được — mà là làm cho nó đáng tin. Năm cạm bẫy dưới đây là những cái mình gặp nhiều nhất.

Cạm bẫy 1 — Watchdog cũng chết theo máy. Nếu VPS tắt hoặc mất mạng, watchdog im lặng cùng lúc với EA chính. Bạn tưởng mình có hệ thống giám sát, thực tế bạn chỉ có một hệ thống im lặng kép. Cách tránh: luôn có một lớp ngoài VPS đọc file heartbeat định kỳ, và lớp đó là lớp chốt cuối.

Cạm bẫy 2 — Báo động giả khi thị trường đóng. Cuối tuần, không có tick, nên mọi phép so sánh "tick cuối cách đây bao lâu" đều vượt ngưỡng. Cách tránh: chỉ kiểm tra tick trong phiên giao dịch của symbol; cuối tuần nâng ngưỡng lên rất cao hoặc tắt hẳn phần kiểm tra tick (nhưng vẫn kiểm tra heartbeat, vì EA vẫn phải sống).

Cạm bẫy 3 — Đếm số lần `OnTimer` thay vì dùng mốc thời gian. Nếu bạn đếm "tôi đã thấy 12 nhịp thì coi là sống", con số đó sai ngay khi terminal bận: OnTimer bị trễ, số nhịp ít hơn, và bạn báo động cho một bot đang chạy tốt. Cách tránh: luôn so mốc thời gian tuyệt đối (TimeLocal() hiện tại trừ thời điểm heartbeat cuối), không đếm vòng lặp.

Cạm bẫy 4 — Tự khởi động lại 200 lần trong một đêm. Khi bạn thêm cơ chế tự sửa (nạp lại EA, chạy lại template), một lỗi hệ thống có thể khiến nó chạy vòng lặp cả đêm: nạp lại, lỗi, nạp lại, lỗi. Đến sáng bạn có một log dài 40.000 dòng và không biết gì hơn lúc đầu. Cách tránh: giới hạn số lần (ví dụ tối đa 3 lần mỗi giờ), và luôn thông báo mỗi lần tự xử lý — tự sửa mà im lặng là kiểu hỏng tệ nhất.

Cạm bẫy 5 — Báo động mà không có ngữ cảnh. "Bot im lặng 300 giây" là thông tin bạn không thể hành động. Bạn phải mở RDP, xem log, tự tính lại mọi thứ. Cách tránh: mỗi cảnh báo phải kèm trạng thái lúc lỗi: heartbeat cuối, tick cuối, equity, số lệnh đang mở, RAM, và 20 dòng log gần nhất nếu có thể.

Năm cạm bẫy khi tự viết Watchdog và cách tránh
Năm cạm bẫy khi tự viết Watchdog và cách tránh

9. Kiểm thử: giả lập bot chết trong 5 phút

Một watchdog chưa từng được kiểm thử là một watchdog bạn không biết có hoạt động hay không. Cách duy nhất để biết là giả lập sự cố, và làm điều đó khi bạn đang thức.

Bài thử 1 — Tắt AutoTrading (30 giây). Bật watchdog với ngưỡng thấp (ví dụ 20 giây), sau đó tắt nút Algo Trading của EA chính. Kết quả mong đợi: nếu heartbeat phát trong OnTimer, không có cảnh báo nào (đúng — vì EA vẫn sống). Nếu heartbeat phát trong OnTick, có cảnh báo sau ngưỡng. Bài thử này giúp bạn xác nhận bạn đang đo đúng thứ: nó cho thấy rõ "EA sống" và "AutoTrading bật" là hai chuyện khác nhau.

Bài thử 2 — Gỡ EA khỏi chart (1 phút). Gỡ EA chính khỏi chart theo cách bình thường. Kết quả mong đợi: sau ngưỡng, watchdog báo động với đầy đủ ngữ cảnh. Đây là bài thử quan trọng nhất, vì EA rớt chart là sự cố phổ biến nhất và cũng dễ giả lập nhất.

Bài thử 3 — Kill terminal (2 phút). Mở Task Manager trên VPS, chọn terminal64.exe và End Task. Kết quả mong đợi: nếu watchdog chạy trong cùng terminal, nó chết cùng và bạn không nhận được gì — đây là bài học trực tiếp về giới hạn của lớp trong VPS. Nếu bạn có lớp ngoài VPS đọc file heartbeat, lớp đó sẽ báo động sau 30–60 giây. So sánh hai kết quả này sẽ cho bạn thấy chính xác bạn đang được bảo vệ tới đâu.

Bài thử 4 — Ngắt mạng (2 phút). Disable card mạng của VPS trong 60 giây rồi bật lại. Kết quả mong đợi: heartbeat tiếp tục (vì OnTimer không cần mạng), tick cuối đứng lại, và nếu bạn có kiểm tra MatDuLieu() thì có cảnh báo "mất dữ liệu" — chính xác là sự cố cần biết.

Sau bốn bài thử, ghi lại thời gian từ lúc sự cố tới lúc bạn nhận được cảnh báo. Mục tiêu thực tế: dưới 60 giây cho lớp trong VPS, dưới 3 phút cho lớp ngoài (vì lớp ngoài thường kiểm tra thưa hơn để tiết kiệm tài nguyên).

Ghi kết quả kiểm thử thành bảng — mẫu dùng ngay

Đừng kiểm thử bằng trí nhớ. Hãy điền bảng sau một lần và giữ lại; mỗi khi bạn sửa watchdog, chạy lại và so sánh với lần trước.

Bài thửCách giả lậpKỳ vọngThời gian tới cảnh báoGhi chú
1. Tắt AutoTradingBấm nút Algo TradingKhông báo (heartbeat vẫn phát)Nếu có báo, heartbeat của bạn đang phát trong OnTick
2. Gỡ EA khỏi chartGỡ EA theo cách bình thườngBáo động sau ngưỡng... giâyBài thử quan trọng nhất
3. Kill terminalEnd Task terminal64.exeLớp trong: không báo. Lớp ngoài: báo... giâyChứng minh giới hạn của lớp trong VPS
4. Ngắt mạngDisable card mạng 60 giâyBáo "mất dữ liệu", không báo "EA chết"... giâyHeartbeat phải tiếp tục chạy

Vì sao phải ghi lại: watchdog là loại phần mềm mà lỗi của nó chỉ lộ ra đúng lúc bạn cần nó nhất. Nếu bạn chỉ kiểm thử một lần bằng cảm giác "thấy có tin nhắn là được rồi", bạn sẽ không biết hệ thống của mình báo đúng loại sự cố hay chỉ báo đúng loại sự cố dễ giả lập nhất. Bảng này biến việc kiểm thử từ một lần thử may rủi thành một phép đo lặp lại được.

Đo "thời gian tới cảnh báo" cho đúng

Có hai con số khác nhau và rất dễ nhầm:

  • Độ trễ phát hiện = thời điểm sự cố xảy ra → thời điểm watchdog ghi dòng "báo động" vào log. Con số này phụ thuộc vào ngưỡng của bạn và chu kỳ kiểm tra.
  • Độ trễ tới tay bạn = thời điểm báo động → thời điểm tin nhắn thực sự hiện trên điện thoại. Con số này phụ thuộc vào kênh: SendNotification thường tới trong vài giây, Telegram API thường 1–3 giây, email có thể tới vài phút.

Chỉ số thứ hai mới là thứ bạn quan tâm khi đang ngủ, và nó thường lớn hơn bạn tưởng: nếu bạn tắt thông báo điện thoại ban đêm (chế độ im lặng, Do Not Disturb), độ trễ tới tay bạn có thể là sáu tiếng — bất kể hệ thống của bạn phát hiện nhanh đến đâu. Đây là lý do mình luôn khuyên cấu hình kênh cảnh báo đi vòng qua chế độ im lặng, ví dụ: tin nhắn cuộc gọi khẩn, hoặc một ứng dụng được ép vượt chế độ im lặng. Một hệ thống phát hiện trong 30 giây nhưng thông báo bị chặn tới 7 giờ sáng thì thực chất tệ ngang một hệ thống không tồn tại — chỉ khác là bạn có log để đọc và tự trách.

10. Giới hạn cố hữu: watchdog trong cùng VPS không bao giờ đủ

Phần này quan trọng đủ để nói riêng một mục. Watchdog chạy trong cùng VPS có năm giới hạn không thể khắc phục bằng code:

Giới hạn 1 — Chết chung. VPS tắt, mất mạng, hoặc Windows khởi động lại: watchdog im lặng cùng EA chính. Không có ngoại lệ.

Giới hạn 2 — `OnTimer` bị trễ khi terminal bận. Khi terminal đang tính toán nặng (nhiều chart, nhiều EA, indicator phức tạp), OnTimer có thể trễ vài giây — thậm chí vài chục giây. Nếu ngưỡng của bạn quá sát, bạn có cảnh báo giả.

Giới hạn 3 — Watchdog tiêu tốn tài nguyên. Mỗi EA thêm vào là thêm RAM và CPU. Trên VPS 1–2 GB RAM, một watchdog "nhẹ" vẫn có thể là lý do khiến EA chính hết bộ nhớ.

Giới hạn 4 — Không thể khởi động lại terminal. Như đã nói ở mục 7, MQL5 không có API để làm việc này. Nếu sự cố của bạn là "MT5 bị kill", watchdog có thể báo nhưng không thể sửa — trừ khi bạn có một tiến trình ngoài MT5.

Giới hạn 5 — Cảnh báo phụ thuộc hạ tầng bên thứ ba. SendNotification đi qua server MetaQuotes, Telegram API đi qua internet. Nếu VPS mất mạng, không có cảnh báo nào ra khỏi VPS — kể cả cảnh báo về việc mất mạng.

Vì năm giới hạn này, kiến trúc đúng luôn có ít nhất hai tầng: watchdog trong VPS (nhanh, chi tiết, nhưng chết cùng máy) và giám sát ngoài VPS (chậm hơn, ít chi tiết hơn, nhưng là thứ duy nhất phát hiện được "VPS đã chết"). Bài 2 lớp canh chừng: Agent & EA Watchdog mô tả kiến trúc này đầy đủ hơn, bao gồm cả phần xử lý từ xa.

11. Checklist 12 điểm cho Watchdog

  1. Heartbeat phát trong OnTimer, không chỉ trong OnTick
  2. Heartbeat phát cả khi thị trường đóng
  3. Ghi heartbeat ra cả biến toàn cục file
  4. File heartbeat nằm ở nơi đọc được từ ngoài MT5
  5. Nội dung heartbeat có cả TimeLocal()TimeCurrent()
  6. Ngưỡng cảnh báo dựa trên mốc thời gian tuyệt đối, không đếm vòng lặp
  7. Có chống báo động trùng lặp (cách nhau tối thiểu N phút)
  8. Cảnh báo có ngữ cảnh: equity, số lệnh, tick cuối
  9. Có lọc cuối tuần / ngoài giờ giao dịch
  10. Có giới hạn số lần tự sửa trong một giờ
  11. Đã thử giả lập EA chết và đo được thời gian tới cảnh báo
  12. Có lớp ngoài VPS đọc heartbeat như chốt cuối

Nếu bạn chỉ làm được ba điểm, hãy làm điểm 1, 5 và 12. Điểm 1 và 5 quyết định watchdog của bạn đo đúng thứ; điểm 12 quyết định bạn có biết khi VPS chết hay không.

12. Tóm lại

Bốn ý cần nhớ:

  1. "Máy còn sống" và "bot đang làm việc" là hai câu hỏi khác nhau. Phần lớn cơ chế giám sát tự làm trả lời câu thứ nhất và bỏ qua câu thứ hai.
  2. Heartbeat là cách đo đúng, vì nó đo hoạt động chứ không đo sự tồn tại. Và heartbeat phải phát trong OnTimer, không phải OnTick.
  3. Watchdog là còi báo cháy, không phải bình chữa cháy. Nó phát hiện và thông báo; việc khởi động lại terminal hay VPS phải do một lớp ngoài làm.
  4. Một watchdog chưa kiểm thử là một watchdog bạn không biết. Bốn bài thử ở mục 9 mất 5 phút và cho bạn biết chính xác mình đang được bảo vệ tới đâu.

Nếu bạn muốn đi tiếp, hai hướng hợp lý: Bot chết lúc 3h sáng: 12 nguyên nhân thật để biết watchdog của bạn cần bắt những gì, và Giám sát VPS MT5 EA 24/7 để xem cách ghép hai lớp lại thành một hệ thống hoàn chỉnh có báo động và xử lý từ xa.

Code của bạn không cần thông minh. Nó cần trung thực — và trung thực nhất là khi nó nói "tôi không biết bot còn sống hay không".
Đọc xong rồi? Hãy thử ngay — miễn phí 7 ngày.