//+------------------------------------------------------------------+ //| StatePersistenceManager.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| StatePersistenceManager.mqh | //+------------------------------------------------------------------+ #include "../Analysers/IAnalyser.mqh" #include // your JSON class #include class StatePersistenceManager { private: string m_fileName; JSON::Object* m_root; // holds all current state // Internal structure to hold a snapshot entry struct Snapshot { string analyserName; string symbol; ENUM_TIMEFRAMES tf; JSON::Object* state; }; Snapshot m_snapshots[]; // current in-memory snapshots int m_snapshotCount; // Helpers to find a snapshot by analyser/symbol/tf int FindSnapshot(string analyserName, string symbol, ENUM_TIMEFRAMES tf, int startFromEnd = 0) { // If startFromEnd = 0, search all; if 1, search from end to get latest for(int i = m_snapshotCount-1; i >= 0; i--) { if(m_snapshots[i].analyserName == analyserName && m_snapshots[i].symbol == symbol && m_snapshots[i].tf == tf) return i; } return -1; } public: StatePersistenceManager(string fileName) : m_fileName(fileName), m_snapshotCount(0) {} // Load all existing snapshots from file (if any) bool LoadFromFile() { if(!FileIsExist(m_fileName)) return false; // no file yet string jsonStr = ""; int handle = FileOpen(m_fileName, FILE_READ|FILE_TXT|FILE_ANSI); if(handle == INVALID_HANDLE) return false; while(!FileIsEnding(handle)) jsonStr += FileReadString(handle, 0); FileClose(handle); JSON::Object* root = new JSON::Object(jsonStr); if(root == NULL) return false; // Parse the stored structure: // { // "snapshots": [ // { // "analyser": "OrderBlockAnalyser", // "symbol": "EURUSD", // "tf": 60, // timeframe as integer // "state": { ... } // }, // ... // ] // } JSON::Array* snapshots = root.getArray("snapshots"); if(snapshots == NULL) { delete root; return false; } for(int i = 0; i < snapshots.getLength(); i++) { JSON::Object* snapObj = snapshots.getObject(i); if(snapObj == NULL) continue; string analyser = snapObj.getString("analyser"); string symbol = snapObj.getString("symbol"); int tfInt = (int)snapObj.getNumber("tf"); ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)tfInt; JSON::Object* state = snapObj.getObject("state"); if(state == NULL) continue; // Add to internal list int idx = m_snapshotCount++; ArrayResize(m_snapshots, idx+1); m_snapshots[idx].analyserName = analyser; m_snapshots[idx].symbol = symbol; m_snapshots[idx].tf = tf; m_snapshots[idx].state = state; // take ownership } delete root; return true; } // Save a snapshot for a specific analyser/symbol/tf. // If a snapshot for the same key exists, it will be replaced (but we still keep history? // The user wants append not rewrite on reset. So we need to preserve old snapshots. // We'll add a new snapshot entry each time, allowing multiple snapshots per key. // Later LoadLatest will get the most recent one. void SaveSnapshot(string analyserName, string symbol, ENUM_TIMEFRAMES tf, JSON::Object* state) { int idx = m_snapshotCount++; ArrayResize(m_snapshots, idx+1); m_snapshots[idx].analyserName = analyserName; m_snapshots[idx].symbol = symbol; m_snapshots[idx].tf = tf; m_snapshots[idx].state = state; } // Load the latest snapshot for a given analyser/symbol/tf. // Returns a copy? We'll return the pointer directly; caller must not delete. JSON::Object* LoadLatest(string analyserName, string symbol, ENUM_TIMEFRAMES tf) { int idx = FindSnapshot(analyserName, symbol, tf); // returns latest (since we search from end) if(idx < 0) return NULL; return m_snapshots[idx].state; } // Write all snapshots to file (append or rewrite? user wants append not rewrite on reset, // but the file itself is rewritten each time with the full list of snapshots, which includes // all old ones because we never delete them. That effectively appends to history.) bool SaveToFile() { JSON::Object* root = new JSON::Object(); JSON::Array* arr = new JSON::Array(); for(int i = 0; i < m_snapshotCount; i++) { JSON::Object* snapObj = new JSON::Object(); snapObj.setProperty("analyser", m_snapshots[i].analyserName); snapObj.setProperty("symbol", m_snapshots[i].symbol); snapObj.setProperty("tf", (int)m_snapshots[i].tf); snapObj.setProperty("state", m_snapshots[i].state); // deep copy? JSON::Object* setProperty will store pointer; our JSON class may not deep copy. We'll assume it does or we need to clone. arr.add(snapObj); } root.setProperty("snapshots", arr); string jsonStr = root.toString(); delete root; int handle = FileOpen(m_fileName, FILE_WRITE|FILE_TXT|FILE_ANSI); if(handle == INVALID_HANDLE) return false; FileWriteString(handle, jsonStr); FileClose(handle); return true; } ~StatePersistenceManager() { for(int i = 0; i < m_snapshotCount; i++) delete m_snapshots[i].state; } }; //+------------------------------------------------------------------+