Bài viết gần đây
| 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 | 30 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.