//+------------------------------------------------------------------+ //| MultiSignal_Confluence_EA.mq5 | //| EA for MultiSignal Confluence Trading | //| FIXED - Actually trades! | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Abbey Road Tech" #property link "https://www.abbeyroadtech.com" #property version "1.13" #property strict #include //--- Input Parameters input group "=== Confluence Trading Settings ===" input int InpMinConfluence = 2; // Min signals to trade (1-3) input double InpMinStrength = 0.90; // Min signal strength (0.0-1.0) - HIGH for quality confluence trades input bool InpRequireAllAgree = true; // All signals must agree input group "=== Risk Management ===" input double InpLotSize = 0.01; // Fixed Lot size (if UseRiskPercent=false) input bool InpUseRiskPercent = true; // Use risk % for dynamic lot sizing input double InpRiskPercent = 1.0; // Risk % per trade (1.0 = 1% of account) input int InpStopLoss = 100; // Stop Loss in points input int InpTakeProfit = 200; // Take Profit in points input bool InpUseTrailingStop = true; // Use trailing stop input int InpMaxPositions = 3; // Max concurrent positions per symbol input group "=== Filters ===" input bool InpUseTrendFilter = false; // Use MA trend filter (disable for more trades) input int InpTrendMAPeriod = 50; // Trend MA period input group "=== Volatility Filter (Anti-Chop) ===" input bool InpUseVolatilityFilter = true; // Filter out low volatility / narrow bands input int InpATRPeriod = 14; // ATR period for volatility measurement input double InpMinATRPercent = 0.5; // Min ATR as % of price (0.5 = 0.5%) input bool InpUseADXFilter = true; // Use ADX trend strength filter input int InpADXPeriod = 14; // ADX period input double InpMinADX = 20.0; // Min ADX to trade (20-25 recommended) input group "=== EA Settings ===" input ulong InpMagicNumber = 777777; // Magic number input int InpSlippage = 3; // Max slippage input bool InpDebugMode = true; // Print debug info //--- Global Variables CTrade Trade; int TrendMAHandle; int ATRHandle; int ADXHandle; datetime lastBarTime = 0; int totalBuyPositions = 0; int totalSellPositions = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize trade object Trade.SetExpertMagicNumber(InpMagicNumber); Trade.SetDeviationInPoints(InpSlippage); Trade.SetTypeFilling(ORDER_FILLING_IOC); // Initialize trend filter if(InpUseTrendFilter) TrendMAHandle = iMA(_Symbol, _Period, InpTrendMAPeriod, 0, MODE_SMA, PRICE_CLOSE); // Initialize volatility filters if(InpUseVolatilityFilter) ATRHandle = iATR(_Symbol, _Period, InpATRPeriod); if(InpUseADXFilter) ADXHandle = iADX(_Symbol, _Period, InpADXPeriod); lastBarTime = iTime(_Symbol, _Period, 0); Print("=== MultiSignal Confluence EA v1.12 Initialized ==="); Print("Symbol: ", _Symbol); Print("Magic: ", InpMagicNumber); Print("Min Confluence: ", InpMinConfluence); Print("Min Strength: ", InpMinStrength); Print("Volatility Filter: ", InpUseVolatilityFilter ? "ON" : "OFF"); Print("ADX Filter: ", InpUseADXFilter ? "ON" : "OFF"); Print("This EA trades DIRECTLY - no external indicator needed!"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(TrendMAHandle != INVALID_HANDLE) IndicatorRelease(TrendMAHandle); if(ATRHandle != INVALID_HANDLE) IndicatorRelease(ATRHandle); if(ADXHandle != INVALID_HANDLE) IndicatorRelease(ADXHandle); Print("EA Deinitialized for ", _Symbol); } //+------------------------------------------------------------------+ //| Get Symbol Point | //+------------------------------------------------------------------+ double GetSymbolPoint() { // FIX: Use direct symbol info instead of CSymbolInfo to avoid cross-symbol contamination double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); // For forex pairs with 5 or 3 digits, point is 0.00001 or 0.001 // For JPY pairs, we need to handle correctly if(_Digits == 3 || _Digits == 5) point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); return point; } //+------------------------------------------------------------------+ //| Get Ask Price (FIXED - uses _Symbol directly) | //+------------------------------------------------------------------+ double GetAsk() { return SymbolInfoDouble(_Symbol, SYMBOL_ASK); } //+------------------------------------------------------------------+ //| Get Bid Price (FIXED - uses _Symbol directly) | //+------------------------------------------------------------------+ double GetBid() { return SymbolInfoDouble(_Symbol, SYMBOL_BID); } //+------------------------------------------------------------------+ //| Count Open Positions | //+------------------------------------------------------------------+ void CountOpenPositions() { totalBuyPositions = 0; totalSellPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionSelectByTicket(ticket)) { // FIX: Only count positions for the CURRENT symbol this EA instance is running on string posSymbol = PositionGetString(POSITION_SYMBOL); if(posSymbol != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) { ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(posType == POSITION_TYPE_BUY) totalBuyPositions++; else if(posType == POSITION_TYPE_SELL) totalSellPositions++; } } } } //+------------------------------------------------------------------+ //| Check Trend Filter | //+------------------------------------------------------------------+ bool CheckTrendFilter(int signalType) { if(!InpUseTrendFilter) return true; if(TrendMAHandle == INVALID_HANDLE) return true; double maValue[1]; if(CopyBuffer(TrendMAHandle, 0, 0, 1, maValue) <= 0) return true; double close = iClose(_Symbol, _Period, 0); if(signalType == 1 && close < maValue[0]) // Buy but below MA { if(InpDebugMode) Print("Trend filter BLOCKED BUY (price below MA)"); return false; } if(signalType == -1 && close > maValue[0]) // Sell but above MA { if(InpDebugMode) Print("Trend filter BLOCKED SELL (price above MA)"); return false; } return true; } //+------------------------------------------------------------------+ //| Check Volatility Filter (Anti-Chop) | //+------------------------------------------------------------------+ bool CheckVolatilityFilter() { // Check ATR (volatility) filter if(InpUseVolatilityFilter) { if(ATRHandle == INVALID_HANDLE) { if(InpDebugMode) Print("Volatility filter: ATR handle invalid, allowing trade"); return true; } double atrValue[1]; if(CopyBuffer(ATRHandle, 0, 0, 1, atrValue) <= 0) { if(InpDebugMode) Print("Volatility filter: Failed to get ATR, allowing trade"); return true; } double close = iClose(_Symbol, _Period, 0); double atrPercent = (atrValue[0] / close) * 100; if(atrPercent < InpMinATRPercent) { if(InpDebugMode) Print("Volatility filter BLOCKED: ATR too low (", DoubleToString(atrPercent, 2), "% < ", DoubleToString(InpMinATRPercent, 2), "%) - Narrow bands/choppy market"); return false; } } // Check ADX (trend strength) filter if(InpUseADXFilter) { if(ADXHandle == INVALID_HANDLE) { if(InpDebugMode) Print("ADX filter: Handle invalid, allowing trade"); return true; } double adxValue[1]; if(CopyBuffer(ADXHandle, 0, 0, 1, adxValue) <= 0) { if(InpDebugMode) Print("ADX filter: Failed to get ADX, allowing trade"); return true; } if(adxValue[0] < InpMinADX) { if(InpDebugMode) Print("ADX filter BLOCKED: Trend too weak (ADX ", DoubleToString(adxValue[0], 1), " < ", DoubleToString(InpMinADX, 1), ")"); return false; } } return true; } //+------------------------------------------------------------------+ //| Calculate Pivots | //+------------------------------------------------------------------+ void CalculatePivots(double &p, double &r1, double &r2, double &s1, double &s2) { double prevHigh = iHigh(_Symbol, PERIOD_D1, 1); double prevLow = iLow(_Symbol, PERIOD_D1, 1); double prevClose = iClose(_Symbol, PERIOD_D1, 1); p = (prevHigh + prevLow + prevClose) / 3.0; r1 = (p * 2) - prevLow; r2 = p + (prevHigh - prevLow); s1 = (p * 2) - prevHigh; s2 = p - (prevHigh - prevLow); } //+------------------------------------------------------------------+ //| Check for Signals | //+------------------------------------------------------------------+ void CheckSignals(int &buyCount, int &sellCount, string &sources) { buyCount = 0; sellCount = 0; sources = ""; double close = iClose(_Symbol, _Period, 0); double open = iOpen(_Symbol, _Period, 0); double high = iHigh(_Symbol, _Period, 0); double low = iLow(_Symbol, _Period, 0); //--- PIVOT SIGNALS double p, r1, r2, s1, s2; CalculatePivots(p, r1, r2, s1, s2); // Dynamic threshold based on symbol point value double point = GetSymbolPoint(); double threshold = 0.0010; // Keep original 10 pips threshold for strong signals // Buy at support if((MathAbs(close - s1) < threshold) || (MathAbs(close - s2) < threshold) || (low <= s1 && close > s1) || (low <= s2 && close > s2)) { buyCount++; sources += "P "; } // Sell at resistance else if((MathAbs(close - r1) < threshold) || (MathAbs(close - r2) < threshold) || (high >= r1 && close < r1) || (high >= r2 && close < r2)) { sellCount++; sources += "P "; } //--- CANDLESTICK SIGNALS double body = MathAbs(close - open); double range = high - low; double upperWick = high - MathMax(open, close); double lowerWick = MathMin(open, close) - low; if(range > 0) { // Bullish Hammer if(close > open && lowerWick > body * 2 && upperWick < body * 0.5 && lowerWick / range > 0.3) { buyCount++; sources += "C "; } // Bearish Shooting Star else if(close < open && upperWick > body * 2 && lowerWick < body * 0.5 && upperWick / range > 0.3) { sellCount++; sources += "C "; } // Bullish Engulfing else if(iClose(_Symbol, _Period, 1) < iOpen(_Symbol, _Period, 1) && close > open && open <= iClose(_Symbol, _Period, 1) && close >= iOpen(_Symbol, _Period, 1)) { buyCount++; sources += "C "; } // Bearish Engulfing else if(iClose(_Symbol, _Period, 1) > iOpen(_Symbol, _Period, 1) && close < open && open >= iClose(_Symbol, _Period, 1) && close <= iOpen(_Symbol, _Period, 1)) { sellCount++; sources += "C "; } } //--- HARMONIC SIGNALS (simplified) if(iBars(_Symbol, _Period) > 5) { double X = iHigh(_Symbol, _Period, 4); double A = iLow(_Symbol, _Period, 3); double B = iHigh(_Symbol, _Period, 2); double C = iLow(_Symbol, _Period, 1); double D = close; double XA = MathAbs(A - X); double AB = MathAbs(B - A); double BC = MathAbs(C - B); double CD = MathAbs(D - C); if(XA > 0 && AB > 0 && BC > 0) { double ab_xa = AB / XA; double bc_ab = BC / AB; double cd_bc = CD / BC; // Bullish AB=CD if(X > A && A < B && B > C && C < D && ab_xa >= 0.5 && ab_xa <= 0.8 && bc_ab >= 0.5 && bc_ab <= 0.8 && cd_bc >= 0.9 && cd_bc <= 1.1) { buyCount++; sources += "H "; } // Bearish AB=CD else if(X < A && A > B && B < C && C > D && ab_xa >= 0.5 && ab_xa <= 0.8 && bc_ab >= 0.5 && bc_ab <= 0.8 && cd_bc >= 0.9 && cd_bc <= 1.1) { sellCount++; sources += "H "; } } } } //+------------------------------------------------------------------+ //| Calculate dynamic lot size based on risk % | //+------------------------------------------------------------------+ double CalculateLotSize(double slPoints) { if(!InpUseRiskPercent) return InpLotSize; double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); if(tickSize <= 0 || slPoints <= 0) return InpLotSize; // Calculate risk amount double riskAmount = accountBalance * InpRiskPercent / 100.0; // Calculate lot size: RiskAmount / (SL_Points * TickValue / TickSize) double lots = riskAmount / (slPoints * tickValue / tickSize); // Normalize to lot step lots = MathFloor(lots / lotStep) * lotStep; // Apply min/max limits lots = MathMax(minLot, MathMin(maxLot, lots)); Print("Risk Lot Calc: Balance=", accountBalance, " Risk%=", InpRiskPercent, " SL=", slPoints, " Lots=", lots); return lots; } //+------------------------------------------------------------------+ //| Validate SL/TP Prices | //+------------------------------------------------------------------+ bool ValidatePrices(double &sl, double &tp, double price, bool isBuy) { double point = GetSymbolPoint(); double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point; // Ensure minimum distance from price if(isBuy) { // For BUY: SL must be below price, TP above double minSL = price - minStopLevel; double maxTP = price + minStopLevel; if(sl > minSL) { sl = minSL - point; Print("SL adjusted to minimum distance for BUY: ", DoubleToString(sl, _Digits)); } if(tp < maxTP) { tp = maxTP + point; Print("TP adjusted to minimum distance for BUY: ", DoubleToString(tp, _Digits)); } } else { // For SELL: SL must be above price, TP below double minSL = price + minStopLevel; double maxTP = price - minStopLevel; if(sl < minSL) { sl = minSL + point; Print("SL adjusted to minimum distance for SELL: ", DoubleToString(sl, _Digits)); } if(tp > maxTP) { tp = maxTP - point; Print("TP adjusted to minimum distance for SELL: ", DoubleToString(tp, _Digits)); } } // Sanity check - ensure SL and TP are reasonable double priceDiffSL = MathAbs(sl - price); double priceDiffTP = MathAbs(tp - price); double maxDiff = 10000 * point; // Max 10000 pips if(priceDiffSL > maxDiff || priceDiffTP > maxDiff) { Print("ERROR: SL/TP prices seem invalid! SL:", DoubleToString(sl, _Digits), " TP:", DoubleToString(tp, _Digits), " Price:", DoubleToString(price, _Digits)); return false; } // Check for frozen/stale prices (possible cross-symbol contamination) double currentAsk = GetAsk(); double currentBid = GetBid(); double priceDiff = MathAbs(price - (isBuy ? currentAsk : currentBid)); if(priceDiff > 100 * point) // If price is more than 100 pips different { Print("WARNING: Price mismatch detected! Using:", DoubleToString(price, _Digits), " Current:", DoubleToString(isBuy ? currentAsk : currentBid, _Digits)); // Update to current price price = isBuy ? currentAsk : currentBid; // Recalculate SL/TP if(isBuy) { sl = price - InpStopLoss * point; tp = price + InpTakeProfit * point; } else { sl = price + InpStopLoss * point; tp = price - InpTakeProfit * point; } } return true; } //+------------------------------------------------------------------+ //| Open Buy Position (FIXED) | //+------------------------------------------------------------------+ void OpenBuyPosition(double strength, string sources) { // FIX: Use direct symbol functions instead of CSymbolInfo double ask = GetAsk(); double bid = GetBid(); double point = GetSymbolPoint(); if(ask <= 0 || bid <= 0) { Print("ERROR: Invalid prices for ", _Symbol, " Ask:", ask, " Bid:", bid); return; } double lotSize = CalculateLotSize(InpStopLoss); double sl = ask - InpStopLoss * point; double tp = ask + InpTakeProfit * point; // Validate and adjust prices if(!ValidatePrices(sl, tp, ask, true)) { Print("ERROR: Price validation failed for BUY on ", _Symbol); return; } string comment = "Conf BUY (" + sources + ")"; Print("Attempting BUY on ", _Symbol, " Ask:", DoubleToString(ask, _Digits), " SL:", DoubleToString(sl, _Digits), " TP:", DoubleToString(tp, _Digits)); if(Trade.Buy(lotSize, _Symbol, ask, sl, tp, comment)) { Print("✅ BUY executed on ", _Symbol, "! Strength: ", DoubleToString(strength, 2), " Sources: ", sources, " Lots: ", lotSize); } else { Print("❌ BUY failed on ", _Symbol, ": ", Trade.ResultRetcodeDescription(), " (code: ", Trade.ResultRetcode(), ")"); } } //+------------------------------------------------------------------+ //| Open Sell Position (FIXED) | //+------------------------------------------------------------------+ void OpenSellPosition(double strength, string sources) { // FIX: Use direct symbol functions instead of CSymbolInfo double ask = GetAsk(); double bid = GetBid(); double point = GetSymbolPoint(); if(ask <= 0 || bid <= 0) { Print("ERROR: Invalid prices for ", _Symbol, " Ask:", ask, " Bid:", bid); return; } double lotSize = CalculateLotSize(InpStopLoss); double sl = bid + InpStopLoss * point; double tp = bid - InpTakeProfit * point; // Validate and adjust prices if(!ValidatePrices(sl, tp, bid, false)) { Print("ERROR: Price validation failed for SELL on ", _Symbol); return; } string comment = "Conf SELL (" + sources + ")"; Print("Attempting SELL on ", _Symbol, " Bid:", DoubleToString(bid, _Digits), " SL:", DoubleToString(sl, _Digits), " TP:", DoubleToString(tp, _Digits)); if(Trade.Sell(lotSize, _Symbol, bid, sl, tp, comment)) { Print("✅ SELL executed on ", _Symbol, "! Strength: ", DoubleToString(strength, 2), " Sources: ", sources, " Lots: ", lotSize); } else { Print("❌ SELL failed on ", _Symbol, ": ", Trade.ResultRetcodeDescription(), " (code: ", Trade.ResultRetcode(), ")"); } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check for new bar datetime currentBarTime = iTime(_Symbol, _Period, 0); bool isNewBar = (currentBarTime != lastBarTime); // Update trailing stops on every tick if(InpUseTrailingStop && !isNewBar) { // Simple trailing stop logic can be added here } // Only process on new bar if(!isNewBar) return; lastBarTime = currentBarTime; // Count positions (only for current symbol) CountOpenPositions(); // Check max positions if(totalBuyPositions + totalSellPositions >= InpMaxPositions) { if(InpDebugMode) Print(_Symbol, " Max positions reached (", totalBuyPositions + totalSellPositions, "/", InpMaxPositions, ")"); return; } // Check for signals int buyCount = 0, sellCount = 0; string sources = ""; CheckSignals(buyCount, sellCount, sources); if(InpDebugMode) Print(_Symbol, " Bar ", TimeToString(currentBarTime), " Buy: ", buyCount, " Sell: ", sellCount, " Sources: ", sources); // Calculate strength double strength = 0; if(buyCount > 0) strength = 0.6 + (buyCount * 0.15); if(sellCount > 0) strength = -(0.6 + (sellCount * 0.15)); if(strength > 1.0) strength = 1.0; if(strength < -1.0) strength = -1.0; // Check minimum strength if(MathAbs(strength) < InpMinStrength) { if(InpDebugMode) Print(_Symbol, " Strength too low: ", DoubleToString(MathAbs(strength), 2), " < ", InpMinStrength); return; } // Check volatility filter (prevents trading in narrow bands / choppy markets) if(!CheckVolatilityFilter()) return; // BUY Signal if(buyCount >= InpMinConfluence && (!InpRequireAllAgree || sellCount == 0)) { if(!CheckTrendFilter(1)) return; if(totalBuyPositions < InpMaxPositions) { Print("🟢 CONFLUENCE BUY on ", _Symbol, "! Signals: ", buyCount, " Strength: ", DoubleToString(strength, 2)); OpenBuyPosition(strength, sources); } } // SELL Signal else if(sellCount >= InpMinConfluence && (!InpRequireAllAgree || buyCount == 0)) { if(!CheckTrendFilter(-1)) return; if(totalSellPositions < InpMaxPositions) { Print("🔴 CONFLUENCE SELL on ", _Symbol, "! Signals: ", sellCount, " Strength: ", DoubleToString(MathAbs(strength), 2)); OpenSellPosition(MathAbs(strength), sources); } } } //+------------------------------------------------------------------+