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.
Nó là:
- 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.
Nó 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.
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 được | KHÔNG phát hiện được |
|---|---|---|
| 1. Ping VPS (ICMP) | VPS tắt, mất mạng hoàn toàn | MT5 treo, EA dừng, AutoTrading tắt |
2. Tiến trình terminal64.exe | MT5 bị kill, thiếu RAM | MT5 treo, AutoTrading tắt, EA rớt chart |
| 3. File heartbeat do EA ghi | EA dừng, chart bị đóng | MT5 mất dữ liệu mà EA vẫn ghi log |
| 4. Global Variable (MQL5) | EA dừng trong cùng terminal | Terminal bị kill, chạy ở terminal khác |
| 5. So tick cuối với giờ hiện tại | Mấ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 margin | Không nên chạy dày: tốn phí, thêm rủi ro |
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ó.
// ===== 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/Filesvà đặt lại quyền trongTools → 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".
OnTickkhô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.
// ===== 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.
// Đọ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ái | Heartbeat | Tick cuối | Nghĩa là | Việc cần làm |
|---|---|---|---|---|
| Bình thường | Mới | Mới | Bot đang làm việc | Không làm gì |
| EA/terminal dừng | Cũ/không có | Cũ | EA rớt chart, terminal treo, hoặc bị kill | Kiểm tra terminal, nạp lại EA |
| Mất dữ liệu | Mới | Cũ | EA sống, kết nối broker có vấn đề | Kiểm tra mạng, server broker |
| Thị trường đóng | Mới | Cũ | Cuố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ức | Hành động | Làm từ đâu | Rủi ro cần biết |
|---|---|---|---|
| 1 | Gửi thông báo (Telegram, push) | Trong MQL5 | Không có — luôn nên bật |
| 2 | Nhắc lại cách nhau, leo thang nếu kéo dài | Trong MQL5 | Báo nhiều quá gây nhờn cảnh báo |
| 3 | Nạp lại EA lên chart | Trong MQL5 | Nạp sai chart → chạy hai EA |
| 4 | Khởi động lại MT5 | Phải làm từ ngoài MQL5 | Mất state; cần lưu trạng thái trước |
| 5 | Khởi động lại VPS | Từ ngoài VPS | Nặ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:
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 đủ.
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ể.
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ập | Kỳ vọng | Thời gian tới cảnh báo | Ghi chú |
|---|---|---|---|---|
| 1. Tắt AutoTrading | Bấm nút Algo Trading | Khô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 chart | Gỡ EA theo cách bình thường | Báo động sau ngưỡng | ... giây | Bài thử quan trọng nhất |
| 3. Kill terminal | End Task terminal64.exe | Lớp trong: không báo. Lớp ngoài: báo | ... giây | Chứng minh giới hạn của lớp trong VPS |
| 4. Ngắt mạng | Disable card mạng 60 giây | Báo "mất dữ liệu", không báo "EA chết" | ... giây | Heartbeat 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:
SendNotificationthườ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
- Heartbeat phát trong
OnTimer, không chỉ trongOnTick - Heartbeat phát cả khi thị trường đóng
- Ghi heartbeat ra cả biến toàn cục và file
- File heartbeat nằm ở nơi đọc được từ ngoài MT5
- Nội dung heartbeat có cả
TimeLocal()vàTimeCurrent() - 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
- Có chống báo động trùng lặp (cách nhau tối thiểu N phút)
- Cảnh báo có ngữ cảnh: equity, số lệnh, tick cuối
- Có lọc cuối tuần / ngoài giờ giao dịch
- Có giới hạn số lần tự sửa trong một giờ
- Đã thử giả lập EA chết và đo được thời gian tới cảnh báo
- 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ớ:
- "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.
- 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ảiOnTick. - 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.
- 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".