Bài viết gần đây
-
Global State & Pandas/NumPy cho Bot Hedging Python
Tháng 7 20, 2026 -
Buổi 18 IB Bot — Lab Pilot 10 Khách Silver 30 Ngày
Tháng 7 20, 2026 -
Buổi 17 IB Bot — Funnel Onboarding Trial → Active
Tháng 7 20, 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 thanhdt vào ngày 25/04/2026 lúc 17:26 | 80 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 <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Arrays\ArrayInt.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 | %s\n", b_f, (m_buy_hedged?"HEDGED":"NORMAL"));
t += StringFormat("Sell Cluster: Float %.2f | %s\n", 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.
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.
Thành ĐT
Founder & Chief Technology Officer, HNDLChuyên gia với hơn 10 năm kinh nghiệm trong phát triển hệ thống giao dịch tự động (Trading Bot), Fintech, Mobile App và phân tích dữ liệu tài chính (Quantitative Analysis). Người sáng lập và trực tiếp dẫn dắt các khóa học thực chiến tại Hướng Nghiệp Dữ Liệu.