//+------------------------------------------------------------------+ //| IchiDCA_CCBSN_PropFirm.mq5 | //| DCA Ichimoku Cloud Break - CCBSN - Prop Firm | //| Version 3.0 - Multi-Indicator + DCA + CCBSN - Partial Close + Trailing | //+------------------------------------------------------------------+ #property copyright "IchiDCA CCBSN MultiSignal v3.0" #property link "" #property version "2.00" #property strict //+------------------------------------------------------------------+ //| INCLUDES | //+------------------------------------------------------------------+ #include #include //+------------------------------------------------------------------+ //| ENUMS | //+------------------------------------------------------------------+ enum ENUM_TRADE_DIR { DIR_BOTH = 0, // Buy & Sell DIR_BUY = 1, // Chỉ Buy DIR_SELL = 2 // Chỉ Sell }; enum ENUM_INDI_MODE { INDI_ICHIMOKU = 0, // Ichimoku Cloud Break INDI_EMA_CROSS = 1, // EMA Crossover INDI_RSI = 2, // RSI OB/OS INDI_BB = 3, // Bollinger Band Bounce INDI_STOCH = 4, // Stochastic Cross INDI_CCI = 5, // CCI OB/OS INDI_MACD_CROSS = 6, // MACD Histogram Cross INDI_SUPERTREND = 7, // Supertrend INDI_MOMENTUM = 8 // Momentum }; //+------------------------------------------------------------------+ //| INPUT PARAMETERS | //+------------------------------------------------------------------+ // === Cơ bản === input int InpMagicID = 9196; // Magic Number input double InpLots = 0.01; // Lot Size input ENUM_TRADE_DIR InpTradeDir = DIR_BOTH; // Hướng trade input ENUM_INDI_MODE InpIndiMode = INDI_ICHIMOKU; // Kieu tin hieu input ENUM_TIMEFRAMES InpTFSignal = PERIOD_M5; // TF cho signal // === RSI Signal === input int InpRSIPeriod = 14; // RSI Period input ENUM_APPLIED_PRICE InpRSIPrice = PRICE_CLOSE; // RSI Applied Price input double InpRSIOB = 75.0; // RSI Overbought input double InpRSIOS = 25.0; // RSI Oversold // === BB Signal === input int InpBBPeriod = 20; // BB Period input double InpBBDeviation = 2.0; // BB Deviation // === CCI Signal === input int InpCCIPeriod = 14; // CCI Period input ENUM_APPLIED_PRICE InpCCIPrice = PRICE_CLOSE; // CCI Applied Price input double InpCCIOB = 100.0; // CCI Overbought input double InpCCIOS = -100.0; // CCI Oversold // === Stochastic Signal === input int InpStochK = 5; // Stoch %K Period input int InpStochD = 3; // Stoch %D Period input int InpStochSlowing = 3; // Stoch Slowing input double InpStochOB = 80.0; // Stoch Overbought input double InpStochOS = 20.0; // Stoch Oversold // === Momentum Signal === input int InpMomentumPeriod = 14; // Momentum Period input ENUM_APPLIED_PRICE InpMomentumPrice = PRICE_CLOSE; // Momentum Price input double InpMomentumOB = 100.45; // Momentum Overbought input double InpMomentumOS = 99.45; // Momentum Oversold // === Supertrend === input int InpSTperiod = 21; // Supertrend Period input double InpSTmultiplier = 3.0; // Supertrend Multiplier // === EMA Signal Cross === input int InpSigEMAFast = 9; // Signal EMA Fast input int InpSigEMASlow = 21; // Signal EMA Slow // === Ichimoku === input int InpIchiTenkan = 9; // Ichimoku Tenkan-sen input int InpIchiKijun = 26; // Ichimoku Kijun-sen input int InpIchiSenkou = 52; // Ichimoku Senkou Span B // === EMA Trend Filter === input bool InpUseEMAFilter = true; // Dùng EMA Filter input ENUM_TIMEFRAMES InpTFEMAFilter = PERIOD_CURRENT; // Timeframe EMA Filter input int InpEMAFast = 34; // EMA nhanh (xu hướng) input int InpEMASlow = 89; // EMA chậm (xu hướng) // === MACD Trend Filter === input bool InpUseMACDFilter = false; // Dùng MACD Filter input ENUM_TIMEFRAMES InpTFMACDFilter = PERIOD_CURRENT; // Timeframe MACD Filter input int InpFastEMAMACD = 30; // MACD Fast EMA input int InpSlowEMAMACD = 50; // MACD Slow EMA input int InpSMAMACD = 5; // MACD Signal SMA input ENUM_APPLIED_PRICE InpAppliedPriceMACD = PRICE_WEIGHTED; // MACD Applied Price // === DCA === input bool InpUseDCA = true; // Bật DCA input double InpDCADistance = 10.0; // Khoảng cách DCA (pips) input double InpDCADistMulti = 1.2; // Hệ số nhân khoảng cách DCA input int InpMaxDCAOrders = 5; // Số lệnh DCA tối đa input double InpDCALotMulti = 1.0; // Hệ số nhân lot DCA (1.0 = lot cố định) input double InpDCATPPips = 50.0; // TP chuỗi DCA (pips) input bool InpDCANeedSignal = true; // DCA phải cùng trend Ichimoku input bool InpDCAOutTime = true; // Cho DCA ngoài giờ trade // === Sniper (Tỉa lệnh) === input bool InpUseSniper = true; // Bật Sniper tỉa lệnh input int InpOrders2StartSniper = 20; // Số lệnh kích hoạt Sniper input int InpFirstOrdersSniper = 2; // Số lệnh đầu chuỗi để tỉa input double InpPercentSniper = 10.0; // % profit tối thiểu để tỉa input double InpTPSniper = 5.0; // TP pips sau khi tỉa // === Quản lý rủi ro (tất cả qua input, không hardcode) === input double InpMaxSpread = 40.0; // Max Spread (pips) input double InpMaxDrawdownMoney = 0.0; // Max DD tiền ($, 0=tắt) input double InpMaxDrawdownPct = 0.0; // Max DD % (0=tắt) input double InpDailyLossLimit = 0.0; // Giới hạn lỗ ngày ($, 0=tắt) input double InpDailyProfitTarget = 0.0; // Mục tiêu lời ngày ($, 0=tắt) input double InpTPPips = 10.0; // TP lệnh đơn (pips) input double InpSLPips = 0.0; // SL lệnh đơn (pips, 0=không SL) // === Equity Trailing === input bool InpUseEquityTrail = false; // Bật Equity Trailing input double InpEquityTrailStart = 15.0; // Equity trail kích hoạt ($) input double InpEquityTrailStep = 5.0; // Equity trail step ($) // === CCBSN: Chốt Cắt Bán Sớm Nửa + Gồng Trailing === input bool InpUsePartialClose = true; // Bật chốt nửa (CCBSN) input double InpPartialPercent = 50.0; // % lot chốt (50 = nửa) input bool InpMoveSLToBE = true; // Move SL breakeven sau chốt input double InpBEOffsetPips = 1.0; // Offset BE (pips, >0 = lock lời) // === Trailing cho phần còn lại === input bool InpUseTrailing = true; // Bật Trailing phần còn lại input double InpTrailStartPips = 10.0; // Pips profit kích hoạt trail input double InpTrailStepPips = 5.0; // Trailing step (pips) input bool InpUseATRTrail = false; // Dùng ATR thay pips cố định input ENUM_TIMEFRAMES InpATRTimeframe = PERIOD_M15; // TF cho ATR input int InpATRPeriod = 14; // ATR Period input double InpATRMultiplier = 1.5; // ATR × multiplier = trail dist // === Anti-Detect (Prop Firm) === input bool InpAntiDetect = true; // Bật Anti-Detect input int InpMinDelaySeconds = 3; // Delay tối thiểu (giây) input int InpMaxDelaySeconds = 15; // Delay tối đa (giây) input int InpMaxOrdersPerDay = 10; // Max lệnh mở mới/ngày input int InpDelayAfterClose = 60; // Delay sau khi đóng chuỗi (giây) // === Thời gian trade === input bool InpUseTradingTime = false; // Giới hạn thời gian trade input string InpStartTime = "08:00"; // Giờ bắt đầu input string InpEndTime = "22:00"; // Giờ kết thúc input bool InpCloseFriday = false; // Đóng hết vào thứ 6 input int InpCloseFridayHour = 20; // Giờ đóng thứ 6 // === Rollover Filter === input int InpFilterStartHour = 1; // Giờ bắt đầu trade (server) input int InpFilterEndHour = 23; // Giờ kết thúc trade (server) //+------------------------------------------------------------------+ //| GLOBAL VARIABLES | //+------------------------------------------------------------------+ CTrade g_trade; CPositionInfo g_posInfo; // Indicator handles int g_handleIchi = INVALID_HANDLE; int g_handleRSI = INVALID_HANDLE; int g_handleBB = INVALID_HANDLE; int g_handleCCI = INVALID_HANDLE; int g_handleStoch = INVALID_HANDLE; int g_handleMom = INVALID_HANDLE; int g_handleSigEF = INVALID_HANDLE; int g_handleSigES = INVALID_HANDLE; int g_handleSTatr = INVALID_HANDLE; int g_handleEMAF = INVALID_HANDLE; int g_handleEMAS = INVALID_HANDLE; int g_handleMACD = INVALID_HANDLE; // State datetime g_lastOrderTime = 0; datetime g_lastCloseTime = 0; int g_ordersOpenedToday = 0; int g_lastDay = -1; double g_dailyStartBalance = 0; double g_maxEquity = 0; datetime g_nextAllowedTime = 0; datetime g_lastBarTime = 0; // New bar detection int g_cachedSignal = 0; // Cache signal per bar // CCBSN State int g_handleATR = INVALID_HANDLE; bool g_buyPartialDone = false; // Đã chốt nửa Buy bool g_sellPartialDone = false; // Đã chốt nửa Sell double g_buyTrailSL = 0; // SL trailing Buy double g_sellTrailSL = 0; // SL trailing Sell double g_buyAvgAfterPartial = 0; // Giá avg sau partial close double g_sellAvgAfterPartial = 0; // Giá avg sau partial close // Anti-detect: comment pool string g_comments[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Auto-detect filling type for broker | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_FILLING GetFillingType() { long fm = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE); if((fm & SYMBOL_FILLING_FOK) != 0) return ORDER_FILLING_FOK; if((fm & SYMBOL_FILLING_IOC) != 0) return ORDER_FILLING_IOC; return ORDER_FILLING_RETURN; } int OnInit() { // Trade setup g_trade.SetExpertMagicNumber(InpMagicID); g_trade.SetDeviationInPoints(10); g_trade.SetTypeFilling(GetFillingType()); // Ichimoku g_handleIchi = iIchimoku(_Symbol, PERIOD_M5, InpIchiTenkan, InpIchiKijun, InpIchiSenkou); if(g_handleIchi == INVALID_HANDLE) { Print("FATAL: Failed to create Ichimoku indicator!"); return INIT_FAILED; } // Multi-indicator signal handles ENUM_TIMEFRAMES sigTF = (InpTFSignal == PERIOD_CURRENT) ? PERIOD_M5 : InpTFSignal; if(InpIndiMode == INDI_RSI) { g_handleRSI = iRSI(_Symbol, sigTF, InpRSIPeriod, InpRSIPrice); } if(InpIndiMode == INDI_BB) { g_handleBB = iBands(_Symbol, sigTF, InpBBPeriod, 0, InpBBDeviation, PRICE_CLOSE); } if(InpIndiMode == INDI_CCI) { g_handleCCI = iCCI(_Symbol, sigTF, InpCCIPeriod, InpCCIPrice); } if(InpIndiMode == INDI_STOCH) { g_handleStoch = iStochastic(_Symbol, sigTF, InpStochK, InpStochD, InpStochSlowing, MODE_SMA, STO_LOWHIGH); } if(InpIndiMode == INDI_MOMENTUM) { g_handleMom = iMomentum(_Symbol, sigTF, InpMomentumPeriod, InpMomentumPrice); } if(InpIndiMode == INDI_EMA_CROSS) { g_handleSigEF = iMA(_Symbol, sigTF, InpSigEMAFast, 0, MODE_EMA, PRICE_CLOSE); g_handleSigES = iMA(_Symbol, sigTF, InpSigEMASlow, 0, MODE_EMA, PRICE_CLOSE); } if(InpIndiMode == INDI_SUPERTREND) { g_handleSTatr = iATR(_Symbol, sigTF, InpSTperiod); } if(InpIndiMode == INDI_MACD_CROSS) { // Reuse MACD handle from filter section if not already created if(g_handleMACD == INVALID_HANDLE) g_handleMACD = iMACD(_Symbol, sigTF, InpFastEMAMACD, InpSlowEMAMACD, InpSMAMACD, InpAppliedPriceMACD); } // ATR for CCBSN trailing if(InpUseATRTrail) { g_handleATR = iATR(_Symbol, InpATRTimeframe, InpATRPeriod); if(g_handleATR == INVALID_HANDLE) { Print("WARNING: Failed to create ATR indicator! Using fixed trail."); } } // EMA Filter (dùng TF riêng) if(InpUseEMAFilter) { ENUM_TIMEFRAMES emaTF = (InpTFEMAFilter == PERIOD_CURRENT) ? PERIOD_M5 : InpTFEMAFilter; g_handleEMAF = iMA(_Symbol, emaTF, InpEMAFast, 0, MODE_EMA, PRICE_CLOSE); g_handleEMAS = iMA(_Symbol, emaTF, InpEMASlow, 0, MODE_EMA, PRICE_CLOSE); if(g_handleEMAF == INVALID_HANDLE || g_handleEMAS == INVALID_HANDLE) { Print("FATAL: Failed to create EMA indicators!"); return INIT_FAILED; } } // MACD Filter (dùng TF riêng) if(InpUseMACDFilter) { ENUM_TIMEFRAMES macdTF = (InpTFMACDFilter == PERIOD_CURRENT) ? PERIOD_M5 : InpTFMACDFilter; g_handleMACD = iMACD(_Symbol, macdTF, InpFastEMAMACD, InpSlowEMAMACD, InpSMAMACD, InpAppliedPriceMACD); if(g_handleMACD == INVALID_HANDLE) { Print("FATAL: Failed to create MACD indicator!"); return INIT_FAILED; } } // Init comment pool for anti-detect InitCommentPool(); // Reset daily counters g_dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); MqlDateTime dt; TimeCurrent(dt); g_lastDay = dt.day; g_ordersOpenedToday = 0; Print("==========================================="); Print("IchiDCA CCBSN MultiSignal v3.0 initialized"); Print("Symbol: ", _Symbol, " | Magic: ", InpMagicID); Print("Lots: ", InpLots, " | DCA Max: ", InpMaxDCAOrders); Print("CCBSN: ", InpUsePartialClose ? "ON" : "OFF", " | Partial: ", InpPartialPercent, "%"); Print("Trailing: ", InpUseTrailing ? "ON" : "OFF", " | ATR: ", InpUseATRTrail ? "ON" : "OFF"); Print("Anti-Detect: ", InpAntiDetect ? "ON" : "OFF"); Print("==========================================="); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(g_handleIchi != INVALID_HANDLE) IndicatorRelease(g_handleIchi); if(g_handleEMAF != INVALID_HANDLE) IndicatorRelease(g_handleEMAF); if(g_handleEMAS != INVALID_HANDLE) IndicatorRelease(g_handleEMAS); if(g_handleMACD != INVALID_HANDLE) IndicatorRelease(g_handleMACD); if(g_handleATR != INVALID_HANDLE) IndicatorRelease(g_handleATR); if(g_handleRSI != INVALID_HANDLE) IndicatorRelease(g_handleRSI); if(g_handleBB != INVALID_HANDLE) IndicatorRelease(g_handleBB); if(g_handleCCI != INVALID_HANDLE) IndicatorRelease(g_handleCCI); if(g_handleStoch != INVALID_HANDLE) IndicatorRelease(g_handleStoch); if(g_handleMom != INVALID_HANDLE) IndicatorRelease(g_handleMom); if(g_handleSigEF != INVALID_HANDLE) IndicatorRelease(g_handleSigEF); if(g_handleSigES != INVALID_HANDLE) IndicatorRelease(g_handleSigES); if(g_handleSTatr != INVALID_HANDLE) IndicatorRelease(g_handleSTatr); Print("IchiDCA CCBSN PropFirm EA deinitialized. Reason: ", reason); } //+------------------------------------------------------------------+ //| Initialize random comment pool | //+------------------------------------------------------------------+ void InitCommentPool() { ArrayResize(g_comments, 12); g_comments[0] = "manual trade"; g_comments[1] = "scalp entry"; g_comments[2] = "trend follow"; g_comments[3] = "breakout"; g_comments[4] = "pullback"; g_comments[5] = "retest"; g_comments[6] = "momentum"; g_comments[7] = "swing"; g_comments[8] = "position"; g_comments[9] = "dip buy"; g_comments[10] = "rally sell"; g_comments[11] = "range trade"; } //+------------------------------------------------------------------+ //| Get random comment (anti-detect) | //+------------------------------------------------------------------+ string GetRandomComment() { if(!InpAntiDetect) return "IchiDCA"; int idx = MathRand() % ArraySize(g_comments); return g_comments[idx]; } //+------------------------------------------------------------------+ //| Get point value adjusted for broker digits | //| Forex 4-digit: 1 pip = 0.0001 (_Point) | //| Forex 5-digit: 1 pip = 0.0001 (_Point*10) | //| XAUUSD 2-digit: 1 pip = 0.1 (_Point*10) | //| XAUUSD 3-digit: 1 pip = 0.1 (_Point*100) | //+------------------------------------------------------------------+ double GetPipPoint() { int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); // Detect metals/gold (typically 2 or 3 digits) if(digits <= 3) { // XAUUSD, XAGUSD, etc. - 1 pip = 0.1 if(digits == 2) return 0.1; // 2850.00 -> pip = 0.1 if(digits == 3) return 0.1; // 2850.000 -> pip = 0.1 if(digits == 1) return 1.0; // JPY pairs or similar } // Forex pairs (4 or 5 digits) if(digits == 5) return _Point * 10; // 1.12345 -> pip = 0.0001 if(digits == 4) return _Point; // 1.1234 -> pip = 0.0001 return _Point; } //+------------------------------------------------------------------+ //| Get current spread in pips | //+------------------------------------------------------------------+ double GetSpreadPips() { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); return (ask - bid) / GetPipPoint(); } //+------------------------------------------------------------------+ //| Normalize lot size | //+------------------------------------------------------------------+ double NormLots(double lots) { double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lots = MathMax(lots, minLot); lots = MathMin(lots, maxLot); lots = MathRound(lots / step) * step; return NormalizeDouble(lots, 2); } //+------------------------------------------------------------------+ //| Count orders by type (our magic only) | //+------------------------------------------------------------------+ int CountOrders(ENUM_POSITION_TYPE type) { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_posInfo.SelectByIndex(i)) { if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() == type) count++; } } return count; } //+------------------------------------------------------------------+ //| Count all our orders | //+------------------------------------------------------------------+ int CountAllOrders() { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_posInfo.SelectByIndex(i)) { if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; count++; } } return count; } //+------------------------------------------------------------------+ //| Get floating profit of our orders | //+------------------------------------------------------------------+ double GetFloatingProfit(ENUM_POSITION_TYPE type = -1) { double total = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_posInfo.SelectByIndex(i)) { if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(type != -1 && g_posInfo.PositionType() != type) continue; total += g_posInfo.Profit() + g_posInfo.Swap() + g_posInfo.Commission(); } } return total; } //+------------------------------------------------------------------+ //| Get average entry price of orders | //+------------------------------------------------------------------+ double GetAveragePrice(ENUM_POSITION_TYPE type) { double totalLots = 0; double totalValue = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_posInfo.SelectByIndex(i)) { if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != type) continue; totalLots += g_posInfo.Volume(); totalValue += g_posInfo.PriceOpen() * g_posInfo.Volume(); } } if(totalLots > 0) return totalValue / totalLots; return 0; } //+------------------------------------------------------------------+ //| Get last entry price | //+------------------------------------------------------------------+ double GetLastEntryPrice(ENUM_POSITION_TYPE type) { datetime lastTime = 0; double lastPrice = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_posInfo.SelectByIndex(i)) { if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != type) continue; if(g_posInfo.Time() > lastTime) { lastTime = g_posInfo.Time(); lastPrice = g_posInfo.PriceOpen(); } } } return lastPrice; } //+------------------------------------------------------------------+ //| Close all orders of a type | //+------------------------------------------------------------------+ void CloseAllByType(ENUM_POSITION_TYPE type) { for(int retry = 0; retry < 3; retry++) { int remaining = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_posInfo.SelectByIndex(i)) continue; if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != type) continue; if(!g_trade.PositionClose(g_posInfo.Ticket())) remaining++; } if(remaining == 0) break; Sleep(500); } g_lastCloseTime = TimeCurrent(); } //+------------------------------------------------------------------+ //| Close ALL orders | //+------------------------------------------------------------------+ void CloseAllOrders() { for(int retry = 0; retry < 3; retry++) { int remaining = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_posInfo.SelectByIndex(i)) continue; if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(!g_trade.PositionClose(g_posInfo.Ticket())) remaining++; } if(remaining == 0) break; Sleep(500); } g_lastCloseTime = TimeCurrent(); } //+------------------------------------------------------------------+ //| CCBSN: Get ATR-based trail distance in price | //+------------------------------------------------------------------+ double GetATRTrailDistance() { if(!InpUseATRTrail || g_handleATR == INVALID_HANDLE) return InpTrailStepPips * GetPipPoint(); // fallback to fixed double atr[]; ArraySetAsSeries(atr, true); if(CopyBuffer(g_handleATR, 0, 0, 2, atr) < 2) return InpTrailStepPips * GetPipPoint(); // fallback return atr[1] * InpATRMultiplier; } //+------------------------------------------------------------------+ //| CCBSN: Partial close X% lot of each position in a chain | //+------------------------------------------------------------------+ void PartialCloseByType(ENUM_POSITION_TYPE type) { double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); int closedCount = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_posInfo.SelectByIndex(i)) continue; if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != type) continue; double currentLots = g_posInfo.Volume(); double closeLots = currentLots * InpPartialPercent / 100.0; // Round to lot step closeLots = MathFloor(closeLots / lotStep) * lotStep; closeLots = NormalizeDouble(closeLots, 2); // Ensure minimum lot remains after close if(closeLots < minLot) { // Lot quá nhỏ, không thể partial close → skip Print("CCBSN: Lot too small for partial close. Ticket=", g_posInfo.Ticket(), " current=", currentLots, " closeLots=", closeLots); continue; } // Ensure remaining lot >= minLot double remainLots = currentLots - closeLots; if(remainLots < minLot) { // Adjust: close less so remaining >= minLot closeLots = currentLots - minLot; closeLots = MathFloor(closeLots / lotStep) * lotStep; closeLots = NormalizeDouble(closeLots, 2); if(closeLots < minLot) continue; // skip if not possible } ulong ticket = g_posInfo.Ticket(); if(g_trade.PositionClosePartial(ticket, closeLots)) { closedCount++; Print(">>> CCBSN PARTIAL CLOSE: ticket=", ticket, " closed=", closeLots, " remain=", NormalizeDouble(currentLots - closeLots, 2)); } else { Print("CCBSN PARTIAL CLOSE FAILED: ticket=", ticket, " error=", g_trade.ResultRetcode(), " - ", g_trade.ResultRetcodeDescription()); } } if(closedCount > 0) { // Mark partial close done if(type == POSITION_TYPE_BUY) { g_buyPartialDone = true; g_buyAvgAfterPartial = GetAveragePrice(POSITION_TYPE_BUY); Print(">>> CCBSN BUY: Partial close done. Avg after=", DoubleToString(g_buyAvgAfterPartial, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS))); } else { g_sellPartialDone = true; g_sellAvgAfterPartial = GetAveragePrice(POSITION_TYPE_SELL); Print(">>> CCBSN SELL: Partial close done. Avg after=", DoubleToString(g_sellAvgAfterPartial, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS))); } // Move SL to breakeven if(InpMoveSLToBE) MoveSLToBreakeven(type); } } //+------------------------------------------------------------------+ //| CCBSN: Move SL to breakeven (avg price + offset) for remaining | //+------------------------------------------------------------------+ void MoveSLToBreakeven(ENUM_POSITION_TYPE type) { double pip = GetPipPoint(); double avgPrice = (type == POSITION_TYPE_BUY) ? g_buyAvgAfterPartial : g_sellAvgAfterPartial; if(avgPrice <= 0) avgPrice = GetAveragePrice(type); double newSL; if(type == POSITION_TYPE_BUY) newSL = avgPrice + InpBEOffsetPips * pip; // SL trên avg = lock lời nhẹ else newSL = avgPrice - InpBEOffsetPips * pip; // SL dưới avg = lock lời nhẹ newSL = NormalizeDouble(newSL, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)); for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_posInfo.SelectByIndex(i)) continue; if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != type) continue; double currentSL = g_posInfo.StopLoss(); double currentTP = g_posInfo.TakeProfit(); // Chỉ move SL nếu SL mới tốt hơn (gần giá hơn = bảo vệ tốt hơn) bool shouldModify = false; if(type == POSITION_TYPE_BUY) shouldModify = (currentSL < newSL || currentSL == 0); else shouldModify = (currentSL > newSL || currentSL == 0); if(shouldModify) { if(g_trade.PositionModify(g_posInfo.Ticket(), newSL, 0)) // TP=0 để trailing quản lý { Print(">>> CCBSN BE: ticket=", g_posInfo.Ticket(), " SL=", DoubleToString(newSL, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS))); } else { Print("CCBSN BE FAILED: ticket=", g_posInfo.Ticket(), " error=", g_trade.ResultRetcode(), " - ", g_trade.ResultRetcodeDescription()); } } } // Initialize trail SL if(type == POSITION_TYPE_BUY) g_buyTrailSL = newSL; else g_sellTrailSL = newSL; } //+------------------------------------------------------------------+ //| CCBSN: Trailing Stop for remaining positions after partial close | //+------------------------------------------------------------------+ void ProcessTrailingStop() { if(!InpUseTrailing) return; double pip = GetPipPoint(); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double trailDist = GetATRTrailDistance(); double trailStartDist = InpTrailStartPips * pip; // === Trailing BUY === if(g_buyPartialDone) { int buyCount = CountOrders(POSITION_TYPE_BUY); if(buyCount == 0) { // Tất cả lệnh đã đóng (trailing SL hit hoặc manual) ResetCCBSNState(POSITION_TYPE_BUY); } else { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double avgPrice = g_buyAvgAfterPartial; double profitDist = bid - avgPrice; // Kích hoạt trailing khi profit đủ xa if(profitDist >= trailStartDist) { double proposedSL = NormalizeDouble(bid - trailDist, digits); // Chỉ nâng SL, không hạ if(proposedSL > g_buyTrailSL) { g_buyTrailSL = proposedSL; // Update SL cho tất cả lệnh Buy for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_posInfo.SelectByIndex(i)) continue; if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != POSITION_TYPE_BUY) continue; if(g_posInfo.StopLoss() < proposedSL || g_posInfo.StopLoss() == 0) { g_trade.PositionModify(g_posInfo.Ticket(), proposedSL, 0); } } Print(">>> CCBSN TRAIL BUY: SL=", DoubleToString(proposedSL, digits), " bid=", DoubleToString(bid, digits), " dist=", DoubleToString(trailDist/pip, 1), "p"); } } } } // === Trailing SELL === if(g_sellPartialDone) { int sellCount = CountOrders(POSITION_TYPE_SELL); if(sellCount == 0) { ResetCCBSNState(POSITION_TYPE_SELL); } else { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double avgPrice = g_sellAvgAfterPartial; double profitDist = avgPrice - ask; if(profitDist >= trailStartDist) { double proposedSL = NormalizeDouble(ask + trailDist, digits); // Chỉ hạ SL (cho Sell, SL thấp hơn = tốt hơn) if(g_sellTrailSL == 0 || proposedSL < g_sellTrailSL) { g_sellTrailSL = proposedSL; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_posInfo.SelectByIndex(i)) continue; if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != POSITION_TYPE_SELL) continue; if(g_posInfo.StopLoss() > proposedSL || g_posInfo.StopLoss() == 0) { g_trade.PositionModify(g_posInfo.Ticket(), proposedSL, 0); } } Print(">>> CCBSN TRAIL SELL: SL=", DoubleToString(proposedSL, digits), " ask=", DoubleToString(ask, digits), " dist=", DoubleToString(trailDist/pip, 1), "p"); } } } } } //+------------------------------------------------------------------+ //| CCBSN: Reset state when all positions of a type are closed | //+------------------------------------------------------------------+ void ResetCCBSNState(ENUM_POSITION_TYPE type) { if(type == POSITION_TYPE_BUY) { g_buyPartialDone = false; g_buyTrailSL = 0; g_buyAvgAfterPartial = 0; Print(">>> CCBSN BUY: Chain closed. State reset."); } else { g_sellPartialDone = false; g_sellTrailSL = 0; g_sellAvgAfterPartial = 0; Print(">>> CCBSN SELL: Chain closed. State reset."); } g_lastCloseTime = TimeCurrent(); } //+------------------------------------------------------------------+ //| Open order with anti-detect features | //+------------------------------------------------------------------+ bool PlaceOrder(ENUM_POSITION_TYPE type, double lots, double tpPips, double slPips, string comment) { lots = NormLots(lots); double pip = GetPipPoint(); double price, tp = 0, sl = 0; if(type == POSITION_TYPE_BUY) { price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if(tpPips > 0) tp = price + tpPips * pip; if(slPips > 0) sl = price - slPips * pip; if(!g_trade.Buy(lots, _Symbol, price, sl, tp, comment)) { Print("BUY FAILED: ", g_trade.ResultRetcode(), " - ", g_trade.ResultRetcodeDescription()); return false; } } else { price = SymbolInfoDouble(_Symbol, SYMBOL_BID); if(tpPips > 0) tp = price - tpPips * pip; if(slPips > 0) sl = price + slPips * pip; if(!g_trade.Sell(lots, _Symbol, price, sl, tp, comment)) { Print("SELL FAILED: ", g_trade.ResultRetcode(), " - ", g_trade.ResultRetcodeDescription()); return false; } } g_lastOrderTime = TimeCurrent(); g_ordersOpenedToday++; Print((type == POSITION_TYPE_BUY ? "BUY" : "SELL"), " opened: lots=", lots, " tp=", tpPips, "p sl=", slPips, "p"); return true; } //+------------------------------------------------------------------+ //| RSI SIGNAL | //+------------------------------------------------------------------+ int GetRSISignal() { if(g_handleRSI == INVALID_HANDLE) return 0; double rsi[3]; ArraySetAsSeries(rsi, true); if(CopyBuffer(g_handleRSI, 0, 0, 3, rsi) < 3) return 0; if(rsi[2] < InpRSIOS && rsi[1] >= InpRSIOS) return +1; if(rsi[2] > InpRSIOB && rsi[1] <= InpRSIOB) return -1; return 0; } //+------------------------------------------------------------------+ //| BB SIGNAL | //+------------------------------------------------------------------+ int GetBBSignal() { if(g_handleBB == INVALID_HANDLE) return 0; double bbU[3], bbL[3]; // 0=BASE, 1=UPPER, 2=LOWER double lo[3], hi[3], cl[3], opn[3]; ArraySetAsSeries(bbU, true); ArraySetAsSeries(bbL, true); ArraySetAsSeries(lo, true); ArraySetAsSeries(hi, true); ArraySetAsSeries(cl, true); ArraySetAsSeries(opn, true); if(CopyBuffer(g_handleBB, 1, 0, 3, bbU) < 3) return 0; if(CopyBuffer(g_handleBB, 2, 0, 3, bbL) < 3) return 0; ENUM_TIMEFRAMES sigTF = (InpTFSignal == PERIOD_CURRENT) ? PERIOD_M5 : InpTFSignal; if(CopyLow(_Symbol, sigTF, 0, 3, lo) < 3) return 0; if(CopyHigh(_Symbol, sigTF, 0, 3, hi) < 3) return 0; if(CopyClose(_Symbol, sigTF, 0, 3, cl) < 3) return 0; if(CopyOpen(_Symbol, sigTF, 0, 3, opn) < 3) return 0; if(lo[2] <= bbL[2] && cl[1] > bbL[1] && cl[1] > opn[1]) return +1; if(hi[2] >= bbU[2] && cl[1] < bbU[1] && cl[1] < opn[1]) return -1; return 0; } //+------------------------------------------------------------------+ //| CCI SIGNAL | //+------------------------------------------------------------------+ int GetCCISignal() { if(g_handleCCI == INVALID_HANDLE) return 0; double cci[3]; ArraySetAsSeries(cci, true); if(CopyBuffer(g_handleCCI, 0, 0, 3, cci) < 3) return 0; if(cci[2] < InpCCIOS && cci[1] >= InpCCIOS) return +1; if(cci[2] > InpCCIOB && cci[1] <= InpCCIOB) return -1; return 0; } //+------------------------------------------------------------------+ //| STOCHASTIC SIGNAL | //+------------------------------------------------------------------+ int GetStochSignal() { if(g_handleStoch == INVALID_HANDLE) return 0; double stK[3], stD[3]; ArraySetAsSeries(stK, true); ArraySetAsSeries(stD, true); if(CopyBuffer(g_handleStoch, 0, 0, 3, stK) < 3) return 0; if(CopyBuffer(g_handleStoch, 1, 0, 3, stD) < 3) return 0; if(stK[1] > stD[1] && stK[2] <= stD[2] && stK[1] < InpStochOS + 20) return +1; if(stK[1] < stD[1] && stK[2] >= stD[2] && stK[1] > InpStochOB - 20) return -1; return 0; } //+------------------------------------------------------------------+ //| MOMENTUM SIGNAL | //+------------------------------------------------------------------+ int GetMomentumSignal() { if(g_handleMom == INVALID_HANDLE) return 0; double mom[3]; ArraySetAsSeries(mom, true); if(CopyBuffer(g_handleMom, 0, 0, 3, mom) < 3) return 0; if(mom[2] < InpMomentumOS && mom[1] >= InpMomentumOS) return +1; if(mom[2] > InpMomentumOB && mom[1] <= InpMomentumOB) return -1; return 0; } //+------------------------------------------------------------------+ //| EMA CROSS SIGNAL | //+------------------------------------------------------------------+ int GetEMACrossSignal() { if(g_handleSigEF == INVALID_HANDLE || g_handleSigES == INVALID_HANDLE) return 0; double ef[3], es[3]; ArraySetAsSeries(ef, true); ArraySetAsSeries(es, true); if(CopyBuffer(g_handleSigEF, 0, 0, 3, ef) < 3) return 0; if(CopyBuffer(g_handleSigES, 0, 0, 3, es) < 3) return 0; if(ef[1] > es[1] && ef[2] <= es[2]) return +1; if(ef[1] < es[1] && ef[2] >= es[2]) return -1; return 0; } //+------------------------------------------------------------------+ //| MACD CROSS SIGNAL (histogram cross zero) | //+------------------------------------------------------------------+ int GetMACDCrossSignal() { if(g_handleMACD == INVALID_HANDLE) return 0; double macdM[3], macdS[3]; ArraySetAsSeries(macdM, true); ArraySetAsSeries(macdS, true); if(CopyBuffer(g_handleMACD, 0, 0, 3, macdM) < 3) return 0; if(CopyBuffer(g_handleMACD, 1, 0, 3, macdS) < 3) return 0; double h1 = macdM[1] - macdS[1], h2 = macdM[2] - macdS[2]; if(h1 > 0 && h2 <= 0) return +1; if(h1 < 0 && h2 >= 0) return -1; return 0; } //+------------------------------------------------------------------+ //| SUPERTREND SIGNAL (ATR-based) | //+------------------------------------------------------------------+ int GetSupertrendSignal() { if(g_handleSTatr == INVALID_HANDLE) return 0; double atr[3]; ArraySetAsSeries(atr, true); if(CopyBuffer(g_handleSTatr, 0, 0, 3, atr) < 3) return 0; ENUM_TIMEFRAMES sigTF = (InpTFSignal == PERIOD_CURRENT) ? PERIOD_M5 : InpTFSignal; double hi[3], lo[3], cl[3]; ArraySetAsSeries(hi, true); ArraySetAsSeries(lo, true); ArraySetAsSeries(cl, true); if(CopyHigh(_Symbol, sigTF, 0, 3, hi) < 3) return 0; if(CopyLow(_Symbol, sigTF, 0, 3, lo) < 3) return 0; if(CopyClose(_Symbol, sigTF, 0, 3, cl) < 3) return 0; double mid2 = (hi[2]+lo[2])/2.0, mid1 = (hi[1]+lo[1])/2.0; double upBand = mid1 - InpSTmultiplier * atr[1]; double dnBand = mid1 + InpSTmultiplier * atr[1]; // Simplified: price above upper = uptrend signal if(cl[2] <= mid2 + InpSTmultiplier*atr[2] && cl[1] > upBand) return +1; if(cl[2] >= mid2 - InpSTmultiplier*atr[2] && cl[1] < dnBand) return -1; return 0; } //+------------------------------------------------------------------+ //| MASTER SIGNAL DISPATCHER | //+------------------------------------------------------------------+ int GetMasterSignal() { switch(InpIndiMode) { case INDI_ICHIMOKU: return GetIchimokuSignal(); case INDI_EMA_CROSS: return GetEMACrossSignal(); case INDI_RSI: return GetRSISignal(); case INDI_BB: return GetBBSignal(); case INDI_STOCH: return GetStochSignal(); case INDI_CCI: return GetCCISignal(); case INDI_MACD_CROSS: return GetMACDCrossSignal(); case INDI_SUPERTREND: return GetSupertrendSignal(); case INDI_MOMENTUM: return GetMomentumSignal(); default: return GetIchimokuSignal(); } } //+------------------------------------------------------------------+ //| ICHIMOKU SIGNAL: Cloud Break + Continuation Detection | //+------------------------------------------------------------------+ // Returns: +1 = BUY signal, -1 = SELL signal, 0 = no signal int GetIchimokuSignal() { // Ichimoku buffers: // 0 = Tenkan-sen, 1 = Kijun-sen // 2 = Senkou Span A, 3 = Senkou Span B // 4 = Chikou Span double tenkan[], kijun[], spanA[], spanB[]; ArraySetAsSeries(tenkan, true); ArraySetAsSeries(kijun, true); ArraySetAsSeries(spanA, true); ArraySetAsSeries(spanB, true); if(CopyBuffer(g_handleIchi, 0, 0, 3, tenkan) < 3) return 0; if(CopyBuffer(g_handleIchi, 1, 0, 3, kijun) < 3) return 0; if(CopyBuffer(g_handleIchi, 2, 0, 3, spanA) < 3) return 0; if(CopyBuffer(g_handleIchi, 3, 0, 3, spanB) < 3) return 0; double close[]; ArraySetAsSeries(close, true); if(CopyClose(_Symbol, PERIOD_M5, 0, 3, close) < 3) return 0; // Xác định mây (cloud) - mây trên và mây dưới double cloudTop1 = MathMax(spanA[1], spanB[1]); double cloudBot1 = MathMin(spanA[1], spanB[1]); double cloudTop2 = MathMax(spanA[2], spanB[2]); double cloudBot2 = MathMin(spanA[2], spanB[2]); // === BUY SIGNAL === // Nến trước trong/dưới mây → nến hiện tại đóng TRÊN mây // + Tenkan > Kijun (uptrend confirmation) bool buyBreak = (close[2] <= cloudTop2) && (close[1] > cloudTop1); bool buyTrend = (tenkan[1] > kijun[1]); if(buyBreak && buyTrend) return +1; // === SELL SIGNAL === // Nến trước trong/trên mây → nến hiện tại đóng DƯỚI mây // + Tenkan < Kijun (downtrend confirmation) bool sellBreak = (close[2] >= cloudBot2) && (close[1] < cloudBot1); bool sellTrend = (tenkan[1] < kijun[1]); if(sellBreak && sellTrend) return -1; return 0; } //+------------------------------------------------------------------+ //| Check Ichimoku trend (for DCA filter) | //| Returns: +1 = uptrend, -1 = downtrend, 0 = no clear trend | //+------------------------------------------------------------------+ int GetIchimokuTrend() { double spanA[], spanB[], close[]; ArraySetAsSeries(spanA, true); ArraySetAsSeries(spanB, true); ArraySetAsSeries(close, true); if(CopyBuffer(g_handleIchi, 2, 0, 2, spanA) < 2) return 0; if(CopyBuffer(g_handleIchi, 3, 0, 2, spanB) < 2) return 0; if(CopyClose(_Symbol, PERIOD_M5, 0, 2, close) < 2) return 0; double cloudTop = MathMax(spanA[1], spanB[1]); double cloudBot = MathMin(spanA[1], spanB[1]); if(close[1] > cloudTop) return +1; // Trên mây = uptrend if(close[1] < cloudBot) return -1; // Dưới mây = downtrend return 0; // Trong mây = sideway } //+------------------------------------------------------------------+ //| EMA TREND FILTER | //| Returns: +1 = uptrend, -1 = downtrend, 0 = no filter/neutral | //+------------------------------------------------------------------+ int GetEMATrend() { if(!InpUseEMAFilter) return 0; // Bypass double emaFast[], emaSlow[]; ArraySetAsSeries(emaFast, true); ArraySetAsSeries(emaSlow, true); if(CopyBuffer(g_handleEMAF, 0, 0, 2, emaFast) < 2) return 0; if(CopyBuffer(g_handleEMAS, 0, 0, 2, emaSlow) < 2) return 0; if(emaFast[1] > emaSlow[1]) return +1; if(emaFast[1] < emaSlow[1]) return -1; return 0; } //+------------------------------------------------------------------+ //| MACD TREND FILTER | //| Returns: +1 = bullish, -1 = bearish, 0 = neutral/off | //+------------------------------------------------------------------+ int GetMACDTrend() { if(!InpUseMACDFilter) return 0; // Bypass if(g_handleMACD == INVALID_HANDLE) return 0; double macdMain[], macdSignal[]; ArraySetAsSeries(macdMain, true); ArraySetAsSeries(macdSignal, true); if(CopyBuffer(g_handleMACD, 0, 0, 2, macdMain) < 2) return 0; if(CopyBuffer(g_handleMACD, 1, 0, 2, macdSignal) < 2) return 0; // MACD > Signal = bullish, MACD < Signal = bearish if(macdMain[1] > macdSignal[1]) return +1; if(macdMain[1] < macdSignal[1]) return -1; return 0; } //+------------------------------------------------------------------+ //| CHECK SPREAD | //+------------------------------------------------------------------+ bool IsSpreadOK() { return (GetSpreadPips() <= InpMaxSpread); } //+------------------------------------------------------------------+ //| CHECK TRADING TIME | //+------------------------------------------------------------------+ bool IsTradingTimeOK() { // Rollover filter MqlDateTime dt; TimeCurrent(dt); if(dt.hour < InpFilterStartHour || dt.hour > InpFilterEndHour) return false; // Custom time window if(!InpUseTradingTime) return true; string currentTime = StringFormat("%02d:%02d", dt.hour, dt.min); return (currentTime >= InpStartTime && currentTime <= InpEndTime); } //+------------------------------------------------------------------+ //| ANTI-DETECT: Check if we can open new order | //+------------------------------------------------------------------+ bool CanOpenNewOrder() { if(!InpAntiDetect) return true; // Daily order limit if(g_ordersOpenedToday >= InpMaxOrdersPerDay) { return false; } // Random delay between orders if(TimeCurrent() < g_nextAllowedTime) { return false; } // Delay after closing chain if(g_lastCloseTime > 0 && (TimeCurrent() - g_lastCloseTime) < InpDelayAfterClose) { return false; } return true; } //+------------------------------------------------------------------+ //| ANTI-DETECT: Set random delay for next order | //+------------------------------------------------------------------+ void SetRandomDelay() { if(!InpAntiDetect) return; int delay = InpMinDelaySeconds + (MathRand() % (InpMaxDelaySeconds - InpMinDelaySeconds + 1)); g_nextAllowedTime = TimeCurrent() + delay; } //+------------------------------------------------------------------+ //| CHECK DAILY COUNTERS & RESET | //+------------------------------------------------------------------+ void CheckDailyReset() { MqlDateTime dt; TimeCurrent(dt); if(dt.day != g_lastDay) { g_lastDay = dt.day; g_ordersOpenedToday = 0; g_dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); Print("NEW DAY: Reset counters. Balance start: $", DoubleToString(g_dailyStartBalance, 2)); } } //+------------------------------------------------------------------+ //| CHECK RISK: Daily loss & Max drawdown | //| Returns true if trading should STOP | //+------------------------------------------------------------------+ bool IsRiskBreached() { double equity = AccountInfoDouble(ACCOUNT_EQUITY); double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Daily loss limit if(InpDailyLossLimit > 0) { double dailyPnL = equity - g_dailyStartBalance; if(dailyPnL <= -InpDailyLossLimit) { Print("!!! DAILY LOSS LIMIT REACHED: $", DoubleToString(-dailyPnL, 2), " >= $", DoubleToString(InpDailyLossLimit, 2)); return true; } } // Max drawdown (money) if(InpMaxDrawdownMoney > 0) { double dd = balance - equity; if(dd >= InpMaxDrawdownMoney) { Print("!!! MAX DD MONEY: $", DoubleToString(dd, 2), " >= $", DoubleToString(InpMaxDrawdownMoney, 2), " -> CUT ALL"); CloseAllOrders(); return true; } } // Max drawdown (percent) if(InpMaxDrawdownPct > 0 && balance > 0) { double ddPct = (balance - equity) / balance * 100.0; if(ddPct >= InpMaxDrawdownPct) { Print("!!! MAX DD %: ", DoubleToString(ddPct, 2), "% >= ", DoubleToString(InpMaxDrawdownPct, 2), "% -> CUT ALL"); CloseAllOrders(); return true; } } // Daily profit target if(InpDailyProfitTarget > 0) { double dailyPnL = equity - g_dailyStartBalance; if(dailyPnL >= InpDailyProfitTarget) { Print("DAILY PROFIT TARGET REACHED: $", DoubleToString(dailyPnL, 2)); return true; // Stop trading, don't close (keep profitable positions) } } return false; } //+------------------------------------------------------------------+ //| EQUITY TRAILING | //+------------------------------------------------------------------+ void ProcessEquityTrailing() { if(!InpUseEquityTrail) return; double equity = AccountInfoDouble(ACCOUNT_EQUITY); double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Kích hoạt khi lời đủ if(equity >= balance + InpEquityTrailStart) { if(g_maxEquity == 0 || equity > g_maxEquity) g_maxEquity = equity; } // Chốt khi tụt lùi if(g_maxEquity > 0) { if(equity <= g_maxEquity - InpEquityTrailStep) { Print(">>> EQUITY TRAILING: Max=", DoubleToString(g_maxEquity, 2), " Now=", DoubleToString(equity, 2)); CloseAllOrders(); g_maxEquity = 0; } if(CountAllOrders() == 0) g_maxEquity = 0; } } //+------------------------------------------------------------------+ //| CHECK DCA TP: Close chain or trigger CCBSN partial close | //+------------------------------------------------------------------+ void CheckDCATP() { if(InpDCATPPips <= 0) return; double pip = GetPipPoint(); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); // Check BUY chain (chỉ khi chưa partial close) int buyCount = CountOrders(POSITION_TYPE_BUY); if(buyCount > 0 && !g_buyPartialDone) { double avgPrice = GetAveragePrice(POSITION_TYPE_BUY); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double profitPips = (bid - avgPrice) / pip; double tpTarget = (buyCount > 1) ? InpDCATPPips : InpTPPips; if(tpTarget <= 0) tpTarget = InpDCATPPips; if(profitPips >= tpTarget) { Print(">>> TP BUY: orders=", buyCount, " avg=", DoubleToString(avgPrice, digits), " bid=", DoubleToString(bid, digits), " profit=", DoubleToString(profitPips, 1), "p target=", DoubleToString(tpTarget, 1)); // CCBSN: partial close thay vì close all if(InpUsePartialClose) { Print(">>> CCBSN: Partial close BUY ", InpPartialPercent, "% + Trailing"); PartialCloseByType(POSITION_TYPE_BUY); } else { CloseAllByType(POSITION_TYPE_BUY); } SetRandomDelay(); } } // Check SELL chain (chỉ khi chưa partial close) int sellCount = CountOrders(POSITION_TYPE_SELL); if(sellCount > 0 && !g_sellPartialDone) { double avgPrice = GetAveragePrice(POSITION_TYPE_SELL); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double profitPips = (avgPrice - ask) / pip; double tpTarget = (sellCount > 1) ? InpDCATPPips : InpTPPips; if(tpTarget <= 0) tpTarget = InpDCATPPips; if(profitPips >= tpTarget) { Print(">>> TP SELL: orders=", sellCount, " avg=", DoubleToString(avgPrice, digits), " ask=", DoubleToString(ask, digits), " profit=", DoubleToString(profitPips, 1), "p target=", DoubleToString(tpTarget, 1)); if(InpUsePartialClose) { Print(">>> CCBSN: Partial close SELL ", InpPartialPercent, "% + Trailing"); PartialCloseByType(POSITION_TYPE_SELL); } else { CloseAllByType(POSITION_TYPE_SELL); } SetRandomDelay(); } } } //+------------------------------------------------------------------+ //| PROCESS DCA: Add to position when conditions met | //+------------------------------------------------------------------+ void ProcessDCA() { if(!InpUseDCA) return; // CCBSN: Khong DCA khi dang trailing sau partial close if(g_buyPartialDone || g_sellPartialDone) return; if(!IsSpreadOK()) return; if(!CanOpenNewOrder()) return; // Check giờ trade (cho phép DCA ngoài giờ nếu InpDCAOutTime=true) if(!InpDCAOutTime && !IsTradingTimeOK()) return; double pip = GetPipPoint(); // DCA BUY int buyCount = CountOrders(POSITION_TYPE_BUY); if(buyCount > 0 && buyCount < InpMaxDCAOrders + 1) // +1 vì lệnh đầu không tính DCA { if(InpTradeDir != DIR_SELL) // Không DCA buy nếu chỉ cho sell { // Check DCA cùng trend (Ichimoku + EMA) bool dcaBuyOK = true; if(InpDCANeedSignal) { int trend = GetIchimokuTrend(); if(trend != +1) dcaBuyOK = false; // Dừng DCA khi EMA đảo chiều int emaTrend = GetEMATrend(); if(emaTrend == -1) dcaBuyOK = false; } if(dcaBuyOK) { double lastPrice = GetLastEntryPrice(POSITION_TYPE_BUY); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Tính khoảng cách DCA (tăng dần theo hệ số) double distance = InpDCADistance; for(int i = 1; i < buyCount; i++) distance *= InpDCADistMulti; // Giá đi xuống đủ xa so với lệnh cuối -> DCA if(lastPrice - ask >= distance * pip) { double dcaLots = InpLots; // Tính lot DCA (nếu có multiplier) for(int i = 0; i < buyCount; i++) dcaLots *= InpDCALotMulti; string comment = GetRandomComment(); Print(">>> DCA BUY #", buyCount + 1, " lots=", NormLots(dcaLots), " dist=", DoubleToString(distance, 1)); PlaceOrder(POSITION_TYPE_BUY, dcaLots, 0, InpSLPips, comment); // TP=0 vì dùng TP chuỗi SetRandomDelay(); } } } } // DCA SELL int sellCount = CountOrders(POSITION_TYPE_SELL); if(sellCount > 0 && sellCount < InpMaxDCAOrders + 1) { if(InpTradeDir != DIR_BUY) // Không DCA sell nếu chỉ cho buy { // Check DCA cùng trend (Ichimoku + EMA) bool dcaSellOK = true; if(InpDCANeedSignal) { int trend = GetIchimokuTrend(); if(trend != -1) dcaSellOK = false; // Dừng DCA khi EMA đảo chiều int emaTrend = GetEMATrend(); if(emaTrend == +1) dcaSellOK = false; } if(!dcaSellOK) return; double lastPrice = GetLastEntryPrice(POSITION_TYPE_SELL); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double distance = InpDCADistance; for(int i = 1; i < sellCount; i++) distance *= InpDCADistMulti; // Giá đi lên đủ xa so với lệnh cuối -> DCA if(bid - lastPrice >= distance * pip) { double dcaLots = InpLots; for(int i = 0; i < sellCount; i++) dcaLots *= InpDCALotMulti; string comment = GetRandomComment(); Print(">>> DCA SELL #", sellCount + 1, " lots=", NormLots(dcaLots), " dist=", DoubleToString(distance, 1)); PlaceOrder(POSITION_TYPE_SELL, dcaLots, 0, InpSLPips, comment); SetRandomDelay(); } } } } //+------------------------------------------------------------------+ //| PROCESS FIRST ORDER (NEW ENTRY) | //+------------------------------------------------------------------+ void ProcessFirstOrder() { int buyCount = CountOrders(POSITION_TYPE_BUY); int sellCount = CountOrders(POSITION_TYPE_SELL); // Chỉ mở lệnh mới khi chưa có chuỗi nào if(buyCount > 0 || sellCount > 0) return; // Debug log mỗi 60 giây static datetime lastDebug = 0; bool doDebug = (TimeCurrent() - lastDebug >= 60); // Kiểm tra điều kiện if(!IsSpreadOK()) { if(doDebug) { Print("DEBUG: Spread blocked. Current=", DoubleToString(GetSpreadPips(),1), " Max=", InpMaxSpread); lastDebug=TimeCurrent(); } return; } if(!IsTradingTimeOK()) { if(doDebug) { Print("DEBUG: Trading time blocked."); lastDebug=TimeCurrent(); } return; } if(!CanOpenNewOrder()) { if(doDebug) { Print("DEBUG: CanOpenNewOrder blocked. OrdersToday=", g_ordersOpenedToday); lastDebug=TimeCurrent(); } return; } if(IsRiskBreached()) return; // Lấy tín hiệu Ichimoku (đã cache từ bar open) int signal = g_cachedSignal; if(signal == 0) { if(doDebug) { Print("DEBUG: No Ichimoku signal."); lastDebug=TimeCurrent(); } return; } // Kiểm tra EMA trend filter int emaTrend = GetEMATrend(); // Kiểm tra MACD trend filter int macdTrend = GetMACDTrend(); // BUY: signal > 0 && EMA uptrend (hoặc bypass) && MACD bullish (hoặc bypass) if(signal > 0 && (emaTrend >= 0) && (macdTrend >= 0)) { if(InpTradeDir == DIR_BOTH || InpTradeDir == DIR_BUY) { double tp = InpTPPips; // Nếu dùng DCA, lệnh đầu không có TP riêng (chờ TP chuỗi) if(InpUseDCA) tp = 0; string comment = GetRandomComment(); Print(">>> SIGNAL BUY: Ichimoku Cloud Break Up + EMA OK"); PlaceOrder(POSITION_TYPE_BUY, InpLots, tp, InpSLPips, comment); SetRandomDelay(); } } // SELL: signal < 0 && EMA downtrend (hoặc bypass) && MACD bearish (hoặc bypass) if(signal < 0 && (emaTrend <= 0) && (macdTrend <= 0)) { if(InpTradeDir == DIR_BOTH || InpTradeDir == DIR_SELL) { double tp = InpTPPips; if(InpUseDCA) tp = 0; string comment = GetRandomComment(); Print(">>> SIGNAL SELL: Ichimoku Cloud Break Down + EMA OK"); PlaceOrder(POSITION_TYPE_SELL, InpLots, tp, InpSLPips, comment); SetRandomDelay(); } } } //+------------------------------------------------------------------+ //| PROCESS SNIPER: Tỉa lệnh đầu chuỗi khi DCA quá nhiều | //+------------------------------------------------------------------+ void ProcessSniper() { if(!InpUseSniper) return; // Check BUY chain int buyCount = CountOrders(POSITION_TYPE_BUY); if(buyCount >= InpOrders2StartSniper) SniperForType(POSITION_TYPE_BUY, buyCount); // Check SELL chain int sellCount = CountOrders(POSITION_TYPE_SELL); if(sellCount >= InpOrders2StartSniper) SniperForType(POSITION_TYPE_SELL, sellCount); } //+------------------------------------------------------------------+ //| Sniper: Tỉa N lệnh đầu (lỗ nặng nhất) của 1 loại | //+------------------------------------------------------------------+ void SniperForType(ENUM_POSITION_TYPE type, int totalOrders) { // Tính tổng floating profit của chuỗi double floatingProfit = GetFloatingProfit(type); // Chỉ tỉa khi tổng floating profit đủ điều kiện // Ví dụ: floating = -100$, cần >= 10% → cần floating >= -90$ (giảm lỗ 10%) // Logic: đóng lệnh lỗ nặng nhất khi giá hồi đủ % if(floatingProfit >= 0) return; // Chuỗi đang lời → không cần tỉa // Thu thập thông tin lệnh struct OrderInfo { ulong ticket; double profit; datetime openTime; }; OrderInfo orders[]; ArrayResize(orders, 0); for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_posInfo.SelectByIndex(i)) { if(g_posInfo.Symbol() != _Symbol) continue; if(g_posInfo.Magic() != InpMagicID) continue; if(g_posInfo.PositionType() != type) continue; int size = ArraySize(orders); ArrayResize(orders, size + 1); orders[size].ticket = g_posInfo.Ticket(); orders[size].profit = g_posInfo.Profit() + g_posInfo.Swap() + g_posInfo.Commission(); orders[size].openTime = g_posInfo.Time(); } } if(ArraySize(orders) < InpOrders2StartSniper) return; // Sort by openTime (lệnh cũ nhất trước) - bubble sort đơn giản for(int i = 0; i < ArraySize(orders) - 1; i++) { for(int j = i + 1; j < ArraySize(orders); j++) { if(orders[j].openTime < orders[i].openTime) { OrderInfo temp = orders[i]; orders[i] = orders[j]; orders[j] = temp; } } } // Kiểm tra: tổng profit của N lệnh đầu cần tỉa double firstOrdersProfit = 0; int trimCount = MathMin(InpFirstOrdersSniper, ArraySize(orders)); for(int i = 0; i < trimCount; i++) firstOrdersProfit += orders[i].profit; // Tổng profit còn lại (sau khi tỉa) double remainProfit = floatingProfit - firstOrdersProfit; // Điều kiện: phần còn lại phải >= InpPercentSniper% so với floating hiện tại // Nghĩa là sau khi tỉa, lỗ phải giảm đi đáng kể double threshold = MathAbs(floatingProfit) * InpPercentSniper / 100.0; if(MathAbs(firstOrdersProfit) <= threshold) { // Đóng các lệnh đầu for(int i = 0; i < trimCount; i++) { g_trade.PositionClose(orders[i].ticket); Print(">>> SNIPER: Closed ticket ", orders[i].ticket, " profit=", DoubleToString(orders[i].profit, 2)); } Print(">>> SNIPER: Trimmed ", trimCount, " orders. Old float=", DoubleToString(floatingProfit, 2), " Est remain=", DoubleToString(remainProfit, 2)); } } //+------------------------------------------------------------------+ //| DISPLAY PANEL | //+------------------------------------------------------------------+ void DisplayPanel() { static datetime lastUpdate = 0; if(TimeCurrent() - lastUpdate < 3) return; lastUpdate = TimeCurrent(); int buyN = CountOrders(POSITION_TYPE_BUY); int sellN = CountOrders(POSITION_TYPE_SELL); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); string info = ""; info += "IchiDCA CCBSN v2.0 | " + _Symbol + "\n"; info += "Magic: " + IntegerToString(InpMagicID) + " | Spread: " + DoubleToString(GetSpreadPips(), 1) + "p\n"; info += "Buy: " + IntegerToString(buyN) + " | Sell: " + IntegerToString(sellN) + "\n"; info += "Float: $" + DoubleToString(GetFloatingProfit(), 2) + "\n"; info += "Orders today: " + IntegerToString(g_ordersOpenedToday) + "/" + IntegerToString(InpMaxOrdersPerDay) + "\n"; if(buyN > 1) info += "BUY avg: " + DoubleToString(GetAveragePrice(POSITION_TYPE_BUY), digits) + "\n"; if(sellN > 1) info += "SELL avg: " + DoubleToString(GetAveragePrice(POSITION_TYPE_SELL), digits) + "\n"; // CCBSN Status if(g_buyPartialDone) info += "[CCBSN BUY] Trail SL: " + DoubleToString(g_buyTrailSL, digits) + "\n"; if(g_sellPartialDone) info += "[CCBSN SELL] Trail SL: " + DoubleToString(g_sellTrailSL, digits) + "\n"; if(!IsTradingTimeOK()) info += "[OUT OF TIME]\n"; if(InpAntiDetect) info += "[STEALTH MODE]\n"; Comment(info); } //+------------------------------------------------------------------+ //| CHECK FRIDAY CLOSE | //+------------------------------------------------------------------+ void CheckFridayClose() { if(!InpCloseFriday) return; MqlDateTime dt; TimeCurrent(dt); if(dt.day_of_week == 5 && dt.hour >= InpCloseFridayHour) { if(CountAllOrders() > 0) { Print("FRIDAY EXIT: Closing all positions before weekend."); CloseAllOrders(); } } } //+------------------------------------------------------------------+ //| Check if new M5 bar has formed | //+------------------------------------------------------------------+ bool IsNewBar() { datetime barTime = iTime(_Symbol, PERIOD_M5, 0); if(barTime == 0) return false; if(barTime != g_lastBarTime) { g_lastBarTime = barTime; return true; } return false; } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Panel (throttled internally) DisplayPanel(); // Daily reset CheckDailyReset(); // === MỖI TICK: Risk & TP (phản ứng nhanh) === // Risk check (closes all if breached) if(IsRiskBreached()) return; // Equity Trailing (cần check mỗi tick) ProcessEquityTrailing(); // Friday close CheckFridayClose(); // DCA TP check (đóng chuỗi ngay khi đạt target hoặc CCBSN partial close) CheckDCATP(); // CCBSN: Trailing Stop cho phần còn lại (cần check mỗi tick) ProcessTrailingStop(); // CCBSN: Reset state nếu tất cả lệnh đã đóng if(g_buyPartialDone && CountOrders(POSITION_TYPE_BUY) == 0) ResetCCBSNState(POSITION_TYPE_BUY); if(g_sellPartialDone && CountOrders(POSITION_TYPE_SELL) == 0) ResetCCBSNState(POSITION_TYPE_SELL); // === CHỈ KHI NẾN MỚI: Signal, DCA, Sniper === if(!IsNewBar()) return; // Cache signal cho bar này g_cachedSignal = GetMasterSignal(); // Sniper: tỉa lệnh khi chuỗi quá dài ProcessSniper(); // DCA: thêm lệnh vào chuỗi hiện có ProcessDCA(); // Mở lệnh mới (chỉ khi không có chuỗi nào đang chạy) ProcessFirstOrder(); } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { if(!MQLInfoInteger(MQL_TESTER)) return 0; double profit = TesterStatistics(STAT_PROFIT); double dd = TesterStatistics(STAT_EQUITY_DD_RELATIVE); if(dd > 0.0001) return profit / dd; return profit; } //+------------------------------------------------------------------+