Bài viết gần đây
-
-
Dart Cú Pháp Cơ Bản Flutter Buổi 2 | var final const Null Safety
Tháng 7 27, 2026 -
Bot Python Chậm Vì Multi-Pass positions_get | PyNhiQuaiBot
Tháng 7 27, 2026 -
Snapshot Positions 1-Pass Python | PyNhiQuaiBot Bot Hedging
Tháng 7 27, 2026 -
Python Dùng Để Làm Gì Trong Tài Chính? (CK, Fintech, Bot Trading)
Tháng 7 26, 2026
Trang chủ → Bài Viết → Bài 9: Equity Lockdown – Quản Trị Rủi Ro Cực Hạn Bằng Thuật Toán Hedging Tuyệt Đối
| Bài 9: Equity Lockdown – Quản Trị Rủi Ro Cực Hạn Bằng Thuật Toán Hedging Tuyệt Đối
Được viết bởi thanhdt vào ngày 01/06/2026 lúc 10:18 | 31 lượt xem
Giới Thiệu: Lưới An Toàn Cuối Cùng
Trong suốt series này, chúng ta đã xây dựng nhiều lớp bảo vệ: FSM chống trùng lệnh, Reopen Buffer ngăn mở lại quá sớm, Basket TP chốt cluster khi đủ lợi nhuận. Nhưng thị trường đôi khi vượt ra ngoài mọi kịch bản đã tính toán — một sự kiện black swan, một thông báo kinh tế bất ngờ, hoặc đơn giản là lưới bị kéo dài quá mức cho phép.
Equity Lockdown là thuật toán phòng thủ cuối cùng: khi equity (vốn thực tế) rơi xuống ngưỡng nguy hiểm InpMinEqui2, hệ thống đóng băng hoàn toàn tài khoản bằng cách mở một lệnh đối ứng với khối lượng bằng đúng |ΔV| (Volume Bias). Từ thời điểm đó, mọi biến động giá không còn ảnh hưởng đến tài khoản — cho đến khi trader can thiệp thủ công.
Đây không phải là giải pháp kiếm tiền. Đây là giải pháp không mất thêm tiền khi mọi thứ đã đi sai hướng nghiêm trọng.
Kiến Trúc Equity Lockdown: Hai Tầng Ngưỡng
EQUITY LOCKDOWN — HAI TẦNG NGƯỠNG BẢO VỆ
Equity
Balance $1000
InpMinEqui1
$700 (Cảnh báo)
InpMinEqui2
$500 (LOCKDOWN)
⚠️ VÙNG CẢNH BÁO — Gửi Alert, hạn chế mở lệnh mới
⛔ VÙNG LOCKDOWN — Mở lệnh hedge |ΔV|
GlobalVariableSet(“_LOCKED”, 1.0)
Tài khoản đóng băng hoàn toàn
Alert + Push Notification
CheckEquityLockdown() kích hoạt
ΔV → OpenHedgeLock() → FROZEN
Equity đóng băng
Hai Tầng Ngưỡng: Cảnh Báo Và Đóng Băng
// === CẤU HÌNH EQUITY LOCKDOWN ===
input double InpMinEqui1 = 700.0; // Ngưỡng cảnh báo (gửi Alert)
input double InpMinEqui2 = 500.0; // Ngưỡng Lockdown (mở hedge + đóng băng)
input bool InpEnableLockdown = true; // Bật/tắt tính năng
input long InpMagicNumber = 20240101;
// GlobalVariable keys
string GV_LOCKED; // "_LOCKED_{magic}"
string GV_HEDGE_TICKET; // "_HEDGE_TICKET_{magic}"
string GV_HEDGE_DELTA; // "_HEDGE_DELTA_{magic}"
string GV_LOCK_TIME; // "_LOCK_TIME_{magic}"
string GV_LOCK_EQUITY; // "_LOCK_EQUITY_{magic}" — equity tại thời điểm lock
int OnInit()
{
GV_LOCKED = "_LOCKED_" + IntegerToString((int)InpMagicNumber);
GV_HEDGE_TICKET = "_HEDGE_TICKET_" + IntegerToString((int)InpMagicNumber);
GV_HEDGE_DELTA = "_HEDGE_DELTA_" + IntegerToString((int)InpMagicNumber);
GV_LOCK_TIME = "_LOCK_TIME_" + IntegerToString((int)InpMagicNumber);
GV_LOCK_EQUITY = "_LOCK_EQUITY_" + IntegerToString((int)InpMagicNumber);
// Kiểm tra trạng thái từ lần chạy trước
if(GlobalVariableCheck(GV_LOCKED) && GlobalVariableGet(GV_LOCKED) == 1.0)
{
datetime lock_time = (datetime)GlobalVariableGet(GV_LOCK_TIME);
double lock_equity = GlobalVariableGet(GV_LOCK_EQUITY);
double delta = GlobalVariableGet(GV_HEDGE_DELTA);
Print("⚠️ EQUITY LOCKDOWN VẪN CÒN ACTIVE từ lần trước!");
Print(" Thời điểm lock: ", TimeToString(lock_time, TIME_DATE|TIME_SECONDS));
Print(" Equity tại lock: $", lock_equity);
Print(" Delta Volume: ", delta);
Alert("EQUITY LOCKDOWN đang active! Kiểm tra tài khoản ngay!");
}
// Validate tham số
if(InpMinEqui2 >= InpMinEqui1)
{
Alert("Lỗi: InpMinEqui2 phải nhỏ hơn InpMinEqui1!");
return INIT_PARAMETERS_INCORRECT;
}
return INIT_SUCCEEDED;
}
Hàm CheckEquityLockdown: Logic Đầy Đủ
//+------------------------------------------------------------------+
//| Kiểm tra và thực thi Equity Lockdown (gọi đầu tiên trong OnTick)|
//| Trả về true nếu tài khoản đang bị lock → dừng mọi hành động |
//+------------------------------------------------------------------+
bool CheckEquityLockdown()
{
if(!InpEnableLockdown) return false;
// === KIỂM TRA 1: ĐÃ LOCK CHƯA? ===
if(GlobalVariableCheck(GV_LOCKED) && GlobalVariableGet(GV_LOCKED) == 1.0)
{
// Tài khoản đang bị đóng băng — báo cáo status và return
double lock_equity = GlobalVariableGet(GV_LOCK_EQUITY);
double current_eq = AccountInfoDouble(ACCOUNT_EQUITY);
double hedge_ticket = GlobalVariableGet(GV_HEDGE_TICKET);
Comment(StringFormat(
"⛔ EQUITY LOCKDOWN ACTIVEn"
" Lock tại: $%.2fn"
" Hiện tại: $%.2fn"
" Hedge ticket: %.0fn"
" Để mở khóa: đóng lệnh hedge và gọi ReleaseHedgeLock()",
lock_equity, current_eq, hedge_ticket
));
return true; // Báo hiệu cho OnTick dừng mọi hành động
}
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
// === KIỂM TRA 2: NGƯỠNG CẢNH BÁO (MinEqui1) ===
if(equity 300) // Cảnh báo mỗi 5 phút
{
Print("⚠️ CẢNH BÁO: Equity=", equity,
" = InpMinEqui2)
{
return false; // An toàn, không cần làm gì
}
// === KÍCH HOẠT LOCKDOWN ===
Print("🚨 EQUITY LOCKDOWN TRIGGER! Equity=", equity,
" 0)
{
_SetLockdownState(hedge_ticket, delta_vol, equity);
// Gửi thông báo khẩn cấp
string msg = StringFormat(
"🚨 EQUITY LOCKDOWN! Equity=$%.2f | Delta=%.2f | Ticket=%d",
equity, delta_vol, (int)hedge_ticket
);
Alert(msg);
SendNotification(msg);
Print("✅ Lockdown thành công! Tài khoản đóng băng.");
}
else
{
// Hedge thất bại — khẩn cấp tối đa
string err_msg = StringFormat(
"🚨🚨 CRITICAL: Equity Lockdown THẤT BẠI! Equity=$%.2f",
equity
);
Print(err_msg);
Alert(err_msg);
SendNotification(err_msg);
}
return true;
}
//+------------------------------------------------------------------+
//| Lưu trạng thái lockdown vào GlobalVariables |
//+------------------------------------------------------------------+
void _SetLockdownState(ulong ticket, double delta, double equity_at_lock)
{
GlobalVariableSet(GV_LOCKED, 1.0);
GlobalVariableSet(GV_HEDGE_TICKET, (double)ticket);
GlobalVariableSet(GV_HEDGE_DELTA, delta);
GlobalVariableSet(GV_LOCK_TIME, (double)TimeCurrent());
GlobalVariableSet(GV_LOCK_EQUITY, equity_at_lock);
}
Hàm OpenHedgeLock: Mở Lệnh Đối Ứng
//+------------------------------------------------------------------+
//| Mở lệnh hedge để đóng băng tài khoản |
//| (Tái sử dụng từ Bài 3 — Volume Bias) |
//+------------------------------------------------------------------+
ulong OpenHedgeLock(double buy_vol, double sell_vol, long magic)
{
double delta = buy_vol - sell_vol;
double abs_delta = NormalizeDouble(MathAbs(delta), 2);
double vol_min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double vol_max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double vol_step= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
// Edge case: delta quá nhỏ
if(abs_delta < vol_min)
{
Print("HedgeLock: |ΔV|=", abs_delta, " vol_max)
{
Print("CẢNH BÁO: |ΔV|=", abs_delta,
" > vol_max=", vol_max, ". Giảm xuống vol_max.");
abs_delta = vol_max;
}
// Làm tròn theo vol_step của broker
abs_delta = MathRound(abs_delta / vol_step) * vol_step;
abs_delta = NormalizeDouble(abs_delta, 2);
// Hướng lệnh đối ứng
ENUM_ORDER_TYPE hedge_type = (delta > 0)
? ORDER_TYPE_SELL // Thừa Buy → bán
: ORDER_TYPE_BUY; // Thừa Sell → mua
double price = (hedge_type == ORDER_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = abs_delta;
request.type = hedge_type;
request.price = NormalizeDouble(price,
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
request.deviation = 50; // Slippage cao hơn bình thường (tình huống khẩn)
request.magic = magic;
request.comment = "EQUITY_LOCKDOWN";
bool sent = OrderSend(request, result);
if(!sent || result.retcode != TRADE_RETCODE_DONE)
{
Print("HedgeLock FAILED: retcode=", result.retcode,
" error=", GetLastError());
ResetLastError();
return 0;
}
Print("HedgeLock DONE: ticket=", result.deal,
" type=", EnumToString(hedge_type),
" vol=", abs_delta,
" price=", result.price);
return result.deal;
}
Hàm ReleaseHedgeLock: Mở Khóa Thủ Công
//+------------------------------------------------------------------+
//| Mở khóa Equity Lockdown — PHẢI được trader xác nhận thủ công |
//| Trả về true nếu mở khóa thành công |
//+------------------------------------------------------------------+
bool ReleaseHedgeLock()
{
// Kiểm tra có đang lock không
if(!GlobalVariableCheck(GV_LOCKED) ||
GlobalVariableGet(GV_LOCKED) != 1.0)
{
Print("Không có Lockdown nào để giải phóng.");
return false;
}
ulong hedge_ticket = (ulong)GlobalVariableGet(GV_HEDGE_TICKET);
// Kiểm tra lệnh hedge còn tồn tại không
if(hedge_ticket > 0 && PositionSelectByTicket(hedge_ticket))
{
double hedge_vol = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE hedge_type =
(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
Print("CẢNH BÁO: Lệnh hedge ticket=", hedge_ticket,
" vol=", hedge_vol,
" type=", EnumToString(hedge_type),
" vẫn đang mở!");
Print("Hãy đóng lệnh hedge này trước khi mở khóa.");
Print("Để đóng thủ công trên MetaTrader, tìm ticket=", hedge_ticket);
return false;
}
// Lấy thông tin để log
datetime lock_time = (datetime)GlobalVariableGet(GV_LOCK_TIME);
double lock_equity = GlobalVariableGet(GV_LOCK_EQUITY);
double lock_delta = GlobalVariableGet(GV_HEDGE_DELTA);
double unlock_equity = AccountInfoDouble(ACCOUNT_EQUITY);
Print("=== EQUITY LOCKDOWN RELEASED ===");
Print(" Lock time: ", TimeToString(lock_time, TIME_DATE|TIME_SECONDS));
Print(" Equity tại lock: $", lock_equity);
Print(" Equity hiện tại: $", unlock_equity);
Print(" Thay đổi: $", unlock_equity - lock_equity);
Print(" Delta vol khi lock: ", lock_delta);
// Xóa tất cả GlobalVariables liên quan
GlobalVariableDel(GV_LOCKED);
GlobalVariableDel(GV_HEDGE_TICKET);
GlobalVariableDel(GV_HEDGE_DELTA);
GlobalVariableDel(GV_LOCK_TIME);
GlobalVariableDel(GV_LOCK_EQUITY);
Print("✅ Lockdown đã được giải phóng. EA tiếp tục hoạt động.");
return true;
}
Tại Sao Bắt Buộc Phải Đóng Lệnh Hedge Trước?
Nếu mở khóa khi lệnh hedge vẫn còn mở, EA sẽ tự động mở thêm lệnh lưới mới — trong khi lệnh hedge đang “triệt tiêu” chúng. Kết quả là tài khoản có nhiều lệnh dư thừa, chi phí spread/commission tăng, và khó quản lý hơn nhiều. Luôn đóng lệnh hedge trước, kiểm tra lại trạng thái tài khoản, rồi mới mở khóa.
Tích Hợp Với Toàn Bộ Series: Thứ Tự Ưu Tiên Trong OnTick
void OnTick()
{
// === ƯU TIÊN 1: EQUITY LOCKDOWN (cao nhất) ===
// Nếu locked → return ngay, không làm gì khác
if(CheckEquityLockdown()) return;
// === ƯU TIÊN 2: REOPEN BUFFER ===
// Mở khóa các tầng đã đủ điều kiện
CheckReopenBuffer();
// === ƯU TIÊN 3: BASKET TP ===
// Kiểm tra và đóng cluster nếu đủ target
if(CheckBasketTP()) return; // Sau BasketTP, cluster đã reset
// === ƯU TIÊN 4: CHỐT LỜI ĐƠN LẺ ===
int total = PositionsTotal();
for(int i = total - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
string comment = PositionGetString(POSITION_COMMENT);
int step = GetStepFromComment(comment);
if(step == STEP_INVALID) continue;
// Trailing Plowing trước
bool plowed = CheckTrailingPlowing(ticket, step);
if(plowed) continue;
}
// Gặt X sau khi đã chạy plowing
CheckHarvestX();
// === ƯU TIÊN 5: MỞ LỆNH MỚI (thấp nhất) ===
// Grid Engine — chỉ mở nếu tầng chưa có lệnh VÀ chưa bị lock
// (không trình bày ở đây)
}
Monitoring Dashboard: Hiển Thị Toàn Bộ Trạng Thái
void DisplayFullStatus()
{
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double floating = GetFloatingProfit(InpMagicNumber);
double realized = GetRealizedProfit(InpMagicNumber);
bool locked = (GlobalVariableCheck(GV_LOCKED) &&
GlobalVariableGet(GV_LOCKED) == 1.0);
int locked_steps = 0;
for(int s = -InpMaxStep; s <= InpMaxStep; s++)
if(GetActiveFlag(InpMagicNumber, s) != FLAG_NONE) locked_steps++;
string status = StringFormat(
"=== EA STATUS ===n"
"Balance: $%.2f | Equity: $%.2f (%.1f%%)n"
"Floating: $%.2f | Realized: $%.2fn"
"Total Net: $%.2f / Basket Target: $%.2fn"
"Equity Lock: %sn"
"Reopen Locked Steps: %dn"
"MinEqui1: $%.0f | MinEqui2: $%.0f",
balance, equity, (equity/balance)*100,
floating, realized,
realized + floating, InpBasketTarget,
locked ? "⛔ ACTIVE" : "✅ CLEAR",
locked_steps,
InpMinEqui1, InpMinEqui2
);
Comment(status);
}
Edge Cases Đặc Biệt Của Equity Lockdown
| Tình huống | Nguyên nhân | Xử lý |
|---|---|---|
| Hedge thất bại lần đầu | Mạng chậm, requote | Tick tiếp theo sẽ thử lại (chưa set GV_LOCKED) |
| Equity dao động quanh MinEqui2 | Spread thay đổi | GV_LOCKED = 1 sau lần đầu, tránh mở hedge lần 2 |
| ΔV = 0 (đã cân bằng) | Lưới đối xứng | Vẫn set GV_LOCKED để chặn lệnh mới |
| Equity phục hồi sau khi lock | Giá đảo chiều | KHÔNG tự động mở khóa — trader phải quyết định |
| Stop Out xảy ra trước khi hedge | Equity rớt quá nhanh | Không tránh được — InpMinEqui2 cần đủ cao |
Cách Chọn InpMinEqui2 Hợp Lý
// Công thức tham khảo:
// InpMinEqui2 >= Margin_required + (max_drawdown_acceptable / 2)
// Ví dụ:
// Balance = $1000
// Margin mỗi lot EURUSD = $33 (leverage 1:30)
// Tổng margin 10 lots = $330
// InpMinEqui2 = $330 + $100 (buffer) = $430
// Nên đặt: InpMinEqui2 = $500 (làm tròn, thêm margin an toàn)
// Quy tắc: InpMinEqui2 KHÔNG bao giờ được thấp hơn mức margin call của broker
double CalcSafeMinEqui2()
{
double margin_call_level = AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_MARGIN);
double margin_call_equity = margin * (margin_call_level / 100.0);
double safe_min = margin_call_equity * 1.5; // 50% buffer
Print("Margin Call Level: ", margin_call_level, "%");
Print("Margin hiện tại: $", margin);
Print("Equity tại Margin Call: $", margin_call_equity);
Print("Khuyến nghị InpMinEqui2 >= $", safe_min);
return safe_min;
}
Tổng Kết Series: 9 Thuật Toán Hoàn Chỉnh
Chúng ta đã đi qua toàn bộ 9 thuật toán cốt lõi của hệ thống lưới MQL5 nâng cao:
| Bài | Thuật toán | Chức năng chính | Key function |
|---|---|---|---|
| 3 | Volume Bias | Tính ΔV = V_Buy – V_Sell | CalculateVolume() |
| 4 | Comment Parser | Khôi phục tầng sau restart | GetStepFromComment() |
| 5 | Reopen Buffer | Khóa tầng sau chốt lời | IsSafeToReopen() |
| 6 | FSM Chống Trùng | READY/BUSY với timestamp | GetFSMState() |
| 7 | Gặt X | Chốt từng tầng tại P±X | CheckHarvestX() |
| 8 | Trailing Plowing | 3 stage Act/Ret | CheckTrailingPlowing() |
| 9 | Reopen Buffer (2 cờ) | _X_CLOSED_ / _PLOW_CLOSED_ | CheckReopenBuffer() |
| 10 | Basket TP | Total_Net = realized + floating | CheckBasketTP() |
| 11 | Equity Lockdown | Đóng băng tài khoản khi nguy hiểm | CheckEquityLockdown() |
Mỗi thuật toán giải quyết một vấn đề cụ thể và phối hợp với các thuật toán khác qua GlobalVariables — bộ nhớ dùng chung duy nhất trong hệ thống. Đây là thiết kế loosely coupled: mỗi module có thể được bật/tắt độc lập mà không phá vỡ phần còn lại.
Sẵn Sàng Xây Dựng EA Thực Chiến Với Cả 9 Thuật Toán?
Khóa học MQL5 Nâng Cao tổng hợp toàn bộ series này thành một EA hoàn chỉnh với source code production-ready, backtesting trên nhiều năm dữ liệu, và framework deployment lên VPS. Bạn sẽ ra khỏi khóa học với một EA thực sự có thể chạy trên tài khoản thực.
Weekly Digest — Nhận Bản Tin Hàng Tuần
Nhận các bài viết phân tích kỹ thuật chuyên sâu, thuật toán giao dịch tự động (Trading Bot) và các giải pháp công nghệ mới nhất từ Hướng Nghiệp Dữ Liệu.
Đặng Trí Thanh
Giám đốc Công nghệ · DNT Digital · Giảng viên HNDLĐào tạo và triển khai thực chiến Python, MT5 và hệ thống bot auto trading / IB cho học viên và doanh nghiệp.