Bài viết gần đây
-
-
Cách Gỡ Cài Đặt Microsoft SQL Server 2022 (64-bit) Trên Windows 11
Tháng 9 21, 2026 -
Lộ Trình Học AI Trading Cho Người Đã Biết Bot Hedging (A–Z)
Tháng 9 8, 2026
| Nhị Quái V9.2.1 Pro: 6 Tuyến Phòng Thủ Toàn Diện Cho Nhà Đầu Tư Quant
Được viết bởi Đặng Trí Thanh vào ngày 25/04/2026 lúc 17:26 | 125 lượt xem
🛡️ 6 Tuyến Phòng Thủ Của Nhị Quái V9
Hệ thống bảo vệ đa tầng giúp nhà đầu tư kiểm soát rủi ro tuyệt đối:
- Lớp 1: Giãn Step Thông Minh (Dynamic Step) – Tự động giãn khoảng cách Grid khi thị trường biến động mạnh.
- Lớp 2: Hồi phục Thông minh (Smart Recovery) – Thuật toán tối ưu Lot Size để thoát lệnh nhanh.
- Lớp 3: Chốt lời ròng (Realized Basket TP) – Quản lý lợi nhuận theo cụm giúp bảo vệ Margin.
- Lớp 4: Bộ lọc Xu hướng & Khoảng cách (EMA Filter) – Chỉ giao dịch khi đúng xu hướng EMA.
- Lớp 5: Watchdog (Hard Drawdown Cut) – Dừng giao dịch khẩn cấp khi chạm ngưỡng rủi ro.
- Lớp 6: Tấm khiên cuối cùng (Auto Lockdown/Hedge) – Tự động Hedge 1:1 bảo vệ tài khoản tuyệt đối.
💻 Mã nguồn MQL5 (V9.2.1 Pro)
Anh/Chị có thể tham khảo mã nguồn chi tiết dưới đây:
//+------------------------------------------------------------------+
//| Bot_Nhi_Quai_V9.2.1_Pro_23_04_1.mq5 |
//| Copyright 2026, NQ |
//| VERSION: 9.2.1 PRO (ULTRA STABLE + SMART RECOVERY)|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, NQ"
#property link "https://google.com"
#property version "9.22"
#property strict
#include <TradeTrade.mqh>
#include <TradePositionInfo.mqh>
#include <TradeSymbolInfo.mqh>
#include <ArraysArrayInt.mqh>
void ReportSymbolSpec() {
Print("=== KIỂM TRA THÔNG SỐ SÀN (9.21.1 - PRO SMART) ===");
Print("Symbol: ", _Symbol);
if(HistorySelect(TimeCurrent()-3600*24, TimeCurrent())) {
double total_comm = 0, total_vol = 0;
for(int i=HistoryDealsTotal()-1; i>=0; i--) {
ulong t = HistoryDealGetTicket(i);
if(HistoryDealGetString(t, DEAL_SYMBOL) == _Symbol) {
total_comm += HistoryDealGetDouble(t, DEAL_COMMISSION);
total_vol += HistoryDealGetDouble(t, DEAL_VOLUME);
}
}
if(total_vol > 0) {
Print("--- DỮ LIỆU THỰC TẾ (24H QUA) ---");
Print("Tổng Volume đã đánh: ", NormalizeDouble(total_vol, 2));
Print("Tổng Phí Hoa Hồng: ", NormalizeDouble(total_comm, 2));
Print("=> Phí trung bình: ", NormalizeDouble(MathAbs(total_comm/total_vol), 2), " USD / 1.0 Lot");
}
}
Print("======================================");
}
//--- INPUT PARAMETERS ---
input group "=== GENERAL SETTINGS ==="
input string InpBotName = "NHI QUAI V9.2.1 PRO";
input string InpStoragePrefix = "NHỊ QUÁI V9";
input string InpStartTime = "00:00";
input string InpEndTime = "23:59";
input double InpMaxSpread = 50.0;
input double InpSlippageAllow = 50.0;
input int InpMaxPositions = 500;
input double InpMaxPriceLimit = 1000000.0;
input group "=== BUY CLUSTER SETTINGS ==="
input bool InpBuyEnable = true;
input double InpBuyLot = 0.01;
input double InpBuyStep = 5.0;
input double InpBuyX = 5.0;
input double InpBuyMinEqui1 = 8000.0;
input double InpBuyMinEqui2 = 8000.0;
input double InpBuyMaxLot = 5.0;
input int InpBuyMaxOrders = 15;
input int InpBuyMagic = 2111;
input group "=== SELL CLUSTER SETTINGS ==="
input bool InpSellEnable = true;
input double InpSellLot = 0.01;
input double InpSellStep = 5.0;
input double InpSellX = 5.0;
input double InpSellMinEqui1 = 8000.0;
input double InpSellMinEqui2 = 8000.0;
input double InpSellMaxLot = 5.0;
input int InpSellMaxOrders = 15;
input int InpSellMagic = 2112;
input group "=== NHỊ QUÁI (BASKET TP) SETTINGS ==="
input double InpBuyBasketTP = 5.0;
input double InpBuyTPMult = 1.0;
input double InpBuyProfitOffset = 0.0;
input double InpSellBasketTP = 5.0;
input double InpSellTPMult = 1.0;
input double InpSellProfitOffset = 0.0;
input double InpBasketSafety = 5.0;
input group "=== WATCHDOG SETTINGS ==="
input double InpMinProfit = 5.0;
input double InHarvestLockNet = -500.0;
input double InpMaxDrawdownCut = 0.0;
input int InpCooldownMinutes= 240;
input bool InpShowMarkers = true;
input group "=== CAU HINH HEDGE (GIA TANG) ==="
input bool InpDynamicStep = true;
input double InpMinStepDistance = 5.0;
input group "=== DCA DIRECTIONAL SETTINGS ==="
input bool InpDCAEnable = true;
input double InpDCATarget = 5.0;
input double InpLotMultiplier = 1.5;
input group "=== TREND PREDICTION (SMART FILTER) ==="
input bool InpUseTrendFilter = false;
input int InpMAPeriod = 200;
input ENUM_TIMEFRAMES InpMATF = PERIOD_H1;
input int InpMAMethod = MODE_EMA;
input int InpMAAppliedPrice = PRICE_CLOSE;
input double InpMADistanceLimit = 15.0;
input group "=== SMART RECOVERY (NEW) ==="
input bool InpEnableSmartRec = true; // Kich hoat phuc hoi thong minh
input double InpMaxRecMult = 3.0; // Toi da gap 3 lan Lot DCA
input double InpTargetRetrace = 2.0; // Khoang hoi mong muon (Gia)
input int InpSmartRecStart = 3; // Bat dau tu lenh thu may (Default: 3)
//--- GLOBAL VARIABLES ---
CTrade m_trade_buy, m_trade_sell;
CSymbolInfo m_symbol;
CPositionInfo m_position;
int m_ma_handle = INVALID_HANDLE;
double m_buy_p0 = 0, m_sell_p0 = 0;
int m_buy_reset_count = 0, m_sell_reset_count = 0;
int m_buy_tp_count_x = 0, m_sell_tp_count_x = 0;
bool m_buy_hedged = false, m_sell_hedged = false;
int m_buy_surplus_count = 0, m_sell_surplus_count = 0;
double m_buy_surplus_lot = 0, m_sell_surplus_lot = 0;
double m_buy_realized_profit = 0, m_sell_realized_profit = 0;
ulong m_pos_buy_tickets[], m_pos_sell_tickets[];
int m_pos_buy_steps[], m_pos_sell_steps[];
ENUM_POSITION_TYPE m_pos_buy_types[], m_pos_sell_types[];
bool m_need_history_scan_buy = true;
bool m_need_history_scan_sell = true;
datetime m_last_trade_tick[3000];
int m_last_trade_step[3000];
//--- HELPERS ---
bool IsClusterLocked(int m) { return GlobalVariableCheck(GetGVPrefix(m) + "_Locked"); }
void SetClusterLocked(int m, bool l) { string gv = GetGVPrefix(m) + "_Locked"; if(l) GlobalVariableSet(gv, 1.0); else GlobalVariableDel(gv); }
string GetGVPrefix(int magic) { return InpStoragePrefix + "_" + IntegerToString((int)AccountInfoInteger(ACCOUNT_LOGIN)) + "_" + IntegerToString(magic); }
int OnInit()
{
if(!m_symbol.Name(_Symbol)) return(INIT_FAILED);
m_trade_buy.SetExpertMagicNumber(InpBuyMagic);
m_trade_buy.SetDeviationInPoints((ulong)InpSlippageAllow);
m_trade_sell.SetExpertMagicNumber(InpSellMagic);
m_trade_sell.SetDeviationInPoints((ulong)InpSlippageAllow);
m_symbol.RefreshRates();
double current_bid = m_symbol.Bid();
string gv_buy_p0 = GetGVPrefix(InpBuyMagic) + "_P0";
string gv_sell_p0 = GetGVPrefix(InpSellMagic) + "_P0";
if(GlobalVariableCheck(gv_buy_p0)) { m_buy_p0 = GlobalVariableGet(gv_buy_p0); if(m_buy_p0 < 100 || m_buy_p0 > InpMaxPriceLimit) { m_buy_p0 = current_bid; GlobalVariableSet(gv_buy_p0, m_buy_p0); } }
else { m_buy_p0 = current_bid; GlobalVariableSet(gv_buy_p0, m_buy_p0); }
if(GlobalVariableCheck(gv_sell_p0)) { m_sell_p0 = GlobalVariableGet(gv_sell_p0); if(m_sell_p0 < 100 || m_sell_p0 > InpMaxPriceLimit) { m_sell_p0 = current_bid; GlobalVariableSet(gv_sell_p0, m_sell_p0); } }
else { m_sell_p0 = current_bid; GlobalVariableSet(gv_sell_p0, m_sell_p0); }
string gv_start_buy = GetGVPrefix(InpBuyMagic) + "_StartTime";
string gv_start_sell = GetGVPrefix(InpSellMagic) + "_StartTime";
if(!GlobalVariableCheck(gv_start_buy)) GlobalVariableSet(gv_start_buy, (double)TimeCurrent());
if(!GlobalVariableCheck(gv_start_sell)) GlobalVariableSet(gv_start_sell, (double)TimeCurrent());
if(GlobalVariableCheck(GetGVPrefix(InpBuyMagic)+"_ResetCount")) m_buy_reset_count = (int)GlobalVariableGet(GetGVPrefix(InpBuyMagic)+"_ResetCount");
if(GlobalVariableCheck(GetGVPrefix(InpSellMagic)+"_ResetCount")) m_sell_reset_count = (int)GlobalVariableGet(GetGVPrefix(InpSellMagic)+"_ResetCount");
if(GlobalVariableCheck(GetGVPrefix(InpBuyMagic)+"_HitXCount")) m_buy_tp_count_x = (int)GlobalVariableGet(GetGVPrefix(InpBuyMagic)+"_HitXCount");
if(GlobalVariableCheck(GetGVPrefix(InpSellMagic)+"_HitXCount")) m_sell_tp_count_x = (int)GlobalVariableGet(GetGVPrefix(InpSellMagic)+"_HitXCount");
for(int i=0; i<3000; i++) { m_last_trade_tick[i] = 0; m_last_trade_step[i] = -999999; }
if(InpUseTrendFilter) {
m_ma_handle = iMA(_Symbol, InpMATF, InpMAPeriod, 0, (ENUM_MA_METHOD)InpMAMethod, (ENUM_APPLIED_PRICE)InpMAAppliedPrice);
}
EventSetTimer(1);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { EventKillTimer(); if(m_ma_handle != INVALID_HANDLE) IndicatorRelease(m_ma_handle); }
void OnTick()
{
if(!IsTradingTime()) return;
if(!m_symbol.RefreshRates() || m_symbol.Spread()*m_symbol.Point() > InpMaxSpread) return;
string pos_sym = _Symbol;
string gv_cooldown = InpStoragePrefix + "_" + IntegerToString((int)AccountInfoInteger(ACCOUNT_LOGIN)) + "_CooldownUntil";
if(GlobalVariableCheck(gv_cooldown)) {
datetime cooldown_until = (datetime)GlobalVariableGet(gv_cooldown);
if(TimeCurrent() < cooldown_until) return;
else GlobalVariableDel(gv_cooldown);
}
CacheAllPositions();
if(InpBuyEnable) ProcessCluster(InpBuyMagic);
if(InpSellEnable) ProcessCluster(InpSellMagic);
}
void OnTimer() { if(TimeCurrent() % 5 == 0) DrawDashboard(); }
void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if(trans.type == TRADE_TRANSACTION_DEAL_ADD) { m_need_history_scan_buy = true; m_need_history_scan_sell = true; } }
void ProcessCluster(int magic)
{
CheckClusterBasketTP(magic);
double p0 = (magic == InpBuyMagic) ? m_buy_p0 : m_sell_p0;
double base_step = (magic == InpBuyMagic) ? InpBuyStep : InpSellStep;
double step_s = base_step;
double pr_ref = m_symbol.Bid();
int b_total = 0, s_total = 0; CountTotalOrders(magic, b_total, s_total);
int cur_s = (int)MathRound((pr_ref - p0) / base_step);
double equity_live = AccountInfoDouble(ACCOUNT_EQUITY);
double min_eq1 = (magic == InpBuyMagic) ? InpBuyMinEqui1 : InpSellMinEqui1;
double min_eq2 = (magic == InpBuyMagic) ? InpBuyMinEqui2 : InpSellMinEqui2;
if(InpDynamicStep) {
int g_orders = (magic == InpBuyMagic) ? b_total : s_total;
if(g_orders >= 3 && g_orders < 6) step_s *= 2.0; // Bat dau tu lenh 4 (khi dang co 3 lenh)
else if(g_orders >= 6) step_s *= 3.0; // Bat dau tu lenh 7 (khi dang co 6 lenh)
}
if(MathAbs(cur_s) > 20) {
string prefix = GetGVPrefix(magic); double new_p0 = pr_ref;
GlobalVariableSet(prefix + "_P0", new_p0);
if(magic == InpBuyMagic) m_buy_p0 = new_p0; else m_sell_p0 = new_p0;
cur_s = 0; p0 = new_p0;
}
UpdateTraversedRange(magic, cur_s);
bool trigger_hedge = (min_eq2 > 0 && equity_live <= min_eq2);
if(trigger_hedge) {
CheckDynamicHedge(magic);
if(!IsClusterLocked(magic)) { SetClusterLocked(magic, true); Print("!!! LOCKDOWN !!! ", magic); }
if(magic == InpBuyMagic) m_buy_hedged = true; else m_sell_hedged = true;
return;
}
if(IsClusterLocked(magic)) {
double bv=0, sv=0; CalculateVolume(magic, bv, sv); double delta = MathAbs(NormalizeDouble(bv - sv, 2));
if(delta > 0.01 || (bv == 0 && sv == 0)) { SetClusterLocked(magic, false); if(magic == InpBuyMagic) m_buy_hedged = false; else m_sell_hedged = false; }
else { if(magic == InpBuyMagic) m_buy_hedged = true; else m_sell_hedged = true; return; }
} else { if(magic == InpBuyMagic) m_buy_hedged = false; else m_sell_hedged = false; }
bool trigger_stop = (min_eq1 > 0 && equity_live <= min_eq1);
if(trigger_stop) return;
double buy_lot = NormalizeLot(((magic==InpBuyMagic)?InpBuyLot:InpSellLot) * MathPow(InpLotMultiplier, b_total));
double sell_lot = NormalizeLot(((magic==InpBuyMagic)?InpBuyLot:InpSellLot) * MathPow(InpLotMultiplier, s_total));
// --- SMART RECOVERY LOGIC (V9.2.1 Integration) ---
if(InpEnableSmartRec) {
double floating = 0;
for(int i=0; i<PositionsTotal(); i++) if(m_position.SelectByIndex(i) && m_position.Magic() == magic && m_position.Symbol() == _Symbol) floating += (m_position.Profit() + m_position.Swap() + m_position.Commission());
if(floating < 0) {
double target_p = ((magic == InpBuyMagic ? InpBuyBasketTP : InpSellBasketTP) * (magic == InpBuyMagic ? InpBuyTPMult : InpSellTPMult)) + InpBasketSafety;
double req_p = target_p - floating;
double point_val = m_symbol.TickValue() / m_symbol.TickSize();
if(b_total >= InpSmartRecStart && b_total > s_total) {
double opt_lot = req_p / (InpTargetRetrace * point_val);
if(opt_lot > buy_lot) buy_lot = MathMin(opt_lot, buy_lot * InpMaxRecMult);
} else if(s_total >= InpSmartRecStart && s_total > b_total) {
double opt_lot = req_p / (InpTargetRetrace * point_val);
if(opt_lot > sell_lot) sell_lot = MathMin(opt_lot, sell_lot * InpMaxRecMult);
}
buy_lot = NormalizeLot(buy_lot); sell_lot = NormalizeLot(sell_lot);
}
}
double last_entry_price = 0;
for(int i=PositionsTotal()-1; i>=0; i--) if(m_position.SelectByIndex(i) && m_position.Magic() == magic && StringCompare(m_position.Symbol(), _Symbol, false) == 0) { last_entry_price = m_position.PriceOpen(); break; }
double dist_real = (last_entry_price > 0) ? MathAbs(pr_ref - last_entry_price) : 999999;
double mandatory_dist = (InpDynamicStep && step_s > InpMinStepDistance) ? step_s : InpMinStepDistance;
if(mandatory_dist > 0 && dist_real < mandatory_dist && last_entry_price > 0) return;
CTrade *trade = (magic == InpBuyMagic) ? &m_trade_buy : &m_trade_sell;
int targets[2]; targets[0] = 0; targets[1] = cur_s;
int trend_dir = 0; bool is_panic = false;
if(InpUseTrendFilter && m_ma_handle != INVALID_HANDLE) {
double ma[]; if(CopyBuffer(m_ma_handle, 0, 0, 1, ma) > 0) {
trend_dir = (pr_ref > ma[0]) ? 1 : -1;
if(InpMADistanceLimit > 0 && MathAbs(pr_ref - ma[0]) >= InpMADistanceLimit) is_panic = true;
}
}
for(int i=0; i<2; i++) {
int s = targets[i];
if(MathAbs(s - (pr_ref - p0) / base_step) > 0.65) continue;
int b_c, s_c; CountOrdersAtStep(magic, s, b_c, s_c);
if(TimeCurrent() - m_last_trade_tick[magic % 3000] < 2) continue;
string c = InpBotName + ((magic == InpBuyMagic) ? "_B_S" : "_S_S") + IntegerToString(s);
bool allow_b = !is_panic; bool allow_s = !is_panic;
if(InpUseTrendFilter && trend_dir != 0) { if(trend_dir == -1 && b_total >= 3) allow_b = false; if(trend_dir == 1 && s_total >= 3) allow_s = false; }
if(InpBuyMaxOrders > 0 && b_total >= InpBuyMaxOrders) allow_b = false;
if(InpSellMaxOrders > 0 && s_total >= InpSellMaxOrders) allow_s = false;
if(magic == InpBuyMagic) allow_s = false; else allow_b = false;
if(IsClusterLocked(magic)) { allow_b = false; allow_s = false; }
if(allow_b && b_c < (s == 0 ? 2 : 1)) {
if(s != 0 && pr_ref < p0 && b_c < 1) { if(trade.Buy(buy_lot,_Symbol,0,0,0,c)) { m_last_trade_tick[magic%3000]=TimeCurrent(); AddCacheTicket(magic,trade.ResultOrder(),s,POSITION_TYPE_BUY); break; } }
if(s != 0 && pr_ref > p0 && b_c < 2) { if(trade.Buy(buy_lot,_Symbol,0,0,0,c)) { m_last_trade_tick[magic%3000]=TimeCurrent(); AddCacheTicket(magic,trade.ResultOrder(),s,POSITION_TYPE_BUY); break; } }
if(s == 0 && b_c < 2) { if(trade.Buy(buy_lot,_Symbol,0,0,0,c)) { m_last_trade_tick[magic%3000]=TimeCurrent(); AddCacheTicket(magic,trade.ResultOrder(),s,POSITION_TYPE_BUY); break; } }
}
if(allow_s && s_c < (s == 0 ? 2 : 1)) {
if(s != 0 && pr_ref > p0 && s_c < 1) { if(trade.Sell(sell_lot,_Symbol,0,0,0,c)) { m_last_trade_tick[magic%3000]=TimeCurrent(); AddCacheTicket(magic,trade.ResultOrder(),s,POSITION_TYPE_SELL); break; } }
if(s != 0 && pr_ref < p0 && s_c < 2) { if(trade.Sell(sell_lot,_Symbol,0,0,0,c)) { m_last_trade_tick[magic%3000]=TimeCurrent(); AddCacheTicket(magic,trade.ResultOrder(),s,POSITION_TYPE_SELL); break; } }
if(s == 0 && s_c < 2) { if(trade.Sell(sell_lot,_Symbol,0,0,0,c)) { m_last_trade_tick[magic%3000]=TimeCurrent(); AddCacheTicket(magic,trade.ResultOrder(),s,POSITION_TYPE_SELL); break; } }
}
}
}
void CheckClusterBasketTP(int magic) {
string gv_start = GetGVPrefix(magic) + "_StartTime"; datetime start_time = GlobalVariableCheck(gv_start) ? (datetime)GlobalVariableGet(gv_start) : 0;
if(magic == InpBuyMagic ? m_need_history_scan_buy : m_need_history_scan_sell) {
double realized = 0; if(HistorySelect(start_time, TimeCurrent())) {
for(int i=HistoryDealsTotal()-1; i>=0; i--) { ulong t = HistoryDealGetTicket(i); if(HistoryDealGetString(t, DEAL_SYMBOL) == _Symbol && HistoryDealGetInteger(t, DEAL_MAGIC) == magic) realized += (HistoryDealGetDouble(t, DEAL_PROFIT) + HistoryDealGetDouble(t, DEAL_SWAP) + HistoryDealGetDouble(t, DEAL_COMMISSION)); }
}
realized += (magic == InpBuyMagic ? InpBuyProfitOffset : InpSellProfitOffset);
if(magic == InpBuyMagic) { m_buy_realized_profit = realized; m_need_history_scan_buy = false; } else { m_sell_realized_profit = realized; m_need_history_scan_sell = false; }
}
double floating = 0; for(int i=PositionsTotal()-1; i>=0; i--) if(m_position.SelectByIndex(i) && m_position.Magic() == magic && StringCompare(m_position.Symbol(), _Symbol, false) == 0) floating += (m_position.Profit() + m_position.Swap() + m_position.Commission());
double total_net = (magic == InpBuyMagic ? m_buy_realized_profit : m_sell_realized_profit) + floating;
double target = ((magic == InpBuyMagic ? InpBuyBasketTP : InpSellBasketTP) * (magic == InpBuyMagic ? InpBuyTPMult : InpSellTPMult)) + InpBasketSafety;
if(total_net >= target && target > 0) ResetCluster(magic);
}
void CheckDynamicHedge(int m) {
double bv=0, sv=0; CalculateVolume(m, bv, sv); double delta = NormalizeDouble(bv - sv, 2);
CTrade *tr = (m == InpBuyMagic) ? &m_trade_buy : &m_trade_sell;
if(MathAbs(delta) >= 0.01) {
string comment = "DYN_HEDGE_" + IntegerToString(m);
if(delta > 0) { if(tr.Sell(MathAbs(delta), _Symbol, 0, 0, 0, comment)) Print(">>> HEDGE: Added Sell ", MathAbs(delta)); }
else { if(tr.Buy(MathAbs(delta), _Symbol, 0, 0, 0, comment)) Print(">>> HEDGE: Added Buy ", MathAbs(delta)); }
}
}
void ResetCluster(int magic) {
CTrade tr; tr.SetExpertMagicNumber(magic); for(int i=PositionsTotal()-1; i>=0; i--) if(m_position.SelectByIndex(i) && m_position.Magic() == magic && StringCompare(m_position.Symbol(), _Symbol, false) == 0) tr.PositionClose(m_position.Ticket());
string pr = GetGVPrefix(magic); GlobalVariableSet(pr + "_StartTime", (double)TimeCurrent() + 1);
GlobalVariableDel(pr + "_P0"); GlobalVariableDel(pr + "_LastX");
double new_p0 = m_symbol.Bid(); GlobalVariableSet(pr + "_P0", new_p0);
if(magic == InpBuyMagic) { m_buy_p0 = new_p0; m_buy_reset_count++; } else { m_sell_p0 = new_p0; m_sell_reset_count++; }
m_need_history_scan_buy = true; m_need_history_scan_sell = true;
}
void CheckXProfit(int magic, double p0, double x_sz, double pr) { TP_Logic(magic); int cur_x = (int)MathFloor(MathAbs(pr - p0) / x_sz); string gv = GetGVPrefix(magic) + "_LastX"; int last_x = GlobalVariableCheck(gv) ? (int)GlobalVariableGet(gv) : -1; if(cur_x != last_x) { GlobalVariableSet(gv, (double)cur_x); m_need_history_scan_buy = true; m_need_history_scan_sell = true; } }
void TP_Logic(int magic) {
int min_s=0, max_s=0; GetTraversedRange(magic, min_s, max_s);
for(int s = min_s; s <= max_s; s++) {
int b_cnt=0, s_cnt=0; CountOrdersAtStep(magic, s, b_cnt, s_cnt);
if(b_cnt >= 2) HarvestSurplusAtStep(magic, s, POSITION_TYPE_BUY, b_cnt - 1);
if(s_cnt >= 2) HarvestSurplusAtStep(magic, s, POSITION_TYPE_SELL, s_cnt - 1);
}
}
void HarvestSurplusAtStep(int magic, int s_idx, ENUM_POSITION_TYPE t, int count) {
int closed = 0; CTrade tr; tr.SetExpertMagicNumber(magic);
for(int i=PositionsTotal()-1; i>=0; i--) {
if(m_position.SelectByIndex(i) && StringCompare(m_position.Symbol(), _Symbol, false) == 0 && (m_position.Magic() == InpBuyMagic || m_position.Magic() == InpSellMagic)) {
if(GetStepFromComment(m_position.Comment()) == s_idx && m_position.PositionType() == t) {
double net = m_position.Profit() + m_position.Swap() + m_position.Commission();
if(net >= InpMinProfit) { if(tr.PositionClose(m_position.Ticket())) closed++; }
}
}
}
}
void CacheAllPositions() {
ArrayResize(m_pos_buy_tickets, 0); ArrayResize(m_pos_sell_tickets, 0);
for(int i=0; i<PositionsTotal(); i++) if(m_position.SelectByIndex(i) && StringCompare(m_position.Symbol(), _Symbol, false) == 0) {
int m = (int)m_position.Magic();
if(m == InpBuyMagic) { int n=ArraySize(m_pos_buy_tickets); ArrayResize(m_pos_buy_tickets,n+1); ArrayResize(m_pos_buy_steps,n+1); ArrayResize(m_pos_buy_types,n+1); m_pos_buy_tickets[n]=m_position.Ticket(); m_pos_buy_steps[n]=GetStepFromComment(m_position.Comment()); m_pos_buy_types[n]=m_position.PositionType(); }
else if(m == InpSellMagic) { int n=ArraySize(m_pos_sell_tickets); ArrayResize(m_pos_sell_tickets,n+1); ArrayResize(m_pos_sell_steps,n+1); ArrayResize(m_pos_sell_types,n+1); m_pos_sell_tickets[n]=m_position.Ticket(); m_pos_sell_steps[n]=GetStepFromComment(m_position.Comment()); m_pos_sell_types[n]=m_position.PositionType(); }
}
}
void CountOrdersAtStep(int magic, int s_idx, int &b, int &s) {
b = 0; s = 0; int lim = (magic==InpBuyMagic)?ArraySize(m_pos_buy_tickets):ArraySize(m_pos_sell_tickets);
for(int i=0; i<lim; i++) if((magic==InpBuyMagic?m_pos_buy_steps[i]:m_pos_sell_steps[i]) == s_idx) { if((magic==InpBuyMagic?m_pos_buy_types[i]:m_pos_sell_types[i]) == POSITION_TYPE_BUY) b++; else s++; }
}
int GetStepFromComment(string c) { int cur=0, last=-1; while((cur=StringFind(c,"_S",cur))!=-1){ last=cur; cur+=1; } if(last!=-1){ return (int)StringToInteger(StringSubstr(c,last+2)); } return 0; }
void SetLastStep(int m, int s) { GlobalVariableSet(GetGVPrefix(m)+"_LastStep", (double)s); }
void GetTraversedRange(int m, int &mi, int &ma) { string k_mi=GetGVPrefix(m)+"_MinStep", k_ma=GetGVPrefix(m)+"_MaxStep"; mi=GlobalVariableCheck(k_mi)?(int)GlobalVariableGet(k_mi):0; ma=GlobalVariableCheck(k_ma)?(int)GlobalVariableGet(k_ma):0; }
void UpdateTraversedRange(int m, int s) { string mi=GetGVPrefix(m)+"_MinStep", ma=GetGVPrefix(m)+"_MaxStep"; if(!GlobalVariableCheck(mi)||s<GlobalVariableGet(mi)) GlobalVariableSet(mi, (double)s); if(!GlobalVariableCheck(ma)||s>GlobalVariableGet(ma)) GlobalVariableSet(ma, (double)s); }
void CalculateVolume(int m, double &bv, double &sv) { bv=0; sv=0; for(int i=0;i<PositionsTotal();i++) if(m_position.SelectByIndex(i)&&m_position.Magic()==m){ if(m_position.PositionType()==POSITION_TYPE_BUY) bv+=m_position.Volume(); else sv+=m_position.Volume(); } }
bool IsTradingTime() { MqlDateTime dt; TimeToStruct(TimeCurrent(),dt); string t=StringFormat("%02d:%02d",dt.hour,dt.min); return (t>=InpStartTime && t<=InpEndTime); }
void AddCacheTicket(int m, ulong t, int s, ENUM_POSITION_TYPE ty) { CacheAllPositions(); }
double NormalizeLot(double vol) { double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(step <= 0) return vol; double lot = MathRound(vol / step) * step; return NormalizeDouble(lot, 2); }
void CountTotalOrders(int magic, int &b, int &s) { b = 0; s = 0; for(int i=0; i<PositionsTotal(); i++) if(m_position.SelectByIndex(i) && m_position.Magic() == magic && StringCompare(m_position.Symbol(), _Symbol, false) == 0) { if(m_position.PositionType() == POSITION_TYPE_BUY) b++; else s++; } }
void DrawDashboard() {
double b_f = 0, s_f = 0; for(int i=0; i<PositionsTotal(); i++) if(m_position.SelectByIndex(i) && StringCompare(m_position.Symbol(), _Symbol, false) == 0) { double p = m_position.Profit() + m_position.Swap() + m_position.Commission(); if(m_position.Magic() == InpBuyMagic) b_f += p; else if(m_position.Magic() == InpSellMagic) s_f += p; }
string t = "=== NHI QUAI V9.2.1 PRO (V23.04_1) ===n";
t += StringFormat("Buy Cluster: Float %.2f | %sn", b_f, (m_buy_hedged?"HEDGED":"NORMAL"));
t += StringFormat("Sell Cluster: Float %.2f | %sn", s_f, (m_sell_hedged?"HEDGED":"NORMAL"));
Comment(t);
}
© 2026 Hướng Nghiệp Dữ Liệu | Hệ thống giao dịch tự động chuyên nghiệp.
Lập trình MQL5 và nhị quái v9 2
MQL5 là ngôn ngữ lập trình cho MetaTrader 5, dùng để xây dựng EA và chỉ báo. Nhị Quái V9 2 là một kỹ thuật quan trọng giúp bạn tạo công cụ giao dịch tự động đáng tin cậy.
Bài viết trình bày về nhị quái v9 2: cú pháp, cách dùng trong EA, lỗi thường gặp và cách tối ưu khi chạy trên VPS.
Cấu trúc một EA cơ bản
//+------------------------------------------------------------------+
//| Expert Advisor mẫu |
//+------------------------------------------------------------------+
input double LotSize = 0.01;
int OnInit() { return INIT_SUCCEEDED; }
void OnTick() {
// logic giao dịch tại đây
Comment("Giá hiện tại: ", SymbolInfoDouble(_Symbol, SYMBOL_BID));
}
void OnDeinit(const int reason) {}
Các khái niệm quan trọng trong nhị quái v9 2
| Khái niệm | Mô tả | Ứng dụng |
|---|---|---|
| OnTick | Sự kiện mỗi tick giá | Cập nhật logic liên tục |
| OrderSend | Gửi lệnh giao dịch | Mua/bán/sửa/đóng |
| PositionSelect | Chọn vị thế đang mở | Quản lý lệnh |
| IndicatorCreate | Tạo chỉ báo kỹ thuật | Tính SMA/RSI |
| FileWrite | Ghi dữ liệu ra file | Log & export |
Lỗi thường gặp và cách xử lý
Lỗi phổ biến là gửi lệnh không kiểm tra kết quả. Luôn kiểm tra mã lỗi từ OrderSend và xử lý các trường hợp như thiếu margin, thị trường đóng, hoặc giá thay đổi.
Mẹo tối ưu khi chạy EA trên VPS
Khi chạy 24/7, hãy tắt các tính năng đồ họa không cần thiết, dùng log có kiểm soát và bật tự khởi động lại Terminal khi cần. Điều này giúp EA hoạt động ổn định.
Câu hỏi thường gặp về nhị quái v9 2
Tôi cần kiến thức gì để học nhị quái v9 2?
Cần hiểu cú pháp C/C++ cơ bản và khái niệm giao dịch. Từ đó, bạn học cách dùng API của MetaTrader 5 qua tài liệu chính thức.
MQL5 có thể làm bot đa chiến lược không?
Có. Bạn có thể quản lý nhiều chiến lược trong một EA bằng cách tổ chức code thành các hàm và lớp riêng.
Nên test EA như thế nào?
Dùng Strategy Tester với nhiều năm dữ liệu, cả forward test trên demo trước khi chạy tài khoản thật.
Kết luận
Nhị Quái V9 2 giúp bạn xây dựng EA chuyên nghiệp và bền vững. Hãy viết code có cấu trúc, kiểm tra lỗi đầy đủ và luôn backtest kỹ trước khi triển khai thật.
Ví dụ thực tế về nhị quái v9 2
Để minh họa rõ hơn, chúng ta xét một ví dụ thực tế áp dụng nhị quái v9 2. Giả sử bạn muốn triển khai nó vào công việc hằng ngày: bắt đầu từ việc xác định mục tiêu, thu thập dữ liệu cần thiết, rồi thực hiện từng bước.
Bước đầu tiên, hãy liệt kê các tài nguyên và công cụ bạn đang có. Bước thứ hai, xây dựng một phiên bản tối giản nhất có thể chạy được. Bước thứ ba, kiểm tra kết quả trên dữ liệu nhỏ trước khi mở rộng.
# ví dụ tối giản: chạy thử từng bước
def step_1():
print('Thu thập dữ liệu')
def step_2():
print('Xử lý và phân tích')
def step_3():
print('Đánh giá kết quả')
for f in (step_1, step_2, step_3):
f()
Kết quả ban đầu có thể chưa hoàn hảo, nhưng quan trọng là bạn có một vòng lặp làm → đo → học. Mỗi vòng lặp giúp bạn hiểu sâu hơn và cải thiện chất lượng.
Điều chỉnh sau khi thử nghiệm
Sau vòng lặp đầu tiên, hãy ghi lại những gì hoạt động tốt và những gì chưa. Dựa trên đó, điều chỉnh một tham số tại một thời điểm để dễ dàng xác định nguyên nhân của sự thay đổi.
So sánh các cách tiếp cận nhị quái v9 2
Không có một cách duy nhất để áp dụng nhị quái v9 2. Tùy vào bối cảnh, bạn có thể chọn cách làm thủ công, bán tự động hoặc tự động hoàn toàn. Mỗi cách có ưu nhược điểm riêng cần cân nhắc.
| Tiêu chí | Thủ công | Bán tự động | Tự động |
|---|---|---|---|
| Tốc độ | Chậm | Trung bình | Nhanh |
| Độ chính xác | Phụ thuộc con người | Khá ổn định | Ổn định, nhất quán |
| Chi phí đầu tư | Thấp | Trung bình | Cao |
| Khả năng mở rộng | Hạn chế | Khá tốt | Rất tốt |
| Rủi ro sai sót | Cao | Trung bình | Thấp nếu đúng quy trình |
| Phù hợp khi | Bắt đầu, khối lượng nhỏ | Đang phát triển | Khối lượng lớn, dài hạn |
Lời khuyên là hãy bắt đầu với cách thủ công có hỗ trợ của công cụ, hiểu rõ quy trình, rồi mới tự động hóa từng phần. Điều này giúp bạn kiểm soát rủi ro và có nền tảng kiến thức vững chắc.
Khi nào nên nâng cấp cách tiếp cận
Bạn nên nâng cấp khi khối lượng công việc tăng đến mức thủ công không theo kịp, hoặc khi bạn đã hiểu đủ rõ quy trình để tin tưởng giao cho máy tính thực hiện. Đừng tự động hóa một quy trình mà bạn chưa hiểu.
Đặng Trí Thanh
Giám đốc Công nghệ · DNT Digital · Giảng viên HNDL Hướng Nghiệp Dữ LiệuĐặng Trí Thanh — Founder & CTO · Hướng Nghiệp Dữ Liệu - DNT Digital. Chuyên đà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.