//+------------------------------------------------------------------+ //| ULTIMATE Bot v5.4 - MT4 VERSION | //| https://www.axumtrading.com | //| Strategy 1: 200EMA Slope | //| Strategy 2: Breakout (BPC) | //| Portfolio-Wide Risk Management | //| GLOBAL SYNC across ALL charts | //| | //| v5.4 CHANGES: | //| - NEW LOSS-BASED MARTINGALE: Uses actual loss × multiplier | //| - Fixed: Max step now USED before reset (was resetting too early) | //| - Fixed: GV_MART_RISK preserved between trades (was being wiped) | //| - Fixed: Always reads fresh from GlobalVariables (no stale cache) | //| - Dashboard shows Last Loss and Next Risk amounts | //| | //| v5.3 CHANGES: | //| - Global portfolio P/L sync across ALL charts | //| - Global trade count sync across ALL charts | //| - Global martingale sync across ALL charts | //| - Proper locking to prevent race conditions | //+------------------------------------------------------------------+ #property copyright "Axum Trading" #property link "https://www.axumtrading.com" #property version "5.40" #property strict #property description "DUAL STRATEGY BOT - 200EMA Slope + Breakout (BPC)" #property description "Portfolio-wide risk management across ALL strategies and pairs" #property description "GLOBAL SYNC: Portfolio, Martingale, Trades across ALL charts" //+------------------------------------------------------------------+ //| 🔑 TRADE IDENTIFIER | //+------------------------------------------------------------------+ input group "=== 🔑 TRADE IDENTIFIER ===" input int Unique_Magic_Number = 654987; // Magic Number (MUST be SAME on ALL charts) //+------------------------------------------------------------------+ //| 🔐 LICENSE SETTINGS | //+------------------------------------------------------------------+ input group "=== 🔐 LICENSE ===" input string License_Key = ""; // Your License Key input string License_Server = "https://axumtrading.com"; // License Server URL bool g_license_valid = false; datetime g_last_license_check = 0; //+------------------------------------------------------------------+ //| 🎛️ MASTER CONTROL SETTINGS | //+------------------------------------------------------------------+ input group "=== 🎛️ MASTER CONTROL ===" input bool Enable_Strategy1_200EMA = true; // ✅ Enable Strategy 1: 200EMA Slope input bool Enable_Strategy2_Breakout = true; // ✅ Enable Strategy 2: Breakout (BPC) input bool Show_Dashboard = true; // Show on-chart dashboard //+------------------------------------------------------------------+ //| 💰 PORTFOLIO-WIDE RISK MANAGEMENT (SYNCED ACROSS ALL CHARTS) | //+------------------------------------------------------------------+ input group "=== 💰 PORTFOLIO RISK (GLOBAL SYNC) ===" input double Portfolio_Daily_Profit_Target = 500.0; // Daily profit target $ (ALL charts) input double Portfolio_Daily_Loss_Limit = 500.0; // Daily loss limit $ (ALL charts) input double Portfolio_StopLoss = 500.0; // Portfolio-wide stop loss $ input double Portfolio_TakeProfit = 500.0; // Portfolio-wide take profit $ input int Max_Trades_All_Pairs = 2; // Max trades across ALL pairs/charts input int Max_Total_Positions = 2; // Max total positions (all charts) //+------------------------------------------------------------------+ //| 🎲 MARTINGALE SYSTEM (GLOBAL SYNC ACROSS ALL CHARTS) | //+------------------------------------------------------------------+ input group "=== 🎲 MARTINGALE (GLOBAL SYNC) ===" input bool Enable_Martingale_Global = true; // Enable Martingale (synced ALL charts) input double Martingale_Multiplier = 2.26; // Multiplier after loss (exponential) input int Martingale_Max_Steps = 4; // Maximum martingale steps input bool Reset_After_Max_Steps = true; // Reset to step 0 after max steps input double Max_Martingale_Risk = 500.0; // Maximum risk per trade $ input bool Force_Reset_Martingale = false; // Manual reset on EA load input bool Martingale_Strategy1 = true; // Apply to Strategy 1 input bool Martingale_Strategy2 = false; // Apply to Strategy 2 //+------------------------------------------------------------------+ //| 📊 STRATEGY 1: 200EMA SLOPE | //+------------------------------------------------------------------+ input group "=== 📊 STRATEGY 1: 200EMA SLOPE ===" input string S1_INFO_1 = "--- EMA SETTINGS ---"; input int S1_Fast_EMA = 20; input int S1_Slow_EMA = 200; input string S1_INFO_2 = "--- SLOPE FILTER ---"; input double S1_MinSlope_Percent = 0.1; input int S1_SlopeLookback = 10; input string S1_INFO_3 = "--- DISTANCE FILTER ---"; input double S1_MaxDistance_Percent = 0.07; input bool S1_EnableDistance_Filter = true; input string S1_INFO_4 = "--- TIME FILTER ---"; input int S1_StartHour = 2; input int S1_EndHour = 23; input string S1_INFO_5 = "--- RISK & STOPS ---"; input double S1_Risk_Per_Trade = 3.11; input double S1_Trade1_SL_Percent = 0.04; input double S1_Trade1_TP_Percent = 0.05; input string S1_INFO_6 = "--- ADX FILTER ---"; input bool S1_EnableADX_Filter = true; input int S1_ADX_Period = 14; input double S1_MinADX = 20.0; input string S1_INFO_7 = "--- POSITION PROTECTION ---"; input bool S1_Enable_Position_Protection = true; input int S1_Protection_Mode = 1; input double S1_Protection_Trigger_Percent = 0.35; input double S1_Breakeven_Offset_Pips = 2.0; input double S1_Trailing_Distance_Percent = 0.15; input string S1_INFO_8 = "--- COOLDOWN ---"; input int S1_Cooldown_Minutes = 1; //+------------------------------------------------------------------+ //| 🎯 STRATEGY 2: BREAKOUT (BPC) | //+------------------------------------------------------------------+ input group "=== 🎯 STRATEGY 2: BREAKOUT (BPC) ===" input bool S2_Show_Key_Levels = true; input string S2_INFO_1 = "--- TIMEFRAMES ---"; input ENUM_TIMEFRAMES S2_SR_Timeframe = PERIOD_M15; input ENUM_TIMEFRAMES S2_Trade_Timeframe = PERIOD_M5; input string S2_INFO_2 = "--- EMA & TREND ---"; input int S2_EMA_fast = 20; input int S2_EMA_slow = 200; input bool S2_UseADX = true; input int S2_ADX_Period = 14; input double S2_ADX_Threshold = 20.0; input string S2_INFO_3 = "--- BREAKOUT DETECTION ---"; input int S2_LookbackPivots = 12; input int S2_ScanBars = 12; input int S2_MinConsecutiveCandles = 1; input double S2_BreakoutMinBodyPips = 2.0; input double S2_PullbackPercent = 25.0; input string S2_INFO_4 = "--- RISK & STOPS ---"; input double S2_Risk_Per_Trade = 3.11; input double S2_TP_percent = 0.001; input double S2_SL_percent = 0.0025; input string S2_INFO_5 = "--- PROTECTION ---"; input bool S2_EnableBreakEven = true; input double S2_BreakEvenTriggerPercent = 0.005; input double S2_BreakEvenOffsetPercent = 0.001; input bool S2_EnableTrailingStop = true; input double S2_TrailingStopTriggerPercent = 0.01; input double S2_TrailingStopDistancePercent = 0.002; input string S2_INFO_6 = "--- LIMITS ---"; input int S2_MaxEntriesPerSignal = 3; input int S2_MaxSpreadPoints = 40; input int S2_MinMinutesBetweenTrades = 15; input string S2_INFO_7 = "--- TIME WINDOW ---"; input string S2_Start_Trade_Time = "02:00"; input string S2_End_Trade_Time = "23:00"; //+------------------------------------------------------------------+ //| 📰 NEWS & HOLIDAY FILTERS | //+------------------------------------------------------------------+ input group "=== 📰 NEWS FILTER ===" input bool Enable_News_Filter = true; input int News_Buffer_Minutes = 15; input group "=== 📅 HOLIDAY TRADING ===" input bool Allow_Trading_On_Holidays = false; //+------------------------------------------------------------------+ //| GLOBAL VARIABLES - Shared across ALL charts via GlobalVariables | //+------------------------------------------------------------------+ // GlobalVariable names (include magic number for isolation) string GV_MART_STEP = ""; string GV_MART_LOSSES = ""; string GV_MART_LAST_TICKET = ""; string GV_MART_RISK = ""; string GV_LOCK = ""; string GV_PORTFOLIO_PL = ""; string GV_PORTFOLIO_DATE = ""; string GV_TOTAL_TRADES = ""; string GV_LAST_PL_UPDATE = ""; // Local cache variables double g_portfolio_daily_pl = 0.0; int g_martingale_current_step = 0; int g_consecutive_losses = 0; int g_martingale_last_checked_order = -1; double g_current_martingale_risk = 0.0; // Stores actual loss for next trade // Strategy-specific local variables datetime g_s1_last_trade_close_time = 0; datetime g_s1_last_bar_time = 0; datetime g_s2_last_bar_time = 0; datetime g_s2_last_trade_time = 0; string g_s2_trade_setup_state = "Scanning"; double g_s2_current_resistance = 0; double g_s2_current_support = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize GlobalVariable names with Magic Number GV_MART_STEP = "ULT_MartStep_" + IntegerToString(Unique_Magic_Number); GV_MART_LOSSES = "ULT_MartLosses_" + IntegerToString(Unique_Magic_Number); GV_MART_LAST_TICKET = "ULT_MartLastTicket_" + IntegerToString(Unique_Magic_Number); GV_MART_RISK = "ULT_MartRisk_" + IntegerToString(Unique_Magic_Number); GV_LOCK = "ULT_Lock_" + IntegerToString(Unique_Magic_Number); GV_PORTFOLIO_PL = "ULT_PortfolioPL_" + IntegerToString(Unique_Magic_Number); GV_PORTFOLIO_DATE = "ULT_PortfolioDate_" + IntegerToString(Unique_Magic_Number); GV_TOTAL_TRADES = "ULT_TotalTrades_" + IntegerToString(Unique_Magic_Number); GV_LAST_PL_UPDATE = "ULT_LastPLUpdate_" + IntegerToString(Unique_Magic_Number); Print("╔════════════════════════════════════════════════════════════╗"); Print("║ ULTIMATE BOT v5.4 - MT4 VERSION ║"); Print("║ GLOBAL SYNC: Portfolio + Martingale + Trades ║"); Print("║ 🔐 LICENSE PROTECTED ║"); Print("╚════════════════════════════════════════════════════════════╝"); Print("🔐 Verifying license..."); g_license_valid = VerifyLicense(); if(!g_license_valid) { Print("❌ LICENSE INVALID - Trading disabled"); Print("Please enter a valid license key in the EA settings"); return(INIT_SUCCEEDED); } Print("✅ License VALID - Bot activated"); Print("Chart: ", Symbol()); Print("Magic Number: ", Unique_Magic_Number); Print(""); Print("🔗 GLOBAL SYNC ENABLED:"); Print(" - Portfolio P/L synced across ALL charts"); Print(" - Trade count synced across ALL charts"); Print(" - Martingale synced across ALL charts"); Print(""); Print("🎲 LOSS-BASED MARTINGALE:"); Print(" - Actual loss × ", Martingale_Multiplier, " = next trade risk"); Print(" - Max Steps: ", Martingale_Max_Steps); // Handle Force Reset Martingale if(Force_Reset_Martingale) { Print(">>> FORCE RESET MARTINGALE - Clearing all state..."); GlobalVariableSet(GV_MART_STEP, 0); GlobalVariableSet(GV_MART_LOSSES, 0); GlobalVariableSet(GV_MART_RISK, 0); } // Initialize GlobalVariables if not exist InitializeGlobalVariables(); // Load martingale state if(!Force_Reset_Martingale) { LoadMartingaleState(); } // Initial sync SyncGlobalPortfolio(); Print("════════════════════════════════════════════════════════════"); Print("Bot initialized on ", Symbol()); Print("════════════════════════════════════════════════════════════"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Initialize GlobalVariables | //+------------------------------------------------------------------+ void InitializeGlobalVariables() { // Martingale Step if(!GlobalVariableCheck(GV_MART_STEP)) { GlobalVariableSet(GV_MART_STEP, 0); } // Martingale Losses if(!GlobalVariableCheck(GV_MART_LOSSES)) { GlobalVariableSet(GV_MART_LOSSES, 0); } // Martingale Last Ticket - Initialize to highest ticket in history if(!GlobalVariableCheck(GV_MART_LAST_TICKET)) { int highest_ticket = 0; for(int i = OrdersHistoryTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderMagicNumber() == Unique_Magic_Number && OrderTicket() > highest_ticket) { highest_ticket = OrderTicket(); } } } GlobalVariableSet(GV_MART_LAST_TICKET, highest_ticket); } // Lock if(!GlobalVariableCheck(GV_LOCK)) { GlobalVariableSet(GV_LOCK, 0); } // Portfolio P/L if(!GlobalVariableCheck(GV_PORTFOLIO_PL)) { GlobalVariableSet(GV_PORTFOLIO_PL, 0.0); } // Portfolio Date if(!GlobalVariableCheck(GV_PORTFOLIO_DATE)) { GlobalVariableSet(GV_PORTFOLIO_DATE, TimeDay(TimeCurrent())); } // Total Trades if(!GlobalVariableCheck(GV_TOTAL_TRADES)) { GlobalVariableSet(GV_TOTAL_TRADES, 0); } // Last P/L Update timestamp if(!GlobalVariableCheck(GV_LAST_PL_UPDATE)) { GlobalVariableSet(GV_LAST_PL_UPDATE, 0); } } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, "ULT_Dashboard_"); ObjectDelete(0, "ULT_S2_Resistance"); ObjectDelete(0, "ULT_S2_Resistance_Label"); ObjectDelete(0, "ULT_S2_Support"); ObjectDelete(0, "ULT_S2_Support_Label"); Print("ULTIMATE Bot v5.4 MT4 deinitialized on ", Symbol(), ". Reason: ", reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // License check - re-verify every 5 minutes if(TimeCurrent() - g_last_license_check > 300) { g_license_valid = VerifyLicense(); g_last_license_check = TimeCurrent(); } // GLOBAL SYNC: Update portfolio P/L across all charts SyncGlobalPortfolio(); // GLOBAL SYNC: Update martingale state UpdateMartingaleFromHistory(); // Display dashboard ALWAYS (even if license invalid - shows status) if(Show_Dashboard) { DisplayDashboard(); } // Draw Strategy 2 key levels ALWAYS (visual only, doesn't trade) if(Enable_Strategy2_Breakout && S2_Show_Key_Levels) { DrawStrategy2KeyLevels(); } else if(!S2_Show_Key_Levels) { ObjectDelete(0, "ULT_S2_Resistance"); ObjectDelete(0, "ULT_S2_Resistance_Label"); ObjectDelete(0, "ULT_S2_Support"); ObjectDelete(0, "ULT_S2_Support_Label"); } // Block trading if license invalid if(!g_license_valid) { return; } // Check portfolio-wide limits (reads from GlobalVariable) if(!CheckPortfolioLimits()) { return; } // STRATEGY 1 if(Enable_Strategy1_200EMA) { Strategy1_200EMA_Logic(); } // STRATEGY 2 if(Enable_Strategy2_Breakout) { Strategy2_Breakout_Logic(); } // Manage position protection ManageAllPositionProtection(); } //+------------------------------------------------------------------+ //| GLOBAL SYNC: Calculate and sync portfolio P/L across ALL charts | //+------------------------------------------------------------------+ void SyncGlobalPortfolio() { // Check for new trading day - reset if needed int current_day = TimeDay(TimeCurrent()); int saved_day = (int)GlobalVariableGet(GV_PORTFOLIO_DATE); if(current_day != saved_day) { Print("📅 NEW TRADING DAY - Resetting global portfolio"); GlobalVariableSet(GV_PORTFOLIO_DATE, current_day); GlobalVariableSet(GV_PORTFOLIO_PL, 0.0); } // Calculate portfolio P/L from ALL orders with our magic number double totalPL = CalculateGlobalPortfolioPL(); // Update GlobalVariable (all charts will see this value) GlobalVariableSet(GV_PORTFOLIO_PL, totalPL); GlobalVariableSet(GV_LAST_PL_UPDATE, (double)TimeCurrent()); // Update local cache g_portfolio_daily_pl = totalPL; // Update total trade count int totalTrades = CountGlobalOpenTrades(); GlobalVariableSet(GV_TOTAL_TRADES, totalTrades); } //+------------------------------------------------------------------+ //| Calculate portfolio P/L from ALL orders (ALL symbols/charts) | //+------------------------------------------------------------------+ double CalculateGlobalPortfolioPL() { double totalPL = 0.0; datetime todayStart = StringToTime(TimeToString(TimeCurrent(), TIME_DATE)); // Open trades P/L (ALL symbols with our magic number) for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == Unique_Magic_Number) { totalPL += OrderProfit() + OrderSwap() + OrderCommission(); } } } // Closed trades P/L today (ALL symbols with our magic number) for(int i = OrdersHistoryTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderMagicNumber() == Unique_Magic_Number && OrderCloseTime() >= todayStart) { totalPL += OrderProfit() + OrderSwap() + OrderCommission(); } } } return totalPL; } //+------------------------------------------------------------------+ //| Count ALL open trades across ALL symbols/charts | //+------------------------------------------------------------------+ int CountGlobalOpenTrades() { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == Unique_Magic_Number) { count++; } } } return count; } //+------------------------------------------------------------------+ //| Check portfolio limits (reads from GlobalVariables) | //+------------------------------------------------------------------+ bool CheckPortfolioLimits() { // Read GLOBAL portfolio P/L double portfolioPL = GlobalVariableGet(GV_PORTFOLIO_PL); // Read GLOBAL trade count int totalTrades = (int)GlobalVariableGet(GV_TOTAL_TRADES); // Daily profit target hit? if(portfolioPL >= Portfolio_Daily_Profit_Target) { return false; } // Daily loss limit hit? if(portfolioPL <= -Portfolio_Daily_Loss_Limit) { return false; } // Portfolio stop loss hit? if(portfolioPL <= -Portfolio_StopLoss) { return false; } // Portfolio take profit hit? if(portfolioPL >= Portfolio_TakeProfit) { return false; } // Max trades reached? if(totalTrades >= Max_Trades_All_Pairs) { return false; } // Max positions reached? if(totalTrades >= Max_Total_Positions) { return false; } return true; } //+------------------------------------------------------------------+ //| Load martingale state from trade history | //| v5.4: Also loads last actual loss for loss-based system | //+------------------------------------------------------------------+ void LoadMartingaleState() { if(!Enable_Martingale_Global) return; Print("🔍 Loading martingale state from history..."); int consecutiveLosses = 0; bool foundWin = false; double lastLossAmount = 0.0; for(int i = OrdersHistoryTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderMagicNumber() == Unique_Magic_Number) { double profit = OrderProfit() + OrderSwap() + OrderCommission(); if(profit > 0) { foundWin = true; break; } else if(profit < 0) { consecutiveLosses++; // Store the MOST RECENT loss amount (first one found going backwards) if(lastLossAmount == 0.0) { lastLossAmount = MathAbs(profit); } } } } } if(!foundWin && consecutiveLosses > 0) { int step = MathMin(consecutiveLosses, Martingale_Max_Steps); GlobalVariableSet(GV_MART_STEP, step); GlobalVariableSet(GV_MART_LOSSES, consecutiveLosses); GlobalVariableSet(GV_MART_RISK, lastLossAmount); // v5.4: Store last loss g_martingale_current_step = step; g_consecutive_losses = consecutiveLosses; g_current_martingale_risk = lastLossAmount; Print(" Found ", consecutiveLosses, " consecutive losses"); Print(" Step: ", step); Print(" Last loss amount: $", DoubleToString(lastLossAmount, 2)); Print(" Next trade risk: $", DoubleToString(lastLossAmount * Martingale_Multiplier, 2)); } } //+------------------------------------------------------------------+ //| Update martingale from closed trades (GLOBAL SYNC) | //| v5.4: Loss-based system - stores actual loss for next trade | //+------------------------------------------------------------------+ void UpdateMartingaleFromHistory() { if(!Enable_Martingale_Global) return; // Read current state from GlobalVariables (always fresh read) double mart_step_gv = GlobalVariableGet(GV_MART_STEP); if(!GlobalVariableCheck(GV_MART_STEP)) { GlobalVariableSet(GV_MART_STEP, 0); mart_step_gv = 0; } g_martingale_current_step = (int)mart_step_gv; g_consecutive_losses = (int)GlobalVariableGet(GV_MART_LOSSES); g_current_martingale_risk = GlobalVariableGet(GV_MART_RISK); int last_ticket_gv = (int)GlobalVariableGet(GV_MART_LAST_TICKET); // Scan ALL history (no break - MT4 sorts by close time, not ticket) for(int i = OrdersHistoryTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderMagicNumber() == Unique_Magic_Number) { int ticket = OrderTicket(); if(ticket <= last_ticket_gv) continue; // New closed trade found - update GlobalVariable GlobalVariableSet(GV_MART_LAST_TICKET, ticket); double profit = OrderProfit() + OrderSwap() + OrderCommission(); if(profit < 0) // LOSS { // Store the ACTUAL LOSS amount for next trade double actual_loss = MathAbs(profit); g_current_martingale_risk = actual_loss; GlobalVariableSet(GV_MART_RISK, actual_loss); double next_risk = actual_loss * Martingale_Multiplier; Print("╔════════════════════════════════════════════════════════════╗"); Print("║ 📉 LOSS DETECTED - MARTINGALE UPDATE ║"); Print("╠════════════════════════════════════════════════════════════╣"); Print("║ Symbol: ", OrderSymbol()); Print("║ Actual Loss: $", DoubleToString(actual_loss, 2)); Print("║ Next Trade Risk: $", DoubleToString(next_risk, 2), " (", DoubleToString(Martingale_Multiplier, 2), "x)"); // Check if we're ALREADY at max step (we USED max step and lost) if(g_martingale_current_step >= Martingale_Max_Steps) { // We USED max step and lost - NOW reset if(Reset_After_Max_Steps) { Print("║ ⚠️ LOSS AT MAX STEP ", g_martingale_current_step, "!"); Print("║ This trade used ", DoubleToString(MathPow(Martingale_Multiplier, Martingale_Max_Steps), 2), "x multiplier"); Print("║ Resetting to step 0 for next trade..."); g_martingale_current_step = 0; g_consecutive_losses = 0; g_current_martingale_risk = 0.0; GlobalVariableSet(GV_MART_STEP, 0); GlobalVariableSet(GV_MART_LOSSES, 0); GlobalVariableSet(GV_MART_RISK, 0); } } else { // Not at max yet - increment step for next trade g_martingale_current_step++; g_consecutive_losses++; GlobalVariableSet(GV_MART_STEP, g_martingale_current_step); GlobalVariableSet(GV_MART_LOSSES, g_consecutive_losses); Print("║ Current Step: ", g_martingale_current_step, "/", Martingale_Max_Steps); } Print("║ ALL charts updated"); Print("╚════════════════════════════════════════════════════════════╝"); } else if(profit > 0) // WIN { if(g_martingale_current_step > 0 || g_consecutive_losses > 0 || g_current_martingale_risk > 0) { Print("╔════════════════════════════════════════════════════════════╗"); Print("║ 🎉 WIN DETECTED - MARTINGALE RESET ║"); Print("╠════════════════════════════════════════════════════════════╣"); Print("║ Symbol: ", OrderSymbol()); Print("║ Profit: $", DoubleToString(profit, 2)); Print("║ Resetting step ", g_martingale_current_step, " → 0"); Print("║ ALL charts updated"); Print("╚════════════════════════════════════════════════════════════╝"); g_martingale_current_step = 0; g_consecutive_losses = 0; g_current_martingale_risk = 0.0; GlobalVariableSet(GV_MART_STEP, 0); GlobalVariableSet(GV_MART_LOSSES, 0); GlobalVariableSet(GV_MART_RISK, 0); } } last_ticket_gv = ticket; } } } } //+------------------------------------------------------------------+ //| Acquire trading lock | //+------------------------------------------------------------------+ bool AcquireTradingLock() { for(int attempt = 0; attempt < 10; attempt++) { double lock_value = GlobalVariableGet(GV_LOCK); if(lock_value == 0) { GlobalVariableSet(GV_LOCK, 1); Sleep(100); if(GlobalVariableGet(GV_LOCK) == 1) { return true; } } Sleep(150); } return false; } //+------------------------------------------------------------------+ //| Release trading lock | //+------------------------------------------------------------------+ void ReleaseTradingLock() { GlobalVariableSet(GV_LOCK, 0); } //+------------------------------------------------------------------+ //| Check if martingale applies to strategy | //+------------------------------------------------------------------+ bool MartingaleApplies(int strategy_number) { if(!Enable_Martingale_Global) return false; if(strategy_number == 1 && Martingale_Strategy1) return true; if(strategy_number == 2 && Martingale_Strategy2) return true; return false; } //+------------------------------------------------------------------+ //| Check if holiday blocked | //+------------------------------------------------------------------+ bool IsHolidayBlocked() { if(Allow_Trading_On_Holidays) return false; datetime current = TimeCurrent(); int month = TimeMonth(current); int day = TimeDay(current); int year = TimeYear(current); int dayOfWeek = TimeDayOfWeek(current); // Christmas (Dec 24-26) if(month == 12 && day >= 24 && day <= 26) return true; // New Year (Dec 31 - Jan 2) if((month == 12 && day == 31) || (month == 1 && day <= 2)) return true; // US Independence Day (July 4) if(month == 7 && day == 4) return true; // Good Friday if((month == 3 || month == 4) && dayOfWeek == 5) { if(IsGoodFriday(year, month, day)) return true; } // US Labor Day (First Monday of September) if(month == 9 && dayOfWeek == 1 && day <= 7) return true; // US Thanksgiving (Fourth Thursday of November) if(month == 11 && dayOfWeek == 4 && day >= 22 && day <= 28) return true; // US Memorial Day (Last Monday of May) if(month == 5 && dayOfWeek == 1 && day >= 25) return true; // US Presidents Day (Third Monday of February) if(month == 2 && dayOfWeek == 1 && day >= 15 && day <= 21) return true; // MLK Day (Third Monday of January) if(month == 1 && dayOfWeek == 1 && day >= 15 && day <= 21) return true; return false; } //+------------------------------------------------------------------+ //| Check if date is Good Friday | //+------------------------------------------------------------------+ bool IsGoodFriday(int year, int month, int day) { int a = year % 19; int b = year / 100; int c = year % 100; int d = b / 4; int e = b % 4; int f = (b + 8) / 25; int g = (b - f + 1) / 3; int h = (19 * a + b - d - g + 15) % 30; int i = c / 4; int k = c % 4; int l = (32 + 2 * e + 2 * i - h - k) % 7; int m = (a + 11 * h + 22 * l) / 451; int easter_month = (h + l - 7 * m + 114) / 31; int easter_day = ((h + l - 7 * m + 114) % 31) + 1; int good_friday_day = easter_day - 2; int good_friday_month = easter_month; if(good_friday_day <= 0) { good_friday_month--; if(good_friday_month == 3) good_friday_day += 31; } return (month == good_friday_month && day == good_friday_day); } //+------------------------------------------------------------------+ //| Display Dashboard | //+------------------------------------------------------------------+ void DisplayDashboard() { int x = 20; int y = 30; int line_height = 18; color text_color = clrWhite; color header_color = clrYellow; int font_size = 9; string font_name = "Consolas"; int line = 0; // Background string bg_name = "ULT_Dashboard_BG"; if(ObjectFind(0, bg_name) < 0) { ObjectCreate(0, bg_name, OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, bg_name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, bg_name, OBJPROP_XDISTANCE, x - 5); ObjectSetInteger(0, bg_name, OBJPROP_YDISTANCE, y - 5); ObjectSetInteger(0, bg_name, OBJPROP_XSIZE, 400); ObjectSetInteger(0, bg_name, OBJPROP_YSIZE, 360); ObjectSetInteger(0, bg_name, OBJPROP_BGCOLOR, C'20,20,40'); ObjectSetInteger(0, bg_name, OBJPROP_BORDER_TYPE, BORDER_FLAT); ObjectSetInteger(0, bg_name, OBJPROP_COLOR, clrDarkGray); ObjectSetInteger(0, bg_name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, bg_name, OBJPROP_BACK, true); } // Header CreateLabel("ULT_Dashboard_Header", x, y + (line * line_height), "╔═══ ULTIMATE BOT v5.4 MT4 (LOSS-BASED) ═══╗", header_color, font_size + 1, font_name); line++; CreateLabel("ULT_Dashboard_Symbol", x, y + (line * line_height), "Chart: " + Symbol() + " | Magic: " + IntegerToString(Unique_Magic_Number), text_color, font_size, font_name); line++; line++; // GLOBAL Portfolio P/L (from GlobalVariable) double globalPL = GlobalVariableGet(GV_PORTFOLIO_PL); color pl_color = (globalPL >= 0) ? clrLime : clrRed; CreateLabel("ULT_Dashboard_GlobalPL", x, y + (line * line_height), "💰 GLOBAL P/L: $" + DoubleToString(globalPL, 2) + " (ALL charts)", pl_color, font_size + 1, font_name); line++; // GLOBAL Trade Count int globalTrades = (int)GlobalVariableGet(GV_TOTAL_TRADES); color trades_color = (globalTrades >= Max_Trades_All_Pairs) ? clrRed : clrLime; CreateLabel("ULT_Dashboard_GlobalTrades", x, y + (line * line_height), "📊 GLOBAL Trades: " + IntegerToString(globalTrades) + "/" + IntegerToString(Max_Trades_All_Pairs) + " (ALL charts)", trades_color, font_size, font_name); line++; // Holiday Status bool holiday_blocked = IsHolidayBlocked(); string holiday_status = Allow_Trading_On_Holidays ? "ALLOWED" : (holiday_blocked ? "BLOCKED" : "OK"); color holiday_color = holiday_blocked ? clrRed : clrLime; CreateLabel("ULT_Dashboard_Holiday", x, y + (line * line_height), "📅 Holidays: " + holiday_status, holiday_color, font_size, font_name); line++; line++; // GLOBAL Martingale Status (v5.4: Loss-based) int display_step = (int)GlobalVariableGet(GV_MART_STEP); double last_loss = GlobalVariableGet(GV_MART_RISK); string mart_status = Enable_Martingale_Global ? "ENABLED (Loss-Based)" : "DISABLED"; color mart_color = Enable_Martingale_Global ? clrOrange : clrGray; CreateLabel("ULT_Dashboard_Mart", x, y + (line * line_height), "🎲 GLOBAL Martingale: " + mart_status, mart_color, font_size, font_name); line++; CreateLabel("ULT_Dashboard_MartStep", x + 15, y + (line * line_height), "Current Step: " + IntegerToString(display_step) + "/" + IntegerToString(Martingale_Max_Steps) + " (ALL charts)", clrAqua, font_size, font_name); line++; // v5.4: Show actual loss and next risk based on loss-based system double base_risk = (Martingale_Strategy1 ? S1_Risk_Per_Trade : S2_Risk_Per_Trade); double next_risk = base_risk; // Default to base risk if(display_step > 0 && last_loss > 0) { // Loss-based: next_risk = last_loss × multiplier next_risk = last_loss * Martingale_Multiplier; CreateLabel("ULT_Dashboard_MartLoss", x + 15, y + (line * line_height), "Last Loss: $" + DoubleToString(last_loss, 2), clrRed, font_size, font_name); line++; if(next_risk > Max_Martingale_Risk) { CreateLabel("ULT_Dashboard_MartRisk", x + 15, y + (line * line_height), "Next Risk: $" + DoubleToString(next_risk, 2) + " (CAPPED: $" + DoubleToString(Max_Martingale_Risk, 2) + ")", clrYellow, font_size, font_name); next_risk = Max_Martingale_Risk; } else { CreateLabel("ULT_Dashboard_MartRisk", x + 15, y + (line * line_height), "Next Risk: $" + DoubleToString(last_loss, 2) + " × " + DoubleToString(Martingale_Multiplier, 2) + " = $" + DoubleToString(next_risk, 2), clrYellow, font_size, font_name); } } else { CreateLabel("ULT_Dashboard_MartLoss", x + 15, y + (line * line_height), "Last Loss: $0.00 (Fresh start)", clrGray, font_size, font_name); line++; CreateLabel("ULT_Dashboard_MartRisk", x + 15, y + (line * line_height), "Next Risk: $" + DoubleToString(base_risk, 2) + " (Base)", clrYellow, font_size, font_name); } line++; line++; // Strategy 1 Status string s1_status = Enable_Strategy1_200EMA ? "ACTIVE" : "DISABLED"; color s1_color = Enable_Strategy1_200EMA ? clrLime : clrGray; CreateLabel("ULT_Dashboard_S1", x, y + (line * line_height), "📊 Strategy 1 (200EMA): " + s1_status, s1_color, font_size, font_name); line++; if(Enable_Strategy1_200EMA) { int s1_trades = CountStrategyTrades("S1_"); string s1_mart = Martingale_Strategy1 ? " [Mart:ON]" : " [Mart:OFF]"; CreateLabel("ULT_Dashboard_S1_Trades", x + 15, y + (line * line_height), "Trades on " + Symbol() + ": " + IntegerToString(s1_trades) + s1_mart, text_color, font_size, font_name); line++; } line++; // Strategy 2 Status string s2_status = Enable_Strategy2_Breakout ? "ACTIVE" : "DISABLED"; color s2_color = Enable_Strategy2_Breakout ? clrLime : clrGray; CreateLabel("ULT_Dashboard_S2", x, y + (line * line_height), "🎯 Strategy 2 (BPC): " + s2_status, s2_color, font_size, font_name); line++; if(Enable_Strategy2_Breakout) { int s2_trades = CountStrategyTrades("S2_"); string s2_mart = Martingale_Strategy2 ? " [Mart:ON]" : " [Mart:OFF]"; CreateLabel("ULT_Dashboard_S2_Trades", x + 15, y + (line * line_height), "Trades on " + Symbol() + ": " + IntegerToString(s2_trades) + s2_mart, text_color, font_size, font_name); line++; color state_color = clrAqua; if(g_s2_trade_setup_state == "Breakout") state_color = clrYellow; else if(g_s2_trade_setup_state == "Pullback") state_color = clrOrange; else if(g_s2_trade_setup_state == "Continuation") state_color = clrLime; CreateLabel("ULT_Dashboard_S2_State", x + 15, y + (line * line_height), "BPC: " + g_s2_trade_setup_state, state_color, font_size, font_name); } } //+------------------------------------------------------------------+ //| Create or Update Label | //+------------------------------------------------------------------+ void CreateLabel(string name, int x, int y, string text, color clr, int size, string font) { if(ObjectFind(0, name) < 0) { ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); } ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size); ObjectSetString(0, name, OBJPROP_FONT, font); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); } //+------------------------------------------------------------------+ //| Count trades for a specific strategy on current symbol | //+------------------------------------------------------------------+ int CountStrategyTrades(string prefix) { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == Unique_Magic_Number && OrderSymbol() == Symbol() && StringFind(OrderComment(), prefix) >= 0) { count++; } } } return count; } //+------------------------------------------------------------------+ //| Check if has open positions for strategy on current symbol | //+------------------------------------------------------------------+ bool HasOpenPositions(string prefix) { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == Unique_Magic_Number && OrderSymbol() == Symbol() && StringFind(OrderComment(), prefix) >= 0) { return true; } } } return false; } //+------------------------------------------------------------------+ //| Draw Strategy 2 Key Levels | //+------------------------------------------------------------------+ void DrawStrategy2KeyLevels() { // Calculate levels if not yet set if(g_s2_current_resistance == 0 || g_s2_current_support == 0) { g_s2_current_resistance = FindResistance(S2_SR_Timeframe, S2_LookbackPivots); g_s2_current_support = FindSupport(S2_SR_Timeframe, S2_LookbackPivots); } if(g_s2_current_resistance > 0) { string resistance_name = "ULT_S2_Resistance"; if(ObjectFind(0, resistance_name) < 0) { ObjectCreate(0, resistance_name, OBJ_HLINE, 0, 0, g_s2_current_resistance); ObjectSetInteger(0, resistance_name, OBJPROP_COLOR, clrYellow); ObjectSetInteger(0, resistance_name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, resistance_name, OBJPROP_WIDTH, 2); } else { ObjectMove(0, resistance_name, 0, 0, g_s2_current_resistance); } } else { ObjectDelete(0, "ULT_S2_Resistance"); } if(g_s2_current_support > 0) { string support_name = "ULT_S2_Support"; if(ObjectFind(0, support_name) < 0) { ObjectCreate(0, support_name, OBJ_HLINE, 0, 0, g_s2_current_support); ObjectSetInteger(0, support_name, OBJPROP_COLOR, clrYellow); ObjectSetInteger(0, support_name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, support_name, OBJPROP_WIDTH, 2); } else { ObjectMove(0, support_name, 0, 0, g_s2_current_support); } } else { ObjectDelete(0, "ULT_S2_Support"); } } //+------------------------------------------------------------------+ //| STRATEGY 1: 200EMA SLOPE LOGIC | //+------------------------------------------------------------------+ void Strategy1_200EMA_Logic() { if(IsHolidayBlocked()) return; datetime current_bar = iTime(Symbol(), 0, 0); if(current_bar == g_s1_last_bar_time) return; g_s1_last_bar_time = current_bar; int minutes_since_last = (int)((TimeCurrent() - g_s1_last_trade_close_time) / 60); if(minutes_since_last < S1_Cooldown_Minutes) return; int hour = TimeHour(TimeCurrent()); if(hour < S1_StartHour || hour >= S1_EndHour) return; if(HasOpenPositions("S1_")) return; double ema20 = iMA(Symbol(), 0, S1_Fast_EMA, 0, MODE_EMA, PRICE_CLOSE, 1); double ema200 = iMA(Symbol(), 0, S1_Slow_EMA, 0, MODE_EMA, PRICE_CLOSE, 1); double ema200_old = iMA(Symbol(), 0, S1_Slow_EMA, 0, MODE_EMA, PRICE_CLOSE, S1_SlopeLookback + 1); double slope = ((ema200 - ema200_old) / ema200_old) * 100.0; double close_price = iClose(Symbol(), 0, 1); double distance = MathAbs((close_price - ema200) / ema200) * 100.0; bool adx_ok = true; if(S1_EnableADX_Filter) { double adx = iADX(Symbol(), 0, S1_ADX_Period, PRICE_CLOSE, MODE_MAIN, 1); adx_ok = (adx >= S1_MinADX); } bool distance_ok = true; if(S1_EnableDistance_Filter) { distance_ok = (distance <= S1_MaxDistance_Percent); } double open_price = iOpen(Symbol(), 0, 1); bool is_bullish_candle = (close_price > open_price); bool is_bearish_candle = (close_price < open_price); bool ema_bullish = (ema20 > ema200); bool ema_bearish = (ema20 < ema200); bool strong_uptrend = (slope >= S1_MinSlope_Percent); if(strong_uptrend && is_bullish_candle && ema_bullish && close_price > ema200 && adx_ok && distance_ok) { ExecuteStrategy1Trade(OP_BUY); } else if(slope <= -S1_MinSlope_Percent && is_bearish_candle && ema_bearish && close_price < ema200 && adx_ok && distance_ok) { ExecuteStrategy1Trade(OP_SELL); } } //+------------------------------------------------------------------+ //| Execute Strategy 1 Trade | //| v5.4: Loss-based martingale - uses actual loss × multiplier | //+------------------------------------------------------------------+ void ExecuteStrategy1Trade(int orderType) { // Force sync before trade SyncGlobalPortfolio(); UpdateMartingaleFromHistory(); if(!CheckPortfolioLimits()) return; if(!AcquireTradingLock()) { Print("⚠️ [S1] Could not acquire lock"); return; } // Double-check after lock SyncGlobalPortfolio(); if(!CheckPortfolioLimits()) { ReleaseTradingLock(); return; } RefreshRates(); double entryPrice = (orderType == OP_BUY) ? Ask : Bid; double stopLoss = CalculateStopLoss(orderType, entryPrice, S1_Trade1_SL_Percent); double takeProfit = CalculateTakeProfit(orderType, entryPrice, S1_Trade1_TP_Percent); // Read martingale state DIRECTLY from GlobalVariables (always fresh) double mart_step_gv = GlobalVariableGet(GV_MART_STEP); if(!GlobalVariableCheck(GV_MART_STEP)) { GlobalVariableSet(GV_MART_STEP, 0); mart_step_gv = 0; } int effective_step = (int)mart_step_gv; g_martingale_current_step = effective_step; // Keep local in sync // v5.4: Loss-based martingale calculation double effective_risk = S1_Risk_Per_Trade; // Default base risk double display_multi = 1.0; if(MartingaleApplies(1) && effective_step > 0) { // Get the ACTUAL LOSS from GlobalVariable double last_loss = GlobalVariableGet(GV_MART_RISK); if(last_loss > 0) { // Calculate risk as: last_loss × multiplier effective_risk = last_loss * Martingale_Multiplier; display_multi = effective_risk / S1_Risk_Per_Trade; Print("╔════════════════════════════════════════════════════════════╗"); Print("║ 🎲 MARTINGALE - LOSS-BASED CALCULATION ║"); Print("╠════════════════════════════════════════════════════════════╣"); Print("║ Last Actual Loss: $", DoubleToString(last_loss, 2)); Print("║ Multiplier: ", DoubleToString(Martingale_Multiplier, 2), "x"); Print("║ This Trade Risk: $", DoubleToString(effective_risk, 2)); Print("╚════════════════════════════════════════════════════════════╝"); } else { // Fallback to step-based if no stored loss (shouldn't happen) double mart_mult = MathPow(Martingale_Multiplier, effective_step); effective_risk = S1_Risk_Per_Trade * mart_mult; display_multi = mart_mult; Print("⚠️ No stored loss found, using step-based: Step ", effective_step, " = ", DoubleToString(mart_mult, 2), "x"); } } // Apply max risk cap if(effective_risk > Max_Martingale_Risk) { Print("⚠️ Risk capped: $", DoubleToString(effective_risk, 2), " → $", DoubleToString(Max_Martingale_Risk, 2)); effective_risk = Max_Martingale_Risk; } double lotSize = CalculateLotSize(entryPrice, stopLoss, effective_risk); lotSize = ValidateLotSize(lotSize); string comment = "S1_200EMA_Step" + IntegerToString(effective_step); Print("═══════════════════════════════════════════"); Print("🚀 STRATEGY 1: ", OrderTypeName(orderType), " on ", Symbol()); Print("Entry: ", DoubleToString(entryPrice, Digits)); Print("Martingale Step: ", effective_step, "/" , Martingale_Max_Steps); Print("Effective Risk: $", DoubleToString(effective_risk, 2)); Print("Lot: ", DoubleToString(lotSize, 2)); Print("═══════════════════════════════════════════"); int ticket = OrderSend(Symbol(), orderType, lotSize, entryPrice, 3, stopLoss, takeProfit, comment, Unique_Magic_Number, 0, clrNONE); ReleaseTradingLock(); if(ticket > 0) { Print("✅ Strategy 1 trade opened. Ticket: ", ticket); g_s1_last_trade_close_time = TimeCurrent(); } else { Print("❌ Strategy 1 trade failed. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| STRATEGY 2: BREAKOUT (BPC) LOGIC | //+------------------------------------------------------------------+ void Strategy2_Breakout_Logic() { if(IsHolidayBlocked()) { g_s2_trade_setup_state = "Holiday Block"; return; } double resistance = FindResistance(S2_SR_Timeframe, S2_LookbackPivots); double support = FindSupport(S2_SR_Timeframe, S2_LookbackPivots); g_s2_current_resistance = resistance; g_s2_current_support = support; datetime current_bar = iTime(Symbol(), S2_Trade_Timeframe, 0); if(current_bar == g_s2_last_bar_time) return; g_s2_last_bar_time = current_bar; if(!IsWithinTradingHours(S2_Start_Trade_Time, S2_End_Trade_Time)) { g_s2_trade_setup_state = "Outside Hours"; return; } int minutes_since_last = (int)((TimeCurrent() - g_s2_last_trade_time) / 60); if(minutes_since_last < S2_MinMinutesBetweenTrades) { g_s2_trade_setup_state = "Cooldown"; return; } double spread = (Ask - Bid) / Point; if(spread > S2_MaxSpreadPoints) { g_s2_trade_setup_state = "Spread High"; return; } if(HasOpenPositions("S2_")) { g_s2_trade_setup_state = "Position Open"; return; } if(resistance == 0 || support == 0) { g_s2_trade_setup_state = "No S/R"; return; } double ema_fast = iMA(Symbol(), S2_SR_Timeframe, S2_EMA_fast, 0, MODE_EMA, PRICE_CLOSE, 1); double ema_slow = iMA(Symbol(), S2_SR_Timeframe, S2_EMA_slow, 0, MODE_EMA, PRICE_CLOSE, 1); bool bias_buy = (ema_fast > ema_slow); bool bias_sell = (ema_fast < ema_slow); if(!bias_buy && !bias_sell) { g_s2_trade_setup_state = "No Bias"; return; } bool adx_ok = true; if(S2_UseADX) { double adx = iADX(Symbol(), S2_Trade_Timeframe, S2_ADX_Period, PRICE_CLOSE, MODE_MAIN, 1); adx_ok = (adx >= S2_ADX_Threshold); if(!adx_ok) { g_s2_trade_setup_state = "ADX Low"; return; } } g_s2_trade_setup_state = "Scanning"; // BPC Detection Logic int breakout_start_index = -1; int breakout_end_index = -1; double breakout_move_high = 0; double breakout_move_low = 0; bool breakout_is_buy = false; for(int s = 1; s <= S2_ScanBars; s++) { if(bias_buy) { int consecutive_green = 0; double move_high = 0; double move_low = 999999; for(int c = s; c <= S2_ScanBars && c < s + 10; c++) { double open_c = iOpen(Symbol(), S2_Trade_Timeframe, c); double close_c = iClose(Symbol(), S2_Trade_Timeframe, c); double high_c = iHigh(Symbol(), S2_Trade_Timeframe, c); double low_c = iLow(Symbol(), S2_Trade_Timeframe, c); if(close_c > open_c) { consecutive_green++; if(high_c > move_high) move_high = high_c; if(low_c < move_low) move_low = low_c; } else break; } if(consecutive_green >= S2_MinConsecutiveCandles) { bool crossed_resistance = (move_high > resistance && move_low < resistance); if(crossed_resistance) { breakout_start_index = s + consecutive_green - 1; breakout_end_index = s; breakout_move_high = move_high; breakout_move_low = move_low; breakout_is_buy = true; break; } } } if(bias_sell) { int consecutive_red = 0; double move_high = 0; double move_low = 999999; for(int c = s; c <= S2_ScanBars && c < s + 10; c++) { double open_c = iOpen(Symbol(), S2_Trade_Timeframe, c); double close_c = iClose(Symbol(), S2_Trade_Timeframe, c); double high_c = iHigh(Symbol(), S2_Trade_Timeframe, c); double low_c = iLow(Symbol(), S2_Trade_Timeframe, c); if(close_c < open_c) { consecutive_red++; if(high_c > move_high) move_high = high_c; if(low_c < move_low) move_low = low_c; } else break; } if(consecutive_red >= S2_MinConsecutiveCandles) { bool crossed_support = (move_high > support && move_low < support); if(crossed_support) { breakout_start_index = s + consecutive_red - 1; breakout_end_index = s; breakout_move_high = move_high; breakout_move_low = move_low; breakout_is_buy = false; break; } } } } if(breakout_start_index < 0) return; g_s2_trade_setup_state = "Breakout"; double breakout_range = breakout_move_high - breakout_move_low; double pullback_ratio = S2_PullbackPercent / 100.0; double pullback_level = breakout_is_buy ? breakout_move_high - (breakout_range * pullback_ratio) : breakout_move_low + (breakout_range * pullback_ratio); int pb_start = breakout_end_index - 1; int pullback_index = -1; double pullback_high = 0; double pullback_low = 0; for(int p = pb_start; p >= 1; p--) { double open_p = iOpen(Symbol(), S2_Trade_Timeframe, p); double close_p = iClose(Symbol(), S2_Trade_Timeframe, p); double high_p = iHigh(Symbol(), S2_Trade_Timeframe, p); double low_p = iLow(Symbol(), S2_Trade_Timeframe, p); if(breakout_is_buy) { if(close_p < open_p && low_p <= pullback_level) { pullback_index = p; pullback_high = high_p; pullback_low = low_p; break; } } else { if(close_p > open_p && high_p >= pullback_level) { pullback_index = p; pullback_high = high_p; pullback_low = low_p; break; } } } if(pullback_index < 0) { g_s2_trade_setup_state = "No Pullback"; return; } g_s2_trade_setup_state = "Pullback"; int cont_index = -1; for(int c = pullback_index - 1; c >= 1; c--) { double cont_close = iClose(Symbol(), S2_Trade_Timeframe, c); if(breakout_is_buy && cont_close > pullback_high) { cont_index = c; break; } else if(!breakout_is_buy && cont_close < pullback_low) { cont_index = c; break; } } if(cont_index < 0) { g_s2_trade_setup_state = "No Continue"; return; } g_s2_trade_setup_state = "Continuation"; int orderType = breakout_is_buy ? OP_BUY : OP_SELL; ExecuteStrategy2Trade(orderType); } //+------------------------------------------------------------------+ //| Execute Strategy 2 Trade | //| v5.4: Loss-based martingale - uses actual loss × multiplier | //+------------------------------------------------------------------+ void ExecuteStrategy2Trade(int orderType) { // Force sync before trade SyncGlobalPortfolio(); UpdateMartingaleFromHistory(); if(!CheckPortfolioLimits()) return; if(!AcquireTradingLock()) { Print("⚠️ [S2] Could not acquire lock"); return; } // Double-check after lock SyncGlobalPortfolio(); if(!CheckPortfolioLimits()) { ReleaseTradingLock(); return; } RefreshRates(); double entryPrice = (orderType == OP_BUY) ? Ask : Bid; double stopLoss = CalculateStopLoss(orderType, entryPrice, S2_SL_percent); double takeProfit = CalculateTakeProfit(orderType, entryPrice, S2_TP_percent); // Read martingale state DIRECTLY from GlobalVariables (always fresh) double mart_step_gv = GlobalVariableGet(GV_MART_STEP); if(!GlobalVariableCheck(GV_MART_STEP)) { GlobalVariableSet(GV_MART_STEP, 0); mart_step_gv = 0; } int effective_step = (int)mart_step_gv; g_martingale_current_step = effective_step; // Keep local in sync // v5.4: Loss-based martingale calculation double effective_risk = S2_Risk_Per_Trade; // Default base risk double display_multi = 1.0; if(MartingaleApplies(2) && effective_step > 0) { // Get the ACTUAL LOSS from GlobalVariable double last_loss = GlobalVariableGet(GV_MART_RISK); if(last_loss > 0) { // Calculate risk as: last_loss × multiplier effective_risk = last_loss * Martingale_Multiplier; display_multi = effective_risk / S2_Risk_Per_Trade; Print("╔════════════════════════════════════════════════════════════╗"); Print("║ 🎲 MARTINGALE - LOSS-BASED CALCULATION ║"); Print("╠════════════════════════════════════════════════════════════╣"); Print("║ Last Actual Loss: $", DoubleToString(last_loss, 2)); Print("║ Multiplier: ", DoubleToString(Martingale_Multiplier, 2), "x"); Print("║ This Trade Risk: $", DoubleToString(effective_risk, 2)); Print("╚════════════════════════════════════════════════════════════╝"); } else { // Fallback to step-based if no stored loss (shouldn't happen) double mart_mult = MathPow(Martingale_Multiplier, effective_step); effective_risk = S2_Risk_Per_Trade * mart_mult; display_multi = mart_mult; Print("⚠️ No stored loss found, using step-based: Step ", effective_step, " = ", DoubleToString(mart_mult, 2), "x"); } } // Apply max risk cap if(effective_risk > Max_Martingale_Risk) { Print("⚠️ Risk capped: $", DoubleToString(effective_risk, 2), " → $", DoubleToString(Max_Martingale_Risk, 2)); effective_risk = Max_Martingale_Risk; } double lotSize = CalculateLotSize(entryPrice, stopLoss, effective_risk); lotSize = ValidateLotSize(lotSize); string comment = "S2_BPC_Step" + IntegerToString(effective_step); Print("═══════════════════════════════════════════"); Print("🎯 STRATEGY 2: ", OrderTypeName(orderType), " on ", Symbol()); Print("Entry: ", DoubleToString(entryPrice, Digits)); Print("Martingale Step: ", effective_step, "/", Martingale_Max_Steps); Print("Effective Risk: $", DoubleToString(effective_risk, 2)); Print("Lot: ", DoubleToString(lotSize, 2)); Print("═══════════════════════════════════════════"); int ticket = OrderSend(Symbol(), orderType, lotSize, entryPrice, 3, stopLoss, takeProfit, comment, Unique_Magic_Number, 0, clrNONE); ReleaseTradingLock(); if(ticket > 0) { Print("✅ Strategy 2 trade opened. Ticket: ", ticket); g_s2_last_trade_time = TimeCurrent(); g_s2_trade_setup_state = "Trade Opened"; } else { Print("❌ Strategy 2 trade failed. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Find Resistance Level | //+------------------------------------------------------------------+ double FindResistance(ENUM_TIMEFRAMES tf, int lookback) { int bars = iBars(Symbol(), tf); if(bars < lookback + 10) return 0; for(int i = 5; i < lookback + 50; i++) { bool isPivot = true; double val = iHigh(Symbol(), tf, i); for(int j = 1; j <= 4; j++) { if(iHigh(Symbol(), tf, i + j) >= val || iHigh(Symbol(), tf, i - j) > val) { isPivot = false; break; } } if(isPivot) return val; } return 0; } //+------------------------------------------------------------------+ //| Find Support Level | //+------------------------------------------------------------------+ double FindSupport(ENUM_TIMEFRAMES tf, int lookback) { int bars = iBars(Symbol(), tf); if(bars < lookback + 10) return 0; for(int i = 5; i < lookback + 50; i++) { bool isPivot = true; double val = iLow(Symbol(), tf, i); for(int j = 1; j <= 4; j++) { if(iLow(Symbol(), tf, i + j) <= val || iLow(Symbol(), tf, i - j) < val) { isPivot = false; break; } } if(isPivot) return val; } return 0; } //+------------------------------------------------------------------+ //| Check if within trading hours | //+------------------------------------------------------------------+ bool IsWithinTradingHours(string startTime, string endTime) { datetime current = TimeCurrent(); string today = TimeToString(current, TIME_DATE); datetime startDT = StringToTime(today + " " + startTime); datetime endDT = StringToTime(today + " " + endTime); return (current >= startDT && current <= endDT); } //+------------------------------------------------------------------+ //| Manage Position Protection | //+------------------------------------------------------------------+ void ManageAllPositionProtection() { if(Enable_Strategy1_200EMA && S1_Enable_Position_Protection) ManageStrategy1Protection(); if(Enable_Strategy2_Breakout) ManageStrategy2Protection(); } //+------------------------------------------------------------------+ //| Strategy 1 Position Protection | //+------------------------------------------------------------------+ void ManageStrategy1Protection() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == Unique_Magic_Number && OrderSymbol() == Symbol() && StringFind(OrderComment(), "S1_") >= 0) { double entry_price = OrderOpenPrice(); double current_price = (OrderType() == OP_BUY) ? Bid : Ask; double current_sl = OrderStopLoss(); double profit_percent = (OrderType() == OP_BUY) ? ((current_price - entry_price) / entry_price) * 100.0 : ((entry_price - current_price) / entry_price) * 100.0; if(profit_percent >= S1_Protection_Trigger_Percent) { if(S1_Protection_Mode == 1) // Break Even { double offset = S1_Breakeven_Offset_Pips * Point * 10; double new_sl = (OrderType() == OP_BUY) ? entry_price + offset : entry_price - offset; bool should_modify = (OrderType() == OP_BUY) ? (current_sl < new_sl) : (current_sl > new_sl || current_sl == 0); if(should_modify) { OrderModify(OrderTicket(), entry_price, new_sl, OrderTakeProfit(), 0, clrBlue); } } else if(S1_Protection_Mode == 2) // Trailing { double trail_distance = entry_price * (S1_Trailing_Distance_Percent / 100.0); double new_sl = (OrderType() == OP_BUY) ? current_price - trail_distance : current_price + trail_distance; bool should_modify = (OrderType() == OP_BUY) ? (new_sl > current_sl) : (new_sl < current_sl || current_sl == 0); if(should_modify) { OrderModify(OrderTicket(), entry_price, new_sl, OrderTakeProfit(), 0, clrBlue); } } } } } } } //+------------------------------------------------------------------+ //| Strategy 2 Position Protection | //+------------------------------------------------------------------+ void ManageStrategy2Protection() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == Unique_Magic_Number && OrderSymbol() == Symbol() && StringFind(OrderComment(), "S2_") >= 0) { double entry_price = OrderOpenPrice(); double current_price = (OrderType() == OP_BUY) ? Bid : Ask; double current_sl = OrderStopLoss(); double profit_percent = (OrderType() == OP_BUY) ? ((current_price - entry_price) / entry_price) * 100.0 : ((entry_price - current_price) / entry_price) * 100.0; // Break Even if(S2_EnableBreakEven && profit_percent >= S2_BreakEvenTriggerPercent * 100.0) { double offset = entry_price * S2_BreakEvenOffsetPercent; double new_sl = (OrderType() == OP_BUY) ? entry_price + offset : entry_price - offset; bool should_modify = (OrderType() == OP_BUY) ? (current_sl < new_sl) : (current_sl > new_sl || current_sl == 0); if(should_modify) { OrderModify(OrderTicket(), entry_price, new_sl, OrderTakeProfit(), 0, clrBlue); } } // Trailing if(S2_EnableTrailingStop && profit_percent >= S2_TrailingStopTriggerPercent * 100.0) { double trail_distance = entry_price * S2_TrailingStopDistancePercent; double new_sl = (OrderType() == OP_BUY) ? current_price - trail_distance : current_price + trail_distance; bool should_modify = (OrderType() == OP_BUY) ? (new_sl > current_sl) : (new_sl < current_sl || current_sl == 0); if(should_modify) { OrderModify(OrderTicket(), entry_price, new_sl, OrderTakeProfit(), 0, clrBlue); } } } } } } //+------------------------------------------------------------------+ //| Calculate Stop Loss | //+------------------------------------------------------------------+ double CalculateStopLoss(int orderType, double entryPrice, double slPercent) { double slDistance = entryPrice * (slPercent / 100.0); return (orderType == OP_BUY) ? NormalizeDouble(entryPrice - slDistance, Digits) : NormalizeDouble(entryPrice + slDistance, Digits); } //+------------------------------------------------------------------+ //| Calculate Take Profit | //+------------------------------------------------------------------+ double CalculateTakeProfit(int orderType, double entryPrice, double tpPercent) { double tpDistance = entryPrice * (tpPercent / 100.0); return (orderType == OP_BUY) ? NormalizeDouble(entryPrice + tpDistance, Digits) : NormalizeDouble(entryPrice - tpDistance, Digits); } //+------------------------------------------------------------------+ //| Calculate Lot Size | //+------------------------------------------------------------------+ double CalculateLotSize(double entryPrice, double stopLoss, double riskAmount) { double slDistance = MathAbs(entryPrice - stopLoss); if(slDistance <= 0) return MarketInfo(Symbol(), MODE_MINLOT); double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE); double minLot = MarketInfo(Symbol(), MODE_MINLOT); double maxLot = MarketInfo(Symbol(), MODE_MAXLOT); double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); if(tickValue == 0 || tickSize == 0) return minLot; double slInTicks = slDistance / tickSize; double lotSize = riskAmount / (slInTicks * tickValue); if(lotSize < minLot) lotSize = minLot; else if(lotSize > maxLot) lotSize = maxLot; else lotSize = MathFloor(lotSize / lotStep) * lotStep; return NormalizeDouble(lotSize, 2); } //+------------------------------------------------------------------+ //| Validate Lot Size | //+------------------------------------------------------------------+ double ValidateLotSize(double lotSize) { double minLot = MarketInfo(Symbol(), MODE_MINLOT); double maxLot = MarketInfo(Symbol(), MODE_MAXLOT); double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); if(lotSize < minLot) return minLot; if(lotSize > maxLot) return maxLot; lotSize = MathFloor(lotSize / lotStep) * lotStep; return NormalizeDouble(lotSize, 2); } //+------------------------------------------------------------------+ //| Order Type Name | //+------------------------------------------------------------------+ string OrderTypeName(int orderType) { if(orderType == OP_BUY) return "BUY"; if(orderType == OP_SELL) return "SELL"; return "UNKNOWN"; } //+------------------------------------------------------------------+ //| Verify License with Server | //+------------------------------------------------------------------+ bool VerifyLicense() { if(StringLen(License_Key) == 0) { Print("❌ No license key entered"); return false; } if(StringLen(License_Server) == 0) { Print("❌ No license server configured"); return false; } string account_number = IntegerToString(AccountNumber()); string broker = AccountCompany(); string hwid = account_number + "_" + broker + "_" + IntegerToString(TerminalInfoInteger(TERMINAL_BUILD)); string url = License_Server + "/api/verify-license"; string post_data = "licenseKey=" + License_Key + "&magicNumber=" + IntegerToString(Unique_Magic_Number) + "&accountNumber=" + account_number + "&hwid=" + hwid + "&balance=" + DoubleToString(AccountBalance(), 2) + "&broker=" + broker; char post[]; char result[]; string headers = "Content-Type: application/x-www-form-urlencoded\r\n"; string result_headers; StringToCharArray(post_data, post, 0, StringLen(post_data)); ArrayResize(post, StringLen(post_data)); int timeout = 5000; int res = WebRequest("POST", url, headers, timeout, post, result, result_headers); if(res == -1) { int error = GetLastError(); if(error == 4060) { Print("❌ WebRequest error: Add URL to allowed list"); Print(" Tools → Options → Expert Advisors → Allow WebRequest"); Print(" Add: ", License_Server); } else { Print("❌ WebRequest failed. Error: ", error); } return false; } string response = CharArrayToString(result); if(StringFind(response, "\"valid\":true") >= 0 || StringFind(response, "\"success\":true") >= 0) { if(StringFind(response, "PERMANENT") >= 0) { Print("✅ PERMANENT LICENSE"); } return true; } Print("❌ License verification failed"); Print(" Response: ", response); return false; } //+------------------------------------------------------------------+