Files
mql5-multisignal-dca-ccbsn/IchiDCA_CCBSN_PropFirm_Fixed.mq5
T
Phạm Phú Nguyễn Hưng 4cb86ecd5f feat: MQL5 MultiSignal DCA CCBSN EA - 9 signal modes + adaptive DCA + CCBSN partial close
- 9 indicator signal modes: Ichimoku, EMA Cross, RSI, BB Bounce, Stoch, CCI, MACD, Supertrend, Momentum
- DCA multi-tier with adaptive distance and lot multiplier
- CCBSN (partial close 50% -> breakeven -> trailing)
- Sniper (trim oldest losing orders)
- Anti-Detect for Prop Firm compliance
- Python ML optimizer for DCA parameters
- Wave strategy brute-force optimizer (2240+ combos)
- 85+ optimizable inputs for MT5 Strategy Tester
- Auto-detect filling type (Exness compatibility)
- Retry logic for order closing

Built by @hungpixi | Comarai.com
2026-03-17 04:16:24 +07:00

1838 lines
68 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
//+------------------------------------------------------------------+
//| 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;
}
//+------------------------------------------------------------------+