| 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

Đặng Trí Thanh

Giám đốc Công nghệ · DNT Digital · Giảng viên HNDL Hướng Nghiệp Dữ Liệu
1.334 Bài viết
15.4k Người theo dõi
120k+ Lượt đọc

Đặ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.

Đội ngũ hỗ trợ

Đặng Trí Thanh
Đặng Trí Thanh
Giám đốc Công nghệ DNT Digital
Zalo 0934145100
Mộng Cầm
Mộng Cầm
Hỗ trợ khách hàng · Huấn luyện viên
Zalo 0927909257
Khánh Linh
Khánh Linh
Hỗ trợ khách hàng · Huấn luyện viên
Zalo 0927909582