//+------------------------------------------------------------------+ //| SmartBot.mq5 | //| Advanced Multi-Timeframe Trading System with AI Assistance | //| Features: Dashboard, Signal Validator, S/D Detector, News Filter| //| Smart TP/SL, Trendline Recognition, Session Heatmap, Trade Log | //| Adaptive Scalping/Swing Modes + AI Suggestions | //+------------------------------------------------------------------+ #property strict #include #include #include // Define WebRequest error constants if not already defined #ifndef ERR_WEBREQUEST_INVALID_ADDRESS #define ERR_WEBREQUEST_INVALID_ADDRESS 4014 #endif #ifndef ERR_WEBREQUEST_CONNECT_FAILED #define ERR_WEBREQUEST_CONNECT_FAILED 4015 #endif #ifndef ERR_WEBREQUEST_REQUEST_FAILED #define ERR_WEBREQUEST_REQUEST_FAILED 4016 #endif #ifndef ERR_WEBREQUEST_TIMEOUT #define ERR_WEBREQUEST_TIMEOUT 4017 #endif #ifndef ERR_WEBREQUEST_INVALID_PARAMETER #define ERR_WEBREQUEST_INVALID_PARAMETER 4018 #endif #ifndef ERR_WEBREQUEST_NOT_ALLOWED #define ERR_WEBREQUEST_NOT_ALLOWED 4019 #endif CTrade trade; CSymbolInfo symbolInfoGlobal; //==================== Inputs ==================== enum ENUM_Mode { MODE_SCALPING=0, MODE_INTRADAY=1, MODE_SWING=2 }; // Multi Timeframe Confirmation Settings input group "=== Multi Timeframe Confirmation ===" input bool EnableMTFConfirmation = true; // Enable MTF Confirmation input double MTF_MinScore = 20.0; // MTF Minimum Score (diturunkan dari 40 untuk lebih agresif) input bool MTF_ApplyToXAUUSD = true; // Apply MTF to XAUUSD only input bool MTF_ApplyToAllPairs = false; // Apply MTF to all pairs input bool MTF_PreventOppositeEntry = true; // Prevent opposite entry when position is open // Global variables for timeframe tracking ENUM_TIMEFRAMES currentTimeframe = PERIOD_CURRENT; bool timeframeChanged = false; // Global variables untuk menyimpan nilai indikator sebelumnya untuk debugging double lastRsi = 0; double lastAdx = 0; double lastEmaF = 0; double lastEmaS = 0; double lastStochK = 0; double lastStochD = 0; double lastVolume = 0; // Global variables untuk toggle buttons bool rsiEnabled = true; bool adxEnabled = true; bool stochEnabled = true; bool mtfApplyToAllPairsEnabled = false; // Toggle untuk MTF_ApplyToAllPairs bool sidewaysDisableTradingEnabled = false; // Toggle untuk Sideways_DisableTrading bool breakoutConfirmationEnabled = false; // Toggle untuk Breakout Confirmation bool engulfingConfirmationEnabled = false; // Toggle untuk Engulfing Confirmation // Global variables untuk re-entry mechanism int buyReEntryCount = 0; int sellReEntryCount = 0; datetime lastBuySignalTime = 0; datetime lastSellSignalTime = 0; // Global MTF Indicator Handles untuk real-time updates int hEmaF_H1 = INVALID_HANDLE, hEmaS_H1 = INVALID_HANDLE, hRsi_H1 = INVALID_HANDLE, hAdx_H1 = INVALID_HANDLE, hStoch_H1 = INVALID_HANDLE; int hEmaF_M15 = INVALID_HANDLE, hEmaS_M15 = INVALID_HANDLE, hRsi_M15 = INVALID_HANDLE, hAdx_M15 = INVALID_HANDLE, hStoch_M15 = INVALID_HANDLE; int hEmaF_M5 = INVALID_HANDLE, hEmaS_M5 = INVALID_HANDLE, hRsi_M5 = INVALID_HANDLE, hAdx_M5 = INVALID_HANDLE, hStoch_M5 = INVALID_HANDLE; int hEmaF_M1 = INVALID_HANDLE, hEmaS_M1 = INVALID_HANDLE, hRsi_M1 = INVALID_HANDLE, hAdx_M1 = INVALID_HANDLE, hStoch_M1 = INVALID_HANDLE; // Global variables untuk auto spread adjustment double averageSpread = 0; int spreadSampleCount = 0; // MTFConfirmation struct definition struct MTFConfirmation { bool h1_buy, h1_sell; bool m15_buy, m15_sell; bool m5_buy, m5_sell; bool m1_buy, m1_sell; double h1_buy_strength, h1_sell_strength; double m15_buy_strength, m15_sell_strength; double m5_buy_strength, m5_sell_strength; double m1_buy_strength, m1_sell_strength; double total_score; double total_buy_score; double total_sell_score; double net_score; string reason; // Copy constructor to fix deprecation warnings MTFConfirmation(const MTFConfirmation& other) { h1_buy = other.h1_buy; h1_sell = other.h1_sell; m15_buy = other.m15_buy; m15_sell = other.m15_sell; m5_buy = other.m5_buy; m5_sell = other.m5_sell; m1_buy = other.m1_buy; m1_sell = other.m1_sell; h1_buy_strength = other.h1_buy_strength; h1_sell_strength = other.h1_sell_strength; m15_buy_strength = other.m15_buy_strength; m15_sell_strength = other.m15_sell_strength; m5_buy_strength = other.m5_buy_strength; m5_sell_strength = other.m5_sell_strength; m1_buy_strength = other.m1_buy_strength; m1_sell_strength = other.m1_sell_strength; total_score = other.total_score; total_buy_score = other.total_buy_score; total_sell_score = other.total_sell_score; net_score = other.net_score; reason = other.reason; } // Default constructor MTFConfirmation() { h1_buy = h1_sell = m15_buy = m15_sell = m5_buy = m5_sell = m1_buy = m1_sell = false; h1_buy_strength = h1_sell_strength = m15_buy_strength = m15_sell_strength = m5_buy_strength = m5_sell_strength = m1_buy_strength = m1_sell_strength = 0; total_score = 0; total_buy_score = 0; total_sell_score = 0; net_score = 0; reason = ""; } }; // Global variables untuk MTF signal tracking dan position management MTFConfirmation lastMTFSignal; bool lastMTFSignalValid = false; datetime lastMTFSignalTime = 0; // Global variables untuk sideway market detection bool isSidewaysMarket = false; int sidewaysConfidence = 0; // 0-100, semakin tinggi semakin yakin sideway string sidewaysReason = ""; datetime lastSidewaysCheck = 0; //==================== Constants ==================== #define BUY 1 #define SELL -1 //==================== Breakout & Engulfing Structures ==================== // Support/Resistance Level Structure struct SRLevel { double price; int strength; // Number of touches datetime lastTouch; bool isResistance; int barIndex; }; // Engulfing Pattern Types enum ENUM_ENGULFING_TYPE { BULLISH_ENGULFING, BEARISH_ENGULFING, DOJI_ENGULFING, HAMMER_ENGULFING, NO_ENGULFING }; // Engulfing Pattern Structure struct EngulfingPattern { ENUM_ENGULFING_TYPE type; double strength; // 0.0 to 1.0 bool isValid; string reason; int barIndex; }; // Global arrays untuk S/R levels SRLevel srLevels[]; int srLevelCount = 0; //==================== Timeframe-Specific Confirmation ==================== // Timeframe awareness untuk confirmation struct TimeframeCache { datetime lastCheck; datetime lastEngulfingCheck; bool breakoutValid; bool engulfingValid; double breakoutLevel; ENUM_ENGULFING_TYPE lastEngulfingType; double engulfingStrength; string engulfingReason; int lastEngulfingDirection; // BUY or SELL }; TimeframeCache tfCache; // Function to reset all indicator handles when timeframe changes void ResetIndicatorHandles() { EssentialLog("🔄 ResetIndicatorHandles: Starting handle reset..."); // Release existing handles if(hEmaF != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing EMA Fast handle " + IntegerToString(hEmaF)); IndicatorRelease(hEmaF); hEmaF = INVALID_HANDLE; } if(hEmaS != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing EMA Slow handle " + IntegerToString(hEmaS)); IndicatorRelease(hEmaS); hEmaS = INVALID_HANDLE; } if(hRsi != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing RSI handle " + IntegerToString(hRsi)); IndicatorRelease(hRsi); hRsi = INVALID_HANDLE; } if(hAdx != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing ADX handle " + IntegerToString(hAdx)); IndicatorRelease(hAdx); hAdx = INVALID_HANDLE; } if(hAtr != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing ATR handle " + IntegerToString(hAtr)); IndicatorRelease(hAtr); hAtr = INVALID_HANDLE; } if(hStoch != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing Stochastic handle " + IntegerToString(hStoch)); IndicatorRelease(hStoch); hStoch = INVALID_HANDLE; } if(hVolume != INVALID_HANDLE) { EssentialLog("🔄 ResetIndicatorHandles: Releasing Volume handle " + IntegerToString(hVolume)); IndicatorRelease(hVolume); hVolume = INVALID_HANDLE; } EssentialLog("✅ ResetIndicatorHandles: All handles reset for new timeframe: " + EnumToString(currentTimeframe)); // Reset MTF handles if enabled if(EnableMTFConfirmation) { EssentialLog("🔄 ResetIndicatorHandles: Resetting MTF handles..."); ReleaseMTFHandles(); InitializeMTFHandles(); } // Force chart refresh to ensure new handles are properly initialized ChartRedraw(); Sleep(100); // Small delay to ensure handles are properly released } // Function to initialize MTF indicator handles void InitializeMTFHandles() { if(!EnableMTFConfirmation) return; EssentialLog("🔄 InitializeMTFHandles: Initializing MTF indicator handles..."); // Initialize H1 handles hEmaF_H1 = iMA(_Symbol, PERIOD_H1, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE); hEmaS_H1 = iMA(_Symbol, PERIOD_H1, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE); hRsi_H1 = iRSI(_Symbol, PERIOD_H1, RSI_Period, PRICE_CLOSE); hAdx_H1 = iADX(_Symbol, PERIOD_H1, ADX_Period); hStoch_H1 = iStochastic(_Symbol, PERIOD_H1, Stochastic_K, Stochastic_D, Stochastic_Slow, MODE_SMA, STO_LOWHIGH); // Initialize M15 handles hEmaF_M15 = iMA(_Symbol, PERIOD_M15, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE); hEmaS_M15 = iMA(_Symbol, PERIOD_M15, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE); hRsi_M15 = iRSI(_Symbol, PERIOD_M15, RSI_Period, PRICE_CLOSE); hAdx_M15 = iADX(_Symbol, PERIOD_M15, ADX_Period); hStoch_M15 = iStochastic(_Symbol, PERIOD_M15, Stochastic_K, Stochastic_D, Stochastic_Slow, MODE_SMA, STO_LOWHIGH); // Initialize M5 handles hEmaF_M5 = iMA(_Symbol, PERIOD_M5, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE); hEmaS_M5 = iMA(_Symbol, PERIOD_M5, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE); hRsi_M5 = iRSI(_Symbol, PERIOD_M5, RSI_Period, PRICE_CLOSE); hAdx_M5 = iADX(_Symbol, PERIOD_M5, ADX_Period); hStoch_M5 = iStochastic(_Symbol, PERIOD_M5, Stochastic_K, Stochastic_D, Stochastic_Slow, MODE_SMA, STO_LOWHIGH); // Initialize M1 handles hEmaF_M1 = iMA(_Symbol, PERIOD_M1, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE); hEmaS_M1 = iMA(_Symbol, PERIOD_M1, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE); hRsi_M1 = iRSI(_Symbol, PERIOD_M1, RSI_Period, PRICE_CLOSE); hAdx_M1 = iADX(_Symbol, PERIOD_M1, ADX_Period); hStoch_M1 = iStochastic(_Symbol, PERIOD_M1, Stochastic_K, Stochastic_D, Stochastic_Slow, MODE_SMA, STO_LOWHIGH); EssentialLog("✅ InitializeMTFHandles: MTF handles initialized successfully"); } // Function to release MTF indicator handles void ReleaseMTFHandles() { EssentialLog("🔄 ReleaseMTFHandles: Releasing MTF indicator handles..."); // Release H1 handles if(hEmaF_H1 != INVALID_HANDLE) { IndicatorRelease(hEmaF_H1); hEmaF_H1 = INVALID_HANDLE; } if(hEmaS_H1 != INVALID_HANDLE) { IndicatorRelease(hEmaS_H1); hEmaS_H1 = INVALID_HANDLE; } if(hRsi_H1 != INVALID_HANDLE) { IndicatorRelease(hRsi_H1); hRsi_H1 = INVALID_HANDLE; } if(hAdx_H1 != INVALID_HANDLE) { IndicatorRelease(hAdx_H1); hAdx_H1 = INVALID_HANDLE; } if(hStoch_H1 != INVALID_HANDLE) { IndicatorRelease(hStoch_H1); hStoch_H1 = INVALID_HANDLE; } // Release M15 handles if(hEmaF_M15 != INVALID_HANDLE) { IndicatorRelease(hEmaF_M15); hEmaF_M15 = INVALID_HANDLE; } if(hEmaS_M15 != INVALID_HANDLE) { IndicatorRelease(hEmaS_M15); hEmaS_M15 = INVALID_HANDLE; } if(hRsi_M15 != INVALID_HANDLE) { IndicatorRelease(hRsi_M15); hRsi_M15 = INVALID_HANDLE; } if(hAdx_M15 != INVALID_HANDLE) { IndicatorRelease(hAdx_M15); hAdx_M15 = INVALID_HANDLE; } if(hStoch_M15 != INVALID_HANDLE) { IndicatorRelease(hStoch_M15); hStoch_M15 = INVALID_HANDLE; } // Release M5 handles if(hEmaF_M5 != INVALID_HANDLE) { IndicatorRelease(hEmaF_M5); hEmaF_M5 = INVALID_HANDLE; } if(hEmaS_M5 != INVALID_HANDLE) { IndicatorRelease(hEmaS_M5); hEmaS_M5 = INVALID_HANDLE; } if(hRsi_M5 != INVALID_HANDLE) { IndicatorRelease(hRsi_M5); hRsi_M5 = INVALID_HANDLE; } if(hAdx_M5 != INVALID_HANDLE) { IndicatorRelease(hAdx_M5); hAdx_M5 = INVALID_HANDLE; } if(hStoch_M5 != INVALID_HANDLE) { IndicatorRelease(hStoch_M5); hStoch_M5 = INVALID_HANDLE; } // Release M1 handles if(hEmaF_M1 != INVALID_HANDLE) { IndicatorRelease(hEmaF_M1); hEmaF_M1 = INVALID_HANDLE; } if(hEmaS_M1 != INVALID_HANDLE) { IndicatorRelease(hEmaS_M1); hEmaS_M1 = INVALID_HANDLE; } if(hRsi_M1 != INVALID_HANDLE) { IndicatorRelease(hRsi_M1); hRsi_M1 = INVALID_HANDLE; } if(hAdx_M1 != INVALID_HANDLE) { IndicatorRelease(hAdx_M1); hAdx_M1 = INVALID_HANDLE; } if(hStoch_M1 != INVALID_HANDLE) { IndicatorRelease(hStoch_M1); hStoch_M1 = INVALID_HANDLE; } EssentialLog("✅ ReleaseMTFHandles: All MTF handles released"); } // Function to create toggle buttons on chart void CreateToggleButtons() { if(!ShowToggleButtons) return; // Calculate position at bottom of dashboard int buttonY = 500; // Position at bottom int buttonHeight = 25; int buttonWidth = 85; int buttonSpacing = 5; int startX = 10; // RSI Toggle Button string rsiButtonName = "RSI_Toggle_Button"; string rsiButtonText = "RSI: " + (rsiEnabled ? "ON" : "OFF"); color rsiButtonColor = rsiEnabled ? clrLimeGreen : clrRed; if(ObjectFind(0, rsiButtonName) < 0) { ObjectCreate(0, rsiButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, rsiButtonName, OBJPROP_TEXT, rsiButtonText); ObjectSetInteger(0, rsiButtonName, OBJPROP_BGCOLOR, rsiButtonColor); ObjectSetInteger(0, rsiButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, rsiButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, rsiButtonName, OBJPROP_XDISTANCE, startX); ObjectSetInteger(0, rsiButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, rsiButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, rsiButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, rsiButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, rsiButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, rsiButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, rsiButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, rsiButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, rsiButtonName, OBJPROP_ZORDER, 1000); // ADX Toggle Button string adxButtonName = "ADX_Toggle_Button"; string adxButtonText = "ADX: " + (adxEnabled ? "ON" : "OFF"); color adxButtonColor = adxEnabled ? clrLimeGreen : clrRed; if(ObjectFind(0, adxButtonName) < 0) { ObjectCreate(0, adxButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, adxButtonName, OBJPROP_TEXT, adxButtonText); ObjectSetInteger(0, adxButtonName, OBJPROP_BGCOLOR, adxButtonColor); ObjectSetInteger(0, adxButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, adxButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, adxButtonName, OBJPROP_XDISTANCE, startX + buttonWidth + buttonSpacing); ObjectSetInteger(0, adxButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, adxButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, adxButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, adxButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, adxButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, adxButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, adxButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, adxButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, adxButtonName, OBJPROP_ZORDER, 1000); // Stochastic Toggle Button string stochButtonName = "Stoch_Toggle_Button"; string stochButtonText = "Stoch: " + (stochEnabled ? "ON" : "OFF"); color stochButtonColor = stochEnabled ? clrLimeGreen : clrRed; if(ObjectFind(0, stochButtonName) < 0) { ObjectCreate(0, stochButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, stochButtonName, OBJPROP_TEXT, stochButtonText); ObjectSetInteger(0, stochButtonName, OBJPROP_BGCOLOR, stochButtonColor); ObjectSetInteger(0, stochButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, stochButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, stochButtonName, OBJPROP_XDISTANCE, startX + (buttonWidth + buttonSpacing) * 2); ObjectSetInteger(0, stochButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, stochButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, stochButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, stochButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, stochButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, stochButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, stochButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, stochButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, stochButtonName, OBJPROP_ZORDER, 1000); // MTF Apply to All Pairs Toggle Button string mtfAllPairsButtonName = "MTF_AllPairs_Toggle_Button"; string mtfAllPairsButtonText = "MTF All: " + (mtfApplyToAllPairsEnabled ? "ON" : "OFF"); color mtfAllPairsButtonColor = mtfApplyToAllPairsEnabled ? clrLimeGreen : clrRed; if(ObjectFind(0, mtfAllPairsButtonName) < 0) { ObjectCreate(0, mtfAllPairsButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, mtfAllPairsButtonName, OBJPROP_TEXT, mtfAllPairsButtonText); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_BGCOLOR, mtfAllPairsButtonColor); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_XDISTANCE, startX + (buttonWidth + buttonSpacing) * 3); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, mtfAllPairsButtonName, OBJPROP_ZORDER, 1000); // Sideways Disable Trading Toggle Button string sidewaysDisableButtonName = "Sideways_Disable_Toggle_Button"; string sidewaysDisableButtonText = "SDWY: " + (sidewaysDisableTradingEnabled ? "DISABLE" : "ENABLE"); color sidewaysDisableButtonColor = sidewaysDisableTradingEnabled ? clrRed : clrLimeGreen; if(ObjectFind(0, sidewaysDisableButtonName) < 0) { ObjectCreate(0, sidewaysDisableButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, sidewaysDisableButtonName, OBJPROP_TEXT, sidewaysDisableButtonText); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_BGCOLOR, sidewaysDisableButtonColor); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_XDISTANCE, startX + (buttonWidth + buttonSpacing) * 4); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, sidewaysDisableButtonName, OBJPROP_ZORDER, 1000); // Breakout Confirmation Toggle Button string breakoutButtonName = "Breakout_Toggle_Button"; string breakoutButtonText = "Breakout: " + (breakoutConfirmationEnabled ? "ON" : "OFF"); color breakoutButtonColor = breakoutConfirmationEnabled ? clrLimeGreen : clrRed; if(ObjectFind(0, breakoutButtonName) < 0) { ObjectCreate(0, breakoutButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, breakoutButtonName, OBJPROP_TEXT, breakoutButtonText); ObjectSetInteger(0, breakoutButtonName, OBJPROP_BGCOLOR, breakoutButtonColor); ObjectSetInteger(0, breakoutButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, breakoutButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, breakoutButtonName, OBJPROP_XDISTANCE, startX + (buttonWidth + buttonSpacing) * 5); ObjectSetInteger(0, breakoutButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, breakoutButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, breakoutButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, breakoutButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, breakoutButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, breakoutButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, breakoutButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, breakoutButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, breakoutButtonName, OBJPROP_ZORDER, 1000); // Engulfing Confirmation Toggle Button string engulfingButtonName = "Engulfing_Toggle_Button"; string engulfingButtonText = "Engulfing: " + (engulfingConfirmationEnabled ? "ON" : "OFF"); color engulfingButtonColor = engulfingConfirmationEnabled ? clrLimeGreen : clrRed; if(ObjectFind(0, engulfingButtonName) < 0) { ObjectCreate(0, engulfingButtonName, OBJ_BUTTON, 0, 0, 0); } ObjectSetString(0, engulfingButtonName, OBJPROP_TEXT, engulfingButtonText); ObjectSetInteger(0, engulfingButtonName, OBJPROP_BGCOLOR, engulfingButtonColor); ObjectSetInteger(0, engulfingButtonName, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, engulfingButtonName, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, engulfingButtonName, OBJPROP_XDISTANCE, startX + (buttonWidth + buttonSpacing) * 6); ObjectSetInteger(0, engulfingButtonName, OBJPROP_YDISTANCE, buttonY); ObjectSetInteger(0, engulfingButtonName, OBJPROP_XSIZE, buttonWidth); ObjectSetInteger(0, engulfingButtonName, OBJPROP_YSIZE, buttonHeight); ObjectSetInteger(0, engulfingButtonName, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, engulfingButtonName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, engulfingButtonName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, engulfingButtonName, OBJPROP_SELECTED, false); ObjectSetInteger(0, engulfingButtonName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, engulfingButtonName, OBJPROP_ZORDER, 1000); ChartRedraw(); } // Function to delete toggle buttons void DeleteToggleButtons() { ObjectDelete(0, "RSI_Toggle_Button"); ObjectDelete(0, "ADX_Toggle_Button"); ObjectDelete(0, "Stoch_Toggle_Button"); ObjectDelete(0, "MTF_AllPairs_Toggle_Button"); ObjectDelete(0, "Sideways_Disable_Toggle_Button"); ObjectDelete(0, "Breakout_Toggle_Button"); ObjectDelete(0, "Engulfing_Toggle_Button"); ChartRedraw(); } // Function to handle button clicks void HandleButtonClick(string objectName) { if(objectName == "RSI_Toggle_Button") { rsiEnabled = !rsiEnabled; EssentialLog("🔄 RSI Toggle: " + (rsiEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } else if(objectName == "ADX_Toggle_Button") { adxEnabled = !adxEnabled; EssentialLog("🔄 ADX Toggle: " + (adxEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } else if(objectName == "Stoch_Toggle_Button") { stochEnabled = !stochEnabled; EssentialLog("🔄 Stochastic Toggle: " + (stochEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } else if(objectName == "MTF_AllPairs_Toggle_Button") { mtfApplyToAllPairsEnabled = !mtfApplyToAllPairsEnabled; EssentialLog("🔄 MTF Apply to All Pairs Toggle: " + (mtfApplyToAllPairsEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } else if(objectName == "Sideways_Disable_Toggle_Button") { sidewaysDisableTradingEnabled = !sidewaysDisableTradingEnabled; EssentialLog("🔄 Sideways Disable Trading Toggle: " + (sidewaysDisableTradingEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } else if(objectName == "Breakout_Toggle_Button") { breakoutConfirmationEnabled = !breakoutConfirmationEnabled; EssentialLog("🔄 Breakout Confirmation Toggle: " + (breakoutConfirmationEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } else if(objectName == "Engulfing_Toggle_Button") { engulfingConfirmationEnabled = !engulfingConfirmationEnabled; EssentialLog("🔄 Engulfing Confirmation Toggle: " + (engulfingConfirmationEnabled ? "ENABLED" : "DISABLED")); CreateToggleButtons(); // Update button appearance } } input ENUM_Mode Mode = MODE_SCALPING; // Mode Scalping, Intraday, Swing input bool AutoTrade = true; // Auto Trade input double RiskPercent = 1.0; // % equity per trade input int Magic = 240812; // Magic Number` // Multi-Timeframe Scanner input bool EnableMTFScanner = true; // Enable MTF Scanner input string PairsToScan = "EURUSD,GBPUSD,USDJPY,USDCHF,AUDUSD,NZDUSD,EURGBP,EURJPY"; // Pairs to scan input int MaxPairsToShow = 8; // Max pairs to show input group "=== VALIDATIONS ===" input int EMA_Fast = 8; // EMA Fast input int EMA_Slow = 13; // EMA Slow input int RSI_Period = 10; // RSI Period (dinaikkan dari 8) input int RSI_Overbought = 80; // RSI Overbought input int RSI_Oversold = 20; // RSI Oversold input int ADX_Period = 14; // ADX Period input int ADX_MinStrength = 5; // ADX Min Strength (diturunkan dari 10 untuk lebih agresif) input int ADX_MinStrength_Scalping = 3; // ADX Min Strength untuk Scalping Mode (diturunkan dari 8) input int MinConfirmations_Scalping = 1; // Min Confirmations untuk Scalping (1 = lebih agresif) input int MinConfirmations_Other = 1; // Min Confirmations untuk Mode Lain (diturunkan dari 2) input int ATR_Period = 14; // ATR Period input int Stochastic_K = 14; // Stochastic K input int Stochastic_D = 3; // Stochastic D input int Stochastic_Slow = 3; // Stochastic Slow input int MaxSpreadPoints = 1000; // MaxSpread 1000 pt ~ 10 pips (5-digit) - lebih agresif input group "=== SUPPORT & RESISTANCE ===" input bool EnableSDDetection = true; // Enable S/D Detection input int SD_Lookback = 100; // bars to look back input int SD_MinTouch = 3; // minimum touches input double SD_ZoneSize = 0.0010; // zone size in price input color SD_SupplyColor = clrRed; // Supply Color input color SD_DemandColor = clrGreen; // Demand Color input group "=== SMART TP/SL ===" input bool UseATR_TP_SL = true; // Use ATR TP/SL input double ATR_SL_Multiplier = 1.5; // ATR SL Multiplier input double ATR_TP_Multiplier = 2.0; // ATR TP Multiplier input bool UseMultiTP = true; // Use Multi TP input double TP1_Ratio = 0.5; // % of total TP input double TP2_Ratio = 0.3; // % of total TP input double TP3_Ratio = 0.2; // % of total TP input group "=== TRAILING & LOCK PROFIT ===" input int TrailStartPts = 150; // Trailing Start Points input int TrailStepPts = 80; // Trailing Step Points input int LockStartPts = 120; // when profit > this, lock input int LockOffsetPts = 20; // lock distance from BE // UseSpreadBuffer dihapus - sekarang selalu otomatis // SpreadBufferMultiplier dihapus - sekarang otomatis dari spread realtime // AutoCheckSpread dihapus - sekarang selalu otomatis // MinStopMultiplier dihapus - sekarang otomatis dari spread dan broker stop level input group "=== NEWS FILTER ===" input bool NewsPauseEnable = true; input datetime UpcomingNewsTime = D'1970.01.01 00:00'; // set manual input int PauseBeforeMin = 15; input int PauseAfterMin = 15; input string HighImpactNews = "NFP,CPI,GDP,Interest Rate,Employment"; input group "=== SESSION TRADING ===" input int TradeStartHour = 7; // broker time start input int TradeEndHour = 22; // broker time input bool EnableSessionFilter = true; // Enable Session Filter input bool TradeAsia = true; // Trade Asia input bool TradeLondon = true; // Trade London input bool TradeNewYork = true; // Trade New York input group "=== TRENDLINE RECOGNITION ===" input bool EnableTrendlines = true; // Enable Trendline Recognition input int TrendlineLookback = 50; // Trendline Lookback input int TrendlineMinTouch = 2; // Trendline Min Touch input color TrendlineColor = clrYellow; // Trendline Color input group "=== TRADE JOURNAL ===" input bool EnableTradeLog = true; // Enable Trade Log input string LogFileName = "SmartBot_Trades.csv"; // Log File Name input group "=== AI ASSIST ===" input bool AI_Assist_Enable = false; // Enable AI Assist input string AI_Endpoint_URL = ""; // contoh: http://127.0.0.1:8000/ai/trade input string AI_API_Key = ""; // AI API Key input int AI_TimeoutMs = 1200; // AI Timeout input int AI_MaxChars = 600; // AI Max Chars input bool AI_RequireApprove = false; // AI Require Approve input group "=== DEEPSEEK AI ===" input bool DeepSeek_Enable = false; // Enable DeepSeek AI input string DeepSeek_API_Key = ""; // DeepSeek API Key input string DeepSeek_Model = "deepseek-chat"; // DeepSeek Model input int DeepSeek_Timeout = 5000; // DeepSeek Timeout (ms) input int DeepSeek_MaxTokens = 500; // Max tokens for response input bool DeepSeek_RequireApprove = true; // Require manual approval input group "=== INDICATOR TOGGLE CONTROLS ===" input bool EnableRSI = true; // Enable RSI Indicator input bool EnableADX = true; // Enable ADX Indicator input bool EnableStochastic = true; // Enable Stochastic Indicator input bool ShowToggleButtons = true; // Show Toggle Buttons on Chart input group "=== CHATGPT AI ===" input bool ChatGPT_Enable = false; // Enable ChatGPT AI input string ChatGPT_API_Key = ""; // ChatGPT API Key input string ChatGPT_Model = "gpt-3.5-turbo"; // ChatGPT Model input int ChatGPT_Timeout = 5000; // ChatGPT Timeout (ms) input int ChatGPT_MaxTokens = 500; // Max tokens for response input bool ChatGPT_RequireApprove = true; // Require manual approval input group "=== RE-ENTRY MECHANISM ===" input bool EnableReEntry = true; // Enable Re-Entry Mechanism input int MaxReEntries = 3; // Maximum Re-Entries per direction input double ReEntryLotMultiplier = 1.5; // Lot multiplier for re-entries input int MinFloatingLossPts = 50; // Minimum floating loss points for re-entry input double ConservativeTrailingMultiplier = 2.0; // Conservative trailing multiplier for profit protection input bool UseConservativeTrailing = true; // Use conservative trailing to protect profits input group "=== SIDEWAYS MARKET DETECTION ===" input bool EnableSidewaysDetection = true; // Enable Sideways Market Detection input int RSI_SidewaysUpper = 65; // RSI Upper bound for sideways input int RSI_SidewaysLower = 35; // RSI Lower bound for sideways input int ADX_SidewaysMax = 20; // ADX Max value for sideways (weak trend) input int Stoch_SidewaysUpper = 70; // Stochastic Upper bound for sideways input int Stoch_SidewaysLower = 30; // Stochastic Lower bound for sideways input bool Sideways_DisableTrading = false; // Disable trading during sideways input bool Sideways_UseRangeStrategy = true; // Use range strategy during sideways input group "=== BREAKOUT & ENGULFING CONFIRMATION ===" input bool EnableBreakoutConfirmation = true; // Enable Breakout Confirmation input int BreakoutLookback = 20; // Bars to look back for S/R levels input double BreakoutThreshold = 0.0001; // Minimum breakout distance input int BreakoutConfirmationBars = 2; // Bars to confirm breakout input bool RequireVolumeSpike = true; // Require volume spike on breakout input double VolumeSpikeMultiplier = 1.5; // Volume spike threshold input bool EnableEngulfingConfirmation = true; // Enable Engulfing Confirmation input bool RequireStrongEngulfing = true; // Require strong engulfing (70%+) input double EngulfingStrengthThreshold = 0.3; // Minimum engulfing strength (diturunkan untuk lebih fleksibel) input bool CheckPreviousTrend = true; // Check previous trend direction input int TrendLookback = 5; // Bars to check previous trend input double MinEnhancedScore = 80.0; // Minimum enhanced score for entry input group "=== DEBUG & LOGGING ===" // ====== DEBUG & LOGGING ====== input bool EnableDebugLogs = false; // Enable verbose debug logging input bool EnableEssentialLogs = true; // Enable essential logs (always on) //==================== Globals ==================== double pt; int hEmaF=-1,hEmaS=-1,hRsi=-1,hAdx=-1,hAtr=-1,hStoch=-1; int hVolume=-1; //==================== Helper Functions ==================== void DebugLog(string message) { if(EnableDebugLogs) { Print("[DEBUG] ", message); } } void EssentialLog(string message) { if(EnableEssentialLogs) { Print("[INFO] ", message); } } // Forward declarations struct SignalPack; bool ValidateSignalWithMTF(SignalPack &s); //==================== AUTO SPREAD & BROKER ADJUSTMENT ==================== // Semua pengaturan otomatis berdasarkan spread realtime dan broker stop level // Tidak perlu deteksi broker manual - semua dihitung otomatis // Calculate dynamic spread buffer based on current spread (AUTO) double CalculateDynamicSpreadBuffer() { int currentSpread = SpreadPoints(); // Auto buffer berbasis spread saat ini double dynamicBuffer = 1.5; // Base multiplier if(currentSpread > 100) dynamicBuffer *= 1.5; // instrumen spread tinggi (mis. XAU) else if(currentSpread > 50) dynamicBuffer *= 1.2; // spread menengah else if(currentSpread < 10) dynamicBuffer *= 0.8; // spread sangat rendah DebugLog("📊 Auto Spread Buffer: Current=" + IntegerToString(currentSpread) + ", Buffer=" + DoubleToString(dynamicBuffer, 2)); return dynamicBuffer; } // Get adjusted trailing step based on spread (AUTO) int GetAdjustedTrailingStep(int baseTrailingStep) { int spreadPts = SpreadPoints(); double dynamicBuffer = CalculateDynamicSpreadBuffer(); double adjustedStep = MathMax((double)baseTrailingStep, spreadPts * dynamicBuffer); if(UseConservativeTrailing) adjustedStep *= ConservativeTrailingMultiplier; // Minimal sesuai broker stop level int minStepPts = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); if(adjustedStep < minStepPts) adjustedStep = minStepPts; DebugLog("🎯 Auto Trailing Step: Base=" + IntegerToString(baseTrailingStep) + ", Spread=" + IntegerToString(spreadPts) + ", Buffer=" + DoubleToString(dynamicBuffer, 2) + ", Adjusted=" + IntegerToString((int)adjustedStep) + ", Cons=" + (UseConservativeTrailing ? "ON" : "OFF")); return (int)adjustedStep; } // Get adjusted stop distance based on spread (AUTO) int GetAdjustedStopDistance(int baseStopDistance) { int spreadPts = SpreadPoints(); int brokerMinPts = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); double dynamicBuffer = CalculateDynamicSpreadBuffer(); int adjusted = MathMax(baseStopDistance, brokerMinPts); adjusted = MathMax(adjusted, (int)(spreadPts * dynamicBuffer)); DebugLog("🛑 Auto Stop Distance: Base=" + IntegerToString(baseStopDistance) + ", Spread=" + IntegerToString(spreadPts) + ", BrokerMin=" + IntegerToString(brokerMinPts) + ", Buffer=" + DoubleToString(dynamicBuffer, 2) + ", Adjusted=" + IntegerToString(adjusted)); return adjusted; } // Check if current spread is acceptable for trading (AUTO) bool IsSpreadAcceptable() { int currentSpread = SpreadPoints(); bool acceptable = currentSpread <= MaxSpreadPoints; if(!acceptable) { DebugLog("⚠️ Auto Spread Check: Too High " + IntegerToString(currentSpread) + " > " + IntegerToString(MaxSpreadPoints)); } return acceptable; } // Calculate safe trailing stop distance to protect profits double CalculateSafeTrailingStop(double entryPrice, double currentPrice, int positionType, double minDistance) { double safeDistance = minDistance; // Calculate profit in points double profitPoints = 0; if(positionType == POSITION_TYPE_BUY) { profitPoints = (currentPrice - entryPrice) / _Point; } else { profitPoints = (entryPrice - currentPrice) / _Point; } // If we have significant profit, use more conservative distance if(profitPoints > 100) { // More than 100 points profit safeDistance = MathMax(safeDistance, profitPoints * 0.3); // Keep at least 30% of profit } else if(profitPoints > 50) { // More than 50 points profit safeDistance = MathMax(safeDistance, profitPoints * 0.4); // Keep at least 40% of profit } else if(profitPoints > 20) { // More than 20 points profit safeDistance = MathMax(safeDistance, profitPoints * 0.5); // Keep at least 50% of profit } // Add extra buffer for high-spread instruments like XAUUSD if(SpreadPoints() > 100) { safeDistance += 20; // Add 20 points extra buffer } DebugLog("🛡️ Safe Trailing Distance: Profit=" + DoubleToString(profitPoints, 1) + "pts, Min=" + DoubleToString(minDistance, 1) + "pts, Safe=" + DoubleToString(safeDistance, 1) + "pts"); return safeDistance; } // Supply & Demand zones struct SDZone { double price; double high, low; int touches; bool isSupply; datetime lastTouch; string name; }; SDZone sdZones[]; int sdZoneCount = 0; // Trendlines struct Trendline { double startPrice, endPrice; datetime startTime, endTime; bool isUptrend; string name; int touches; }; Trendline trendlines[]; int trendlineCount = 0; // Trade Journal struct TradeRecord { datetime openTime; string pair; int type; double lot, openPrice, sl, tp; string reason; double closePrice; datetime closeTime; double profit; string notes; }; TradeRecord tradeHistory[]; int tradeHistoryCount = 0; //==================== Utils ==================== int SpreadPoints() { return (int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD); } //==================== Breakout Detection Functions ==================== // Find Support/Resistance levels void FindSRLevels() { if(!EnableBreakoutConfirmation) return; ArrayResize(srLevels, 0); srLevelCount = 0; double high[], low[], close[]; ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); // Increased lookback for better S/R detection int lookback = MathMax(BreakoutLookback, 50); if(CopyHigh(_Symbol, _Period, 0, lookback, high) < lookback) return; if(CopyLow(_Symbol, _Period, 0, lookback, low) < lookback) return; if(CopyClose(_Symbol, _Period, 0, lookback, close) < lookback) return; // More flexible threshold for S/R detection double threshold = MathMax(BreakoutThreshold, 10 * pt); // Find resistance levels (highs) - more flexible detection for(int i = 3; i < lookback-3; i++) { // Check if this is a significant high (more flexible) bool isHigh = true; for(int j = 1; j <= 2; j++) { if(high[i] <= high[i-j] || high[i] <= high[i+j]) { isHigh = false; break; } } if(isHigh) { // Check for touches with more flexible threshold int touches = 0; for(int j = 0; j < lookback; j++) { if(MathAbs(high[j] - high[i]) <= threshold * 2) { // Double threshold for touch detection touches++; } } if(touches >= 2) { // Minimum 2 touches ArrayResize(srLevels, srLevelCount + 1); srLevels[srLevelCount].price = high[i]; srLevels[srLevelCount].strength = touches; srLevels[srLevelCount].lastTouch = iTime(_Symbol, _Period, i); srLevels[srLevelCount].isResistance = true; srLevels[srLevelCount].barIndex = i; srLevelCount++; } } } // Find support levels (lows) - more flexible detection for(int i = 3; i < lookback-3; i++) { // Check if this is a significant low (more flexible) bool isLow = true; for(int j = 1; j <= 2; j++) { if(low[i] >= low[i-j] || low[i] >= low[i+j]) { isLow = false; break; } } if(isLow) { // Check for touches with more flexible threshold int touches = 0; for(int j = 0; j < lookback; j++) { if(MathAbs(low[j] - low[i]) <= threshold * 2) { // Double threshold for touch detection touches++; } } if(touches >= 2) { // Minimum 2 touches ArrayResize(srLevels, srLevelCount + 1); srLevels[srLevelCount].price = low[i]; srLevels[srLevelCount].strength = touches; srLevels[srLevelCount].lastTouch = iTime(_Symbol, _Period, i); srLevels[srLevelCount].isResistance = false; srLevels[srLevelCount].barIndex = i; srLevelCount++; } } } DebugLog("🔍 Found " + IntegerToString(srLevelCount) + " S/R levels (optimized detection)"); } // Find nearest S/R level SRLevel FindNearestSRLevel(int direction) { SRLevel nearest; nearest.price = 0; nearest.strength = 0; nearest.isResistance = false; nearest.barIndex = -1; if(srLevelCount == 0) return nearest; double currentPrice = (direction == BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double minDistance = 999999; double maxDistance = 100 * pt; // Maximum distance to consider (100 points) for(int i = 0; i < srLevelCount; i++) { double distance = MathAbs(currentPrice - srLevels[i].price); // Only consider levels within reasonable distance if(distance > maxDistance) continue; // For BUY, look for resistance levels above current price if(direction == BUY && srLevels[i].isResistance && srLevels[i].price > currentPrice) { if(distance < minDistance) { minDistance = distance; nearest = srLevels[i]; } } // For SELL, look for support levels below current price else if(direction == SELL && !srLevels[i].isResistance && srLevels[i].price < currentPrice) { if(distance < minDistance) { minDistance = distance; nearest = srLevels[i]; } } } return nearest; } // Check if price broke through S/R level bool IsBreakoutConfirmed(int direction) { // Skip jika bukan timeframe yang tepat if(!ShouldApplyBreakoutConfirmation()) return true; // Hanya cek pada M1 dan M5 if(!IsEntryTimeframe() && !IsSetupTimeframe()) return true; // Find S/R levels FindSRLevels(); // Find nearest S/R level SRLevel nearestLevel = FindNearestSRLevel(direction); if(nearestLevel.barIndex == -1) { DebugLog("🔍 No S/R level found for " + (direction == BUY ? "BUY" : "SELL") + " direction"); return true; // Allow entry if no S/R level found } double currentPrice = (direction == BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); // More flexible breakout threshold double breakoutThreshold = MathMax(BreakoutThreshold, 5 * pt); // Check if price broke through bool priceBreakout = false; if(direction == BUY) { priceBreakout = (currentPrice > nearestLevel.price + breakoutThreshold); } else { priceBreakout = (currentPrice < nearestLevel.price - breakoutThreshold); } if(!priceBreakout) { DebugLog("🔍 No price breakout - Current: " + DoubleToString(currentPrice, 5) + " Level: " + DoubleToString(nearestLevel.price, 5) + " Threshold: " + DoubleToString(breakoutThreshold, 5)); return false; } // Simplified confirmation bars check bool confirmationBars = CheckBreakoutConfirmationBars(direction, nearestLevel.price); // Volume spike is optional and less strict bool volumeSpike = true; // Default to true if(RequireVolumeSpike) { volumeSpike = CheckVolumeSpike(); } bool result = confirmationBars && volumeSpike; DebugLog("🔍 Breakout result: " + (result ? "CONFIRMED" : "REJECTED") + " - Price: " + (priceBreakout ? "YES" : "NO") + " Bars: " + (confirmationBars ? "YES" : "NO") + " Volume: " + (volumeSpike ? "YES" : "NO")); return result; } //==================== Engulfing Pattern Detection Functions ==================== // Detect engulfing patterns with direction alignment EngulfingPattern DetectEngulfingPattern(int direction) { EngulfingPattern pattern; pattern.type = NO_ENGULFING; pattern.strength = 0.0; pattern.isValid = false; pattern.reason = "No pattern detected"; pattern.barIndex = 0; // Skip jika bukan timeframe yang tepat if(!ShouldApplyEngulfingConfirmation()) { pattern.isValid = true; pattern.reason = "Engulfing confirmation disabled for this timeframe"; return pattern; } // Hanya cek pada M1 dan M5 if(!IsEntryTimeframe() && !IsSetupTimeframe()) { pattern.isValid = true; pattern.reason = "Not entry/setup timeframe"; return pattern; } if(!EnableEngulfingConfirmation || !engulfingConfirmationEnabled) { pattern.isValid = true; pattern.reason = "Engulfing confirmation disabled"; return pattern; } double open[], high[], low[], close[]; ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); if(CopyOpen(_Symbol, _Period, 0, 3, open) < 3) return pattern; if(CopyHigh(_Symbol, _Period, 0, 3, high) < 3) return pattern; if(CopyLow(_Symbol, _Period, 0, 3, low) < 3) return pattern; if(CopyClose(_Symbol, _Period, 0, 3, close) < 3) return pattern; // Check pattern sesuai dengan direction yang diminta if(direction == BUY) { // Untuk BUY signal, cari bullish patterns if(IsBullishEngulfing(open, high, low, close)) { pattern.type = BULLISH_ENGULFING; pattern.strength = CalculateEngulfingStrength(BUY, open, high, low, close); pattern.isValid = (pattern.strength >= EngulfingStrengthThreshold); pattern.reason = "Bullish Engulfing - Strength: " + DoubleToString(pattern.strength, 2) + " (Min: " + DoubleToString(EngulfingStrengthThreshold, 2) + ")"; pattern.barIndex = 0; DebugLog("🟢 BUY Direction - Bullish Engulfing detected - Strength: " + DoubleToString(pattern.strength, 2) + " Valid: " + (pattern.isValid ? "YES" : "NO")); } // Hammer juga bullish pattern else if(IsHammerEngulfing(open, high, low, close)) { pattern.type = HAMMER_ENGULFING; pattern.strength = 0.6; // Medium-high strength for hammer pattern.isValid = true; pattern.reason = "Hammer Engulfing (Bullish) - Medium-high strength"; pattern.barIndex = 0; DebugLog("🟢 BUY Direction - Hammer Engulfing detected - Valid: YES"); } // Doji bisa bullish jika dalam uptrend else if(IsDojiEngulfing(open, high, low, close)) { pattern.type = DOJI_ENGULFING; pattern.strength = 0.4; // Lower strength for doji in buy direction pattern.isValid = true; pattern.reason = "Doji Engulfing (Bullish) - Medium strength"; pattern.barIndex = 0; DebugLog("🟢 BUY Direction - Doji Engulfing detected - Valid: YES"); } } else if(direction == SELL) { // Untuk SELL signal, cari bearish patterns if(IsBearishEngulfing(open, high, low, close)) { pattern.type = BEARISH_ENGULFING; pattern.strength = CalculateEngulfingStrength(SELL, open, high, low, close); pattern.isValid = (pattern.strength >= EngulfingStrengthThreshold); pattern.reason = "Bearish Engulfing - Strength: " + DoubleToString(pattern.strength, 2) + " (Min: " + DoubleToString(EngulfingStrengthThreshold, 2) + ")"; pattern.barIndex = 0; DebugLog("🔴 SELL Direction - Bearish Engulfing detected - Strength: " + DoubleToString(pattern.strength, 2) + " Valid: " + (pattern.isValid ? "YES" : "NO")); } // Inverted Hammer bisa bearish pattern else if(IsInvertedHammerEngulfing(open, high, low, close)) { pattern.type = HAMMER_ENGULFING; pattern.strength = 0.6; // Medium-high strength for inverted hammer pattern.isValid = true; pattern.reason = "Inverted Hammer Engulfing (Bearish) - Medium-high strength"; pattern.barIndex = 0; DebugLog("🔴 SELL Direction - Inverted Hammer Engulfing detected - Valid: YES"); } // Doji bisa bearish jika dalam downtrend else if(IsDojiEngulfing(open, high, low, close)) { pattern.type = DOJI_ENGULFING; pattern.strength = 0.4; // Lower strength for doji in sell direction pattern.isValid = true; pattern.reason = "Doji Engulfing (Bearish) - Medium strength"; pattern.barIndex = 0; DebugLog("🔴 SELL Direction - Doji Engulfing detected - Valid: YES"); } } // Debug logging jika tidak ada pattern yang terdeteksi if(pattern.type == NO_ENGULFING) { string directionStr = (direction == BUY) ? "BUY" : "SELL"; DebugLog("🔍 No " + directionStr + " engulfing pattern detected - Current candle analysis completed"); } return pattern; } // Check for Bullish Engulfing (more flexible) bool IsBullishEngulfing(const double &open[], const double &high[], const double &low[], const double &close[]) { // Current candle (index 0) must be bullish if(close[0] <= open[0]) return false; // Previous candle (index 1) must be bearish if(close[1] >= open[1]) return false; // Current candle must engulf previous candle body bool bodyEngulfing = (open[0] < close[1] && close[0] > open[1]); // More flexible: also check if current candle is significantly larger double currentBody = close[0] - open[0]; double previousBody = open[1] - close[1]; // Previous was bearish bool sizeEngulfing = (currentBody > previousBody * 1.5); // 50% larger // Optional: Check if current candle also engulfs the high and low bool fullEngulfing = (low[0] <= low[1] && high[0] >= high[1]); return bodyEngulfing || sizeEngulfing || fullEngulfing; } // Check for Bearish Engulfing (more flexible) bool IsBearishEngulfing(const double &open[], const double &high[], const double &low[], const double &close[]) { // Current candle (index 0) must be bearish if(close[0] >= open[0]) return false; // Previous candle (index 1) must be bullish if(close[1] <= open[1]) return false; // Current candle must engulf previous candle body bool bodyEngulfing = (open[0] > close[1] && close[0] < open[1]); // More flexible: also check if current candle is significantly larger double currentBody = open[0] - close[0]; double previousBody = close[1] - open[1]; // Previous was bullish bool sizeEngulfing = (currentBody > previousBody * 1.5); // 50% larger // Optional: Check if current candle also engulfs the high and low bool fullEngulfing = (low[0] <= low[1] && high[0] >= high[1]); return bodyEngulfing || sizeEngulfing || fullEngulfing; } // Check for Doji Engulfing bool IsDojiEngulfing(const double &open[], const double &high[], const double &low[], const double &close[]) { // Current candle must be a doji (very small body) double bodySize = MathAbs(close[0] - open[0]); double totalRange = high[0] - low[0]; if(totalRange == 0) return false; double bodyRatio = bodySize / totalRange; if(bodyRatio > 0.1) return false; // Body must be less than 10% of total range // Previous candle must have a significant body double prevBodySize = MathAbs(close[1] - open[1]); double prevTotalRange = high[1] - low[1]; if(prevTotalRange == 0) return false; double prevBodyRatio = prevBodySize / prevTotalRange; if(prevBodyRatio < 0.3) return false; // Previous body must be at least 30% return true; } // Check for Hammer Engulfing (Bullish) bool IsHammerEngulfing(const double &open[], const double &high[], const double &low[], const double &close[]) { // Current candle must be bullish if(close[0] <= open[0]) return false; double bodySize = MathAbs(close[0] - open[0]); double totalRange = high[0] - low[0]; if(totalRange == 0) return false; // Lower shadow must be at least 2x the body size double lowerShadow = MathMin(open[0], close[0]) - low[0]; if(lowerShadow < bodySize * 2) return false; // Upper shadow should be small double upperShadow = high[0] - MathMax(open[0], close[0]); if(upperShadow > bodySize * 0.5) return false; return true; } // Check for Inverted Hammer Engulfing (Bearish) bool IsInvertedHammerEngulfing(const double &open[], const double &high[], const double &low[], const double &close[]) { // Current candle must be bearish if(close[0] >= open[0]) return false; double bodySize = MathAbs(close[0] - open[0]); double totalRange = high[0] - low[0]; if(totalRange == 0) return false; // Upper shadow must be at least 2x the body size double upperShadow = high[0] - MathMax(open[0], close[0]); if(upperShadow < bodySize * 2) return false; // Lower shadow should be small double lowerShadow = MathMin(open[0], close[0]) - low[0]; if(lowerShadow > bodySize * 0.5) return false; return true; } // Calculate engulfing strength (more flexible) double CalculateEngulfingStrength(int direction, const double &open[], const double &high[], const double &low[], const double &close[]) { double currentBody = MathAbs(close[0] - open[0]); double previousBody = MathAbs(close[1] - open[1]); if(previousBody == 0) return 0.0; // Calculate how much the current candle engulfs the previous one double engulfingRatio = currentBody / previousBody; // More flexible normalization: 1.0x = 50% strength, 2.0x = 75% strength, 3.0x = 100% strength double strength = 0.0; if(engulfingRatio >= 1.0) { strength = 0.5 + (engulfingRatio - 1.0) * 0.25; // 1.0x = 50%, 2.0x = 75%, 3.0x = 100% } else if(engulfingRatio >= 0.8) { strength = engulfingRatio * 0.625; // 0.8x = 50% } else { strength = engulfingRatio * 0.5; // Linear scaling for smaller ratios } // Additional strength for full engulfing (high and low) if(high[0] >= high[1] && low[0] <= low[1]) { strength += 0.15; // Bonus for full engulfing (dikurangi dari 0.2) } // Check previous trend if enabled if(CheckPreviousTrend) { bool trendAligned = CheckPreviousTrendAlignment(direction); if(trendAligned) { strength += 0.1; // Bonus for trend alignment } } DebugLog("🔍 Engulfing Strength Calc: Ratio=" + DoubleToString(engulfingRatio, 2) + " Base=" + DoubleToString(strength, 2) + " Full=" + ((high[0] >= high[1] && low[0] <= low[1]) ? "YES" : "NO") + " Trend=" + (CheckPreviousTrend ? (CheckPreviousTrendAlignment(direction) ? "ALIGNED" : "NOT_ALIGNED") : "DISABLED")); return MathMin(strength, 1.0); // Cap at 1.0 } //==================== Timeframe-Specific Functions ==================== // Konfirmasi hanya pada timeframe entry (M1/M5) bool IsEntryTimeframe() { return (_Period == PERIOD_M1 || _Period == PERIOD_M5); } // Konfirmasi hanya pada timeframe setup (M5) bool IsSetupTimeframe() { return (_Period == PERIOD_M5); } // Konfirmasi hanya pada timeframe trend (H1) bool IsTrendTimeframe() { return (_Period == PERIOD_H1); } // Conditional confirmation logic bool ShouldApplyBreakoutConfirmation() { // Breakout hanya pada timeframe entry dan setup return (EnableBreakoutConfirmation && breakoutConfirmationEnabled && (IsEntryTimeframe() || IsSetupTimeframe())); } bool ShouldApplyEngulfingConfirmation() { // Engulfing hanya pada timeframe entry dan setup return (EnableEngulfingConfirmation && engulfingConfirmationEnabled && (IsEntryTimeframe() || IsSetupTimeframe())); } // Cached detection untuk performance bool IsBreakoutConfirmedCached(int direction) { // Check cache validity (5 seconds) if(TimeCurrent() - tfCache.lastCheck < 5) { return tfCache.breakoutValid; } // Perform fresh detection bool result = IsBreakoutConfirmed(direction); // Update cache tfCache.lastCheck = TimeCurrent(); tfCache.breakoutValid = result; return result; } // Cached engulfing detection untuk performance EngulfingPattern DetectEngulfingPatternCached(int direction) { // Check cache validity (5 seconds) - but only if direction matches if(TimeCurrent() - tfCache.lastEngulfingCheck < 5 && tfCache.lastEngulfingDirection == direction) { // Return cached result if available EngulfingPattern cachedPattern; cachedPattern.type = tfCache.lastEngulfingType; cachedPattern.isValid = tfCache.engulfingValid; cachedPattern.strength = tfCache.engulfingStrength; cachedPattern.reason = tfCache.engulfingReason; cachedPattern.barIndex = 0; return cachedPattern; } // Perform fresh detection EngulfingPattern result = DetectEngulfingPattern(direction); // Update cache tfCache.lastEngulfingCheck = TimeCurrent(); tfCache.lastEngulfingDirection = direction; tfCache.engulfingValid = result.isValid; tfCache.lastEngulfingType = result.type; tfCache.engulfingStrength = result.strength; tfCache.engulfingReason = result.reason; return result; } //==================== Enhanced Signal Strength Calculation ==================== // Log enhanced entry decisions void LogEnhancedEntryDecision(const SignalPack &sp, int direction) { string directionStr = (direction == BUY) ? "BUY" : "SELL"; EssentialLog("🎯 Enhanced Entry Decision - " + directionStr); EssentialLog(" Base Score: " + DoubleToString(sp.signalStrength, 1)); EssentialLog(" Breakout: " + (sp.breakoutConfirmed ? "YES" : "NO") + " (Strength: " + DoubleToString(sp.breakoutStrength, 2) + ")"); EssentialLog(" Engulfing: " + (sp.engulfingConfirmed ? "YES" : "NO") + " (Strength: " + DoubleToString(sp.engulfingStrength, 2) + ")"); EssentialLog(" Total Score: " + DoubleToString(sp.totalConfirmationScore, 1)); EssentialLog(" Decision: " + (sp.totalConfirmationScore >= MinEnhancedScore ? "APPROVED" : "REJECTED")); } // Calculate enhanced signal strength with breakout and engulfing confirmations void CalculateEnhancedSignalStrength(SignalPack &sp) { double baseScore = sp.signalStrength; double breakoutBonus = 0; double engulfingBonus = 0; // Breakout Bonus (0-30 points) if(sp.breakoutConfirmed) { breakoutBonus = 30 * sp.breakoutStrength; } // Engulfing Bonus (0-25 points) if(sp.engulfingConfirmed) { engulfingBonus = 25 * sp.engulfingStrength; } sp.totalConfirmationScore = baseScore + breakoutBonus + engulfingBonus; DebugLog("🎯 Enhanced Score: Base=" + DoubleToString(baseScore, 1) + " + Breakout=" + DoubleToString(breakoutBonus, 1) + " + Engulfing=" + DoubleToString(engulfingBonus, 1) + " = Total=" + DoubleToString(sp.totalConfirmationScore, 1)); } // Enhanced entry validation bool IsEnhancedEntryValid(const SignalPack &sp, int direction) { // Base conditions - calculate minConfirmations based on mode int minConfirmations = (Mode == MODE_SCALPING ? MinConfirmations_Scalping : MinConfirmations_Other); bool baseConditions = (sp.confirmationCount >= minConfirmations); // Breakout confirmation bool breakoutOK = !EnableBreakoutConfirmation || !breakoutConfirmationEnabled || sp.breakoutConfirmed; // Engulfing confirmation bool engulfingOK = !EnableEngulfingConfirmation || !engulfingConfirmationEnabled || sp.engulfingConfirmed; // Minimum total score bool scoreOK = (sp.totalConfirmationScore >= MinEnhancedScore); return baseConditions && breakoutOK && engulfingOK && scoreOK; } // Check previous trend alignment bool CheckPreviousTrendAlignment(int direction) { double close[]; ArraySetAsSeries(close, true); if(CopyClose(_Symbol, _Period, 0, TrendLookback + 1, close) < TrendLookback + 1) { return false; } // Calculate trend direction double trendStart = close[TrendLookback]; double trendEnd = close[1]; // Previous candle if(direction == BUY) { return (trendEnd > trendStart); // Uptrend for bullish engulfing } else { return (trendEnd < trendStart); // Downtrend for bearish engulfing } } // Check breakout confirmation bars (simplified) bool CheckBreakoutConfirmationBars(int direction, double levelPrice) { double close[]; ArraySetAsSeries(close, true); if(CopyClose(_Symbol, _Period, 0, 3, close) < 3) return true; // Default to true if data unavailable // Simplified confirmation: just check if current close is beyond the level if(direction == BUY) { return (close[0] > levelPrice); } else { return (close[0] < levelPrice); } } // Check volume spike (simplified) bool CheckVolumeSpike() { if(!RequireVolumeSpike) return true; long volume[]; ArraySetAsSeries(volume, true); if(CopyTickVolume(_Symbol, _Period, 0, 5, volume) < 5) return true; // Default to true if data unavailable // Calculate average volume long avgVolume = 0; for(int i = 1; i < 5; i++) { avgVolume += volume[i]; } avgVolume /= 4; // Check if current volume is higher than average bool volumeSpike = (volume[0] > avgVolume * 1.2); // 20% higher than average DebugLog("🔍 Volume spike: " + (volumeSpike ? "YES" : "NO") + " - Current: " + IntegerToString(volume[0]) + " Average: " + IntegerToString(avgVolume)); return volumeSpike; } //==================== Sideways Market Detection ==================== // Detect sideways market condition based on RSI, ADX, and Stochastic bool DetectSidewaysMarket() { if(!EnableSidewaysDetection) return false; // Check if we need to update (every 5 seconds) if(TimeCurrent() - lastSidewaysCheck < 5) { return isSidewaysMarket; } lastSidewaysCheck = TimeCurrent(); // Get current indicator values double rsi = 0, adx = 0, stoch_k = 0, stoch_d = 0; GetRSI(_Symbol, _Period, RSI_Period, rsi); GetADXv(_Symbol, _Period, ADX_Period, adx); GetStoch(_Symbol, _Period, stoch_k, stoch_d); // Initialize confidence and reason int confidence = 0; string localReason = ""; // RSI Sideways Check (40% weight) bool rsi_sideways = (rsi >= RSI_SidewaysLower && rsi <= RSI_SidewaysUpper); if(rsi_sideways) { confidence += 40; localReason += "RSI(" + DoubleToString(rsi, 1) + ") "; } // ADX Sideways Check (35% weight) - weak trend bool adx_sideways = (adx <= ADX_SidewaysMax); if(adx_sideways) { confidence += 35; localReason += "ADX(" + DoubleToString(adx, 1) + ") "; } // Stochastic Sideways Check (25% weight) bool stoch_sideways = (stoch_k >= Stoch_SidewaysLower && stoch_k <= Stoch_SidewaysUpper); if(stoch_sideways) { confidence += 25; localReason += "Stoch(" + DoubleToString(stoch_k, 1) + ") "; } // Update global variables sidewaysConfidence = confidence; sidewaysReason = localReason; // Market is considered sideways if confidence >= 70% bool newSidewaysStatus = (confidence >= 70); // Log status change if(newSidewaysStatus != isSidewaysMarket) { if(newSidewaysStatus) { EssentialLog("🔄 Sideways Market DETECTED - Confidence: " + IntegerToString(confidence) + "% | " + localReason); } else { EssentialLog("🔄 Sideways Market ENDED - Confidence: " + IntegerToString(confidence) + "% | " + localReason); } } isSidewaysMarket = newSidewaysStatus; return isSidewaysMarket; } // Get sideways market status bool IsSidewaysMarket() { return DetectSidewaysMarket(); } // Get sideways confidence level int GetSidewaysConfidence() { DetectSidewaysMarket(); return sidewaysConfidence; } // Get sideways reason string GetSidewaysReason() { DetectSidewaysMarket(); return sidewaysReason; } //==================== Re-Entry Functions ==================== // Check if there are floating loss positions in a specific direction with progressive distance bool HasFloatingLossPositions(int direction) { if(!EnableReEntry) return false; int currentReEntryCount = GetReEntryCount(direction); if(currentReEntryCount >= MaxReEntries) { EssentialLog("⚠️ Re-Entry: Maximum re-entries (" + IntegerToString(MaxReEntries) + ") reached for " + (direction == POSITION_TYPE_BUY ? "BUY" : "SELL") + " direction"); return false; } // Calculate required floating loss points based on re-entry count // Re-entry 1: MinFloatingLossPts (e.g., 200 points) // Re-entry 2: MinFloatingLossPts * 2 (e.g., 400 points) // Re-entry 3: MinFloatingLossPts * 3 (e.g., 600 points) int requiredLossPoints = MinFloatingLossPts * (currentReEntryCount + 1); for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic) { int posType = (int)PositionGetInteger(POSITION_TYPE); double posProfit = PositionGetDouble(POSITION_PROFIT); // Check if position is in the same direction and has floating loss if(posType == direction && posProfit < 0) { double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentPrice = (direction == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); int lossPoints = (int)MathAbs((currentPrice - openPrice) / pt); if(lossPoints >= requiredLossPoints) { EssentialLog("💰 Re-Entry: Found floating loss position - Direction: " + (direction == POSITION_TYPE_BUY ? "BUY" : "SELL") + " Re-Entry #" + IntegerToString(currentReEntryCount + 1) + " Loss: " + DoubleToString(posProfit, 2) + " Points: " + IntegerToString(lossPoints) + " Required: " + IntegerToString(requiredLossPoints)); return true; } } } } return false; } // Get current re-entry count for a direction int GetReEntryCount(int direction) { return (direction == POSITION_TYPE_BUY) ? buyReEntryCount : sellReEntryCount; } // Check if re-entry is allowed for a direction bool IsReEntryAllowed(int direction) { if(!EnableReEntry) return false; int currentCount = GetReEntryCount(direction); if(currentCount >= MaxReEntries) { EssentialLog("⚠️ Re-Entry: Maximum re-entries (" + IntegerToString(MaxReEntries) + ") reached for " + (direction == POSITION_TYPE_BUY ? "BUY" : "SELL") + " direction. Count: " + IntegerToString(currentCount)); return false; } return true; } // Calculate lot size for re-entry with progressive multiplier double CalculateReEntryLot(double baseLot, int direction) { if(!EnableReEntry) return baseLot; int currentReEntryCount = GetReEntryCount(direction); // Calculate progressive lot multiplier // Re-entry 1: ReEntryLotMultiplier^1 (e.g., 1.5) // Re-entry 2: ReEntryLotMultiplier^2 (e.g., 2.25) // Re-entry 3: ReEntryLotMultiplier^3 (e.g., 3.375) double progressiveMultiplier = MathPow(ReEntryLotMultiplier, currentReEntryCount + 1); double reEntryLot = baseLot * progressiveMultiplier; double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Ensure lot size is within valid range reEntryLot = MathMax(minLot, MathMin(maxLot, reEntryLot)); // Round to nearest lot step reEntryLot = MathRound(reEntryLot / lotStep) * lotStep; EssentialLog("💰 Re-Entry: Calculated lot size - Direction: " + (direction == POSITION_TYPE_BUY ? "BUY" : "SELL") + " Re-Entry #" + IntegerToString(currentReEntryCount + 1) + " Base: " + DoubleToString(baseLot, 2) + " Multiplier: " + DoubleToString(progressiveMultiplier, 3) + " Re-Entry: " + DoubleToString(reEntryLot, 2)); return reEntryLot; } // Check and reset re-entry counters when positions are closed void CheckAndResetReEntryCounters() { if(!EnableReEntry) return; // Check if there are any BUY positions int buyPositions = CountPositions(ORDER_TYPE_BUY); if(buyPositions == 0 && buyReEntryCount > 0) { EssentialLog("💰 Re-Entry: All BUY positions closed, resetting BUY counter from " + IntegerToString(buyReEntryCount) + " to 0"); buyReEntryCount = 0; } // Check if there are any SELL positions int sellPositions = CountPositions(ORDER_TYPE_SELL); if(sellPositions == 0 && sellReEntryCount > 0) { EssentialLog("💰 Re-Entry: All SELL positions closed, resetting SELL counter from " + IntegerToString(sellReEntryCount) + " to 0"); sellReEntryCount = 0; } } // Update re-entry counters void UpdateReEntryCounters(int direction, bool isReEntry) { if(!EnableReEntry) return; if(isReEntry) { if(direction == POSITION_TYPE_BUY) { buyReEntryCount++; EssentialLog("💰 Re-Entry: BUY re-entry count increased to " + IntegerToString(buyReEntryCount)); } else { sellReEntryCount++; EssentialLog("💰 Re-Entry: SELL re-entry count increased to " + IntegerToString(sellReEntryCount)); } } else { // Reset counters when new signal in opposite direction if(direction == POSITION_TYPE_BUY) { sellReEntryCount = 0; EssentialLog("💰 Re-Entry: SELL counter reset due to new BUY signal"); } else { buyReEntryCount = 0; EssentialLog("💰 Re-Entry: BUY counter reset due to new SELL signal"); } } } // Get detailed spread and stop level information string GetSpreadInfo(){ double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double spread = ask - bid; int spreadPoints = (int)(spread / _Point); double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point; double minStopDistance = MathMax(minStopLevel, spread * 2); return StringFormat("Spread: %.5f (%d pts) | MinStop: %.5f | MinDistance: %.5f", spread, spreadPoints, minStopLevel, minStopDistance); } // Validate if stop loss is valid for current market conditions bool IsValidStopLoss(double price, double stopLoss, int positionType){ double currentPrice = (positionType == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point; double currentSpread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID); double minStopDistance = MathMax(minStopLevel, currentSpread * 2); if(positionType == POSITION_TYPE_BUY){ return (currentPrice - stopLoss) >= minStopDistance; } else { return (stopLoss - currentPrice) >= minStopDistance; } } double AccountEquity() { return AccountInfoDouble(ACCOUNT_EQUITY); } bool NewBar(){ static datetime last=0; datetime t=(datetime)SeriesInfoInteger(_Symbol,_Period,SERIES_LASTBAR_DATE); if(t!=last){ last=t; return true;} return false; } string SessionName(int hour){ if(hour>=0 && hour<7) return "Asia"; if(hour>=7 && hour<13) return "London-Open"; if(hour>=13 && hour<21) return "NY"; return "Afterhours"; } bool WithinTradingHours(){ MqlDateTime waktu; TimeToStruct(TimeCurrent(), waktu); int h = waktu.hour; if(TradeStartHour <= TradeEndHour) return (h >= TradeStartHour && h < TradeEndHour); else return (h >= TradeStartHour || h < TradeEndHour); } bool IsSessionActive(int hour) { if(!EnableSessionFilter) return true; if(hour >= 0 && hour < 7) return TradeAsia; if(hour >= 7 && hour < 13) return TradeLondon; if(hour >= 13 && hour < 21) return TradeNewYork; return false; } bool NewsWindowActive(){ if(!NewsPauseEnable || UpcomingNewsTime==0) return false; int dt=(int)MathAbs((int) (TimeCurrent()-UpcomingNewsTime))/60; if(TimeCurrent()s){ tr="BUY"; ema="OK"; strength+=25; } else if(f= 80) strength+=20; // Extreme oversold/overbought if(r <= 30 || r >= 70) strength+=15; // Oversold/overbought zones // ADX strength if(a>=25) strength+=25; if(a>=35) strength+=10; // Stochastic if(k<20 || k>80) strength+=15; if(d<20 || d>80) strength+=10; stoch=(k<20?"Oversold":(k>80?"Overbought":"Neutral")); vol=(a>=25?"High":"Med"); string line = StringFormat("%-5s %-6s %-7s %-5.2f %-5.0f %-8s %-5s %-8.0f\n", names[i], tr, ema, r, a, stoch, vol, strength); out += line; // DebugLog(names[i] + " - Line generated: '" + line + "'"); // DebugLog(names[i] + " - Final: Trend=" + tr + " EMA=" + ema + " Strength=" + DoubleToString(strength,0)); } // Add debug info if no data is showing if(StringLen(out) <= StringLen("TF Trend EMA8/13 RSI ADX Stoch Vol Strength\n")) { // DebugLog("=== NO DATA DETECTED - STARTING DETAILED DEBUG ==="); out += "DEBUG: No data retrieved - checking indicators...\n"; out += "Symbol: " + _Symbol + " | Current TF: " + EnumToString(_Period) + "\n"; out += "Data availability check:\n"; // Test data availability for each timeframe for(int i=0;i<4;i++){ double test[]; int copied = CopyClose(_Symbol, tfs[i], 0, 1, test); // DebugLog("CopyClose " + names[i] + ": copied=" + IntegerToString(copied) + " array_size=" + IntegerToString(ArraySize(test))); if(copied < 1) { out += " " + names[i] + ": NO DATA\n"; // DebugLog(" " + names[i] + ": NO DATA - CopyClose failed"); } else { out += " " + names[i] + ": DATA OK (" + DoubleToString(test[0], 5) + ")\n"; // DebugLog(" " + names[i] + ": DATA OK (" + DoubleToString(test[0], 5) + ")"); } } // Additional debug for indicator functions out += "Indicator function debug:\n"; for(int i=0;i<4;i++){ double f,s,r,a,k,d; bool okf=GetEMA(_Symbol,tfs[i],EMA_Fast,f); bool oks=GetEMA(_Symbol,tfs[i],EMA_Slow,s); bool okr=GetRSI(_Symbol,tfs[i],RSI_Period,r); bool oka=GetADXv(_Symbol,tfs[i],ADX_Period,a); bool oksc=GetStoch(_Symbol,tfs[i],k,d); out += " " + names[i] + ": EMA_F=" + (okf?"OK":"FAIL") + " EMA_S=" + (oks?"OK":"FAIL") + " RSI=" + (okr?"OK":"FAIL") + " ADX=" + (oka?"OK":"FAIL") + " Stoch=" + (oksc?"OK":"FAIL") + "\n"; // DebugLog(" " + names[i] + " Debug: EMA_F=" + (okf?"OK":"FAIL") + " EMA_S=" + (oks?"OK":"FAIL") + // " RSI=" + (okr?"OK":"FAIL") + " ADX=" + (oka?"OK":"FAIL") + " Stoch=" + (oksc?"OK":"FAIL")); } } else { // DebugLog("=== MTF DATA SUCCESSFULLY GENERATED ==="); // DebugLog("Final output length: " + IntegerToString(StringLen(out)) + " characters"); // DebugLog("Final output preview: '" + StringSubstr(out, 0, 100) + "...'"); } // DebugLog("=== MTF SCANNER DEBUG END ==="); return out; } // Helper to draw multi-line text as individual labels int DrawMultiline(string prefix,int x,int y,string text,color clr,int font,int lineSpacing=14){ string lines[]; int cnt=StringSplit(text,'\n',lines); if(cnt<=0){ DrawLabel(prefix,x,y,text,clr,font); return 1; } for(int i=0;i 0.01) EssentialLog("🔄 ADX changed: " + DoubleToString(lastAdx,2) + " → " + DoubleToString(s.adx,2)); if(MathAbs(s.emaF - lastEmaF) > 0.00001) EssentialLog("🔄 EMA8 changed: " + DoubleToString(lastEmaF,5) + " → " + DoubleToString(s.emaF,5)); if(MathAbs(s.emaS - lastEmaS) > 0.00001) EssentialLog("🔄 EMA13 changed: " + DoubleToString(lastEmaS,5) + " → " + DoubleToString(s.emaS,5)); if(MathAbs(s.stochK - lastStochK) > 0.01) EssentialLog("🔄 StochK changed: " + DoubleToString(lastStochK,2) + " → " + DoubleToString(s.stochK,2)); if(MathAbs(s.stochD - lastStochD) > 0.01) EssentialLog("🔄 StochD changed: " + DoubleToString(lastStochD,2) + " → " + DoubleToString(s.stochD,2)); if(MathAbs(s.volume - lastVolume) > 0.01) EssentialLog("🔄 Volume changed: " + DoubleToString(lastVolume,0) + " → " + DoubleToString(s.volume,0)); // Update nilai sebelumnya lastRsi = s.rsi; lastAdx = s.adx; lastEmaF = s.emaF; lastEmaS = s.emaS; lastStochK = s.stochK; lastStochD = s.stochD; lastVolume = s.volume; } // Standard RSI validation (tanpa level filtering) bool emaUp = (s.emaF > s.emaS); bool emaDn = (s.emaF < s.emaS); bool trendOk= (s.adx >= ADX_MinStrength); bool rsiBuyOk = (rsiEnabled ? (s.rsi < 80) : true); // RSI buy condition: RSI < 80 (lebih agresif) bool rsiSellOk = (rsiEnabled ? (s.rsi > 20) : true); // RSI sell condition: RSI > 20 (lebih agresif) bool stochBuyOk = (stochEnabled ? (s.stochK < 95 && s.stochD < 95) : true); // Stochastic buy: < 95 (lebih agresif) bool stochSellOk = (stochEnabled ? (s.stochK > 5 && s.stochD > 5) : true); // Stochastic sell: > 5 (lebih agresif) bool volumeOk = (s.volume > 0); // Basic volume check // Entry condition logging (reduced frequency) static datetime lastDebugLog = 0; if(TimeCurrent() - lastDebugLog > 30) { // Log setiap 30 detik EssentialLog("🔍 BuildSignal: EMA=" + (emaUp ? "UP" : "DOWN") + " RSI=" + DoubleToString(s.rsi, 1) + " ADX=" + DoubleToString(s.adx, 1) + " Stoch=" + DoubleToString(s.stochK, 1)); lastDebugLog = TimeCurrent(); } // Count confirmations untuk BUY dan SELL secara terpisah int buyConfirmations = 0; int sellConfirmations = 0; // BUY confirmations if(emaUp) buyConfirmations++; if(adxEnabled && trendOk) buyConfirmations++; if(rsiEnabled && rsiBuyOk) buyConfirmations++; if(stochEnabled && stochBuyOk) buyConfirmations++; if(volumeOk) buyConfirmations++; // SELL confirmations if(emaDn) sellConfirmations++; if(adxEnabled && trendOk) sellConfirmations++; if(rsiEnabled && rsiSellOk) sellConfirmations++; if(stochEnabled && stochSellOk) sellConfirmations++; if(volumeOk) sellConfirmations++; // Use the higher confirmation count s.confirmationCount = MathMax(buyConfirmations, sellConfirmations); // Confirmation count logging (reduced frequency) if(TimeCurrent() - lastDebugLog > 30) { EssentialLog("🔍 BuildSignal: BUY=" + IntegerToString(buyConfirmations) + " SELL=" + IntegerToString(sellConfirmations) + " Final=" + IntegerToString(s.confirmationCount)); } // Calculate signal strength dengan standard RSI bonus s.signalStrength = s.confirmationCount * 20; // 20 points per confirmation if(adxEnabled && s.adx >= 35) s.signalStrength += 10; // RSI bonus (lebih agresif) if(rsiEnabled) { if(s.rsi <= 25 || s.rsi >= 75) s.signalStrength += 20; // Extreme oversold/overbought if(s.rsi <= 35 || s.rsi >= 65) s.signalStrength += 15; // Oversold/overbought zones } if(stochEnabled && (s.stochK < 15 || s.stochK > 85)) s.signalStrength += 10; // Stochastic bonus (lebih agresif) // Check for sideways market condition bool isSideways = IsSidewaysMarket(); int sidewaysConf = GetSidewaysConfidence(); string localSidewaysReason = GetSidewaysReason(); // Generate signals based on mode - Menggunakan parameter yang dapat disesuaikan int minConfirmations = (Mode == MODE_SCALPING ? MinConfirmations_Scalping : MinConfirmations_Other); // Signal generation logging (reduced frequency) if(TimeCurrent() - lastDebugLog > 30) { EssentialLog("🔍 BuildSignal: Mode=" + (Mode == MODE_SCALPING ? "SCALPING" : "OTHER") + " MinConf=" + IntegerToString(minConfirmations) + " Strength=" + DoubleToString(s.signalStrength, 1)); if(isSideways) { EssentialLog("🔄 BuildSignal: SIDEWAYS Market Detected - Confidence: " + IntegerToString(sidewaysConf) + "% | " + localSidewaysReason); } } if(s.confirmationCount >= minConfirmations) { // Handle sideways market condition if(isSideways) { if(sidewaysDisableTradingEnabled) { EssentialLog("⚠️ BuildSignal: Trading DISABLED due to sideways market - Confidence: " + IntegerToString(sidewaysConf) + "%"); s.reason = "Sideways Market - Trading Disabled"; return; // Exit without generating signals } if(Sideways_UseRangeStrategy) { // Use range strategy for sideways market EssentialLog("🔄 BuildSignal: Using RANGE strategy for sideways market"); // Range strategy: Buy at oversold, Sell at overbought if(s.rsi <= 30 && s.stochK <= 20) { // Oversold condition s.buy = true; s.reason = StringFormat("Sideways Range BUY - RSI: %.2f (Oversold), Stoch: %.2f (Oversold), Confidence: %d%%", s.rsi, s.stochK, sidewaysConf); EssentialLog("🟢 Sideways Range BUY Signal: " + s.reason); } else if(s.rsi >= 70 && s.stochK >= 80) { // Overbought condition s.sell = true; s.reason = StringFormat("Sideways Range SELL - RSI: %.2f (Overbought), Stoch: %.2f (Overbought), Confidence: %d%%", s.rsi, s.stochK, sidewaysConf); EssentialLog("🔴 Sideways Range SELL Signal: " + s.reason); } else { s.reason = StringFormat("Sideways Market - No Range Signal (RSI: %.2f, Stoch: %.2f), Confidence: %d%%", s.rsi, s.stochK, sidewaysConf); EssentialLog("⚠️ Sideways Market - No range signal generated"); } return; // Exit after range strategy } } // Normal trend-following strategy (when not sideways or range strategy disabled) bool trendOkScalping = (Mode == MODE_SCALPING ? (s.adx >= ADX_MinStrength_Scalping) : (s.adx >= ADX_MinStrength)); if(emaUp && rsiBuyOk && (adxEnabled ? trendOkScalping : true) && stochBuyOk){ s.buy=true; string stochStatus = stochEnabled ? "Stoch OK" : "Stoch OFF"; s.reason=StringFormat("EMA8>EMA13, RSI: %.2f (Buy OK), ADX>%d, %s", s.rsi, (Mode == MODE_SCALPING ? ADX_MinStrength_Scalping : ADX_MinStrength), stochStatus); EssentialLog("🟢 BUY Signal Generated: " + s.reason); } if(emaDn && rsiSellOk && (adxEnabled ? trendOkScalping : true) && stochSellOk){ s.sell=true; string stochStatus = stochEnabled ? "Stoch OK" : "Stoch OFF"; s.reason=StringFormat("EMA8%d, %s", s.rsi, (Mode == MODE_SCALPING ? ADX_MinStrength_Scalping : ADX_MinStrength), stochStatus); EssentialLog("🔴 SELL Signal Generated: " + s.reason); } // Apply Multi Timeframe Confirmation if(EnableMTFConfirmation) { bool shouldApplyMTF = false; if(MTF_ApplyToXAUUSD && (_Symbol == "XAUUSD" || _Symbol == "GOLD")) { shouldApplyMTF = true; } if(mtfApplyToAllPairsEnabled) { shouldApplyMTF = true; } if(shouldApplyMTF) { EssentialLog("🔍 BuildSignal: Applying MTF Confirmation..."); bool mtfResult = ValidateSignalWithMTF(s); EssentialLog("🔍 BuildSignal: MTF Result - Buy=" + (s.buy ? "YES" : "NO") + " Sell=" + (s.sell ? "YES" : "NO") + " Success=" + (mtfResult ? "YES" : "NO")); if(!mtfResult) { s.buy = false; s.sell = false; EssentialLog("❌ BuildSignal: MTF Confirmation REJECTED signal"); } else { EssentialLog("✅ BuildSignal: MTF Confirmation APPROVED signal"); } } } // Enhanced Breakout & Engulfing Confirmation (hanya pada timeframe entry) if(s.buy || s.sell) { int direction = s.buy ? BUY : SELL; // Breakout Confirmation (hanya M1/M5) - menggunakan cached version untuk performa if(ShouldApplyBreakoutConfirmation()) { s.breakoutConfirmed = IsBreakoutConfirmedCached(direction); if(s.breakoutConfirmed) { s.breakoutStrength = 1.0; s.breakoutReason = "Breakout confirmed on " + EnumToString(_Period); } else { s.breakoutStrength = 0.0; s.breakoutReason = "No breakout on " + EnumToString(_Period); } } else { s.breakoutConfirmed = true; // Skip for non-entry timeframes s.breakoutStrength = 1.0; s.breakoutReason = "Breakout not required for " + EnumToString(_Period); } // Engulfing Confirmation (hanya M1/M5) - menggunakan cached version untuk performa if(ShouldApplyEngulfingConfirmation()) { EngulfingPattern pattern = DetectEngulfingPatternCached(direction); s.engulfingConfirmed = pattern.isValid; s.engulfingStrength = pattern.strength; s.engulfingReason = pattern.reason + " on " + EnumToString(_Period); } else { s.engulfingConfirmed = true; // Skip for non-entry timeframes s.engulfingStrength = 1.0; s.engulfingReason = "Engulfing not required for " + EnumToString(_Period); } // Calculate enhanced signal strength CalculateEnhancedSignalStrength(s); // Check if enhanced entry is valid if(!IsEnhancedEntryValid(s, direction)) { s.buy = false; s.sell = false; EssentialLog("❌ Enhanced confirmation REJECTED on " + EnumToString(_Period) + " - Score: " + DoubleToString(s.totalConfirmationScore, 1)); } else { EssentialLog("✅ Enhanced confirmation APPROVED on " + EnumToString(_Period) + " - Score: " + DoubleToString(s.totalConfirmationScore, 1)); LogEnhancedEntryDecision(s, direction); } } } else { if(TimeCurrent() - lastDebugLog > 10) { EssentialLog("⚠️ BuildSignal: Insufficient confirmations - " + IntegerToString(s.confirmationCount) + "/" + IntegerToString(minConfirmations)); } } } //==================== Supply & Demand Detection ==================== void DetectSupplyDemand() { if(!EnableSDDetection) return; // Clear old zones for(int i=0; i high[i-1] && high[i] > high[i-2] && high[i] > high[i+1] && high[i] > high[i+2]) { // Check for touches int touches = 0; for(int j=0; j= SD_MinTouch) { ArrayResize(sdZones, sdZoneCount + 1); sdZones[sdZoneCount].price = high[i]; sdZones[sdZoneCount].high = high[i] + SD_ZoneSize/2; sdZones[sdZoneCount].low = high[i] - SD_ZoneSize/2; sdZones[sdZoneCount].touches = touches; sdZones[sdZoneCount].isSupply = true; sdZones[sdZoneCount].lastTouch = TimeCurrent(); sdZones[sdZoneCount].name = "SD_Supply_" + IntegerToString(sdZoneCount); // Draw zone ObjectCreate(0, sdZones[sdZoneCount].name, OBJ_RECTANGLE, 0, TimeCurrent() - SD_Lookback * PeriodSeconds(_Period), sdZones[sdZoneCount].high, TimeCurrent(), sdZones[sdZoneCount].low); ObjectSetInteger(0, sdZones[sdZoneCount].name, OBJPROP_COLOR, SD_SupplyColor); ObjectSetInteger(0, sdZones[sdZoneCount].name, OBJPROP_FILL, true); ObjectSetInteger(0, sdZones[sdZoneCount].name, OBJPROP_BACK, true); sdZoneCount++; } } } // Find demand zones (support) for(int i=2; i= SD_MinTouch) { ArrayResize(sdZones, sdZoneCount + 1); sdZones[sdZoneCount].price = low[i]; sdZones[sdZoneCount].high = low[i] + SD_ZoneSize/2; sdZones[sdZoneCount].low = low[i] - SD_ZoneSize/2; sdZones[sdZoneCount].touches = touches; sdZones[sdZoneCount].isSupply = false; sdZones[sdZoneCount].lastTouch = TimeCurrent(); sdZones[sdZoneCount].name = "SD_Demand_" + IntegerToString(sdZoneCount); // Draw zone ObjectCreate(0, sdZones[sdZoneCount].name, OBJ_RECTANGLE, 0, TimeCurrent() - SD_Lookback * PeriodSeconds(_Period), sdZones[sdZoneCount].high, TimeCurrent(), sdZones[sdZoneCount].low); ObjectSetInteger(0, sdZones[sdZoneCount].name, OBJPROP_COLOR, SD_DemandColor); ObjectSetInteger(0, sdZones[sdZoneCount].name, OBJPROP_FILL, true); ObjectSetInteger(0, sdZones[sdZoneCount].name, OBJPROP_BACK, true); sdZoneCount++; } } } } //==================== Smart TP/SL Calculator ==================== void CalculateTPSL(int type, double entryPrice, double &sl, double &tp1, double &tp2, double &tp3) { double atr_pts = 0; if(UseATR_TP_SL && hAtr != -1) { double atr; if(GetBuf(hAtr, 0, 1, atr)) { atr_pts = atr / pt; } } if(atr_pts <= 0) atr_pts = 200; // Default fallback // Get broker minimum stop level long stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); double minStopDistance = stopLevel * pt; // Ensure minimum distance for SL/TP double sl_pts = MathMax(ATR_SL_Multiplier * atr_pts, stopLevel * 1.5); double tp_pts = MathMax(ATR_TP_Multiplier * atr_pts, stopLevel * 2.0); if(type == ORDER_TYPE_BUY) { sl = entryPrice - sl_pts * pt; tp1 = entryPrice + tp_pts * pt * TP1_Ratio; tp2 = entryPrice + tp_pts * pt * (TP1_Ratio + TP2_Ratio); tp3 = entryPrice + tp_pts * pt; } else { sl = entryPrice + sl_pts * pt; tp1 = entryPrice - tp_pts * pt * TP1_Ratio; tp2 = entryPrice - tp_pts * pt * (TP1_Ratio + TP2_Ratio); tp3 = entryPrice - tp_pts * pt; } // Debug log for SL/TP calculation EssentialLog("🔧 SL/TP Calc: ATR=" + DoubleToString(atr_pts, 1) + " StopLevel=" + IntegerToString(stopLevel) + " SL_pts=" + DoubleToString(sl_pts, 1) + " TP_pts=" + DoubleToString(tp_pts, 1)); } //==================== AI Assist ==================== string BuildPayload(const SignalPack &sp,const string candidate){ string json="{"; json+="\"pair\":\""+_Symbol+"\","; json+="\"tf\":\""+EnumToString(_Period)+"\","; json+="\"spread\":"+IntegerToString(SpreadPoints())+","; json+="\"atr\":"+DoubleToString(sp.atr,2)+","; json+="\"indicators\":{"; json+="\"ema_fast\":"+DoubleToString(sp.emaF,5)+","; json+="\"ema_slow\":"+DoubleToString(sp.emaS,5)+","; json+="\"rsi\":"+DoubleToString(sp.rsi,2)+","; json+="\"adx\":"+DoubleToString(sp.adx,2)+","; json+="\"stoch_k\":"+DoubleToString(sp.stochK,2)+","; json+="\"stoch_d\":"+DoubleToString(sp.stochD,2)+","; json+="\"volume\":"+DoubleToString(sp.volume,2)+"},"; json+="\"candidate\":\""+candidate+"\","; json+="\"mode\":\""+(Mode==MODE_SCALPING?"scalping":(Mode==MODE_INTRADAY?"intraday":"swing"))+"\","; json+="\"confirmations\":"+IntegerToString(sp.confirmationCount)+","; json+="\"signal_strength\":"+DoubleToString(sp.signalStrength,2); json+="}"; return json; } //==================== DeepSeek AI ==================== string BuildDeepSeekPayload(const SignalPack &sp, const string candidate) { string prompt = "You are a professional forex trading analyst. Analyze this trading signal and provide a clear recommendation.\n\n"; prompt += "Trading Signal Analysis:\n"; prompt += "- Pair: " + _Symbol + "\n"; prompt += "- Timeframe: " + EnumToString(_Period) + "\n"; prompt += "- Mode: " + (Mode==MODE_SCALPING?"Scalping":(Mode==MODE_INTRADAY?"Intraday":"Swing")) + "\n"; prompt += "- Candidate: " + candidate + "\n"; prompt += "- Spread: " + IntegerToString(SpreadPoints()) + " points\n"; prompt += "- ATR: " + DoubleToString(sp.atr, 2) + "\n"; prompt += "- Indicators:\n"; prompt += " * EMA Fast: " + DoubleToString(sp.emaF, 5) + "\n"; prompt += " * EMA Slow: " + DoubleToString(sp.emaS, 5) + "\n"; prompt += " * RSI: " + DoubleToString(sp.rsi, 2) + "\n"; prompt += " * ADX: " + DoubleToString(sp.adx, 2) + "\n"; prompt += " * Stochastic K: " + DoubleToString(sp.stochK, 2) + "\n"; prompt += " * Stochastic D: " + DoubleToString(sp.stochD, 2) + "\n"; prompt += " * Volume: " + DoubleToString(sp.volume, 2) + "\n"; prompt += "- Confirmations: " + IntegerToString(sp.confirmationCount) + "\n"; prompt += "- Signal Strength: " + DoubleToString(sp.signalStrength, 0) + "\n\n"; prompt += "Please analyze this signal and respond with ONLY one of these options:\n"; prompt += "1. CONFIRM_BUY - if you recommend taking this BUY signal\n"; prompt += "2. CONFIRM_SELL - if you recommend taking this SELL signal\n"; prompt += "3. REJECT - if you recommend NOT taking this signal\n"; prompt += "4. WAIT - if you recommend waiting for better conditions\n\n"; prompt += "Provide a brief reason for your decision (max 100 words)."; string json = "{"; json += "\"model\":\"" + DeepSeek_Model + "\","; json += "\"messages\":["; json += "{\"role\":\"user\",\"content\":\"" + prompt + "\"}"; json += "],"; json += "\"max_tokens\":" + IntegerToString(DeepSeek_MaxTokens) + ","; json += "\"temperature\":0.3"; json += "}"; return json; } string CallDeepSeek(const string payload, string &err) { err = ""; if(!DeepSeek_Enable || DeepSeek_API_Key == "") { return ""; } string url = "https://api.deepseek.com/v1/chat/completions"; uchar data[]; StringToCharArray(payload, data, 0, WHOLE_ARRAY, CP_UTF8); string headers = "Content-Type: application/json\r\n"; headers += "Authorization: Bearer " + DeepSeek_API_Key + "\r\n"; uchar result[]; string result_headers = ""; ResetLastError(); EssentialLog("📡 Sending WebRequest to: " + url); EssentialLog("🧾 Headers: " + headers); EssentialLog("🧾 Payload: " + payload); int code = WebRequest("POST", url, headers, DeepSeek_Timeout, data, result, result_headers); if(code == -1) { err = "WebRequest failed: " + IntegerToString(GetLastError()); return ""; } if(code != 200) { err = "HTTP " + IntegerToString(code); return ""; } string resp = CharArrayToString(result, 0, (int)ArraySize(result), CP_UTF8); // Parse DeepSeek response string content = ParseDeepSeekResponse(resp); if(content == "") { err = "Failed to parse DeepSeek response"; return ""; } return content; } string ParseDeepSeekResponse(const string response) { // Simple JSON parsing for DeepSeek response int contentStart = StringFind(response, "\"content\":\""); if(contentStart == -1) return ""; contentStart += 12; // Skip "content":" int contentEnd = StringFind(response, "\"", contentStart); if(contentEnd == -1) return ""; return StringSubstr(response, contentStart, contentEnd - contentStart); } bool DeepSeek_ConfirmBuy(const string response) { return (StringFind(response, "CONFIRM_BUY") >= 0); } bool DeepSeek_ConfirmSell(const string response) { return (StringFind(response, "CONFIRM_SELL") >= 0); } bool DeepSeek_Reject(const string response) { return (StringFind(response, "REJECT") >= 0); } bool DeepSeek_Wait(const string response) { return (StringFind(response, "WAIT") >= 0); } //==================== ChatGPT AI ==================== string EscapeJSONString(string str) { string out = ""; for(int i = 0; i < StringLen(str); i++) { ushort c = StringGetCharacter(str, i); if (c == 34) out += "\\\""; // " else if (c == 92) out += "\\\\"; // \ else if (c == 10) out += "\\n"; // newline else if (c == 13) out += "\\r"; // carriage return else out += (string)CharToString((uchar)c); } return out; } string BuildChatGPTPayload(const SignalPack &sp, const string candidate) { string prompt = "You are a professional forex trading analyst. Analyze this trading signal and provide a clear recommendation.\n\n"; prompt += "Trading Signal Analysis:\n"; prompt += "- Pair: " + _Symbol + "\n"; prompt += "- Timeframe: " + EnumToString(_Period) + "\n"; prompt += "- Mode: " + (Mode==MODE_SCALPING?"Scalping":(Mode==MODE_INTRADAY?"Intraday":"Swing")) + "\n"; prompt += "- Candidate: " + candidate + "\n"; prompt += "- Spread: " + IntegerToString(SpreadPoints()) + " points\n"; prompt += "- ATR: " + DoubleToString(sp.atr, 2) + "\n"; prompt += "- Indicators:\n"; prompt += " * EMA Fast: " + DoubleToString(sp.emaF, 5) + "\n"; prompt += " * EMA Slow: " + DoubleToString(sp.emaS, 5) + "\n"; prompt += " * RSI: " + DoubleToString(sp.rsi, 2) + "\n"; prompt += " * ADX: " + DoubleToString(sp.adx, 2) + "\n"; prompt += " * Stochastic K: " + DoubleToString(sp.stochK, 2) + "\n"; prompt += " * Stochastic D: " + DoubleToString(sp.stochD, 2) + "\n"; prompt += " * Volume: " + DoubleToString(sp.volume, 2) + "\n"; prompt += "- Confirmations: " + IntegerToString(sp.confirmationCount) + "\n"; prompt += "- Signal Strength: " + DoubleToString(sp.signalStrength, 0) + "\n\n"; prompt += "Please analyze this signal and respond with ONLY one of these options:\n"; prompt += "1. CONFIRM_BUY - if you recommend taking this BUY signal\n"; prompt += "2. CONFIRM_SELL - if you recommend taking this SELL signal\n"; prompt += "3. REJECT - if you recommend NOT taking this signal\n"; prompt += "4. WAIT - if you recommend waiting for better conditions\n\n"; prompt += "Provide a brief reason for your decision (max 100 words)."; string safePrompt = EscapeJSONString(prompt); string json = "{"; json += "\"model\":\"" + ChatGPT_Model + "\","; json += "\"messages\":["; json += "{\"role\":\"user\",\"content\":\"" + safePrompt + "\"}"; json += "],"; json += "\"max_tokens\":" + IntegerToString(ChatGPT_MaxTokens) + ","; json += "\"temperature\":0.3"; json += "}"; return json; } string CallChatGPT(const string payload, string &err) { err = ""; if(!ChatGPT_Enable || ChatGPT_API_Key == "") { err = "ChatGPT disabled or API key empty"; return ""; } string url = "https://api.openai.com/v1/chat/completions"; // --- Encode payload ke UTF-8 dan HAPUS terminator null --- uchar data[]; ResetLastError(); // Pakai -1/WHOLE_ARRAY: MQL5 akan copy + terminator null di akhir int bytes_copied = StringToCharArray(payload, data, 0, -1, CP_UTF8); if(bytes_copied <= 0) { err = "Failed to encode payload to UTF-8"; return ""; } // Hapus byte null terakhir agar JSON murni (tanpa \0) if(ArraySize(data) > 0) { ArrayResize(data, ArraySize(data) - 1); } // --- Header HTTP --- string headers = "Content-Type: application/json\r\n" "Accept: application/json\r\n" "Authorization: Bearer " + ChatGPT_API_Key + "\r\n"; uchar result[]; string result_headers = ""; ResetLastError(); //EssentialLog("🌐 Calling ChatGPT API..."); //EssentialLog("Timeout: " + IntegerToString(ChatGPT_Timeout) + "ms"); //EssentialLog("Payload length (chars): " + IntegerToString(StringLen(payload))); //EssentialLog("Payload bytes sent: " + IntegerToString(ArraySize(data))); //EssentialLog("📡 Sending WebRequest to: " + url); //EssentialLog("🧾 Headers: " + headers); //EssentialLog("🧾 Payload: " + payload); int code = WebRequest("POST", url, headers, ChatGPT_Timeout, data, result, result_headers); if(code == -1) { int lastError = GetLastError(); err = "WebRequest failed: " + IntegerToString(lastError); switch(lastError) { case ERR_WEBREQUEST_INVALID_ADDRESS: err += " (Invalid URL)"; break; case ERR_WEBREQUEST_CONNECT_FAILED: err += " (Connection failed)"; break; case ERR_WEBREQUEST_REQUEST_FAILED: err += " (Request failed)"; break; case ERR_WEBREQUEST_TIMEOUT: err += " (Timeout)"; break; case ERR_WEBREQUEST_INVALID_PARAMETER:err += " (Invalid parameter)"; break; case ERR_WEBREQUEST_NOT_ALLOWED: err += " (WebRequest not allowed - check MT5 settings)"; break; default: err += " (Unknown error)"; } EssentialLog("❌ " + err); return ""; } EssentialLog("📡 HTTP Response Code: " + IntegerToString(code)); EssentialLog("📄 Response Headers: " + result_headers); string resp = CharArrayToString(result, 0, (int)ArraySize(result), CP_UTF8); if(code != 200) { err = "HTTP " + IntegerToString(code) + " - " + resp; EssentialLog("❌ " + err); return ""; } EssentialLog("✅ ChatGPT response received: " + IntegerToString(StringLen(resp)) + " chars"); string content = ParseChatGPTResponse(resp); if(content == "") { err = "Failed to parse ChatGPT response"; EssentialLog("❌ " + err); EssentialLog("Raw response: " + resp); return ""; } EssentialLog("🎯 Parsed content: " + content); return content; } string ParseChatGPTResponse(const string response) { // Cari key "content": int keyPos = StringFind(response, "\"content\":"); if (keyPos == -1) return ""; // Cari quote pembuka value string int openQuote = StringFind(response, "\"", keyPos + 10); if (openQuote == -1) return ""; string out = ""; bool esc = false; // Mulai baca setelah quote pembuka for (int i = openQuote + 1; i < (int)StringLen(response); i++) { ushort ch = StringGetCharacter(response, i); if (esc) { // Tangani karakter escape standar JSON if (ch == 'n') out += "\n"; else if (ch == 'r') out += "\r"; else if (ch == 't') out += "\t"; else if (ch == '\\') out += "\\"; else if (ch == '\"') out += "\""; else out += (string)CharToString((uchar)ch); esc = false; } else { if (ch == '\\') { esc = true; // masuk mode escape untuk char berikutnya } else if (ch == '\"') { // ketemu quote penutup string "content" break; } else { out += (string)CharToString((uchar)ch); } } } return out; } // Ubah ke huruf besar dengan aman (tanpa pass const-by-ref) string ToUpperStr(const string text) { string s = text; // salin agar bukan const StringToUpper(s); // ubah in-place; return bool diabaikan return s; } bool ChatGPT_ConfirmBuy(const string content) { string s = ToUpperStr(content); return (StringFind(s, "CONFIRM_BUY") >= 0); } bool ChatGPT_ConfirmSell(const string content) { string s = ToUpperStr(content); return (StringFind(s, "CONFIRM_SELL") >= 0); } bool ChatGPT_Reject(const string content) { string s = ToUpperStr(content); return (StringFind(s, "REJECT") >= 0); } bool ChatGPT_Wait(const string content) { string s = ToUpperStr(content); return (StringFind(s, "WAIT") >= 0); } // KEMBALIKAN "" jika AI OFF / URL kosong -> aman compile & run string CallAI(const string endpoint,const string payload,const string apiKey,int timeout_ms,string &err) { err = ""; if(!AI_Assist_Enable || endpoint == "") // safety gate return ""; uchar data[]; StringToCharArray(payload, data, 0, WHOLE_ARRAY, CP_UTF8); string headers = "Content-Type: application/json\r\n"; if(StringLen(apiKey) > 0) headers += "Authorization: Bearer " + apiKey + "\r\n"; uchar result[]; string result_headers = ""; ResetLastError(); int code = WebRequest("POST", endpoint, headers, timeout_ms, data, result, result_headers); if(code == -1){ err = StringFormat("WebRequest:%d", GetLastError()); return ""; } string resp = CharArrayToString(result, 0, (int)ArraySize(result), CP_UTF8); if(code != 200){ err = StringFormat("HTTP %d", code); return ""; } if(StringLen(resp) > AI_MaxChars) resp = StringSubstr(resp, 0, AI_MaxChars); return resp; } bool AI_ConfirmBuy(const string resp){ return (StringFind(resp,"confirm_buy")>=0 || StringFind(resp,"\"verdict\":\"confirm_buy\"")>=0); } bool AI_ConfirmSell(const string resp){ return (StringFind(resp,"confirm_sell")>=0 || StringFind(resp,"\"verdict\":\"confirm_sell\"")>=0); } //==================== Trade Journal ==================== void LogTrade(const TradeRecord &record) { if(!EnableTradeLog) return; string filename = LogFileName; int handle = FileOpen(filename, FILE_WRITE|FILE_CSV|FILE_ANSI, '\t'); if(handle == INVALID_HANDLE) { DebugLog("Failed to open trade log file: " + filename); return; } // Write header if file is empty if(FileSize(handle) == 0) { FileWrite(handle, "OpenTime", "Pair", "Type", "Lot", "OpenPrice", "SL", "TP", "Reason", "CloseTime", "ClosePrice", "Profit", "Notes"); } string typeStr = (record.type == ORDER_TYPE_BUY) ? "BUY" : "SELL"; string openTimeStr = TimeToString(record.openTime); string closeTimeStr = (record.closeTime > 0) ? TimeToString(record.closeTime) : ""; FileWrite(handle, openTimeStr, record.pair, typeStr, DoubleToString(record.lot, 2), DoubleToString(record.openPrice, 5), DoubleToString(record.sl, 5), DoubleToString(record.tp, 5), record.reason, closeTimeStr, DoubleToString(record.closePrice, 5), DoubleToString(record.profit, 2), record.notes); FileClose(handle); } //==================== Trading Helpers ==================== int CountPositions(int type){ int c=0; for(int i=0;iLockStartPts){ double lock_sl=open + (LockOffsetPts + spreadBuffer)*pt; // Validate minimum stop distance if(cur - lock_sl >= minStopDistance) { if(sl==0.0 || lock_sl>sl) { if(trade.PositionModify(ticket, lock_sl, tp)) { DebugLog("Lock profit BUY: SL=" + DoubleToString(lock_sl, _Digits) + " (min=" + DoubleToString(minStopDistance, _Digits) + ")"); } else { DebugLog("Lock profit BUY failed: " + IntegerToString(GetLastError()) + " - SL=" + DoubleToString(lock_sl, _Digits)); } } } else { DebugLog("Lock profit BUY: SL too close to price. Required=" + DoubleToString(minStopDistance, _Digits) + ", Actual=" + DoubleToString(cur - lock_sl, _Digits)); } } } else{ if(profit_pts_sell>LockStartPts){ double lock_sl=open - (LockOffsetPts + spreadBuffer)*pt; // Validate minimum stop distance if(lock_sl - cur >= minStopDistance) { if(sl==0.0 || lock_sl= minStopDistance) { if((sl==0.0 || new_sl>sl) && new_sl= minStopDistance) { if((sl==0.0 || new_slcur) { if(trade.PositionModify(ticket,new_sl,tp)) { EssentialLog("✅ Trailing SELL SUCCESS: SL=" + DoubleToString(new_sl, _Digits) + " (min=" + DoubleToString(minStopDistance, _Digits) + ", step=" + IntegerToString(adjustedTrailingStep) + ")"); } else { EssentialLog("❌ Trailing SELL failed: " + IntegerToString(GetLastError()) + " - SL=" + DoubleToString(new_sl, _Digits)); } } else { EssentialLog("⚠️ Trailing SELL: SL not improved. Current=" + DoubleToString(sl, _Digits) + ", New=" + DoubleToString(new_sl, _Digits)); } } else { EssentialLog("❌ Trailing SELL: SL too close to price. Required=" + DoubleToString(minStopDistance, _Digits) + ", Actual=" + DoubleToString(new_sl - cur, _Digits)); } } } } // Check and reset re-entry counters after managing positions CheckAndResetReEntryCounters(); } //==================== HUD ==================== void DrawLabel(string name,int x,int y,string text,color clr,int font=10,ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER){ // Force delete existing object first if(ObjectFind(0,name)>=0) ObjectDelete(0,name); // Create new object if(ObjectCreate(0,name,OBJ_LABEL,0,0,0)) { ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_ANCHOR,anchor); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font); ObjectSetString(0,name,OBJPROP_FONT,"Consolas"); // monospaced for alignment ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,false); ObjectSetInteger(0,name,OBJPROP_ZORDER,0); // DebugLog("DrawLabel: Created object '" + name + "' at (" + IntegerToString(x) + "," + IntegerToString(y) + ") with text: '" + text + "'"); } else { // DebugLog("DrawLabel: FAILED to create object '" + name + "' - Error: " + IntegerToString(GetLastError())); } } void CheckObjectVisibility(string name) { if(ObjectFind(0,name) >= 0) { // DebugLog("Object '" + name + "' EXISTS and is visible"); string text = ObjectGetString(0,name,OBJPROP_TEXT); int x = (int)ObjectGetInteger(0,name,OBJPROP_XDISTANCE); int y = (int)ObjectGetInteger(0,name,OBJPROP_YDISTANCE); DebugLog(" - Text: '" + text + "'"); DebugLog(" - Position: (" + IntegerToString(x) + "," + IntegerToString(y) + ")"); } else { DebugLog("Object '" + name + "' NOT FOUND"); } } void ForceChartRefresh() { ChartRedraw(); // DebugLog("Chart refresh forced"); } void RenderHUD(const SignalPack &sp){ // Dashboard update tracking (reduced frequency) static datetime lastDashboardLog = 0; if(TimeCurrent() - lastDashboardLog > 60) { // Log setiap 60 detik EssentialLog("🖥️ Dashboard: Updating with Buy=" + (sp.buy ? "YES" : "NO") + " Sell=" + (sp.sell ? "YES" : "NO")); lastDashboardLog = TimeCurrent(); } MqlDateTime waktu; TimeToStruct(TimeCurrent(), waktu); string sess = SessionName(waktu.hour); string modeStr = (Mode==MODE_SCALPING?"Scalping":(Mode==MODE_INTRADAY?"Intraday":"Swing")); string aiStatus = ""; if(DeepSeek_Enable) aiStatus = "DeepSeek:ON"; else if(ChatGPT_Enable) aiStatus = "ChatGPT:ON"; else if(AI_Assist_Enable) aiStatus = "AI:ON"; else aiStatus = "AI:OFF"; // Calculate spread buffer for display (auto mode) int currentSpread = SpreadPoints(); int spreadBuffer = 0; // Auto spread buffer selalu aktif double dynamicSpreadBuffer = CalculateDynamicSpreadBuffer(); spreadBuffer = (int)(currentSpread * dynamicSpreadBuffer); // Get detailed spread info for display (auto mode) string spreadInfo = ""; double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point; double minStopDistance = MathMax(minStopLevel, currentSpread * _Point * 2.0); // Auto multiplier spreadInfo = StringFormat("[Spread:%d] [Buffer:%d] [MinStop:%.1f]", SpreadPoints(), spreadBuffer, minStopDistance/_Point); // Add timeframe change indicator (broker info hidden in auto mode) string tfIndicator = timeframeChanged ? " 🔄" : ""; string brokerInfo = ""; // Shortened header to prevent truncation string hdr = StringFormat("[%s] [TF:%s] %s [ATR:%.1f] %s %s %s", _Symbol, EnumToString(_Period), spreadInfo, sp.atr, modeStr, aiStatus, sess); DrawLabel("hdr",10,10,hdr,clrWhite,10); // Multi-timeframe scanner with enhanced debug info // DebugLog("=== RENDERHUD DEBUG START ==="); string mtfData = BuildScanner(); // DebugLog("BuildScanner returned: '" + mtfData + "'"); // DebugLog("MTF Data length: " + IntegerToString(StringLen(mtfData))); int yStart = 30; // Enhanced debug info for MTF scanner if(EnableMTFScanner) { string debugInfo = "MTF Scanner: ENABLED - Data should be visible below"; DrawLabel("mtf_debug",10,yStart,debugInfo,clrYellow,8); // Additional debug info for indicators string indicatorDebugInfo = StringFormat("Indicators: EMA(%d,%d) RSI(%d) ADX(%d) Stoch(%d,%d,%d)", hEmaF, hEmaS, hRsi, hAdx, hStoch, Stochastic_K, Stochastic_D); DrawLabel("indicator_debug",10,yStart+15,indicatorDebugInfo,clrCyan,8); // MTF handles debug info if(EnableMTFConfirmation) { string mtfHandlesInfo = StringFormat("MTF Handles: H1(EMA:%d,%d RSI:%d ADX:%d Stoch:%d) M15(EMA:%d,%d RSI:%d ADX:%d Stoch:%d)", hEmaF_H1, hEmaS_H1, hRsi_H1, hAdx_H1, hStoch_H1, hEmaF_M15, hEmaS_M15, hRsi_M15, hAdx_M15, hStoch_M15); DrawLabel("mtf_handles_debug",10,yStart+45,mtfHandlesInfo,clrMagenta,8); string mtfHandlesInfo2 = StringFormat("MTF Handles: M5(EMA:%d,%d RSI:%d ADX:%d Stoch:%d) M1(EMA:%d,%d RSI:%d ADX:%d Stoch:%d)", hEmaF_M5, hEmaS_M5, hRsi_M5, hAdx_M5, hStoch_M5, hEmaF_M1, hEmaS_M1, hRsi_M1, hAdx_M1, hStoch_M1); DrawLabel("mtf_handles_debug2",10,yStart+60,mtfHandlesInfo2,clrMagenta,8); } // Show current symbol and timeframe with tracking status string tfStatus = timeframeChanged ? " (CHANGED)" : " (TRACKING)"; string symbolInfo = StringFormat("Symbol: %s | TF: %s%s | Spread: %d%s", _Symbol, EnumToString(_Period), tfStatus, SpreadPoints(), ""); DrawLabel("symbol_debug",10,yStart+75,symbolInfo,clrLightSteelBlue,8); // Show MTF data length for debugging string dataLengthInfo = StringFormat("MTF Data Length: %d characters", StringLen(mtfData)); DrawLabel("data_length_debug",10,yStart+90,dataLengthInfo,clrOrange,8); // Display MTF data as multiline labels for reliability int mtfY = yStart + 105; // DebugLog("About to draw MTF data (multiline) at Y=" + IntegerToString(mtfY) + " with first line: '" + StringSubstr(mtfData, 0, 50) + "...'"); int linesDrawn = DrawMultiline("mtf",10,mtfY,mtfData,clrSilver,9,14); //EssentialLog("MTF Dashboard updated successfully"); // Add separator line under the table int sepY = mtfY + linesDrawn*14 + 2; string separator = "=========================================="; DrawLabel("separator",10,sepY,separator,clrGray,8); // Place the rest below the table int baseY = sepY + 12; string sig = (sp.buy?"BUY":(sp.sell?"SELL":"-")); color sigColor = (sp.buy?clrLime:(sp.sell?clrTomato:clrGray)); // Standard RSI color coding color rsiColor = clrWhite; // Set color based on standard RSI thresholds if(sp.rsi <= 30) { rsiColor = clrLime; // Green for oversold } else if(sp.rsi >= 70) { rsiColor = clrTomato; // Red for overbought } else if(sp.rsi > 30 && sp.rsi < 70) { rsiColor = clrYellow; // Yellow for neutral } DrawLabel("sig",10,baseY,StringFormat("Signal: %s Strength: %.0f Confirmations: %d",sig,sp.signalStrength,sp.confirmationCount),sigColor,10); // Show RSI status with standard thresholds string rsiStatus = rsiEnabled ? StringFormat("RSI: %.2f (Buy<70, Sell>30)",sp.rsi) : "RSI: DISABLED"; DrawLabel("rsi_level",10,baseY+18,rsiStatus,rsiEnabled ? rsiColor : clrGray,9); DrawLabel("reason",10,baseY+36,StringFormat("Reason: %s",sp.reason),clrLightSteelBlue,9); // Sideways market status if(EnableSidewaysDetection) { bool isSideways = IsSidewaysMarket(); int sidewaysConf = GetSidewaysConfidence(); string localSidewaysReason = GetSidewaysReason(); string sidewaysStatus = isSideways ? StringFormat("SIDEWAYS: %d%% | %s", sidewaysConf, localSidewaysReason) : StringFormat("TRENDING: %d%% | %s", 100-sidewaysConf, localSidewaysReason); color sidewaysColor = isSideways ? clrOrange : clrCyan; DrawLabel("sideways_status",10,baseY+54,sidewaysStatus,sidewaysColor,9); } // Add MTF information below reason for better visibility if(EnableMTFConfirmation) { MTFConfirmation mtf = GetMTFConfirmation(); // Calculate total buy and sell strength for display double total_buy_strength = mtf.h1_buy_strength + mtf.m15_buy_strength + mtf.m5_buy_strength + mtf.m1_buy_strength; double total_sell_strength = mtf.h1_sell_strength + mtf.m15_sell_strength + mtf.m5_sell_strength + mtf.m1_sell_strength; double max_strength = MathMax(total_buy_strength, total_sell_strength); string mtfInfo = StringFormat("MTF: Score=%.1f (Min:%.1f) | Buy:%.1f Sell:%.1f | %s", mtf.total_score, MTF_MinScore, mtf.total_buy_score, mtf.total_sell_score, mtf.total_score >= MTF_MinScore ? "READY" : "WAITING"); color mtfColor = (mtf.total_score >= MTF_MinScore) ? clrLime : clrOrange; DrawLabel("mtf_info",10,baseY+72,mtfInfo,mtfColor,9); // Add MTF Dominant signal display string dominantSignal = ""; color dominantColor = clrGray; if(mtf.total_buy_score > mtf.total_sell_score) { dominantSignal = StringFormat("MTF Dominant: BUY (%.1f > %.1f)", mtf.total_buy_score, mtf.total_sell_score); dominantColor = clrLime; } else if(mtf.total_sell_score > mtf.total_buy_score) { dominantSignal = StringFormat("MTF Dominant: SELL (%.1f > %.1f)", mtf.total_sell_score, mtf.total_buy_score); dominantColor = clrTomato; } else { dominantSignal = StringFormat("MTF Dominant: NEUTRAL (Buy:%.1f, Sell:%.1f)", mtf.total_buy_score, mtf.total_sell_score); dominantColor = clrGray; } DrawLabel("mtf_dominant",10,baseY+90,dominantSignal,dominantColor,9); // Adjust other elements position double eq=AccountInfoDouble(ACCOUNT_EQUITY), bal=AccountInfoDouble(ACCOUNT_BALANCE), fl=AccountInfoDouble(ACCOUNT_PROFIT); DrawLabel("pl",10,baseY+108,StringFormat("Eq:%.2f Bal:%.2f Float:%.2f Risk%%:%.2f", eq, bal, fl, RiskPercent),clrLightSteelBlue,9); // News-safe status string ns = (NewsWindowActive()?"PAUSE around NEWS":"OK"); DrawLabel("news",10,baseY+126,StringFormat("News: %s (upcoming: %s)", ns, (string)UpcomingNewsTime), clrYellow, 9); // Session status string sessionStatus = (IsSessionActive(waktu.hour)?"ACTIVE":"INACTIVE"); DrawLabel("session",10,baseY+144,StringFormat("Session: %s (%s) - %s", sess, sessionStatus, (WithinTradingHours()?"Trading Hours":"Outside Hours")), clrCyan, 9); // Supply/Demand zones count DrawLabel("sd",10,baseY+162,StringFormat("S/D Zones: %d Trendlines: %d", sdZoneCount, trendlineCount), clrOrange, 9); // Indicator status summary string indicatorStatus = StringFormat("Indicators: RSI(%s) ADX(%s) Stoch(%s)", rsiEnabled ? "ON" : "OFF", adxEnabled ? "ON" : "OFF", stochEnabled ? "ON" : "OFF"); DrawLabel("indicator_status",10,baseY+180,indicatorStatus,clrLightSteelBlue,9); // Re-Entry status if(EnableReEntry) { // Calculate required loss points for next re-entry int buyRequiredLoss = buyReEntryCount < MaxReEntries ? MinFloatingLossPts * (buyReEntryCount + 1) : 0; int sellRequiredLoss = sellReEntryCount < MaxReEntries ? MinFloatingLossPts * (sellReEntryCount + 1) : 0; string reEntryStatus = StringFormat("Re-Entry: BUY(%d/%d) SELL(%d/%d) | Next: BUY=%dpts SELL=%dpts", buyReEntryCount, MaxReEntries, sellReEntryCount, MaxReEntries, buyRequiredLoss, sellRequiredLoss); color reEntryColor = (buyReEntryCount > 0 || sellReEntryCount > 0) ? clrOrange : clrLightSteelBlue; DrawLabel("reentry_status",10,baseY+198,reEntryStatus,reEntryColor,9); } // Enhanced Confirmation Status if(EnableBreakoutConfirmation || EnableEngulfingConfirmation) { string breakoutStatus = sp.breakoutConfirmed ? "✅ Breakout: " + sp.breakoutReason : "❌ Breakout: " + sp.breakoutReason; color breakoutColor = sp.breakoutConfirmed ? clrLime : clrRed; DrawLabel("breakout_status",10,baseY+216,breakoutStatus,breakoutColor,9); string engulfingStatus = sp.engulfingConfirmed ? "✅ Engulfing: " + sp.engulfingReason : "❌ Engulfing: " + sp.engulfingReason; color engulfingColor = sp.engulfingConfirmed ? clrLime : clrRed; DrawLabel("engulfing_status",10,baseY+234,engulfingStatus,engulfingColor,9); string totalScore = StringFormat("Total Score: %.1f (Min: %.1f)", sp.totalConfirmationScore, MinEnhancedScore); color scoreColor = sp.totalConfirmationScore >= MinEnhancedScore ? clrLime : clrOrange; DrawLabel("total_score",10,baseY+252,totalScore,scoreColor,9); } } else { // Original positioning when MTF is disabled double eq=AccountInfoDouble(ACCOUNT_EQUITY), bal=AccountInfoDouble(ACCOUNT_BALANCE), fl=AccountInfoDouble(ACCOUNT_PROFIT); DrawLabel("pl",10,baseY+54,StringFormat("Eq:%.2f Bal:%.2f Float:%.2f Risk%%:%.2f", eq, bal, fl, RiskPercent),clrLightSteelBlue,9); // Sideways market status (when MTF is disabled) if(EnableSidewaysDetection) { bool isSideways = IsSidewaysMarket(); int sidewaysConf = GetSidewaysConfidence(); string localSidewaysReason = GetSidewaysReason(); string sidewaysStatus = isSideways ? StringFormat("SIDEWAYS: %d%% | %s", sidewaysConf, localSidewaysReason) : StringFormat("TRENDING: %d%% | %s", 100-sidewaysConf, localSidewaysReason); color sidewaysColor = isSideways ? clrOrange : clrCyan; DrawLabel("sideways_status",10,baseY+72,sidewaysStatus,sidewaysColor,9); } // News-safe status string ns = (NewsWindowActive()?"PAUSE around NEWS":"OK"); DrawLabel("news",10,baseY+90,StringFormat("News: %s (upcoming: %s)", ns, (string)UpcomingNewsTime), clrYellow, 9); // Session status string sessionStatus = (IsSessionActive(waktu.hour)?"ACTIVE":"INACTIVE"); DrawLabel("session",10,baseY+108,StringFormat("Session: %s (%s) - %s", sess, sessionStatus, (WithinTradingHours()?"Trading Hours":"Outside Hours")), clrCyan, 9); // Supply/Demand zones count DrawLabel("sd",10,baseY+126,StringFormat("S/D Zones: %d Trendlines: %d", sdZoneCount, trendlineCount), clrOrange, 9); // Indicator status summary string indicatorStatus = StringFormat("Indicators: RSI(%s) ADX(%s) Stoch(%s)", rsiEnabled ? "ON" : "OFF", adxEnabled ? "ON" : "OFF", stochEnabled ? "ON" : "OFF"); DrawLabel("indicator_status",10,baseY+144,indicatorStatus,clrLightSteelBlue,9); // Re-Entry status if(EnableReEntry) { // Calculate required loss points for next re-entry int buyRequiredLoss = buyReEntryCount < MaxReEntries ? MinFloatingLossPts * (buyReEntryCount + 1) : 0; int sellRequiredLoss = sellReEntryCount < MaxReEntries ? MinFloatingLossPts * (sellReEntryCount + 1) : 0; string reEntryStatus = StringFormat("Re-Entry: BUY(%d/%d) SELL(%d/%d) | Next: BUY=%dpts SELL=%dpts", buyReEntryCount, MaxReEntries, sellReEntryCount, MaxReEntries, buyRequiredLoss, sellRequiredLoss); color reEntryColor = (buyReEntryCount > 0 || sellReEntryCount > 0) ? clrOrange : clrLightSteelBlue; DrawLabel("reentry_status",10,baseY+162,reEntryStatus,reEntryColor,9); } // Enhanced Confirmation Status (when MTF is disabled) if(EnableBreakoutConfirmation || EnableEngulfingConfirmation) { string breakoutStatus = sp.breakoutConfirmed ? "✅ Breakout: " + sp.breakoutReason : "❌ Breakout: " + sp.breakoutReason; color breakoutColor = sp.breakoutConfirmed ? clrLime : clrRed; DrawLabel("breakout_status",10,baseY+180,breakoutStatus,breakoutColor,9); string engulfingStatus = sp.engulfingConfirmed ? "✅ Engulfing: " + sp.engulfingReason : "❌ Engulfing: " + sp.engulfingReason; color engulfingColor = sp.engulfingConfirmed ? clrLime : clrRed; DrawLabel("engulfing_status",10,baseY+198,engulfingStatus,engulfingColor,9); string totalScore = StringFormat("Total Score: %.1f (Min: %.1f)", sp.totalConfirmationScore, MinEnhancedScore); color scoreColor = sp.totalConfirmationScore >= MinEnhancedScore ? clrLime : clrOrange; DrawLabel("total_score",10,baseY+216,totalScore,scoreColor,9); } } } else { string debugInfo = "MTF Scanner: DISABLED - Enable in settings"; DrawLabel("mtf_debug",10,yStart,debugInfo,clrRed,8); } // Check object visibility after drawing // DebugLog("=== CHECKING OBJECT VISIBILITY ==="); CheckObjectVisibility("hdr"); CheckObjectVisibility("mtf_debug"); CheckObjectVisibility("mtf_0"); CheckObjectVisibility("sig"); // Force chart refresh ForceChartRefresh(); // Update toggle buttons if they exist if(ShowToggleButtons) { CreateToggleButtons(); } // DebugLog("=== RENDERHUD DEBUG END ==="); } //==================== Core ==================== int OnInit(){ EssentialLog("🚀 SmartBot Initializing..."); EssentialLog("Symbol: " + _Symbol + " | Timeframe: " + EnumToString(_Period)); EssentialLog("Mode: " + (Mode==MODE_SCALPING?"Scalping":(Mode==MODE_INTRADAY?"Intraday":"Swing"))); EssentialLog("MTF Scanner: " + (EnableMTFScanner ? "ON" : "OFF")); // Initialize timeframe tracking currentTimeframe = Period(); timeframeChanged = false; EssentialLog("📊 Timeframe tracking initialized: " + EnumToString(currentTimeframe)); pt = SymbolInfoDouble(_Symbol,SYMBOL_POINT); EssentialLog("Point value: " + DoubleToString(pt, 5)); EssentialLog("📊 Loading indicators..."); if(!EnsureIndicators()) { EssentialLog("❌ Failed to load indicators"); return INIT_FAILED; } EssentialLog("✅ All indicators loaded successfully"); // DebugLog("Indicator handles: EMA_F=" + IntegerToString(hEmaF) + " EMA_S=" + IntegerToString(hEmaS) + " RSI=" + IntegerToString(hRsi) + " ADX=" + IntegerToString(hAdx) + " ATR=" + IntegerToString(hAtr) + " Stoch=" + IntegerToString(hStoch) + " Vol=" + IntegerToString(hVolume)); // Initialize symbol info symbolInfoGlobal.Name(_Symbol); symbolInfoGlobal.RefreshRates(); EssentialLog("📈 Symbol info initialized"); // Set up trade object trade.SetExpertMagicNumber(Magic); trade.SetDeviationInPoints(10); trade.SetTypeFilling(ORDER_FILLING_FOK); EssentialLog("💼 Trade object configured"); // Initialize arrays ArrayResize(sdZones, 0); ArrayResize(trendlines, 0); ArrayResize(tradeHistory, 0); EssentialLog("📋 Arrays initialized"); // Enable chart events for timeframe change detection and button clicks EssentialLog("📊 Enabling chart events..."); ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true); ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true); EssentialLog("✅ Chart events enabled"); // Initialize toggle button states rsiEnabled = EnableRSI; adxEnabled = EnableADX; stochEnabled = EnableStochastic; mtfApplyToAllPairsEnabled = MTF_ApplyToAllPairs; sidewaysDisableTradingEnabled = Sideways_DisableTrading; breakoutConfirmationEnabled = EnableBreakoutConfirmation; engulfingConfirmationEnabled = EnableEngulfingConfirmation; EssentialLog("🎛️ Toggle states initialized - RSI:" + (rsiEnabled ? "ON" : "OFF") + " ADX:" + (adxEnabled ? "ON" : "OFF") + " Stoch:" + (stochEnabled ? "ON" : "OFF") + " MTF All:" + (mtfApplyToAllPairsEnabled ? "ON" : "OFF") + " Sideways:" + (sidewaysDisableTradingEnabled ? "DISABLE" : "ENABLE") + " Breakout:" + (breakoutConfirmationEnabled ? "ON" : "OFF") + " Engulfing:" + (engulfingConfirmationEnabled ? "ON" : "OFF")); // Log timeframe-specific confirmation scope string tfScope = ""; if(IsEntryTimeframe()) { tfScope = "Entry Timeframe (M1/M5) - Confirmation Active"; } else if(IsSetupTimeframe()) { tfScope = "Setup Timeframe (M5) - Confirmation Active"; } else { tfScope = "Trend Timeframe (H1) - Confirmation Skipped"; } EssentialLog("🎯 Timeframe Confirmation Scope: " + tfScope); // Broker detection disabled: all adjustments are auto from spread & broker stop level // Initialize MTF handles if enabled if(EnableMTFConfirmation) { InitializeMTFHandles(); } // Create toggle buttons on chart CreateToggleButtons(); EssentialLog("🎛️ Toggle buttons created on chart"); // Ensure buttons are clickable and visible EssentialLog("🎛️ Ensuring button clickability..."); ObjectSetInteger(0, "RSI_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "ADX_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "Stoch_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "MTF_AllPairs_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "Sideways_Disable_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "Breakout_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "Engulfing_Toggle_Button", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "RSI_Toggle_Button", OBJPROP_HIDDEN, false); ObjectSetInteger(0, "ADX_Toggle_Button", OBJPROP_HIDDEN, false); ObjectSetInteger(0, "Stoch_Toggle_Button", OBJPROP_HIDDEN, false); ObjectSetInteger(0, "MTF_AllPairs_Toggle_Button", OBJPROP_HIDDEN, false); ObjectSetInteger(0, "Sideways_Disable_Toggle_Button", OBJPROP_HIDDEN, false); ObjectSetInteger(0, "Breakout_Toggle_Button", OBJPROP_HIDDEN, false); ObjectSetInteger(0, "Engulfing_Toggle_Button", OBJPROP_HIDDEN, false); ChartRedraw(); // Force immediate dashboard update EssentialLog("🖥️ Building dashboard..."); SignalPack sp; BuildSignal(sp); RenderHUD(sp); EssentialLog("✅ SmartBot initialized successfully"); EssentialLog("🎯 Ready for trading - Mode: " + (Mode==MODE_SCALPING?"Scalping":(Mode==MODE_INTRADAY?"Intraday":"Swing"))); return INIT_SUCCEEDED; } void OnDeinit(const int reason){ string names[] = {"hdr","mtf","mtf_debug","indicator_debug","symbol_debug","data_length_debug","separator","sig","rsi_level","reason","pl","news","session","sd","indicator_status","reentry_status","mtf_handles_debug","mtf_handles_debug2","sideways_status","breakout_status","engulfing_status","total_score","tf_confirmation_scope"}; for(int i=0;i=0) ObjectDelete(0,names[i]); } // remove multiline mtf_* labels generously for(int i=0;i<30;i++){ string nm = "mtf_"+IntegerToString(i); if(ObjectFind(0,nm)>=0) ObjectDelete(0,nm); } // Release MTF handles ReleaseMTFHandles(); // Delete toggle buttons DeleteToggleButtons(); EssentialLog("🎛️ Toggle buttons removed from chart"); } void TryEntry(const SignalPack &sp){ EssentialLog("🎯 TryEntry: Function called - Buy=" + (sp.buy ? "YES" : "NO") + " Sell=" + (sp.sell ? "YES" : "NO")); if(!AutoTrade) { EssentialLog("❌ TryEntry: AutoTrade is DISABLED"); return; } if(!IsSpreadAcceptable()) { EssentialLog("❌ TryEntry: Spread not acceptable - Current=" + IntegerToString(SpreadPoints()) + " Max=" + IntegerToString(MaxSpreadPoints)); return; } if(!WithinTradingHours()) { EssentialLog("❌ TryEntry: Outside trading hours"); return; } if(NewsWindowActive()) { EssentialLog("❌ TryEntry: News window active"); return; } MqlDateTime currentTime; TimeToStruct(TimeCurrent(), currentTime); if(!IsSessionActive(currentTime.hour)) { EssentialLog("❌ TryEntry: Session not active - Hour=" + IntegerToString(currentTime.hour)); return; } EssentialLog("✅ TryEntry: All basic conditions passed"); // Calculate spread buffer for entry with broker-specific adjustments int currentSpread = SpreadPoints(); int spreadBuffer = 0; // Auto spread buffer selalu aktif double dynamicSpreadBuffer = CalculateDynamicSpreadBuffer(); spreadBuffer = (int)(currentSpread * dynamicSpreadBuffer); int dir=-1; string candidate=""; bool isReEntry = false; // Check for BUY signal if(sp.buy) { EssentialLog("🔍 TryEntry: Checking BUY signal..."); if(CountPositions(ORDER_TYPE_BUY) == 0) { // New BUY signal - no existing positions dir = ORDER_TYPE_BUY; candidate = "BUY"; UpdateReEntryCounters(POSITION_TYPE_BUY, false); // Reset SELL counter lastBuySignalTime = TimeCurrent(); EssentialLog("✅ TryEntry: New BUY signal - no existing positions"); } else if(EnableReEntry && HasFloatingLossPositions(POSITION_TYPE_BUY) && IsReEntryAllowed(POSITION_TYPE_BUY)) { // Re-entry BUY signal - existing floating loss positions dir = ORDER_TYPE_BUY; candidate = "BUY RE-ENTRY"; isReEntry = true; UpdateReEntryCounters(POSITION_TYPE_BUY, true); lastBuySignalTime = TimeCurrent(); EssentialLog("✅ TryEntry: BUY RE-ENTRY signal"); } else { EssentialLog("⚠️ TryEntry: BUY signal ignored - existing positions or re-entry not allowed"); } } // Check for SELL signal if(sp.sell && dir == -1) { EssentialLog("🔍 TryEntry: Checking SELL signal..."); if(CountPositions(ORDER_TYPE_SELL) == 0) { // New SELL signal - no existing positions dir = ORDER_TYPE_SELL; candidate = "SELL"; UpdateReEntryCounters(POSITION_TYPE_SELL, false); // Reset BUY counter lastSellSignalTime = TimeCurrent(); EssentialLog("✅ TryEntry: New SELL signal - no existing positions"); } else if(EnableReEntry && HasFloatingLossPositions(POSITION_TYPE_SELL) && IsReEntryAllowed(POSITION_TYPE_SELL)) { // Re-entry SELL signal - existing floating loss positions dir = ORDER_TYPE_SELL; candidate = "SELL RE-ENTRY"; isReEntry = true; UpdateReEntryCounters(POSITION_TYPE_SELL, true); lastSellSignalTime = TimeCurrent(); EssentialLog("✅ TryEntry: SELL RE-ENTRY signal"); } else { EssentialLog("⚠️ TryEntry: SELL signal ignored - existing positions or re-entry not allowed"); } } if(dir == -1) { EssentialLog("❌ TryEntry: No valid signal direction determined"); return; } EssentialLog("🎯 Signal detected: " + candidate + " - Checking AI approval..."); // Check DeepSeek AI first if(DeepSeek_Enable && DeepSeek_API_Key != "") { EssentialLog("🤖 Calling DeepSeek AI for analysis..."); string err, resp = CallDeepSeek(BuildDeepSeekPayload(sp, candidate), err); if(resp != "") { EssentialLog("DeepSeek response: " + resp); bool confirmed = false; if(dir == ORDER_TYPE_BUY && DeepSeek_ConfirmBuy(resp)) { confirmed = true; } else if(dir == ORDER_TYPE_SELL && DeepSeek_ConfirmSell(resp)) { confirmed = true; } if(DeepSeek_Reject(resp)) { EssentialLog("❌ DeepSeek REJECTED the signal: " + resp); return; } if(DeepSeek_Wait(resp)) { EssentialLog("⏳ DeepSeek recommends WAITING: " + resp); return; } if(!confirmed) { EssentialLog("❌ DeepSeek did not confirm the signal: " + resp); return; } if(DeepSeek_RequireApprove) { EssentialLog("✅ DeepSeek confirmed, waiting manual approve"); return; } EssentialLog("✅ DeepSeek confirmed the signal, proceeding with trade"); } else { EssentialLog("❌ DeepSeek call failed: " + err); // Continue with ChatGPT if DeepSeek fails } } // Check ChatGPT if enabled if(ChatGPT_Enable && ChatGPT_API_Key != "") { EssentialLog("🤖 Calling ChatGPT AI for analysis..."); string err, resp = CallChatGPT(BuildChatGPTPayload(sp, candidate), err); if(resp != "") { EssentialLog("ChatGPT response: " + resp); bool confirmed = false; if(dir == ORDER_TYPE_BUY && ChatGPT_ConfirmBuy(resp)) { confirmed = true; } else if(dir == ORDER_TYPE_SELL && ChatGPT_ConfirmSell(resp)) { confirmed = true; } if(ChatGPT_Reject(resp)) { EssentialLog("❌ ChatGPT REJECTED the signal: " + resp); return; } if(ChatGPT_Wait(resp)) { EssentialLog("⏳ ChatGPT recommends WAITING: " + resp); return; } if(!confirmed) { EssentialLog("❌ ChatGPT did not confirm the signal: " + resp); return; } if(ChatGPT_RequireApprove) { EssentialLog("✅ ChatGPT confirmed, waiting manual approve"); return; } EssentialLog("✅ ChatGPT confirmed the signal, proceeding with trade"); } else { EssentialLog("❌ ChatGPT call failed: " + err); // Continue with other AI if ChatGPT fails } } // Fallback to other AI if enabled if(AI_Assist_Enable && AI_Endpoint_URL!="" && !DeepSeek_Enable && !ChatGPT_Enable){ EssentialLog("🤖 Calling Legacy AI for analysis..."); string err,resp=CallAI(AI_Endpoint_URL,BuildPayload(sp,candidate),AI_API_Key,AI_TimeoutMs,err); if(resp!=""){ EssentialLog("Legacy AI response: " + resp); bool ok=(dir==ORDER_TYPE_BUY?AI_ConfirmBuy(resp):AI_ConfirmSell(resp)); if(!ok){ EssentialLog("❌ Legacy AI veto: " + resp); return; } if(AI_RequireApprove){ EssentialLog("✅ Legacy AI confirmed, waiting manual approve"); return; } EssentialLog("✅ Legacy AI confirmed the signal, proceeding with trade"); } else { EssentialLog("❌ Legacy AI call failed: " + err); } } EssentialLog("✅ TryEntry: All checks passed, executing trade"); // Calculate TP/SL double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK), bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); double price = (dir==ORDER_TYPE_BUY? ask: bid); double sl, tp1, tp2, tp3; CalculateTPSL(dir, price, sl, tp1, tp2, tp3); double baseLot = LotByRisk(MathAbs(price - sl) / pt); double lot = isReEntry ? CalculateReEntryLot(baseLot, dir == ORDER_TYPE_BUY ? POSITION_TYPE_BUY : POSITION_TYPE_SELL) : baseLot; trade.SetExpertMagicNumber(Magic); bool ok=false; if(dir==ORDER_TYPE_BUY) { ok=trade.Buy(lot,NULL,price,sl,tp1); } else { ok=trade.Sell(lot,NULL,price,sl,tp1); } if(ok) { string tradeType = isReEntry ? "RE-ENTRY " : ""; string direction = (dir==ORDER_TYPE_BUY) ? "BUY" : "SELL"; EssentialLog("✅ Opened " + tradeType + direction + " lot=" + DoubleToString(lot,2) + " SL=" + DoubleToString(sl,5) + " TP1=" + DoubleToString(tp1,5)); if(isReEntry) { EssentialLog("💰 Re-Entry: " + direction + " re-entry #" + IntegerToString(GetReEntryCount(dir == ORDER_TYPE_BUY ? POSITION_TYPE_BUY : POSITION_TYPE_SELL)) + " opened with lot size " + DoubleToString(lot,2)); } // Log trade if(EnableTradeLog) { TradeRecord record; record.openTime = TimeCurrent(); record.pair = _Symbol; record.type = dir; record.lot = lot; record.openPrice = price; record.sl = sl; record.tp = tp1; record.reason = sp.reason; record.closeTime = 0; record.closePrice = 0; record.profit = 0; record.notes = "Signal Strength: " + DoubleToString(sp.signalStrength, 0) + (isReEntry ? " | Re-Entry #" + IntegerToString(GetReEntryCount(dir == ORDER_TYPE_BUY ? POSITION_TYPE_BUY : POSITION_TYPE_SELL)) : ""); LogTrade(record); } } else { EssentialLog("❌ Open failed: " + IntegerToString(GetLastError())); } } ENUM_TIMEFRAMES changeTimeframe = NULL; void OnTick(){ if(!EnsureIndicators()) return; // Check and reset re-entry counters if positions are closed CheckAndResetReEntryCounters(); // Reset MTF signal tracking if position is closed ResetMTFSignalTracking(); // Check if timeframe has changed ENUM_TIMEFRAMES newTimeframe = Period(); if(newTimeframe != changeTimeframe) { EssentialLog("🔄 OnTick: Timeframe change detected: " + EnumToString(currentTimeframe) + " → " + EnumToString(newTimeframe)); changeTimeframe = newTimeframe; timeframeChanged = true; EssentialLog("🔄 OnTick: Timeframe changed to: " + EnumToString(currentTimeframe)); // Reset indicator handles to force reload with new timeframe EssentialLog("🔄 OnTick: Calling ResetIndicatorHandles()..."); ResetIndicatorHandles(); // Force immediate indicator reload EssentialLog("🔄 OnTick: Calling EnsureIndicators()..."); if(!EnsureIndicators()) { EssentialLog("❌ OnTick: Failed to reload indicators for new timeframe"); return; } EssentialLog("✅ OnTick: Indicators reloaded successfully for new timeframe"); // Force immediate signal rebuild and dashboard update EssentialLog("🔄 OnTick: Rebuilding signal and updating dashboard..."); SignalPack sp; BuildSignal(sp); RenderHUD(sp); EssentialLog("✅ OnTick: Dashboard updated for new timeframe"); } //EssentialLog("LOG : "+ timeframeChanged); // Always update dashboard on every tick for better responsiveness SignalPack sp; BuildSignal(sp); RenderHUD(sp); // Entry condition check (reduced logging) if(!AutoTrade) { EssentialLog("❌ OnTick: AutoTrade is OFF - skipping entry"); return; } if(SpreadPoints() > MaxSpreadPoints) { EssentialLog("❌ OnTick: Spread too high (" + IntegerToString(SpreadPoints()) + " > " + IntegerToString(MaxSpreadPoints) + ") - skipping entry"); return; } if(!WithinTradingHours()) { EssentialLog("❌ OnTick: Outside trading hours - skipping entry"); return; } if(NewsWindowActive()) { EssentialLog("❌ OnTick: News window active - skipping entry"); return; } if(NewBar() || timeframeChanged){ EssentialLog("🔄 OnTick: New bar detected, checking for entry..."); // CRITICAL DEBUG: Log signal details before TryEntry EssentialLog("🎯 OnTick: Signal details:"); EssentialLog(" Buy Signal: " + (sp.buy ? "YES" : "NO")); EssentialLog(" Sell Signal: " + (sp.sell ? "YES" : "NO")); EssentialLog(" Signal Strength: " + DoubleToString(sp.signalStrength, 1)); EssentialLog(" Confirmation Count: " + IntegerToString(sp.confirmationCount)); EssentialLog(" Reason: " + sp.reason); // Check if we have any signal at all if(!sp.buy && !sp.sell) { EssentialLog("❌ OnTick: NO SIGNAL GENERATED - skipping TryEntry"); } else { EssentialLog("✅ OnTick: SIGNAL DETECTED - calling TryEntry"); TryEntry(sp); } // Update S/D zones periodically static int sdUpdateCounter = 0; sdUpdateCounter++; if(sdUpdateCounter >= 10) { // Update every 10 bars DetectSupplyDemand(); sdUpdateCounter = 0; } // Reset timeframe changed flag timeframeChanged = false; } ManageTrailing(); } //+------------------------------------------------------------------+ //| Chart Event Handler - Detects timeframe changes and other chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { //EssentialLog("📊 OnChartEvent: Event ID=" + IntegerToString(id) + " detected"); // Handle chart timeframe change if(id == CHARTEVENT_CHART_CHANGE) { //EssentialLog("📊 OnChartEvent: CHARTEVENT_CHART_CHANGE detected"); ENUM_TIMEFRAMES newTimeframe = Period(); //EssentialLog("📊 OnChartEvent: Current TF=" + EnumToString(currentTimeframe) + " New TF=" + EnumToString(newTimeframe)); if(newTimeframe != currentTimeframe) { // EssentialLog("🔄 OnChartEvent: Timeframe change detected: " + EnumToString(currentTimeframe) + " → " + EnumToString(newTimeframe)); currentTimeframe = newTimeframe; timeframeChanged = true; EssentialLog("🔄 OnChartEvent: Timeframe changed to: " + EnumToString(currentTimeframe)); // Reset indicator handles to force reload with new timeframe EssentialLog("🔄 OnChartEvent: Calling ResetIndicatorHandles()..."); ResetIndicatorHandles(); // Reset MTF handles if enabled if(EnableMTFConfirmation) { EssentialLog("🔄 OnChartEvent: Calling ReleaseMTFHandles()..."); ReleaseMTFHandles(); EssentialLog("🔄 OnChartEvent: Calling InitializeMTFHandles()..."); InitializeMTFHandles(); } // Force immediate indicator reload EssentialLog("🔄 OnChartEvent: Calling EnsureIndicators()..."); if(!EnsureIndicators()) { EssentialLog("❌ OnChartEvent: Failed to reload indicators for new timeframe"); return; } EssentialLog("✅ OnChartEvent: Indicators reloaded successfully"); // Force immediate dashboard update EssentialLog("🔄 OnChartEvent: Updating dashboard..."); SignalPack sp; BuildSignal(sp); RenderHUD(sp); EssentialLog("✅ OnChartEvent: Dashboard updated successfully"); } } // Handle button clicks if(id == CHARTEVENT_OBJECT_CLICK) { EssentialLog("🎛️ OnChartEvent: Object click detected - Object: " + sparam); if(sparam == "RSI_Toggle_Button" || sparam == "ADX_Toggle_Button" || sparam == "Stoch_Toggle_Button" || sparam == "MTF_AllPairs_Toggle_Button" || sparam == "Sideways_Disable_Toggle_Button" || sparam == "Breakout_Toggle_Button" || sparam == "Engulfing_Toggle_Button") { EssentialLog("🎛️ OnChartEvent: Toggle button clicked: " + sparam); HandleButtonClick(sparam); ChartRedraw(); // Force chart refresh after button click } } // Handle mouse clicks as fallback (using CHARTEVENT_MOUSE_CLICK is not available in MQL5) // Mouse clicks are handled automatically by CHARTEVENT_OBJECT_CLICK for chart objects } //==================== Multi Timeframe Confirmation System ==================== // Anti-repaint function for MTF data reading int ShiftFor(ENUM_TIMEFRAMES tf) { datetime t = iTime(_Symbol, PERIOD_CURRENT, 1); // bar closed if(t == 0) t = iTime(_Symbol, PERIOD_CURRENT, 0); int sh = iBarShift(_Symbol, tf, t, true); return (sh < 1 ? 1 : sh); } MTFConfirmation GetMTFConfirmation() { EssentialLog("🔍 GetMTFConfirmation: Function called"); MTFConfirmation mtf; // Uses default constructor // Check if MTF handles are initialized if(!EnableMTFConfirmation) { EssentialLog("🔍 GetMTFConfirmation: MTF Confirmation is DISABLED, returning early"); return mtf; } // Debug: Log MTF handle status static datetime lastMTFLog = 0; if(TimeCurrent() - lastMTFLog > 5) { // Log every 5 seconds EssentialLog("🔍 MTF Debug - Handles: H1(EMA:" + IntegerToString(hEmaF_H1) + "," + IntegerToString(hEmaS_H1) + " RSI:" + IntegerToString(hRsi_H1) + " ADX:" + IntegerToString(hAdx_H1) + " Stoch:" + IntegerToString(hStoch_H1) + ")"); EssentialLog("🔍 MTF Debug - Handles: M15(EMA:" + IntegerToString(hEmaF_M15) + "," + IntegerToString(hEmaS_M15) + " RSI:" + IntegerToString(hRsi_M15) + " ADX:" + IntegerToString(hAdx_M15) + " Stoch:" + IntegerToString(hStoch_M15) + ")"); EssentialLog("🔍 MTF Debug - Handles: M5(EMA:" + IntegerToString(hEmaF_M5) + "," + IntegerToString(hEmaS_M5) + " RSI:" + IntegerToString(hRsi_M5) + " ADX:" + IntegerToString(hAdx_M5) + " Stoch:" + IntegerToString(hStoch_M5) + ")"); EssentialLog("🔍 MTF Debug - Handles: M1(EMA:" + IntegerToString(hEmaF_M1) + "," + IntegerToString(hEmaS_M1) + " RSI:" + IntegerToString(hRsi_M1) + " ADX:" + IntegerToString(hAdx_M1) + " Stoch:" + IntegerToString(hStoch_M1) + ")"); lastMTFLog = TimeCurrent(); } // H1 Analysis (40% weight) - Using global handles for real-time updates double h1_ema_f = 0, h1_ema_s = 0, h1_rsi = 0, h1_adx = 0, h1_stoch_k = 0, h1_stoch_d = 0; if(hEmaF_H1 != INVALID_HANDLE && hEmaS_H1 != INVALID_HANDLE) { double h1_ema_f_buffer[1], h1_ema_s_buffer[1]; int shift = ShiftFor(PERIOD_H1); int copied_f = CopyBuffer(hEmaF_H1, 0, shift, 1, h1_ema_f_buffer); int copied_s = CopyBuffer(hEmaS_H1, 0, shift, 1, h1_ema_s_buffer); EssentialLog("🔍 H1 EMA CopyBuffer: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); if(copied_f > 0 && copied_s > 0) { h1_ema_f = h1_ema_f_buffer[0]; h1_ema_s = h1_ema_s_buffer[0]; EssentialLog("✅ H1 EMA Data: Fast=" + DoubleToString(h1_ema_f, 5) + " Slow=" + DoubleToString(h1_ema_s, 5)); } else { EssentialLog("❌ H1 EMA CopyBuffer failed: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); } } else { EssentialLog("❌ H1 EMA Handles invalid: Fast=" + IntegerToString(hEmaF_H1) + " Slow=" + IntegerToString(hEmaS_H1)); } if(hRsi_H1 != INVALID_HANDLE) { double h1_rsi_buffer[1]; int shift = ShiftFor(PERIOD_H1); int copied = CopyBuffer(hRsi_H1, 0, shift, 1, h1_rsi_buffer); EssentialLog("🔍 H1 RSI CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { h1_rsi = h1_rsi_buffer[0]; EssentialLog("✅ H1 RSI Data: " + DoubleToString(h1_rsi, 2)); } else { EssentialLog("❌ H1 RSI CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ H1 RSI Handle invalid: " + IntegerToString(hRsi_H1)); } if(hAdx_H1 != INVALID_HANDLE) { double h1_adx_buffer[1]; int shift = ShiftFor(PERIOD_H1); int copied = CopyBuffer(hAdx_H1, 0, shift, 1, h1_adx_buffer); EssentialLog("🔍 H1 ADX CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { h1_adx = h1_adx_buffer[0]; EssentialLog("✅ H1 ADX Data: " + DoubleToString(h1_adx, 2)); } else { EssentialLog("❌ H1 ADX CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ H1 ADX Handle invalid: " + IntegerToString(hAdx_H1)); } if(hStoch_H1 != INVALID_HANDLE) { double h1_stoch_k_buffer[1], h1_stoch_d_buffer[1]; int shift = ShiftFor(PERIOD_H1); int copied_k = CopyBuffer(hStoch_H1, 0, shift, 1, h1_stoch_k_buffer); int copied_d = CopyBuffer(hStoch_H1, 1, shift, 1, h1_stoch_d_buffer); EssentialLog("🔍 H1 Stochastic CopyBuffer: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); if(copied_k > 0 && copied_d > 0) { h1_stoch_k = h1_stoch_k_buffer[0]; h1_stoch_d = h1_stoch_d_buffer[0]; EssentialLog("✅ H1 Stochastic Data: K=" + DoubleToString(h1_stoch_k, 2) + " D=" + DoubleToString(h1_stoch_d, 2)); } else { EssentialLog("❌ H1 Stochastic CopyBuffer failed: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); } } else { EssentialLog("❌ H1 Stochastic Handle invalid: " + IntegerToString(hStoch_H1)); } bool h1_ema_up = h1_ema_f > h1_ema_s; bool h1_rsi_buy = (h1_rsi < 50); // RSI < 50 untuk buy (lebih fleksibel) bool h1_rsi_sell = (h1_rsi > 50); // RSI > 50 untuk sell (lebih fleksibel) bool h1_adx_buy = (h1_adx >= 10); // ADX >= 10 untuk buy (lebih fleksibel) bool h1_adx_sell = (h1_adx >= 10); // ADX >= 10 untuk sell (lebih fleksibel) bool h1_stoch_buy = (h1_stoch_k < 40); // Stoch < 40 untuk buy (lebih fleksibel) bool h1_stoch_sell = (h1_stoch_k > 60); // Stoch > 60 untuk sell (lebih fleksibel) EssentialLog("🔍 H1 Conditions: EMA=" + (h1_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(h1_rsi, 1) + " ADX=" + DoubleToString(h1_adx, 1) + " Stoch=" + DoubleToString(h1_stoch_k, 1)); EssentialLog("🔍 H1 Signals: RSI_Buy=" + (h1_rsi_buy ? "YES" : "NO") + " RSI_Sell=" + (h1_rsi_sell ? "YES" : "NO") + " ADX_Buy=" + (h1_adx_buy ? "YES" : "NO") + " ADX_Sell=" + (h1_adx_sell ? "YES" : "NO") + " Stoch_Buy=" + (h1_stoch_buy ? "YES" : "NO") + " Stoch_Sell=" + (h1_stoch_sell ? "YES" : "NO")); // Log jumlah kondisi yang terpenuhi int h1_buy_count = 0, h1_sell_count = 0; if(h1_ema_up) h1_buy_count++; if(h1_rsi_buy) h1_buy_count++; if(h1_adx_buy) h1_buy_count++; if(h1_stoch_buy) h1_buy_count++; if(!h1_ema_up) h1_sell_count++; if(h1_rsi_sell) h1_sell_count++; if(h1_adx_sell) h1_sell_count++; if(h1_stoch_sell) h1_sell_count++; EssentialLog("🔍 H1 Condition Count: Buy=" + IntegerToString(h1_buy_count) + "/4 Sell=" + IntegerToString(h1_sell_count) + "/4"); // Perhitungan strength yang lebih dinamis untuk H1 dengan mutual exclusion int h1_buy_conditions = 0; if(h1_ema_up) h1_buy_conditions++; if(h1_rsi_buy) h1_buy_conditions++; if(h1_adx_buy) h1_buy_conditions++; if(h1_stoch_buy) h1_buy_conditions++; int h1_sell_conditions = 0; if(!h1_ema_up) h1_sell_conditions++; if(h1_rsi_sell) h1_sell_conditions++; if(h1_adx_sell) h1_sell_conditions++; if(h1_stoch_sell) h1_sell_conditions++; // Mutual exclusion: Hanya ambil sinyal yang lebih kuat if(h1_buy_conditions > h1_sell_conditions && h1_buy_conditions >= 1) { mtf.h1_buy = true; mtf.h1_sell = false; // Score lebih dinamis: 40 untuk 4 kondisi, 30 untuk 3, 20 untuk 2, 10 untuk 1 mtf.h1_buy_strength = 40 * (h1_buy_conditions / 4.0); mtf.h1_sell_strength = 0; // Reset sell strength // Bonus untuk kondisi yang sangat kuat if(h1_buy_conditions >= 3) mtf.h1_buy_strength += 10; if(h1_buy_conditions == 4) mtf.h1_buy_strength += 10; EssentialLog("🟢 H1 BUY Signal: Conditions=" + IntegerToString(h1_buy_conditions) + "/4 EMA=" + (h1_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(h1_rsi, 1) + " ADX=" + DoubleToString(h1_adx, 1) + " Stoch=" + DoubleToString(h1_stoch_k, 1)); } else if(h1_sell_conditions > h1_buy_conditions && h1_sell_conditions >= 1) { mtf.h1_sell = true; mtf.h1_buy = false; // Score lebih dinamis: 40 untuk 4 kondisi, 30 untuk 3, 20 untuk 2, 10 untuk 1 mtf.h1_sell_strength = 40 * (h1_sell_conditions / 4.0); mtf.h1_buy_strength = 0; // Reset buy strength // Bonus untuk kondisi yang sangat kuat if(h1_sell_conditions >= 3) mtf.h1_sell_strength += 10; if(h1_sell_conditions == 4) mtf.h1_sell_strength += 10; EssentialLog("🔴 H1 SELL Signal: Conditions=" + IntegerToString(h1_sell_conditions) + "/4 EMA=" + (h1_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(h1_rsi, 1) + " ADX=" + DoubleToString(h1_adx, 1) + " Stoch=" + DoubleToString(h1_stoch_k, 1)); } else if(h1_buy_conditions == h1_sell_conditions && h1_buy_conditions >= 2) { // Jika sama dan cukup kuat, gunakan EMA sebagai tie-breaker if(h1_ema_up) { mtf.h1_buy = true; mtf.h1_sell = false; mtf.h1_buy_strength = 40 * (h1_buy_conditions / 4.0); mtf.h1_sell_strength = 0; if(h1_buy_conditions >= 3) mtf.h1_buy_strength += 10; if(h1_buy_conditions == 4) mtf.h1_buy_strength += 10; EssentialLog("🟢 H1 BUY Signal (Tie-breaker): Conditions=" + IntegerToString(h1_buy_conditions) + "/4"); } else { mtf.h1_sell = true; mtf.h1_buy = false; mtf.h1_sell_strength = 40 * (h1_sell_conditions / 4.0); mtf.h1_buy_strength = 0; if(h1_sell_conditions >= 3) mtf.h1_sell_strength += 10; if(h1_sell_conditions == 4) mtf.h1_sell_strength += 10; EssentialLog("🔴 H1 SELL Signal (Tie-breaker): Conditions=" + IntegerToString(h1_sell_conditions) + "/4"); } } else { // Tidak ada sinyal yang jelas mtf.h1_buy = false; mtf.h1_sell = false; mtf.h1_buy_strength = 0; mtf.h1_sell_strength = 0; EssentialLog("⚪ H1 NO Signal: Buy=" + IntegerToString(h1_buy_conditions) + " Sell=" + IntegerToString(h1_sell_conditions)); } // M15 Analysis (30% weight) - Using global handles for real-time updates double m15_ema_f = 0, m15_ema_s = 0, m15_rsi = 0, m15_adx = 0, m15_stoch_k = 0, m15_stoch_d = 0; if(hEmaF_M15 != INVALID_HANDLE && hEmaS_M15 != INVALID_HANDLE) { double m15_ema_f_buffer[1], m15_ema_s_buffer[1]; int shift = ShiftFor(PERIOD_M15); int copied_f = CopyBuffer(hEmaF_M15, 0, shift, 1, m15_ema_f_buffer); int copied_s = CopyBuffer(hEmaS_M15, 0, shift, 1, m15_ema_s_buffer); EssentialLog("🔍 M15 EMA CopyBuffer: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); if(copied_f > 0 && copied_s > 0) { m15_ema_f = m15_ema_f_buffer[0]; m15_ema_s = m15_ema_s_buffer[0]; EssentialLog("✅ M15 EMA Data: Fast=" + DoubleToString(m15_ema_f, 5) + " Slow=" + DoubleToString(m15_ema_s, 5)); } else { EssentialLog("❌ M15 EMA CopyBuffer failed: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); } } else { EssentialLog("❌ M15 EMA Handles invalid: Fast=" + IntegerToString(hEmaF_M15) + " Slow=" + IntegerToString(hEmaS_M15)); } if(hRsi_M15 != INVALID_HANDLE) { double m15_rsi_buffer[1]; int shift = ShiftFor(PERIOD_M15); int copied = CopyBuffer(hRsi_M15, 0, shift, 1, m15_rsi_buffer); EssentialLog("🔍 M15 RSI CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { m15_rsi = m15_rsi_buffer[0]; EssentialLog("✅ M15 RSI Data: " + DoubleToString(m15_rsi, 2)); } else { EssentialLog("❌ M15 RSI CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ M15 RSI Handle invalid: " + IntegerToString(hRsi_M15)); } if(hAdx_M15 != INVALID_HANDLE) { double m15_adx_buffer[1]; int shift = ShiftFor(PERIOD_M15); int copied = CopyBuffer(hAdx_M15, 0, shift, 1, m15_adx_buffer); EssentialLog("🔍 M15 ADX CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { m15_adx = m15_adx_buffer[0]; EssentialLog("✅ M15 ADX Data: " + DoubleToString(m15_adx, 2)); } else { EssentialLog("❌ M15 ADX CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ M15 ADX Handle invalid: " + IntegerToString(hAdx_M15)); } if(hStoch_M15 != INVALID_HANDLE) { double m15_stoch_k_buffer[1], m15_stoch_d_buffer[1]; int shift = ShiftFor(PERIOD_M15); int copied_k = CopyBuffer(hStoch_M15, 0, shift, 1, m15_stoch_k_buffer); int copied_d = CopyBuffer(hStoch_M15, 1, shift, 1, m15_stoch_d_buffer); EssentialLog("🔍 M15 Stochastic CopyBuffer: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); if(copied_k > 0 && copied_d > 0) { m15_stoch_k = m15_stoch_k_buffer[0]; m15_stoch_d = m15_stoch_d_buffer[0]; EssentialLog("✅ M15 Stochastic Data: K=" + DoubleToString(m15_stoch_k, 2) + " D=" + DoubleToString(m15_stoch_d, 2)); } else { EssentialLog("❌ M15 Stochastic CopyBuffer failed: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); } } else { EssentialLog("❌ M15 Stochastic Handle invalid: " + IntegerToString(hStoch_M15)); } bool m15_ema_up = m15_ema_f > m15_ema_s; bool m15_rsi_buy = (m15_rsi < 50); // RSI < 50 untuk buy (lebih fleksibel) bool m15_rsi_sell = (m15_rsi > 50); // RSI > 50 untuk sell (lebih fleksibel) bool m15_adx_buy = (m15_adx >= 8); // ADX >= 8 untuk buy (lebih fleksibel) bool m15_adx_sell = (m15_adx >= 8); // ADX >= 8 untuk sell (lebih fleksibel) bool m15_stoch_buy = (m15_stoch_k < 40); // Stoch < 40 untuk buy (lebih fleksibel) bool m15_stoch_sell = (m15_stoch_k > 60); // Stoch > 60 untuk sell (lebih fleksibel) EssentialLog("🔍 M15 Conditions: EMA=" + (m15_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m15_rsi, 1) + " ADX=" + DoubleToString(m15_adx, 1) + " Stoch=" + DoubleToString(m15_stoch_k, 1)); EssentialLog("🔍 M15 Signals: RSI_Buy=" + (m15_rsi_buy ? "YES" : "NO") + " RSI_Sell=" + (m15_rsi_sell ? "YES" : "NO") + " ADX_Buy=" + (m15_adx_buy ? "YES" : "NO") + " ADX_Sell=" + (m15_adx_sell ? "YES" : "NO") + " Stoch_Buy=" + (m15_stoch_buy ? "YES" : "NO") + " Stoch_Sell=" + (m15_stoch_sell ? "YES" : "NO")); // Perhitungan strength yang lebih dinamis untuk M15 dengan mutual exclusion int m15_buy_conditions = 0; if(m15_ema_up) m15_buy_conditions++; if(m15_rsi_buy) m15_buy_conditions++; if(m15_adx_buy) m15_buy_conditions++; if(m15_stoch_buy) m15_buy_conditions++; int m15_sell_conditions = 0; if(!m15_ema_up) m15_sell_conditions++; if(m15_rsi_sell) m15_sell_conditions++; if(m15_adx_sell) m15_sell_conditions++; if(m15_stoch_sell) m15_sell_conditions++; // Mutual exclusion: Hanya ambil sinyal yang lebih kuat if(m15_buy_conditions > m15_sell_conditions && m15_buy_conditions >= 1) { mtf.m15_buy = true; mtf.m15_sell = false; // Score lebih dinamis: 30 untuk 4 kondisi, 22.5 untuk 3, 15 untuk 2, 7.5 untuk 1 mtf.m15_buy_strength = 30 * (m15_buy_conditions / 4.0); mtf.m15_sell_strength = 0; // Reset sell strength // Bonus untuk kondisi yang sangat kuat if(m15_buy_conditions >= 3) mtf.m15_buy_strength += 7.5; if(m15_buy_conditions == 4) mtf.m15_buy_strength += 7.5; EssentialLog("🟢 M15 BUY Signal: Conditions=" + IntegerToString(m15_buy_conditions) + "/4 EMA=" + (m15_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m15_rsi, 1) + " ADX=" + DoubleToString(m15_adx, 1) + " Stoch=" + DoubleToString(m15_stoch_k, 1)); } else if(m15_sell_conditions > m15_buy_conditions && m15_sell_conditions >= 1) { mtf.m15_sell = true; mtf.m15_buy = false; // Score lebih dinamis: 30 untuk 4 kondisi, 22.5 untuk 3, 15 untuk 2, 7.5 untuk 1 mtf.m15_sell_strength = 30 * (m15_sell_conditions / 4.0); mtf.m15_buy_strength = 0; // Reset buy strength // Bonus untuk kondisi yang sangat kuat if(m15_sell_conditions >= 3) mtf.m15_sell_strength += 7.5; if(m15_sell_conditions == 4) mtf.m15_sell_strength += 7.5; EssentialLog("🔴 M15 SELL Signal: Conditions=" + IntegerToString(m15_sell_conditions) + "/4 EMA=" + (m15_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m15_rsi, 1) + " ADX=" + DoubleToString(m15_adx, 1) + " Stoch=" + DoubleToString(m15_stoch_k, 1)); } else if(m15_buy_conditions == m15_sell_conditions && m15_buy_conditions >= 2) { // Jika sama dan cukup kuat, gunakan EMA sebagai tie-breaker if(m15_ema_up) { mtf.m15_buy = true; mtf.m15_sell = false; mtf.m15_buy_strength = 30 * (m15_buy_conditions / 4.0); mtf.m15_sell_strength = 0; if(m15_buy_conditions >= 3) mtf.m15_buy_strength += 7.5; if(m15_buy_conditions == 4) mtf.m15_buy_strength += 7.5; EssentialLog("🟢 M15 BUY Signal (Tie-breaker): Conditions=" + IntegerToString(m15_buy_conditions) + "/4"); } else { mtf.m15_sell = true; mtf.m15_buy = false; mtf.m15_sell_strength = 30 * (m15_sell_conditions / 4.0); mtf.m15_buy_strength = 0; if(m15_sell_conditions >= 3) mtf.m15_sell_strength += 7.5; if(m15_sell_conditions == 4) mtf.m15_sell_strength += 7.5; EssentialLog("🔴 M15 SELL Signal (Tie-breaker): Conditions=" + IntegerToString(m15_sell_conditions) + "/4"); } } else { // Tidak ada sinyal yang jelas mtf.m15_buy = false; mtf.m15_sell = false; mtf.m15_buy_strength = 0; mtf.m15_sell_strength = 0; EssentialLog("⚪ M15 NO Signal: Buy=" + IntegerToString(m15_buy_conditions) + " Sell=" + IntegerToString(m15_sell_conditions)); } // M5 Analysis (20% weight) - Using global handles for real-time updates double m5_ema_f = 0, m5_ema_s = 0, m5_rsi = 0, m5_adx = 0, m5_stoch_k = 0, m5_stoch_d = 0; if(hEmaF_M5 != INVALID_HANDLE && hEmaS_M5 != INVALID_HANDLE) { double m5_ema_f_buffer[1], m5_ema_s_buffer[1]; int shift = ShiftFor(PERIOD_M5); int copied_f = CopyBuffer(hEmaF_M5, 0, shift, 1, m5_ema_f_buffer); int copied_s = CopyBuffer(hEmaS_M5, 0, shift, 1, m5_ema_s_buffer); EssentialLog("🔍 M5 EMA CopyBuffer: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); if(copied_f > 0 && copied_s > 0) { m5_ema_f = m5_ema_f_buffer[0]; m5_ema_s = m5_ema_s_buffer[0]; EssentialLog("✅ M5 EMA Data: Fast=" + DoubleToString(m5_ema_f, 5) + " Slow=" + DoubleToString(m5_ema_s, 5)); } else { EssentialLog("❌ M5 EMA CopyBuffer failed: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); } } else { EssentialLog("❌ M5 EMA Handles invalid: Fast=" + IntegerToString(hEmaF_M5) + " Slow=" + IntegerToString(hEmaS_M5)); } if(hRsi_M5 != INVALID_HANDLE) { double m5_rsi_buffer[1]; int shift = ShiftFor(PERIOD_M5); int copied = CopyBuffer(hRsi_M5, 0, shift, 1, m5_rsi_buffer); EssentialLog("🔍 M5 RSI CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { m5_rsi = m5_rsi_buffer[0]; EssentialLog("✅ M5 RSI Data: " + DoubleToString(m5_rsi, 2)); } else { EssentialLog("❌ M5 RSI CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ M5 RSI Handle invalid: " + IntegerToString(hRsi_M5)); } if(hAdx_M5 != INVALID_HANDLE) { double m5_adx_buffer[1]; int shift = ShiftFor(PERIOD_M5); int copied = CopyBuffer(hAdx_M5, 0, shift, 1, m5_adx_buffer); EssentialLog("🔍 M5 ADX CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { m5_adx = m5_adx_buffer[0]; EssentialLog("✅ M5 ADX Data: " + DoubleToString(m5_adx, 2)); } else { EssentialLog("❌ M5 ADX CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ M5 ADX Handle invalid: " + IntegerToString(hAdx_M5)); } if(hStoch_M5 != INVALID_HANDLE) { double m5_stoch_k_buffer[1], m5_stoch_d_buffer[1]; int shift = ShiftFor(PERIOD_M5); int copied_k = CopyBuffer(hStoch_M5, 0, shift, 1, m5_stoch_k_buffer); int copied_d = CopyBuffer(hStoch_M5, 1, shift, 1, m5_stoch_d_buffer); EssentialLog("🔍 M5 Stochastic CopyBuffer: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); if(copied_k > 0 && copied_d > 0) { m5_stoch_k = m5_stoch_k_buffer[0]; m5_stoch_d = m5_stoch_d_buffer[0]; EssentialLog("✅ M5 Stochastic Data: K=" + DoubleToString(m5_stoch_k, 2) + " D=" + DoubleToString(m5_stoch_d, 2)); } else { EssentialLog("❌ M5 Stochastic CopyBuffer failed: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); } } else { EssentialLog("❌ M5 Stochastic Handle invalid: " + IntegerToString(hStoch_M5)); } bool m5_ema_up = m5_ema_f > m5_ema_s; bool m5_rsi_buy = (m5_rsi < 50); // RSI < 50 untuk buy (lebih fleksibel) bool m5_rsi_sell = (m5_rsi > 50); // RSI > 50 untuk sell (lebih fleksibel) bool m5_adx_buy = (m5_adx >= 6); // ADX >= 6 untuk buy (lebih fleksibel) bool m5_adx_sell = (m5_adx >= 6); // ADX >= 6 untuk sell (lebih fleksibel) bool m5_stoch_buy = (m5_stoch_k < 40); // Stoch < 40 untuk buy (lebih fleksibel) bool m5_stoch_sell = (m5_stoch_k > 60); // Stoch > 60 untuk sell (lebih fleksibel) EssentialLog("🔍 M5 Conditions: EMA=" + (m5_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m5_rsi, 1) + " ADX=" + DoubleToString(m5_adx, 1) + " Stoch=" + DoubleToString(m5_stoch_k, 1)); EssentialLog("🔍 M5 Signals: RSI_Buy=" + (m5_rsi_buy ? "YES" : "NO") + " RSI_Sell=" + (m5_rsi_sell ? "YES" : "NO") + " ADX_Buy=" + (m5_adx_buy ? "YES" : "NO") + " ADX_Sell=" + (m5_adx_sell ? "YES" : "NO") + " Stoch_Buy=" + (m5_stoch_buy ? "YES" : "NO") + " Stoch_Sell=" + (m5_stoch_sell ? "YES" : "NO")); // Perhitungan strength yang lebih dinamis untuk M5 dengan mutual exclusion int m5_buy_conditions = 0; if(m5_ema_up) m5_buy_conditions++; if(m5_rsi_buy) m5_buy_conditions++; if(m5_adx_buy) m5_buy_conditions++; if(m5_stoch_buy) m5_buy_conditions++; int m5_sell_conditions = 0; if(!m5_ema_up) m5_sell_conditions++; if(m5_rsi_sell) m5_sell_conditions++; if(m5_adx_sell) m5_sell_conditions++; if(m5_stoch_sell) m5_sell_conditions++; // Mutual exclusion: Hanya ambil sinyal yang lebih kuat if(m5_buy_conditions > m5_sell_conditions && m5_buy_conditions >= 1) { mtf.m5_buy = true; mtf.m5_sell = false; // Score lebih dinamis: 20 untuk 4 kondisi, 15 untuk 3, 10 untuk 2, 5 untuk 1 mtf.m5_buy_strength = 20 * (m5_buy_conditions / 4.0); mtf.m5_sell_strength = 0; // Reset sell strength // Bonus untuk kondisi yang sangat kuat if(m5_buy_conditions >= 3) mtf.m5_buy_strength += 5; if(m5_buy_conditions == 4) mtf.m5_buy_strength += 5; EssentialLog("🟢 M5 BUY Signal: Conditions=" + IntegerToString(m5_buy_conditions) + "/4 EMA=" + (m5_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m5_rsi, 1) + " ADX=" + DoubleToString(m5_adx, 1) + " Stoch=" + DoubleToString(m5_stoch_k, 1)); } else if(m5_sell_conditions > m5_buy_conditions && m5_sell_conditions >= 1) { mtf.m5_sell = true; mtf.m5_buy = false; // Score lebih dinamis: 20 untuk 4 kondisi, 15 untuk 3, 10 untuk 2, 5 untuk 1 mtf.m5_sell_strength = 20 * (m5_sell_conditions / 4.0); mtf.m5_buy_strength = 0; // Reset buy strength // Bonus untuk kondisi yang sangat kuat if(m5_sell_conditions >= 3) mtf.m5_sell_strength += 5; if(m5_sell_conditions == 4) mtf.m5_sell_strength += 5; EssentialLog("🔴 M5 SELL Signal: Conditions=" + IntegerToString(m5_sell_conditions) + "/4 EMA=" + (m5_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m5_rsi, 1) + " ADX=" + DoubleToString(m5_adx, 1) + " Stoch=" + DoubleToString(m5_stoch_k, 1)); } else if(m5_buy_conditions == m5_sell_conditions && m5_buy_conditions >= 2) { // Jika sama dan cukup kuat, gunakan EMA sebagai tie-breaker if(m5_ema_up) { mtf.m5_buy = true; mtf.m5_sell = false; mtf.m5_buy_strength = 20 * (m5_buy_conditions / 4.0); mtf.m5_sell_strength = 0; if(m5_buy_conditions >= 3) mtf.m5_buy_strength += 5; if(m5_buy_conditions == 4) mtf.m5_buy_strength += 5; EssentialLog("🟢 M5 BUY Signal (Tie-breaker): Conditions=" + IntegerToString(m5_buy_conditions) + "/4"); } else { mtf.m5_sell = true; mtf.m5_buy = false; mtf.m5_sell_strength = 20 * (m5_sell_conditions / 4.0); mtf.m5_buy_strength = 0; if(m5_sell_conditions >= 3) mtf.m5_sell_strength += 5; if(m5_sell_conditions == 4) mtf.m5_sell_strength += 5; EssentialLog("🔴 M5 SELL Signal (Tie-breaker): Conditions=" + IntegerToString(m5_sell_conditions) + "/4"); } } else { // Tidak ada sinyal yang jelas mtf.m5_buy = false; mtf.m5_sell = false; mtf.m5_buy_strength = 0; mtf.m5_sell_strength = 0; EssentialLog("⚪ M5 NO Signal: Buy=" + IntegerToString(m5_buy_conditions) + " Sell=" + IntegerToString(m5_sell_conditions)); } // M1 Analysis (10% weight) - Using global handles for real-time updates double m1_ema_f = 0, m1_ema_s = 0, m1_rsi = 0, m1_adx = 0, m1_stoch_k = 0, m1_stoch_d = 0; if(hEmaF_M1 != INVALID_HANDLE && hEmaS_M1 != INVALID_HANDLE) { double m1_ema_f_buffer[1], m1_ema_s_buffer[1]; int shift = ShiftFor(PERIOD_M1); int copied_f = CopyBuffer(hEmaF_M1, 0, shift, 1, m1_ema_f_buffer); int copied_s = CopyBuffer(hEmaS_M1, 0, shift, 1, m1_ema_s_buffer); EssentialLog("🔍 M1 EMA CopyBuffer: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); if(copied_f > 0 && copied_s > 0) { m1_ema_f = m1_ema_f_buffer[0]; m1_ema_s = m1_ema_s_buffer[0]; EssentialLog("✅ M1 EMA Data: Fast=" + DoubleToString(m1_ema_f, 5) + " Slow=" + DoubleToString(m1_ema_s, 5)); } else { EssentialLog("❌ M1 EMA CopyBuffer failed: Fast=" + IntegerToString(copied_f) + " Slow=" + IntegerToString(copied_s)); } } else { EssentialLog("❌ M1 EMA Handles invalid: Fast=" + IntegerToString(hEmaF_M1) + " Slow=" + IntegerToString(hEmaS_M1)); } if(hRsi_M1 != INVALID_HANDLE) { double m1_rsi_buffer[1]; int shift = ShiftFor(PERIOD_M1); int copied = CopyBuffer(hRsi_M1, 0, shift, 1, m1_rsi_buffer); EssentialLog("🔍 M1 RSI CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { m1_rsi = m1_rsi_buffer[0]; EssentialLog("✅ M1 RSI Data: " + DoubleToString(m1_rsi, 2)); } else { EssentialLog("❌ M1 RSI CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ M1 RSI Handle invalid: " + IntegerToString(hRsi_M1)); } if(hAdx_M1 != INVALID_HANDLE) { double m1_adx_buffer[1]; int shift = ShiftFor(PERIOD_M1); int copied = CopyBuffer(hAdx_M1, 0, shift, 1, m1_adx_buffer); EssentialLog("🔍 M1 ADX CopyBuffer: " + IntegerToString(copied)); if(copied > 0) { m1_adx = m1_adx_buffer[0]; EssentialLog("✅ M1 ADX Data: " + DoubleToString(m1_adx, 2)); } else { EssentialLog("❌ M1 ADX CopyBuffer failed: " + IntegerToString(copied)); } } else { EssentialLog("❌ M1 ADX Handle invalid: " + IntegerToString(hAdx_M1)); } if(hStoch_M1 != INVALID_HANDLE) { double m1_stoch_k_buffer[1], m1_stoch_d_buffer[1]; int shift = ShiftFor(PERIOD_M1); int copied_k = CopyBuffer(hStoch_M1, 0, shift, 1, m1_stoch_k_buffer); int copied_d = CopyBuffer(hStoch_M1, 1, shift, 1, m1_stoch_d_buffer); EssentialLog("🔍 M1 Stochastic CopyBuffer: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); if(copied_k > 0 && copied_d > 0) { m1_stoch_k = m1_stoch_k_buffer[0]; m1_stoch_d = m1_stoch_d_buffer[0]; EssentialLog("✅ M1 Stochastic Data: K=" + DoubleToString(m1_stoch_k, 2) + " D=" + DoubleToString(m1_stoch_d, 2)); } else { EssentialLog("❌ M1 Stochastic CopyBuffer failed: K=" + IntegerToString(copied_k) + " D=" + IntegerToString(copied_d)); } } else { EssentialLog("❌ M1 Stochastic Handle invalid: " + IntegerToString(hStoch_M1)); } bool m1_ema_up = m1_ema_f > m1_ema_s; bool m1_rsi_buy = (m1_rsi < 50); // RSI < 50 untuk buy (lebih fleksibel) bool m1_rsi_sell = (m1_rsi > 50); // RSI > 50 untuk sell (lebih fleksibel) bool m1_adx_buy = (m1_adx >= 5); // ADX >= 5 untuk buy (lebih fleksibel) bool m1_adx_sell = (m1_adx >= 5); // ADX >= 5 untuk sell (lebih fleksibel) bool m1_stoch_buy = (m1_stoch_k < 40); // Stoch < 40 untuk buy (lebih fleksibel) bool m1_stoch_sell = (m1_stoch_k > 60); // Stoch > 60 untuk sell (lebih fleksibel) EssentialLog("🔍 M1 Conditions: EMA=" + (m1_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m1_rsi, 1) + " ADX=" + DoubleToString(m1_adx, 1) + " Stoch=" + DoubleToString(m1_stoch_k, 1)); EssentialLog("🔍 M1 Signals: RSI_Buy=" + (m1_rsi_buy ? "YES" : "NO") + " RSI_Sell=" + (m1_rsi_sell ? "YES" : "NO") + " ADX_Buy=" + (m1_adx_buy ? "YES" : "NO") + " ADX_Sell=" + (m1_adx_sell ? "YES" : "NO") + " Stoch_Buy=" + (m1_stoch_buy ? "YES" : "NO") + " Stoch_Sell=" + (m1_stoch_sell ? "YES" : "NO")); // Perhitungan strength yang lebih dinamis untuk M1 dengan mutual exclusion int m1_buy_conditions = 0; if(m1_ema_up) m1_buy_conditions++; if(m1_rsi_buy) m1_buy_conditions++; if(m1_adx_buy) m1_buy_conditions++; if(m1_stoch_buy) m1_buy_conditions++; int m1_sell_conditions = 0; if(!m1_ema_up) m1_sell_conditions++; if(m1_rsi_sell) m1_sell_conditions++; if(m1_adx_sell) m1_sell_conditions++; if(m1_stoch_sell) m1_sell_conditions++; // Mutual exclusion: Hanya ambil sinyal yang lebih kuat if(m1_buy_conditions > m1_sell_conditions && m1_buy_conditions >= 1) { mtf.m1_buy = true; mtf.m1_sell = false; // Score lebih dinamis: 10 untuk 4 kondisi, 7.5 untuk 3, 5 untuk 2, 2.5 untuk 1 mtf.m1_buy_strength = 10 * (m1_buy_conditions / 4.0); mtf.m1_sell_strength = 0; // Reset sell strength // Bonus untuk kondisi yang sangat kuat if(m1_buy_conditions >= 3) mtf.m1_buy_strength += 2.5; if(m1_buy_conditions == 4) mtf.m1_buy_strength += 2.5; EssentialLog("🟢 M1 BUY Signal: Conditions=" + IntegerToString(m1_buy_conditions) + "/4 EMA=" + (m1_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m1_rsi, 1) + " ADX=" + DoubleToString(m1_adx, 1) + " Stoch=" + DoubleToString(m1_stoch_k, 1)); } else if(m1_sell_conditions > m1_buy_conditions && m1_sell_conditions >= 1) { mtf.m1_sell = true; mtf.m1_buy = false; // Score lebih dinamis: 10 untuk 4 kondisi, 7.5 untuk 3, 5 untuk 2, 2.5 untuk 1 mtf.m1_sell_strength = 10 * (m1_sell_conditions / 4.0); mtf.m1_buy_strength = 0; // Reset buy strength // Bonus untuk kondisi yang sangat kuat if(m1_sell_conditions >= 3) mtf.m1_sell_strength += 2.5; if(m1_sell_conditions == 4) mtf.m1_sell_strength += 2.5; EssentialLog("🔴 M1 SELL Signal: Conditions=" + IntegerToString(m1_sell_conditions) + "/4 EMA=" + (m1_ema_up ? "UP" : "DOWN") + " RSI=" + DoubleToString(m1_rsi, 1) + " ADX=" + DoubleToString(m1_adx, 1) + " Stoch=" + DoubleToString(m1_stoch_k, 1)); } else if(m1_buy_conditions == m1_sell_conditions && m1_buy_conditions >= 2) { // Jika sama dan cukup kuat, gunakan EMA sebagai tie-breaker if(m1_ema_up) { mtf.m1_buy = true; mtf.m1_sell = false; mtf.m1_buy_strength = 10 * (m1_buy_conditions / 4.0); mtf.m1_sell_strength = 0; if(m1_buy_conditions >= 3) mtf.m1_buy_strength += 2.5; if(m1_buy_conditions == 4) mtf.m1_buy_strength += 2.5; EssentialLog("🟢 M1 BUY Signal (Tie-breaker): Conditions=" + IntegerToString(m1_buy_conditions) + "/4"); } else { mtf.m1_sell = true; mtf.m1_buy = false; mtf.m1_sell_strength = 10 * (m1_sell_conditions / 4.0); mtf.m1_buy_strength = 0; if(m1_sell_conditions >= 3) mtf.m1_sell_strength += 2.5; if(m1_sell_conditions == 4) mtf.m1_sell_strength += 2.5; EssentialLog("🔴 M1 SELL Signal (Tie-breaker): Conditions=" + IntegerToString(m1_sell_conditions) + "/4"); } } else { // Tidak ada sinyal yang jelas mtf.m1_buy = false; mtf.m1_sell = false; mtf.m1_buy_strength = 0; mtf.m1_sell_strength = 0; EssentialLog("⚪ M1 NO Signal: Buy=" + IntegerToString(m1_buy_conditions) + " Sell=" + IntegerToString(m1_sell_conditions)); } // Calculate Total Score dengan mutual exclusion double buy_score = 0, sell_score = 0; if(mtf.h1_buy) buy_score += mtf.h1_buy_strength; if(mtf.m15_buy) buy_score += mtf.m15_buy_strength; if(mtf.m5_buy) buy_score += mtf.m5_buy_strength; if(mtf.m1_buy) buy_score += mtf.m1_buy_strength; if(mtf.h1_sell) sell_score += mtf.h1_sell_strength; if(mtf.m15_sell) sell_score += mtf.m15_sell_strength; if(mtf.m5_sell) sell_score += mtf.m5_sell_strength; if(mtf.m1_sell) sell_score += mtf.m1_sell_strength; // Enhanced debugging untuk masalah score MTF EssentialLog("🔍 MTF Score Debug - Buy Conditions: H1=" + (mtf.h1_buy ? "YES" : "NO") + " M15=" + (mtf.m15_buy ? "YES" : "NO") + " M5=" + (mtf.m5_buy ? "YES" : "NO") + " M1=" + (mtf.m1_buy ? "YES" : "NO")); EssentialLog("🔍 MTF Score Debug - Sell Conditions: H1=" + (mtf.h1_sell ? "YES" : "NO") + " M15=" + (mtf.m15_sell ? "YES" : "NO") + " M5=" + (mtf.m5_sell ? "YES" : "NO") + " M1=" + (mtf.m1_sell ? "YES" : "NO")); EssentialLog("🔍 MTF Score Debug - Buy Strengths: H1=" + DoubleToString(mtf.h1_buy_strength,1) + " M15=" + DoubleToString(mtf.m15_buy_strength,1) + " M5=" + DoubleToString(mtf.m5_buy_strength,1) + " M1=" + DoubleToString(mtf.m1_buy_strength,1)); EssentialLog("🔍 MTF Score Debug - Sell Strengths: H1=" + DoubleToString(mtf.h1_sell_strength,1) + " M15=" + DoubleToString(mtf.m15_sell_strength,1) + " M5=" + DoubleToString(mtf.m5_sell_strength,1) + " M1=" + DoubleToString(mtf.m1_sell_strength,1)); // Log detail kondisi untuk debugging EssentialLog("🔍 MTF Signal Details - H1: " + (mtf.h1_buy ? "BUY" : (mtf.h1_sell ? "SELL" : "NONE")) + " | M15: " + (mtf.m15_buy ? "BUY" : (mtf.m15_sell ? "SELL" : "NONE")) + " | M5: " + (mtf.m5_buy ? "BUY" : (mtf.m5_sell ? "SELL" : "NONE")) + " | M1: " + (mtf.m1_buy ? "BUY" : (mtf.m1_sell ? "SELL" : "NONE"))); // Debug: Log individual strengths EssentialLog("🔍 MTF Strengths - H1: Buy=" + DoubleToString(mtf.h1_buy_strength, 1) + " Sell=" + DoubleToString(mtf.h1_sell_strength, 1) + " | M15: Buy=" + DoubleToString(mtf.m15_buy_strength, 1) + " Sell=" + DoubleToString(mtf.m15_sell_strength, 1) + " | M5: Buy=" + DoubleToString(mtf.m5_buy_strength, 1) + " Sell=" + DoubleToString(mtf.m5_sell_strength, 1) + " | M1: Buy=" + DoubleToString(mtf.m1_buy_strength, 1) + " Sell=" + DoubleToString(mtf.m1_sell_strength, 1)); EssentialLog("🔍 MTF Total Scores - Buy=" + DoubleToString(buy_score, 1) + " Sell=" + DoubleToString(sell_score, 1)); // Assign new score fields mtf.total_buy_score = buy_score; mtf.total_sell_score = sell_score; mtf.net_score = buy_score - sell_score; mtf.total_score = buy_score + sell_score; // konfluensi tanpa arah // Optional logging for new scores EssentialLog("🔎 MTF Scores → BUY=" + DoubleToString(mtf.total_buy_score,1) + " | SELL=" + DoubleToString(mtf.total_sell_score,1) + " | NET=" + DoubleToString(mtf.net_score,1) + " | TOTAL=" + DoubleToString(mtf.total_score,1)); // Build reason string string buy_tfs = "", sell_tfs = ""; if(mtf.h1_buy) buy_tfs += "H1 "; if(mtf.m15_buy) buy_tfs += "M15 "; if(mtf.m5_buy) buy_tfs += "M5 "; if(mtf.m1_buy) buy_tfs += "M1 "; if(mtf.h1_sell) sell_tfs += "H1 "; if(mtf.m15_sell) sell_tfs += "M15 "; if(mtf.m5_sell) sell_tfs += "M5 "; if(mtf.m1_sell) sell_tfs += "M1 "; if(buy_score > sell_score && buy_score >= 10) { // Lebih agresif: score >= 10 mtf.reason = "MTF BUY: " + buy_tfs + "Score: " + DoubleToString(buy_score, 1) + " (Net: " + DoubleToString(buy_score - sell_score, 1) + ")"; } else if(sell_score > buy_score && sell_score >= 10) { // Lebih agresif: score >= 10 mtf.reason = "MTF SELL: " + sell_tfs + "Score: " + DoubleToString(sell_score, 1) + " (Net: " + DoubleToString(sell_score - buy_score, 1) + ")"; } else { mtf.reason = "MTF: No clear signal (Buy: " + DoubleToString(buy_score, 1) + " Sell: " + DoubleToString(sell_score, 1) + ")"; } // Debug: Log final MTF result if(TimeCurrent() - lastMTFLog > 5) { EssentialLog("📊 MTF Final Result: Score=" + DoubleToString(mtf.total_score, 1) + " | " + mtf.reason); } // Apply opposite entry prevention MTFConfirmation filteredMTF = PreventOppositeEntry(mtf); // Store current signal for future reference (only if it's valid) if(filteredMTF.total_score >= MTF_MinScore) { lastMTFSignal = filteredMTF; lastMTFSignalValid = true; lastMTFSignalTime = TimeCurrent(); EssentialLog("💾 GetMTFConfirmation: Stored valid signal for future reference"); } EssentialLog("🔍 GetMTFConfirmation: Function completed, returning score=" + DoubleToString(filteredMTF.total_score, 1)); return filteredMTF; } // Function untuk mengecek apakah ada posisi terbuka bool HasOpenPosition() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionSelectByTicket(PositionGetTicket(i))) { if(PositionGetString(POSITION_SYMBOL) == _Symbol) { return true; } } } return false; } // Function untuk mendapatkan direction posisi terbuka (1=BUY, -1=SELL, 0=NONE) int GetOpenPositionDirection() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionSelectByTicket(PositionGetTicket(i))) { if(PositionGetString(POSITION_SYMBOL) == _Symbol) { ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(posType == POSITION_TYPE_BUY) return 1; if(posType == POSITION_TYPE_SELL) return -1; } } } return 0; } // Function untuk mencegah entry yang berlawanan dengan posisi terbuka MTFConfirmation PreventOppositeEntry(MTFConfirmation &mtf) { if(!MTF_PreventOppositeEntry) { EssentialLog("🔍 PreventOppositeEntry: Feature disabled, allowing all signals"); return mtf; } if(!HasOpenPosition()) { EssentialLog("🔍 PreventOppositeEntry: No open position, allowing all signals"); return mtf; } int openPosDirection = GetOpenPositionDirection(); if(openPosDirection == 0) { EssentialLog("🔍 PreventOppositeEntry: No valid open position direction"); return mtf; } // Tentukan direction sinyal baru int newSignalDirection = 0; if(mtf.total_buy_score > mtf.total_sell_score && mtf.total_buy_score >= MTF_MinScore) { newSignalDirection = 1; // BUY } else if(mtf.total_sell_score > mtf.total_buy_score && mtf.total_sell_score >= MTF_MinScore) { newSignalDirection = -1; // SELL } // Jika sinyal baru berlawanan dengan posisi terbuka if(newSignalDirection != 0 && newSignalDirection != openPosDirection) { EssentialLog("⚠️ PreventOppositeEntry: OPPOSITE SIGNAL DETECTED!"); EssentialLog("🔍 Current Position: " + (openPosDirection == 1 ? "BUY" : "SELL")); EssentialLog("🔍 New Signal: " + (newSignalDirection == 1 ? "BUY" : "SELL")); // Jika ada sinyal sebelumnya yang valid dan searah dengan posisi terbuka if(lastMTFSignalValid && lastMTFSignalTime > 0) { int lastSignalDirection = 0; if(lastMTFSignal.total_buy_score > lastMTFSignal.total_sell_score && lastMTFSignal.total_buy_score >= MTF_MinScore) { lastSignalDirection = 1; // BUY } else if(lastMTFSignal.total_sell_score > lastMTFSignal.total_buy_score && lastMTFSignal.total_sell_score >= MTF_MinScore) { lastSignalDirection = -1; // SELL } // Jika sinyal sebelumnya searah dengan posisi terbuka, gunakan sinyal sebelumnya if(lastSignalDirection == openPosDirection) { EssentialLog("✅ PreventOppositeEntry: Using previous signal to maintain position direction"); EssentialLog("🔍 Previous Signal: " + (lastSignalDirection == 1 ? "BUY" : "SELL") + " Score: " + DoubleToString(lastSignalDirection == 1 ? lastMTFSignal.total_buy_score : lastMTFSignal.total_sell_score, 1)); // Return sinyal sebelumnya dengan timestamp update lastMTFSignalTime = TimeCurrent(); return lastMTFSignal; } } // Jika tidak ada sinyal sebelumnya yang valid, block sinyal baru EssentialLog("❌ PreventOppositeEntry: Blocking opposite signal - no valid previous signal"); mtf.total_buy_score = 0; mtf.total_sell_score = 0; mtf.net_score = 0; mtf.total_score = 0; mtf.reason = "MTF: Signal blocked - opposite to open position"; return mtf; } EssentialLog("✅ PreventOppositeEntry: Signal direction allowed or no signal"); return mtf; } // Function untuk reset MTF signal tracking ketika posisi ditutup void ResetMTFSignalTracking() { if(lastMTFSignalValid && !HasOpenPosition()) { EssentialLog("🔄 ResetMTFSignalTracking: Position closed, resetting signal tracking"); lastMTFSignalValid = false; lastMTFSignalTime = 0; } } // Enhanced signal validation with MTF confirmation bool ValidateSignalWithMTF(SignalPack &s) { MTFConfirmation mtf = GetMTFConfirmation(); EssentialLog("🔍 ValidateSignalWithMTF: Starting validation with score=" + DoubleToString(mtf.total_score, 1) + " MinScore=" + DoubleToString(MTF_MinScore, 1)); // Check confluence threshold (total_score = buy + sell) if(mtf.total_score < MTF_MinScore) { s.reason += " | MTF Confluence too low: TOTAL=" + DoubleToString(mtf.total_score,1) + " (Min:" + DoubleToString(MTF_MinScore,1) + ")"; EssentialLog("❌ ValidateSignalWithMTF: Confluence too low - " + DoubleToString(mtf.total_score, 1) + " < " + DoubleToString(MTF_MinScore, 1)); return false; } // Enhanced debugging untuk signal dominan EssentialLog("🔍 ValidateSignalWithMTF: Signal Decision - Buy Score=" + DoubleToString(mtf.total_buy_score,1) + " Sell Score=" + DoubleToString(mtf.total_sell_score,1) + " Difference=" + DoubleToString(mtf.total_buy_score - mtf.total_sell_score,1)); // Sudah lolos konfluensi → tentukan arah if(mtf.total_buy_score > mtf.total_sell_score) { s.buy = true; s.sell = false; s.reason += " | MTF → BUY (Buy=" + DoubleToString(mtf.total_buy_score,1) + ", Sell=" + DoubleToString(mtf.total_sell_score,1) + ")"; EssentialLog("🟢 MTF Signal Generated: BUY (Buy: " + DoubleToString(mtf.total_buy_score, 1) + " > Sell: " + DoubleToString(mtf.total_sell_score, 1) + ")"); } else if(mtf.total_sell_score > mtf.total_buy_score) { s.buy = false; s.sell = true; s.reason += " | MTF → SELL (Sell=" + DoubleToString(mtf.total_sell_score,1) + ", Buy=" + DoubleToString(mtf.total_buy_score,1) + ")"; EssentialLog("🔴 MTF Signal Generated: SELL (Sell: " + DoubleToString(mtf.total_sell_score, 1) + " > Buy: " + DoubleToString(mtf.total_buy_score, 1) + ")"); } else { // Imbang → no trade / butuh filter tambahan s.buy = s.sell = false; s.reason += " | MTF → Balanced (no clear edge)"; EssentialLog("⚠️ MTF: Balanced scores (Buy: " + DoubleToString(mtf.total_buy_score, 1) + " = Sell: " + DoubleToString(mtf.total_sell_score, 1) + ")"); return false; } // Add MTF info to reason s.reason += " | " + mtf.reason; // Boost signal strength based on MTF confluence s.signalStrength += (mtf.total_score - 60) * 2; // Bonus points for high MTF confluence return true; }