v5.8: self-heal gridPlaced from real broker state at OnInit
Fixes the duplicate-grid bug found 2026-08-11: gridPlaced (and the rest of SaveGridState's GlobalVariable-backed state) never reaches disk in this environment -- confirmed no .gvr file exists anywhere under the MT5 data dir. Every full container restart mid-cycle wiped it back to false while a grid was still resting live on the broker, and OnTick would place a brand new duplicate grid on top of the old, uncancelled one. Found 189 stacked pending orders account-wide before cleanup (CancelAllPendingNow.mq5), one symbol alone had 29 spanning 24+ hours of normal cycles -- this has likely been happening on every watchdog restart for weeks, not just from that day's testing. Adds HasLiveGridPresence(): checks actual OrdersTotal()/PositionsTotal() for this symbol+magic. At OnInit, if gridPlaced reads false but real orders or positions already exist, trust reality over the flag and recover gridPlaced=true -- so a restart mid-cycle can no longer cause a duplicate placement, regardless of whether GlobalVariable persistence ever gets fixed underneath. Deployed and recompiled live (all 14 EAs confirmed reinitialized as v5.8); not yet validated against a live container-restart cycle given the risk of disrupting the account further right after cleanup -- logic mirrors the existing broker-state query pattern already used in OnTick's monitor branch.
This commit is contained in:
@@ -5,12 +5,12 @@
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
#property copyright "Copyright 2024, Garfield Heron"
|
#property copyright "Copyright 2024, Garfield Heron"
|
||||||
#property link "https://fetcherpay.com"
|
#property link "https://fetcherpay.com"
|
||||||
#property version "5.7"
|
#property version "5.8"
|
||||||
|
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
#include <Trade\PositionInfo.mqh>
|
#include <Trade\PositionInfo.mqh>
|
||||||
|
|
||||||
#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_TRADES 600
|
||||||
#define MAX_LOG_TRADES 1200
|
#define MAX_LOG_TRADES 1200
|
||||||
|
|
||||||
@@ -1078,6 +1078,35 @@ int CountPendingOrders(int type)
|
|||||||
return count;
|
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)
|
void CancelAllOrders(string reason)
|
||||||
{
|
{
|
||||||
int cancelled = 0;
|
int cancelled = 0;
|
||||||
@@ -1351,6 +1380,20 @@ int OnInit()
|
|||||||
|
|
||||||
LoadGridState();
|
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
|
// GridHigh/GridLow are NOT persisted (only lastPivotCalcDate is) — if a
|
||||||
// prior process already recalculated today before this restart, the
|
// prior process already recalculated today before this restart, the
|
||||||
// date-match guard alone would skip CalculatePivotPoints() and leave the
|
// date-match guard alone would skip CalculatePivotPoints() and leave the
|
||||||
|
|||||||
Reference in New Issue
Block a user