diff --git a/OrdersEA_Smart_Grid_BO.mq5 b/OrdersEA_Smart_Grid_BO.mq5 index fc75b88..fbd0f00 100755 --- a/OrdersEA_Smart_Grid_BO.mq5 +++ b/OrdersEA_Smart_Grid_BO.mq5 @@ -5,12 +5,12 @@ //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Garfield Heron" #property link "https://fetcherpay.com" -#property version "5.7" +#property version "5.8" #include #include -#define VERSION "Version 5.7 Smart Grid Breakout BO MT5" +#define VERSION "Version 5.8 Smart Grid Breakout BO MT5" #define MAX_TRADES 600 #define MAX_LOG_TRADES 1200 @@ -1078,6 +1078,35 @@ int CountPendingOrders(int type) return count; } +// True if this symbol+magic already has any pending order or open position +// on the broker, regardless of what gridPlaced (GlobalVariable-backed, +// found 2026-08-11 to never actually reach disk in this environment) says. +// Used at OnInit to recover from a restart wiping that flag to false while +// a grid is still resting live -- without this check, every restart mid- +// cycle placed a brand new duplicate grid on top of the old one (see vault +// "2026-08-11 Duplicate Grid Orders" note; found 189 stacked pending +// orders account-wide, one symbol alone had 29 spanning 24+ hours). +bool HasLiveGridPresence() + { + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + return true; + } + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + return true; + } + return false; + } + void CancelAllOrders(string reason) { int cancelled = 0; @@ -1351,6 +1380,20 @@ int OnInit() LoadGridState(); + // Self-heal (2026-08-11): gridPlaced is GlobalVariable-backed and was + // found to never actually reach disk in this environment (no .gvr file + // anywhere under the MT5 data dir) — so a full container restart mid- + // cycle silently resets it to false while the grid is still resting + // live on the broker, and the code below would place a brand new + // duplicate grid on top of it. Trust reality over the flag: if this + // symbol+magic already has orders or positions out, we already have a + // grid, no matter what LoadGridState() just said. + if(!gridPlaced && HasLiveGridPresence()) + { + PrintS("Self-heal: found existing orders/positions on init — recovering gridPlaced=true (see 2026-08-11 duplicate-grid fix)"); + gridPlaced = true; + } + // GridHigh/GridLow are NOT persisted (only lastPivotCalcDate is) — if a // prior process already recalculated today before this restart, the // date-match guard alone would skip CalculatePivotPoints() and leave the