diff --git a/src/SniperEA_backup.mq5 b/src/SniperEA_backup.mq5 deleted file mode 100644 index bc09285..0000000 --- a/src/SniperEA_backup.mq5 +++ /dev/null @@ -1,1856 +0,0 @@ -//+------------------------------------------------------------------+ -//| SniperEA.mq5 | -//| MT5 Sniper Strategy Expert Advisor | -//| OB + BOS + Liquidity Sweep + FVG | -//+------------------------------------------------------------------+ -#property copyright "Sniper Strategy EA" -#property link "" -#property version "1.00" -#property strict - -//--- Include files -#include -#include -#include - -//--- Global objects -CTrade trade; -CPositionInfo position; -COrderInfo order; - -//--- Input parameters -input group "=== Core Settings ===" input int MaxTradesPerDay = 3; // Maximum trades per symbol per day -input double RiskPercent = 1.0; // Risk percentage per trade -input double MinRR = 2.0; // Minimum risk-reward ratio -input bool UseTimeFilter = true; // Enable session filtering - -input group "=== Session Settings ===" input string AsiaStart = "00:00"; // Asia session start (GMT) -input string AsiaEnd = "09:00"; // Asia session end (GMT) -input string LondonStart = "08:00"; // London session start (GMT) -input string LondonEnd = "17:00"; // London session end (GMT) -input string NYStart = "13:00"; // New York session start (GMT) -input string NYEnd = "22:00"; // New York session end (GMT) - -input group "=== Risk Management ===" input int MaxSL = 50; // Maximum stop loss in pips -input int MinSL = 10; // Minimum stop loss in pips -input double MaxSlippage = 2.0; // Maximum slippage in pips -input int MaxPositions = 10; // Maximum total positions -input int MaxPositionsPerSymbol = 3; // Maximum positions per symbol - -input group "=== Pattern Detection ===" input int OBLookback = 20; // Order Block lookback candles -input double MinFVGSize = 3.0; // Minimum FVG size in pips -input double MinSweepDistance = 5.0; // Minimum sweep distance in pips -input int BOSConfirmationCandles = 3; // BOS confirmation within candles -input int SwingLookback = 10; // Swing high/low lookback period -input double OBStrengthFilter = 0.5; // Order Block strength filter (0-1) -input bool RequireMultiTFConfirmation = true; // Require multi-timeframe confirmation - -input group "=== Visualization ===" input bool ShowOrderBlocks = true; // Show Order Block zones -input bool ShowFVG = true; // Show Fair Value Gaps -input bool ShowBOS = true; // Show Break of Structure -input bool ShowSweeps = true; // Show Liquidity Sweeps -input bool ShowTradeLevels = true; // Show Entry/SL/TP levels - -input group "=== Symbols to Trade ===" input string Symbol1 = "EURUSD"; // Symbol 1 -input string Symbol2 = "GBPUSD"; // Symbol 2 -input string Symbol3 = "USDJPY"; // Symbol 3 -input string Symbol4 = "USDCHF"; // Symbol 4 -input string Symbol5 = "AUDUSD"; // Symbol 5 -input string Symbol6 = "USDCAD"; // Symbol 6 -input string Symbol7 = "NZDUSD"; // Symbol 7 -input string Symbol8 = "XAUUSD"; // Symbol 8 (Gold) - -input group "=== Logging & Debug ===" input bool EnableDetailedLogging = true; // Enable detailed logging -input bool EnableDebugMode = false; // Enable debug mode -input bool LogPatternDetection = true; // Log pattern detection events -input bool LogTradeExecution = true; // Log trade execution details - -//--- Global variables -string SymbolsToTrade[]; -int TotalSymbols = 0; -datetime LastBarTime = 0; -bool IsInitialized = false; -string LogPrefix = "SniperEA"; -int LogLevel = 0; // 0=Info, 1=Warning, 2=Error, 3=Debug - -//--- Structure definitions -struct OrderBlock -{ - double high; - double low; - datetime time; - bool is_bullish; - bool is_fresh; - int strength; -}; - -struct FairValueGap -{ - double top; - double bottom; - datetime time; - bool is_bullish; - bool is_filled; -}; - -struct LiquiditySweep -{ - double level; - datetime time; - bool is_high_sweep; - bool confirmed; -}; - -struct BreakOfStructure -{ - double level; - datetime time; - bool is_bullish; - bool confirmed; -}; - -//+------------------------------------------------------------------+ -//| Expert initialization function | -//+------------------------------------------------------------------+ -int OnInit() -{ - Print("=== Sniper EA Initialization Started ==="); - - // Initialize trade object - trade.SetExpertMagicNumber(123456); - trade.SetDeviationInPoints((int)(MaxSlippage * 10)); - trade.SetTypeFilling(ORDER_FILLING_FOK); - - // Setup symbols array - if (!SetupSymbolsArray()) - { - Print("ERROR: Failed to setup symbols array"); - return INIT_FAILED; - } - - // Validate input parameters - if (!ValidateInputs()) - { - Print("ERROR: Invalid input parameters"); - return INIT_FAILED; - } - - // Initialize chart objects - if (!InitializeChartObjects()) - { - Print("ERROR: Failed to initialize chart objects"); - return INIT_FAILED; - } - - // Initialize multi-timeframe analysis - if (!InitializeMultiTimeframeAnalysis()) - { - Print("ERROR: Failed to initialize multi-timeframe analysis"); - return INIT_FAILED; - } - - IsInitialized = true; - LastBarTime = iTime(_Symbol, PERIOD_M1, 0); - - Print("=== Sniper EA Initialization Completed Successfully ==="); - Print("Trading Symbols: ", TotalSymbols); - Print("Risk per Trade: ", RiskPercent, "%"); - Print("Minimum R:R Ratio: ", MinRR, ":1"); - - return INIT_SUCCEEDED; -} - -//+------------------------------------------------------------------+ -//| Expert deinitialization function | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) -{ - Print("=== Sniper EA Deinitialization Started ==="); - - // Clean up chart objects - CleanupChartObjects(); - - // Print deinitialization reason - string deinit_reason = ""; - switch (reason) - { - case REASON_PROGRAM: - deinit_reason = "Expert Advisor terminated"; - break; - case REASON_REMOVE: - deinit_reason = "Expert Advisor removed from chart"; - break; - case REASON_RECOMPILE: - deinit_reason = "Expert Advisor recompiled"; - break; - case REASON_CHARTCHANGE: - deinit_reason = "Chart symbol or period changed"; - break; - case REASON_CHARTCLOSE: - deinit_reason = "Chart closed"; - break; - case REASON_PARAMETERS: - deinit_reason = "Input parameters changed"; - break; - case REASON_ACCOUNT: - deinit_reason = "Account changed"; - break; - default: - deinit_reason = "Unknown reason"; - break; - } - - Print("Deinitialization Reason: ", deinit_reason); - Print("=== Sniper EA Deinitialization Completed ==="); -} - -//+------------------------------------------------------------------+ -//| Expert tick function | -//+------------------------------------------------------------------+ -void OnTick() -{ - if (!IsInitialized) - return; - - // Check for new bar - datetime current_bar_time = iTime(_Symbol, PERIOD_M1, 0); - if (current_bar_time == LastBarTime) - return; - - LastBarTime = current_bar_time; - - // Main trading logic will be implemented here - ProcessTradingLogic(); -} - -//+------------------------------------------------------------------+ -//| Setup symbols array from input parameters | -//+------------------------------------------------------------------+ -bool SetupSymbolsArray() -{ - ArrayResize(SymbolsToTrade, 0); - TotalSymbols = 0; - - string symbols[8] = {Symbol1, Symbol2, Symbol3, Symbol4, Symbol5, Symbol6, Symbol7, Symbol8}; - - for (int i = 0; i < 8; i++) - { - if (symbols[i] != "" && symbols[i] != "NONE") - { - ArrayResize(SymbolsToTrade, TotalSymbols + 1); - SymbolsToTrade[TotalSymbols] = symbols[i]; - TotalSymbols++; - } - } - - return TotalSymbols > 0; -} - -//+------------------------------------------------------------------+ -//| Validate input parameters | -//+------------------------------------------------------------------+ -bool ValidateInputs() -{ - if (RiskPercent <= 0 || RiskPercent > 10) - { - Print("ERROR: Risk percent must be between 0 and 10"); - return false; - } - - if (MinRR < 1.0) - { - Print("ERROR: Minimum R:R ratio must be at least 1.0"); - return false; - } - - if (MaxSL <= MinSL) - { - Print("ERROR: Maximum SL must be greater than Minimum SL"); - return false; - } - - if (MaxTradesPerDay <= 0) - { - Print("ERROR: Max trades per day must be positive"); - return false; - } - - return true; -} - -//+------------------------------------------------------------------+ -//| Initialize chart objects | -//+------------------------------------------------------------------+ -bool InitializeChartObjects() -{ - // Set chart properties for better visualization - ChartSetInteger(0, CHART_SHOW_GRID, false); - ChartSetInteger(0, CHART_SHOW_VOLUMES, false); - ChartSetInteger(0, CHART_SHOW_OHLC, true); - - // Create information panel background - if (ObjectCreate(0, "SniperEA_InfoPanel", OBJ_RECTANGLE_LABEL, 0, 0, 0)) - { - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_CORNER, CORNER_LEFT_UPPER); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_XDISTANCE, 10); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_YDISTANCE, 30); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_XSIZE, 250); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_YSIZE, 150); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BGCOLOR, clrDarkSlateGray); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BORDER_TYPE, BORDER_FLAT); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_COLOR, clrWhite); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_STYLE, STYLE_SOLID); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_WIDTH, 1); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BACK, false); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_SELECTABLE, false); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_SELECTED, false); - ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_HIDDEN, true); - } - - // Create EA status label - if (ObjectCreate(0, "SniperEA_Status", OBJ_LABEL, 0, 0, 0)) - { - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_CORNER, CORNER_LEFT_UPPER); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_XDISTANCE, 20); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_YDISTANCE, 40); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_COLOR, clrLime); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_FONTSIZE, 10); - ObjectSetString(0, "SniperEA_Status", OBJPROP_FONT, "Arial Bold"); - ObjectSetString(0, "SniperEA_Status", OBJPROP_TEXT, "Sniper EA - ACTIVE"); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_SELECTABLE, false); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_SELECTED, false); - ObjectSetInteger(0, "SniperEA_Status", OBJPROP_HIDDEN, true); - } - - Print("Chart objects initialized successfully"); - return true; -} - -//+------------------------------------------------------------------+ -//| Clean up chart objects | -//+------------------------------------------------------------------+ -void CleanupChartObjects() -{ - // Clean up all chart objects created by the EA - int total_objects = ObjectsDeleteAll(0, "SniperEA_"); - Print("Cleaned up ", total_objects, " chart objects"); -} - -//+------------------------------------------------------------------+ -//| Update information panel | -//+------------------------------------------------------------------+ -void UpdateInfoPanel() -{ - // Get current session - string current_session = GetCurrentSession(); - - // Get account information - double account_balance = AccountInfoDouble(ACCOUNT_BALANCE); - double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); - double account_margin = AccountInfoDouble(ACCOUNT_MARGIN); - - // Count current positions - int total_positions = PositionsTotal(); - - // Create info text - string info_text = StringFormat( - "Session: %s\n" + - "Balance: %.2f\n" + - "Equity: %.2f\n" + - "Margin: %.2f\n" + - "Positions: %d/%d", - current_session, - account_balance, - account_equity, - account_margin, - total_positions, - MaxPositions); - - // Update info label - if (ObjectFind(0, "SniperEA_Info") < 0) - { - ObjectCreate(0, "SniperEA_Info", OBJ_LABEL, 0, 0, 0); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_CORNER, CORNER_LEFT_UPPER); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_XDISTANCE, 20); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_YDISTANCE, 60); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_COLOR, clrWhite); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_FONTSIZE, 8); - ObjectSetString(0, "SniperEA_Info", OBJPROP_FONT, "Courier New"); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_SELECTABLE, false); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_SELECTED, false); - ObjectSetInteger(0, "SniperEA_Info", OBJPROP_HIDDEN, true); - } - - ObjectSetString(0, "SniperEA_Info", OBJPROP_TEXT, info_text); -} - -//+------------------------------------------------------------------+ -//| Get current trading session | -//+------------------------------------------------------------------+ -string GetCurrentSession() -{ - MqlDateTime dt; - TimeToStruct(TimeGMT(), dt); - - int current_hour = dt.hour; - int current_minute = dt.min; - int current_time_minutes = current_hour * 60 + current_minute; - - // Convert session times to minutes - int asia_start = StringToTime("1970.01.01 " + AsiaStart) % 86400 / 60; - int asia_end = StringToTime("1970.01.01 " + AsiaEnd) % 86400 / 60; - int london_start = StringToTime("1970.01.01 " + LondonStart) % 86400 / 60; - int london_end = StringToTime("1970.01.01 " + LondonEnd) % 86400 / 60; - int ny_start = StringToTime("1970.01.01 " + NYStart) % 86400 / 60; - int ny_end = StringToTime("1970.01.01 " + NYEnd) % 86400 / 60; - - // Check which session we're in - if ((current_time_minutes >= asia_start && current_time_minutes < asia_end) || - (asia_start > asia_end && (current_time_minutes >= asia_start || current_time_minutes < asia_end))) - return "ASIA"; - - if ((current_time_minutes >= london_start && current_time_minutes < london_end) || - (london_start > london_end && (current_time_minutes >= london_start || current_time_minutes < london_end))) - return "LONDON"; - - if ((current_time_minutes >= ny_start && current_time_minutes < ny_end) || - (ny_start > ny_end && (current_time_minutes >= ny_start || current_time_minutes < ny_end))) - return "NEW YORK"; - - return "OFF HOURS"; -} - -//+------------------------------------------------------------------+ -//| Main trading logic processor | -//+------------------------------------------------------------------+ -void ProcessTradingLogic() -{ - // Update information panel - UpdateInfoPanel(); - - // Check if trading is allowed in current session - if (UseTimeFilter && GetCurrentSession() == "OFF HOURS") - return; - - // Main trading logic will be implemented here - // This is where we'll call all the market structure analysis functions -} - -//+------------------------------------------------------------------+ -//| Logging Functions | -//+------------------------------------------------------------------+ -void LogInfo(string message) -{ - if (EnableDetailedLogging) - { - string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - Print("[", timestamp, "] [INFO] ", LogPrefix, ": ", message); - } -} - -void LogWarning(string message) -{ - string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - Print("[", timestamp, "] [WARNING] ", LogPrefix, ": ", message); -} - -void LogError(string message) -{ - string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - Print("[", timestamp, "] [ERROR] ", LogPrefix, ": ", message); -} - -void LogDebug(string message) -{ - if (EnableDebugMode) - { - string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - Print("[", timestamp, "] [DEBUG] ", LogPrefix, ": ", message); - } -} - -void LogPattern(string pattern_type, string symbol, string details) -{ - if (LogPatternDetection) - { - string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - Print("[", timestamp, "] [PATTERN] ", LogPrefix, ": ", pattern_type, " detected on ", symbol, " - ", details); - } -} - -void LogTrade(string action, string symbol, string details) -{ - if (LogTradeExecution) - { - string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - Print("[", timestamp, "] [TRADE] ", LogPrefix, ": ", action, " on ", symbol, " - ", details); - } -} - -//+------------------------------------------------------------------+ -//| Error Handling Functions | -//+------------------------------------------------------------------+ -bool HandleTradeError(int error_code, string operation) -{ - string error_description = ""; - bool is_critical = false; - - switch (error_code) - { - case TRADE_RETCODE_REQUOTE: - error_description = "Requote"; - break; - case TRADE_RETCODE_REJECT: - error_description = "Request rejected"; - is_critical = true; - break; - case TRADE_RETCODE_CANCEL: - error_description = "Request canceled by trader"; - break; - case TRADE_RETCODE_PLACED: - error_description = "Order placed"; - return true; // Success - case TRADE_RETCODE_DONE: - error_description = "Request completed"; - return true; // Success - case TRADE_RETCODE_DONE_PARTIAL: - error_description = "Request partially completed"; - return true; // Partial success - case TRADE_RETCODE_ERROR: - error_description = "Request processing error"; - is_critical = true; - break; - case TRADE_RETCODE_TIMEOUT: - error_description = "Request timeout"; - break; - case TRADE_RETCODE_INVALID: - error_description = "Invalid request"; - is_critical = true; - break; - case TRADE_RETCODE_INVALID_VOLUME: - error_description = "Invalid volume"; - is_critical = true; - break; - case TRADE_RETCODE_INVALID_PRICE: - error_description = "Invalid price"; - break; - case TRADE_RETCODE_INVALID_STOPS: - error_description = "Invalid stops"; - break; - case TRADE_RETCODE_TRADE_DISABLED: - error_description = "Trade disabled"; - is_critical = true; - break; - case TRADE_RETCODE_MARKET_CLOSED: - error_description = "Market closed"; - break; - case TRADE_RETCODE_NO_MONEY: - error_description = "No money"; - is_critical = true; - break; - case TRADE_RETCODE_PRICE_CHANGED: - error_description = "Price changed"; - break; - case TRADE_RETCODE_PRICE_OFF: - error_description = "Off quotes"; - break; - case TRADE_RETCODE_INVALID_EXPIRATION: - error_description = "Invalid expiration"; - break; - case TRADE_RETCODE_ORDER_CHANGED: - error_description = "Order state changed"; - break; - case TRADE_RETCODE_TOO_MANY_REQUESTS: - error_description = "Too many requests"; - break; - case TRADE_RETCODE_NO_CHANGES: - error_description = "No changes"; - break; - case TRADE_RETCODE_SERVER_DISABLES_AT: - error_description = "Autotrading disabled by server"; - is_critical = true; - break; - case TRADE_RETCODE_CLIENT_DISABLES_AT: - error_description = "Autotrading disabled by client"; - is_critical = true; - break; - case TRADE_RETCODE_LOCKED: - error_description = "Request locked"; - break; - case TRADE_RETCODE_FROZEN: - error_description = "Order or position frozen"; - break; - case TRADE_RETCODE_INVALID_FILL: - error_description = "Invalid fill"; - break; - case TRADE_RETCODE_CONNECTION: - error_description = "No connection"; - is_critical = true; - break; - case TRADE_RETCODE_ONLY_REAL: - error_description = "Only real accounts allowed"; - is_critical = true; - break; - case TRADE_RETCODE_LIMIT_ORDERS: - error_description = "Limit orders limit reached"; - break; - case TRADE_RETCODE_LIMIT_VOLUME: - error_description = "Volume limit reached"; - break; - case TRADE_RETCODE_INVALID_ORDER: - error_description = "Invalid order"; - is_critical = true; - break; - case TRADE_RETCODE_POSITION_CLOSED: - error_description = "Position already closed"; - break; - default: - error_description = "Unknown error"; - is_critical = true; - break; - } - - if (is_critical) - { - LogError(StringFormat("%s failed with critical error %d: %s", operation, error_code, error_description)); - } - else - { - LogWarning(StringFormat("%s failed with error %d: %s", operation, error_code, error_description)); - } - - return false; -} - -//+------------------------------------------------------------------+ -//| Utility Functions | -//+------------------------------------------------------------------+ -double NormalizePrice(string symbol, double price) -{ - return NormalizeDouble(price, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS)); -} - -double CalculatePipValue(string symbol) -{ - double pip_size = SymbolInfoDouble(symbol, SYMBOL_POINT); - if (SymbolInfoInteger(symbol, SYMBOL_DIGITS) == 5 || SymbolInfoInteger(symbol, SYMBOL_DIGITS) == 3) - pip_size *= 10; - return pip_size; -} - -bool IsNewBar(string symbol, ENUM_TIMEFRAMES timeframe) -{ - static datetime last_bar_time = 0; - datetime current_bar_time = iTime(symbol, timeframe, 0); - - if (current_bar_time != last_bar_time) - { - last_bar_time = current_bar_time; - return true; - } - - return false; -} - -//+------------------------------------------------------------------+ -//| Order Block Detection Functions | -//+------------------------------------------------------------------+ -bool DetectOrderBlocks(string symbol, ENUM_TIMEFRAMES timeframe, OrderBlock &order_blocks[]) -{ - ArrayResize(order_blocks, 0); - - int bars_to_analyze = MathMin(OBLookback * 2, iBars(symbol, timeframe) - 10); - if (bars_to_analyze < 10) - return false; - - LogDebug(StringFormat("Analyzing %d bars for Order Blocks on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); - - // Look for potential Order Blocks - for (int i = 5; i < bars_to_analyze; i++) - { - // Get candle data - double high = iHigh(symbol, timeframe, i); - double low = iLow(symbol, timeframe, i); - double open = iOpen(symbol, timeframe, i); - double close = iClose(symbol, timeframe, i); - datetime time = iTime(symbol, timeframe, i); - - // Check for bullish Order Block (demand zone) - if (IsBullishOrderBlock(symbol, timeframe, i)) - { - OrderBlock ob; - ob.high = high; - ob.low = low; - ob.time = time; - ob.is_bullish = true; - ob.is_fresh = IsOrderBlockFresh(symbol, timeframe, i, true); - ob.strength = CalculateOrderBlockStrength(symbol, timeframe, i, true); - - if (ob.strength >= OBStrengthFilter) - { - ArrayResize(order_blocks, ArraySize(order_blocks) + 1); - order_blocks[ArraySize(order_blocks) - 1] = ob; - - LogPattern("Order Block", symbol, StringFormat("Bullish OB at %.5f-%.5f, Strength: %.2f", ob.low, ob.high, ob.strength)); - } - } - - // Check for bearish Order Block (supply zone) - if (IsBearishOrderBlock(symbol, timeframe, i)) - { - OrderBlock ob; - ob.high = high; - ob.low = low; - ob.time = time; - ob.is_bullish = false; - ob.is_fresh = IsOrderBlockFresh(symbol, timeframe, i, false); - ob.strength = CalculateOrderBlockStrength(symbol, timeframe, i, false); - - if (ob.strength >= OBStrengthFilter) - { - ArrayResize(order_blocks, ArraySize(order_blocks) + 1); - order_blocks[ArraySize(order_blocks) - 1] = ob; - - LogPattern("Order Block", symbol, StringFormat("Bearish OB at %.5f-%.5f, Strength: %.2f", ob.low, ob.high, ob.strength)); - } - } - } - - LogDebug(StringFormat("Found %d Order Blocks on %s %s", ArraySize(order_blocks), symbol, EnumToString(timeframe))); - return ArraySize(order_blocks) > 0; -} - -bool IsBullishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) -{ - // Get current candle data - double open = iOpen(symbol, timeframe, index); - double close = iClose(symbol, timeframe, index); - double high = iHigh(symbol, timeframe, index); - double low = iLow(symbol, timeframe, index); - - // Must be a bullish candle - if (close <= open) - return false; - - // Check for strong bullish momentum (body > 60% of total range) - double body_size = close - open; - double total_range = high - low; - if (total_range == 0) - return false; - - double body_ratio = body_size / total_range; - if (body_ratio < 0.6) - return false; - - // Check for significant volume increase (if available) - long current_volume = iVolume(symbol, timeframe, index); - long avg_volume = 0; - for (int i = 1; i <= 5; i++) - { - avg_volume += iVolume(symbol, timeframe, index + i); - } - avg_volume /= 5; - - if (current_volume < avg_volume * 1.2) - return false; - - // Check for price rejection from this level in subsequent candles - bool has_rejection = false; - for (int i = 1; i <= 5; i++) - { - if (index - i < 0) - break; - - double test_low = iLow(symbol, timeframe, index - i); - double test_close = iClose(symbol, timeframe, index - i); - - // Price came back to test the OB zone and bounced - if (test_low <= high && test_low >= low && test_close > high) - { - has_rejection = true; - break; - } - } - - return has_rejection; -} - -bool IsBearishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) -{ - // Get current candle data - double open = iOpen(symbol, timeframe, index); - double close = iClose(symbol, timeframe, index); - double high = iHigh(symbol, timeframe, index); - double low = iLow(symbol, timeframe, index); - - // Must be a bearish candle - if (close >= open) - return false; - - // Check for strong bearish momentum (body > 60% of total range) - double body_size = open - close; - double total_range = high - low; - if (total_range == 0) - return false; - - double body_ratio = body_size / total_range; - if (body_ratio < 0.6) - return false; - - // Check for significant volume increase (if available) - long current_volume = iVolume(symbol, timeframe, index); - long avg_volume = 0; - for (int i = 1; i <= 5; i++) - { - avg_volume += iVolume(symbol, timeframe, index + i); - } - avg_volume /= 5; - - if (current_volume < avg_volume * 1.2) - return false; - - // Check for price rejection from this level in subsequent candles - bool has_rejection = false; - for (int i = 1; i <= 5; i++) - { - if (index - i < 0) - break; - - double test_high = iHigh(symbol, timeframe, index - i); - double test_close = iClose(symbol, timeframe, index - i); - - // Price came back to test the OB zone and bounced - if (test_high >= low && test_high <= high && test_close < low) - { - has_rejection = true; - break; - } - } - - return has_rejection; -} - -bool IsOrderBlockFresh(string symbol, ENUM_TIMEFRAMES timeframe, int ob_index, bool is_bullish) -{ - double ob_high = iHigh(symbol, timeframe, ob_index); - double ob_low = iLow(symbol, timeframe, ob_index); - - // Check if price has significantly broken through the OB zone - for (int i = 0; i < ob_index; i++) - { - double test_high = iHigh(symbol, timeframe, i); - double test_low = iLow(symbol, timeframe, i); - - if (is_bullish) - { - // For bullish OB, check if price broke significantly below - if (test_low < ob_low - (ob_high - ob_low) * 0.5) - return false; - } - else - { - // For bearish OB, check if price broke significantly above - if (test_high > ob_high + (ob_high - ob_low) * 0.5) - return false; - } - } - - return true; -} - -double CalculateOrderBlockStrength(string symbol, ENUM_TIMEFRAMES timeframe, int index, bool is_bullish) -{ - double strength = 0.0; - - // Factor 1: Candle body size relative to average - double body_size = MathAbs(iClose(symbol, timeframe, index) - iOpen(symbol, timeframe, index)); - double avg_body = 0; - for (int i = 1; i <= 10; i++) - { - avg_body += MathAbs(iClose(symbol, timeframe, index + i) - iOpen(symbol, timeframe, index + i)); - } - avg_body /= 10; - - if (avg_body > 0) - strength += (body_size / avg_body) * 0.3; // 30% weight - - // Factor 2: Volume relative to average - long current_volume = iVolume(symbol, timeframe, index); - long avg_volume = 0; - for (int i = 1; i <= 10; i++) - { - avg_volume += iVolume(symbol, timeframe, index + i); - } - avg_volume /= 10; - - if (avg_volume > 0) - strength += ((double)current_volume / avg_volume) * 0.2; // 20% weight - - // Factor 3: Number of times price respected the level - int respect_count = 0; - double ob_high = iHigh(symbol, timeframe, index); - double ob_low = iLow(symbol, timeframe, index); - - for (int i = 1; i < index && i <= 20; i++) - { - double test_high = iHigh(symbol, timeframe, index - i); - double test_low = iLow(symbol, timeframe, index - i); - double test_close = iClose(symbol, timeframe, index - i); - - if (is_bullish) - { - if (test_low <= ob_high && test_low >= ob_low && test_close > ob_high) - respect_count++; - } - else - { - if (test_high >= ob_low && test_high <= ob_high && test_close < ob_low) - respect_count++; - } - } - - strength += respect_count * 0.1; // 10% weight per respect - - // Factor 4: Time since formation (fresher = stronger) - double time_factor = 1.0 - (index / (double)OBLookback); - strength += time_factor * 0.3; // 30% weight - - return MathMin(strength, 2.0); // Cap at 2.0 -} - -//+------------------------------------------------------------------+ -//| Break of Structure Detection Functions | -//+------------------------------------------------------------------+ -bool DetectBreakOfStructure(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos_events[]) -{ - ArrayResize(bos_events, 0); - - int bars_to_analyze = MathMin(SwingLookback * 3, iBars(symbol, timeframe) - 10); - if (bars_to_analyze < 20) - return false; - - LogDebug(StringFormat("Analyzing %d bars for Break of Structure on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); - - // Find swing highs and lows first - double swing_highs[]; - double swing_lows[]; - datetime swing_high_times[]; - datetime swing_low_times[]; - - FindSwingPoints(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times); - - // Analyze for BOS patterns - AnalyzeBOSPatterns(symbol, timeframe, swing_highs, swing_lows, swing_high_times, swing_low_times, bos_events); - - LogDebug(StringFormat("Found %d BOS events on %s %s", ArraySize(bos_events), symbol, EnumToString(timeframe))); - return ArraySize(bos_events) > 0; -} - -void FindSwingPoints(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, - double &swing_highs[], double &swing_lows[], - datetime &swing_high_times[], datetime &swing_low_times[]) -{ - ArrayResize(swing_highs, 0); - ArrayResize(swing_lows, 0); - ArrayResize(swing_high_times, 0); - ArrayResize(swing_low_times, 0); - - for (int i = SwingLookback; i < bars_to_analyze - SwingLookback; i++) - { - double current_high = iHigh(symbol, timeframe, i); - double current_low = iLow(symbol, timeframe, i); - datetime current_time = iTime(symbol, timeframe, i); - - // Check for swing high - bool is_swing_high = true; - for (int j = 1; j <= SwingLookback; j++) - { - if (iHigh(symbol, timeframe, i - j) >= current_high || - iHigh(symbol, timeframe, i + j) >= current_high) - { - is_swing_high = false; - break; - } - } - - if (is_swing_high) - { - ArrayResize(swing_highs, ArraySize(swing_highs) + 1); - ArrayResize(swing_high_times, ArraySize(swing_high_times) + 1); - swing_highs[ArraySize(swing_highs) - 1] = current_high; - swing_high_times[ArraySize(swing_high_times) - 1] = current_time; - } - - // Check for swing low - bool is_swing_low = true; - for (int j = 1; j <= SwingLookback; j++) - { - if (iLow(symbol, timeframe, i - j) <= current_low || - iLow(symbol, timeframe, i + j) <= current_low) - { - is_swing_low = false; - break; - } - } - - if (is_swing_low) - { - ArrayResize(swing_lows, ArraySize(swing_lows) + 1); - ArrayResize(swing_low_times, ArraySize(swing_low_times) + 1); - swing_lows[ArraySize(swing_lows) - 1] = current_low; - swing_low_times[ArraySize(swing_low_times) - 1] = current_time; - } - } -} - -void AnalyzeBOSPatterns(string symbol, ENUM_TIMEFRAMES timeframe, - double &swing_highs[], double &swing_lows[], - datetime &swing_high_times[], datetime &swing_low_times[], - BreakOfStructure &bos_events[]) -{ - // Analyze bullish BOS (breaking above previous swing high) - for (int i = 1; i < ArraySize(swing_highs); i++) - { - double previous_high = swing_highs[i]; - datetime previous_time = swing_high_times[i]; - - // Look for price breaking above this high - int start_bar = iBarShift(symbol, timeframe, previous_time); - if (start_bar < 0) - continue; - - for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) - { - double current_high = iHigh(symbol, timeframe, j); - double current_close = iClose(symbol, timeframe, j); - datetime current_time = iTime(symbol, timeframe, j); - - if (current_high > previous_high && current_close > previous_high) - { - // Confirm the break with subsequent candles - bool confirmed = ConfirmBOS(symbol, timeframe, j, true, previous_high); - - if (confirmed) - { - BreakOfStructure bos; - bos.level = previous_high; - bos.time = current_time; - bos.is_bullish = true; - bos.confirmed = true; - - ArrayResize(bos_events, ArraySize(bos_events) + 1); - bos_events[ArraySize(bos_events) - 1] = bos; - - LogPattern("Break of Structure", symbol, StringFormat("Bullish BOS at %.5f", previous_high)); - break; - } - } - } - } - - // Analyze bearish BOS (breaking below previous swing low) - for (int i = 1; i < ArraySize(swing_lows); i++) - { - double previous_low = swing_lows[i]; - datetime previous_time = swing_low_times[i]; - - // Look for price breaking below this low - int start_bar = iBarShift(symbol, timeframe, previous_time); - if (start_bar < 0) - continue; - - for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) - { - double current_low = iLow(symbol, timeframe, j); - double current_close = iClose(symbol, timeframe, j); - datetime current_time = iTime(symbol, timeframe, j); - - if (current_low < previous_low && current_close < previous_low) - { - // Confirm the break with subsequent candles - bool confirmed = ConfirmBOS(symbol, timeframe, j, false, previous_low); - - if (confirmed) - { - BreakOfStructure bos; - bos.level = previous_low; - bos.time = current_time; - bos.is_bullish = false; - bos.confirmed = true; - - ArrayResize(bos_events, ArraySize(bos_events) + 1); - bos_events[ArraySize(bos_events) - 1] = bos; - - LogPattern("Break of Structure", symbol, StringFormat("Bearish BOS at %.5f", previous_low)); - break; - } - } - } - } -} - -bool ConfirmBOS(string symbol, ENUM_TIMEFRAMES timeframe, int break_bar, bool is_bullish, double level) -{ - int confirmation_count = 0; - - // Check subsequent candles for confirmation - for (int i = 0; i < BOSConfirmationCandles && break_bar - i >= 0; i++) - { - double close_price = iClose(symbol, timeframe, break_bar - i); - - if (is_bullish) - { - if (close_price > level) - confirmation_count++; - } - else - { - if (close_price < level) - confirmation_count++; - } - } - - // Require at least 2 out of 3 confirmation candles - return confirmation_count >= MathMax(2, BOSConfirmationCandles / 2); -} - -bool IsBOSValid(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos) -{ - // Check if BOS is recent enough - datetime current_time = iTime(symbol, timeframe, 0); - int time_diff = (int)((current_time - bos.time) / PeriodSeconds(timeframe)); - - if (time_diff > BOSConfirmationCandles * 3) - return false; - - // Check if price is still respecting the BOS level - double current_price = iClose(symbol, timeframe, 0); - - if (bos.is_bullish) - { - return current_price > bos.level; - } - else - { - return current_price < bos.level; - } -} - -//+------------------------------------------------------------------+ -//| Fair Value Gap Detection Functions | -//+------------------------------------------------------------------+ -bool DetectFairValueGaps(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg_array[]) -{ - ArrayResize(fvg_array, 0); - - int bars_to_analyze = MathMin(50, iBars(symbol, timeframe) - 5); - if (bars_to_analyze < 10) - return false; - - LogDebug(StringFormat("Analyzing %d bars for Fair Value Gaps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); - - double pip_value = CalculatePipValue(symbol); - double min_gap_size = MinFVGSize * pip_value; - - // Look for FVG patterns (3-candle pattern) - for (int i = 2; i < bars_to_analyze; i++) - { - // Get three consecutive candles - double high1 = iHigh(symbol, timeframe, i); // First candle - double low1 = iLow(symbol, timeframe, i); - double high2 = iHigh(symbol, timeframe, i - 1); // Middle candle (impulse) - double low2 = iLow(symbol, timeframe, i - 1); - double high3 = iHigh(symbol, timeframe, i - 2); // Third candle - double low3 = iLow(symbol, timeframe, i - 2); - - datetime gap_time = iTime(symbol, timeframe, i - 1); - - // Check for bullish FVG (gap between candle 1 high and candle 3 low) - if (low3 > high1) - { - double gap_size = low3 - high1; - if (gap_size >= min_gap_size) - { - FairValueGap fvg; - fvg.top = low3; - fvg.bottom = high1; - fvg.time = gap_time; - fvg.is_bullish = true; - fvg.is_filled = IsFVGFilled(symbol, timeframe, i - 2, fvg.top, fvg.bottom, true); - - if (!fvg.is_filled) - { - ArrayResize(fvg_array, ArraySize(fvg_array) + 1); - fvg_array[ArraySize(fvg_array) - 1] = fvg; - - LogPattern("Fair Value Gap", symbol, StringFormat("Bullish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); - } - } - } - - // Check for bearish FVG (gap between candle 1 low and candle 3 high) - if (high3 < low1) - { - double gap_size = low1 - high3; - if (gap_size >= min_gap_size) - { - FairValueGap fvg; - fvg.top = low1; - fvg.bottom = high3; - fvg.time = gap_time; - fvg.is_bullish = false; - fvg.is_filled = IsFVGFilled(symbol, timeframe, i - 2, fvg.top, fvg.bottom, false); - - if (!fvg.is_filled) - { - ArrayResize(fvg_array, ArraySize(fvg_array) + 1); - fvg_array[ArraySize(fvg_array) - 1] = fvg; - - LogPattern("Fair Value Gap", symbol, StringFormat("Bearish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); - } - } - } - } - - LogDebug(StringFormat("Found %d unfilled FVGs on %s %s", ArraySize(fvg_array), symbol, EnumToString(timeframe))); - return ArraySize(fvg_array) > 0; -} - -bool IsFVGFilled(string symbol, ENUM_TIMEFRAMES timeframe, int start_bar, double top, double bottom, bool is_bullish) -{ - // Check if price has filled the FVG since its formation - for (int i = 0; i < start_bar; i++) - { - double high = iHigh(symbol, timeframe, i); - double low = iLow(symbol, timeframe, i); - - if (is_bullish) - { - // For bullish FVG, check if price came back down to fill the gap - if (low <= bottom) - return true; - } - else - { - // For bearish FVG, check if price came back up to fill the gap - if (high >= top) - return true; - } - } - - return false; -} - -bool IsFVGValid(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg) -{ - // Check if FVG is still unfilled - if (fvg.is_filled) - return false; - - // Check current price position relative to FVG - double current_price = iClose(symbol, timeframe, 0); - - if (fvg.is_bullish) - { - // For bullish FVG, price should be above the gap - return current_price > fvg.top; - } - else - { - // For bearish FVG, price should be below the gap - return current_price < fvg.bottom; - } -} - -double GetFVGMidpoint(FairValueGap &fvg) -{ - return (fvg.top + fvg.bottom) / 2.0; -} - -bool IsPriceInFVG(double price, FairValueGap &fvg) -{ - return price >= fvg.bottom && price <= fvg.top; -} - -void UpdateFVGStatus(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg_array[]) -{ - // Update the filled status of existing FVGs - for (int i = 0; i < ArraySize(fvg_array); i++) - { - if (!fvg_array[i].is_filled) - { - double current_high = iHigh(symbol, timeframe, 0); - double current_low = iLow(symbol, timeframe, 0); - - if (fvg_array[i].is_bullish) - { - if (current_low <= fvg_array[i].bottom) - { - fvg_array[i].is_filled = true; - LogPattern("Fair Value Gap", symbol, "Bullish FVG filled"); - } - } - else - { - if (current_high >= fvg_array[i].top) - { - fvg_array[i].is_filled = true; - LogPattern("Fair Value Gap", symbol, "Bearish FVG filled"); - } - } - } - } -} - -//+------------------------------------------------------------------+ -//| Liquidity Sweep Detection Functions | -//+------------------------------------------------------------------+ -bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep_array[]) -{ - ArrayResize(sweep_array, 0); - - int bars_to_analyze = MathMin(100, iBars(symbol, timeframe) - 10); - if (bars_to_analyze < 20) - return false; - - LogDebug(StringFormat("Analyzing %d bars for Liquidity Sweeps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); - - double pip_value = CalculatePipValue(symbol); - double min_sweep_distance = MinSweepDistance * pip_value; - - // Find equal highs and lows first - double equal_highs[]; - double equal_lows[]; - datetime equal_high_times[]; - datetime equal_low_times[]; - - FindEqualHighsLows(symbol, timeframe, bars_to_analyze, equal_highs, equal_lows, equal_high_times, equal_low_times); - - // Look for liquidity sweeps above equal highs - for (int i = 0; i < ArraySize(equal_highs); i++) - { - double equal_high = equal_highs[i]; - datetime equal_time = equal_high_times[i]; - - int equal_bar = iBarShift(symbol, timeframe, equal_time); - if (equal_bar < 0) - continue; - - // Look for sweep above this equal high - for (int j = 0; j < equal_bar && j < 20; j++) - { - double current_high = iHigh(symbol, timeframe, j); - double current_close = iClose(symbol, timeframe, j); - datetime current_time = iTime(symbol, timeframe, j); - - // Check if price swept above equal high - if (current_high > equal_high + min_sweep_distance) - { - // Check for rejection (close back below equal high) - if (current_close < equal_high) - { - LiquiditySweep sweep; - sweep.level = equal_high; - sweep.time = current_time; - sweep.is_high_sweep = true; - sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, true, equal_high); - - if (sweep.confirmed) - { - ArrayResize(sweep_array, ArraySize(sweep_array) + 1); - sweep_array[ArraySize(sweep_array) - 1] = sweep; - - LogPattern("Liquidity Sweep", symbol, StringFormat("High sweep at %.5f, Distance: %.1f pips", equal_high, (current_high - equal_high) / pip_value)); - } - break; - } - } - } - } - - // Look for liquidity sweeps below equal lows - for (int i = 0; i < ArraySize(equal_lows); i++) - { - double equal_low = equal_lows[i]; - datetime equal_time = equal_low_times[i]; - - int equal_bar = iBarShift(symbol, timeframe, equal_time); - if (equal_bar < 0) - continue; - - // Look for sweep below this equal low - for (int j = 0; j < equal_bar && j < 20; j++) - { - double current_low = iLow(symbol, timeframe, j); - double current_close = iClose(symbol, timeframe, j); - datetime current_time = iTime(symbol, timeframe, j); - - // Check if price swept below equal low - if (current_low < equal_low - min_sweep_distance) - { - // Check for rejection (close back above equal low) - if (current_close > equal_low) - { - LiquiditySweep sweep; - sweep.level = equal_low; - sweep.time = current_time; - sweep.is_high_sweep = false; - sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, false, equal_low); - - if (sweep.confirmed) - { - ArrayResize(sweep_array, ArraySize(sweep_array) + 1); - sweep_array[ArraySize(sweep_array) - 1] = sweep; - - LogPattern("Liquidity Sweep", symbol, StringFormat("Low sweep at %.5f, Distance: %.1f pips", equal_low, (equal_low - current_low) / pip_value)); - } - break; - } - } - } - } - - LogDebug(StringFormat("Found %d Liquidity Sweeps on %s %s", ArraySize(sweep_array), symbol, EnumToString(timeframe))); - return ArraySize(sweep_array) > 0; -} - -void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, - double &equal_highs[], double &equal_lows[], - datetime &equal_high_times[], datetime &equal_low_times[]) -{ - ArrayResize(equal_highs, 0); - ArrayResize(equal_lows, 0); - ArrayResize(equal_high_times, 0); - ArrayResize(equal_low_times, 0); - - double pip_value = CalculatePipValue(symbol); - double tolerance = 2.0 * pip_value; // 2 pip tolerance for "equal" levels - - // Find swing points first - double swing_highs[]; - double swing_lows[]; - datetime swing_high_times[]; - datetime swing_low_times[]; - - FindSwingPoints(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times); - - // Find equal highs - for (int i = 0; i < ArraySize(swing_highs); i++) - { - double current_high = swing_highs[i]; - datetime current_time = swing_high_times[i]; - int equal_count = 1; - - // Count how many swing highs are at similar level - for (int j = i + 1; j < ArraySize(swing_highs); j++) - { - if (MathAbs(swing_highs[j] - current_high) <= tolerance) - { - equal_count++; - } - } - - // If we have at least 2 equal highs, add to array - if (equal_count >= 2) - { - // Check if this level is already in the array - bool already_exists = false; - for (int k = 0; k < ArraySize(equal_highs); k++) - { - if (MathAbs(equal_highs[k] - current_high) <= tolerance) - { - already_exists = true; - break; - } - } - - if (!already_exists) - { - ArrayResize(equal_highs, ArraySize(equal_highs) + 1); - ArrayResize(equal_high_times, ArraySize(equal_high_times) + 1); - equal_highs[ArraySize(equal_highs) - 1] = current_high; - equal_high_times[ArraySize(equal_high_times) - 1] = current_time; - } - } - } - - // Find equal lows - for (int i = 0; i < ArraySize(swing_lows); i++) - { - double current_low = swing_lows[i]; - datetime current_time = swing_low_times[i]; - int equal_count = 1; - - // Count how many swing lows are at similar level - for (int j = i + 1; j < ArraySize(swing_lows); j++) - { - if (MathAbs(swing_lows[j] - current_low) <= tolerance) - { - equal_count++; - } - } - - // If we have at least 2 equal lows, add to array - if (equal_count >= 2) - { - // Check if this level is already in the array - bool already_exists = false; - for (int k = 0; k < ArraySize(equal_lows); k++) - { - if (MathAbs(equal_lows[k] - current_low) <= tolerance) - { - already_exists = true; - break; - } - } - - if (!already_exists) - { - ArrayResize(equal_lows, ArraySize(equal_lows) + 1); - ArrayResize(equal_low_times, ArraySize(equal_low_times) + 1); - equal_lows[ArraySize(equal_lows) - 1] = current_low; - equal_low_times[ArraySize(equal_low_times) - 1] = current_time; - } - } - } -} - -bool ConfirmLiquiditySweep(string symbol, ENUM_TIMEFRAMES timeframe, int sweep_bar, bool is_high_sweep, double level) -{ - // Check for strong rejection after the sweep - double sweep_high = iHigh(symbol, timeframe, sweep_bar); - double sweep_low = iLow(symbol, timeframe, sweep_bar); - double sweep_close = iClose(symbol, timeframe, sweep_bar); - - if (is_high_sweep) - { - // For high sweep, look for bearish rejection - double wick_size = sweep_high - sweep_close; - double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); - - // Wick should be at least 2x the body size - if (wick_size < body_size * 2) - return false; - - // Close should be below the swept level - if (sweep_close >= level) - return false; - } - else - { - // For low sweep, look for bullish rejection - double wick_size = sweep_close - sweep_low; - double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); - - // Wick should be at least 2x the body size - if (wick_size < body_size * 2) - return false; - - // Close should be above the swept level - if (sweep_close <= level) - return false; - } - - return true; -} - -bool IsLiquiditySweepValid(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep) -{ - if (!sweep.confirmed) - return false; - - // Check if sweep is recent enough - datetime current_time = iTime(symbol, timeframe, 0); - int time_diff = (int)((current_time - sweep.time) / PeriodSeconds(timeframe)); - - if (time_diff > 10) - return false; // Must be within last 10 candles - - // Check current price position - double current_price = iClose(symbol, timeframe, 0); - - if (sweep.is_high_sweep) - { - // For high sweep, price should be below the swept level - return current_price < sweep.level; - } - else - { - // For low sweep, price should be above the swept level - return current_price > sweep.level; - } -} - -//+------------------------------------------------------------------+ -//| Multi-Timeframe Analysis Engine | -//+------------------------------------------------------------------+ -struct MarketStructureData -{ - OrderBlock order_blocks[]; - FairValueGap fair_value_gaps[]; - BreakOfStructure bos_events[]; - LiquiditySweep liquidity_sweeps[]; - ENUM_TIMEFRAMES timeframe; - datetime last_update; - bool is_valid; -}; - -// Global market structure data for different timeframes -MarketStructureData MTF_Data_M1; -MarketStructureData MTF_Data_M15; -MarketStructureData MTF_Data_H4; -MarketStructureData MTF_Data_D1; -MarketStructureData MTF_Data_W1; - -bool InitializeMultiTimeframeAnalysis() -{ - LogInfo("Initializing Multi-Timeframe Analysis Engine"); - - // Initialize timeframe data structures - MTF_Data_M1.timeframe = PERIOD_M1; - MTF_Data_M1.is_valid = false; - MTF_Data_M1.last_update = 0; - - MTF_Data_M15.timeframe = PERIOD_M15; - MTF_Data_M15.is_valid = false; - MTF_Data_M15.last_update = 0; - - MTF_Data_H4.timeframe = PERIOD_H4; - MTF_Data_H4.is_valid = false; - MTF_Data_H4.last_update = 0; - - MTF_Data_D1.timeframe = PERIOD_D1; - MTF_Data_D1.is_valid = false; - MTF_Data_D1.last_update = 0; - - MTF_Data_W1.timeframe = PERIOD_W1; - MTF_Data_W1.is_valid = false; - MTF_Data_W1.last_update = 0; - - LogInfo("Multi-Timeframe Analysis Engine initialized successfully"); - return true; -} - -bool UpdateMultiTimeframeAnalysis(string symbol) -{ - LogDebug("Updating Multi-Timeframe Analysis for " + symbol); - - bool updated = false; - - // Update M1 analysis (most frequent) - if (IsNewBar(symbol, PERIOD_M1) || !MTF_Data_M1.is_valid) - { - updated |= UpdateTimeframeData(symbol, MTF_Data_M1); - } - - // Update M15 analysis - if (IsTimeframeUpdateNeeded(symbol, MTF_Data_M15) || !MTF_Data_M15.is_valid) - { - updated |= UpdateTimeframeData(symbol, MTF_Data_M15); - } - - // Update H4 analysis - if (IsTimeframeUpdateNeeded(symbol, MTF_Data_H4) || !MTF_Data_H4.is_valid) - { - updated |= UpdateTimeframeData(symbol, MTF_Data_H4); - } - - // Update D1 analysis - if (IsTimeframeUpdateNeeded(symbol, MTF_Data_D1) || !MTF_Data_D1.is_valid) - { - updated |= UpdateTimeframeData(symbol, MTF_Data_D1); - } - - // Update W1 analysis (least frequent) - if (IsTimeframeUpdateNeeded(symbol, MTF_Data_W1) || !MTF_Data_W1.is_valid) - { - updated |= UpdateTimeframeData(symbol, MTF_Data_W1); - } - - if (updated) - { - LogDebug("Multi-Timeframe Analysis updated for " + symbol); - } - - return updated; -} - -bool IsTimeframeUpdateNeeded(string symbol, MarketStructureData &mtf_data) -{ - datetime current_bar_time = iTime(symbol, mtf_data.timeframe, 0); - return current_bar_time != mtf_data.last_update; -} - -bool UpdateTimeframeData(string symbol, MarketStructureData &mtf_data) -{ - LogDebug(StringFormat("Updating %s analysis for %s", EnumToString(mtf_data.timeframe), symbol)); - - bool success = true; - - // Update Order Blocks - success &= DetectOrderBlocks(symbol, mtf_data.timeframe, mtf_data.order_blocks); - - // Update Fair Value Gaps - success &= DetectFairValueGaps(symbol, mtf_data.timeframe, mtf_data.fair_value_gaps); - - // Update Break of Structure events - success &= DetectBreakOfStructure(symbol, mtf_data.timeframe, mtf_data.bos_events); - - // Update Liquidity Sweeps - success &= DetectLiquiditySweeps(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps); - - // Update metadata - mtf_data.last_update = iTime(symbol, mtf_data.timeframe, 0); - mtf_data.is_valid = success; - - if (success) - { - LogDebug(StringFormat("%s analysis completed: OB=%d, FVG=%d, BOS=%d, Sweeps=%d", - EnumToString(mtf_data.timeframe), - ArraySize(mtf_data.order_blocks), - ArraySize(mtf_data.fair_value_gaps), - ArraySize(mtf_data.bos_events), - ArraySize(mtf_data.liquidity_sweeps))); - } - - return success; -} - -string GetMarketBias(string symbol) -{ - // Analyze higher timeframes for overall market bias - string h4_bias = GetTimeframeBias(symbol, MTF_Data_H4); - string d1_bias = GetTimeframeBias(symbol, MTF_Data_D1); - string w1_bias = GetTimeframeBias(symbol, MTF_Data_W1); - - // Weight the biases (Weekly > Daily > H4) - if (w1_bias == d1_bias && d1_bias == h4_bias) - { - return w1_bias; // All timeframes agree - } - else if (w1_bias == d1_bias) - { - return w1_bias; // Higher timeframes agree - } - else if (d1_bias == h4_bias) - { - return d1_bias; // Lower timeframes agree - } - else - { - return w1_bias; // Default to highest timeframe - } -} - -string GetTimeframeBias(string symbol, MarketStructureData &mtf_data) -{ - if (!mtf_data.is_valid) - return "NEUTRAL"; - - int bullish_signals = 0; - int bearish_signals = 0; - - // Analyze BOS events - for (int i = 0; i < ArraySize(mtf_data.bos_events); i++) - { - if (IsBOSValid(symbol, mtf_data.timeframe, mtf_data.bos_events[i])) - { - if (mtf_data.bos_events[i].is_bullish) - bullish_signals++; - else - bearish_signals++; - } - } - - // Analyze Order Blocks - for (int i = 0; i < ArraySize(mtf_data.order_blocks); i++) - { - if (mtf_data.order_blocks[i].is_fresh && mtf_data.order_blocks[i].strength > OBStrengthFilter) - { - if (mtf_data.order_blocks[i].is_bullish) - bullish_signals++; - else - bearish_signals++; - } - } - - // Analyze Liquidity Sweeps - for (int i = 0; i < ArraySize(mtf_data.liquidity_sweeps); i++) - { - if (IsLiquiditySweepValid(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps[i])) - { - if (mtf_data.liquidity_sweeps[i].is_high_sweep) - bearish_signals++; // High sweep typically leads to bearish move - else - bullish_signals++; // Low sweep typically leads to bullish move - } - } - - // Determine bias - if (bullish_signals > bearish_signals + 1) - return "BULLISH"; - else if (bearish_signals > bullish_signals + 1) - return "BEARISH"; - else - return "NEUTRAL"; -} - -bool IsMultiTimeframeAligned(string symbol, bool is_bullish_setup) -{ - if (!RequireMultiTFConfirmation) - return true; - - string market_bias = GetMarketBias(symbol); - - if (is_bullish_setup) - { - return market_bias == "BULLISH" || market_bias == "NEUTRAL"; - } - else - { - return market_bias == "BEARISH" || market_bias == "NEUTRAL"; - } -} - -MarketStructureData *GetTimeframeData(ENUM_TIMEFRAMES timeframe) -{ - switch (timeframe) - { - case PERIOD_M1: - return &MTF_Data_M1; - case PERIOD_M15: - return &MTF_Data_M15; - case PERIOD_H4: - return &MTF_Data_H4; - case PERIOD_D1: - return &MTF_Data_D1; - case PERIOD_W1: - return &MTF_Data_W1; - default: - return NULL; - } -} - -void PrintMultiTimeframeStatus(string symbol) -{ - if (!EnableDebugMode) - return; - - string status = StringFormat( - "=== Multi-Timeframe Status for %s ===\n" + - "Market Bias: %s\n" + - "M1 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + - "M15 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + - "H4 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + - "D1 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + - "W1 - OB:%d FVG:%d BOS:%d Sweeps:%d", - symbol, - GetMarketBias(symbol), - ArraySize(MTF_Data_M1.order_blocks), ArraySize(MTF_Data_M1.fair_value_gaps), ArraySize(MTF_Data_M1.bos_events), ArraySize(MTF_Data_M1.liquidity_sweeps), - ArraySize(MTF_Data_M15.order_blocks), ArraySize(MTF_Data_M15.fair_value_gaps), ArraySize(MTF_Data_M15.bos_events), ArraySize(MTF_Data_M15.liquidity_sweeps), - ArraySize(MTF_Data_H4.order_blocks), ArraySize(MTF_Data_H4.fair_value_gaps), ArraySize(MTF_Data_H4.bos_events), ArraySize(MTF_Data_H4.liquidity_sweeps), - ArraySize(MTF_Data_D1.order_blocks), ArraySize(MTF_Data_D1.fair_value_gaps), ArraySize(MTF_Data_D1.bos_events), ArraySize(MTF_Data_D1.liquidity_sweeps), - ArraySize(MTF_Data_W1.order_blocks), ArraySize(MTF_Data_W1.fair_value_gaps), ArraySize(MTF_Data_W1.bos_events), ArraySize(MTF_Data_W1.liquidity_sweeps)); - - LogDebug(status); -}