🚀 AI One-Click Generation of mql4 Strategy Code

How to Store and Retrieve Custom Data Structures in Mql4 Using Binary Files

mql4 | Published on: 2025/12/2
The following content is generated by EasyQuant .

Problem Description

What's the most efficient way to save a custom data structure to a file and read it back later?

Solution

The most efficient way to save and retrieve custom data structures in MQL4 is using Binary Files combined with the FileWriteStruct() and FileReadStruct() functions.

Why this is the most efficient method:

  1. Speed: It dumps the memory block of the structure directly to the disk without converting data to text (strings), avoiding costly parsing operations.
  2. Precision: Floating-point numbers (double) are saved exactly as they are represented in memory, preventing precision loss associated with CSV/Text files (e.g., truncation of decimals).
  3. Simplicity: You can read/write complex structures in a single line of code.

Important Limitation

To use FileWriteStruct, your structure cannot contain:

  • Dynamic arrays.
  • Dynamic strings (use fixed-length uchar or char arrays instead).
  • Class objects with virtual functions.

MQL4 Implementation

Here is a complete, runnable script demonstrating how to define a structure, populate it with data, save it to a binary file, and read it back.

//+------------------------------------------------------------------+
//|                                       StructBinaryIO_Example.mq4 |
//|                        Copyright 2023, Quantitative AI Assistant |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI Assistant"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property script_show_inputs

//--- Input parameters
input string FileName = "TradeData.bin"; // File name to save in MQL4/Files

//+------------------------------------------------------------------+
//| Define the Custom Structure (POD - Plain Old Data)               |
//+------------------------------------------------------------------+
struct MyTradeData
{
   datetime    time;          // 8 bytes
   double      price;         // 8 bytes
   double      lots;          // 8 bytes
   int         ticket;        // 4 bytes
   int         type;          // 4 bytes
   char        symbol[12];    // Fixed length string for binary safety
   char        comment[64];   // Fixed length string for binary safety
};

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
   // --- 1. PREPARE DATA ---
   MyTradeData dataToWrite[];
   ArrayResize(dataToWrite, 3);
   
   // Fill with sample data
   for(int i=0; i<3; i++)
   {
      dataToWrite[i].time     = TimeCurrent();
      dataToWrite[i].price    = Ask;
      dataToWrite[i].lots     = 0.1 * (i+1);
      dataToWrite[i].ticket   = 1000 + i;
      dataToWrite[i].type     = OP_BUY;
      
      // String conversion to fixed char array
      string sym = Symbol();
      string comm = "Trade #" + IntegerToString(i);
      StringToCharArray(sym, dataToWrite[i].symbol);
      StringToCharArray(comm, dataToWrite[i].comment);
   }
   
   Print("--- Starting Write Operation ---");
   
   // --- 2. SAVE TO FILE ---
   if(SaveStructsToFile(FileName, dataToWrite))
   {
      Print("Successfully saved ", ArraySize(dataToWrite), " records.");
   }
   
   // --- 3. READ FROM FILE ---
   Print("--- Starting Read Operation ---");
   MyTradeData dataRead[];
   
   if(LoadStructsFromFile(FileName, dataRead))
   {
      Print("Successfully loaded ", ArraySize(dataRead), " records.");
      
      // Verify data
      for(int i=0; i<ArraySize(dataRead); i++)
      {
         PrintFormat("Record %d: Ticket=%d, Symbol=%s, Price=%.5f, Comment=%s",
            i,
            dataRead[i].ticket,
            CharArrayToString(dataRead[i].symbol),
            dataRead[i].price,
            CharArrayToString(dataRead[i].comment)
         );
      }
   }
}

//+------------------------------------------------------------------+
//| Function to Save Array of Structures to Binary File              |
//+------------------------------------------------------------------+
bool SaveStructsToFile(string filename, MyTradeData &data[])
{
   // Reset error state
   ResetLastError();
   
   // Open file for Writing in Binary mode
   // FILE_COMMON can be added to flags if you want to share between terminals
   int handle = FileOpen(filename, FILE_WRITE | FILE_BIN);
   
   if(handle == INVALID_HANDLE)
   {
      Print("Error opening file for writing: ", GetLastError());
      return(false);
   }
   
   int count = ArraySize(data);
   
   // Write the data
   // Note: We can loop through the array, or write individually.
   // FileWriteStruct writes one structure at a time.
   uint written = 0;
   for(int i=0; i<count; i++)
   {
      written += FileWriteStruct(handle, data[i]);
   }
   
   FileClose(handle);
   
   // Check if bytes written matches expected size
   // Size of one struct * number of items
   if(written != sizeof(MyTradeData) * count)
   {
      Print("Warning: Data size mismatch during write.");
      return(false);
   }
   
   return(true);
}

//+------------------------------------------------------------------+
//| Function to Read Array of Structures from Binary File            |
//+------------------------------------------------------------------+
bool LoadStructsFromFile(string filename, MyTradeData &result[])
{
   ResetLastError();
   
   // Open file for Reading in Binary mode
   int handle = FileOpen(filename, FILE_READ | FILE_BIN);
   
   if(handle == INVALID_HANDLE)
   {
      if(GetLastError() == ERR_FILE_NOT_EXIST) // Error 5004
         Print("File does not exist: ", filename);
      else
         Print("Error opening file for reading: ", GetLastError());
      return(false);
   }
   
   // Clear result array
   ArrayFree(result);
   
   // Read loop
   int i = 0;
   while(!FileIsEnding(handle))
   {
      // Resize array to fit new item
      ArrayResize(result, i + 1);
      
      // Read structure directly into the array index
      uint bytesRead = FileReadStruct(handle, result[i]);
      
      // Validation: Ensure we actually read a full struct
      if(bytesRead != sizeof(MyTradeData))
      {
         // If 0 bytes read (EOF), resize back and break
         if(bytesRead == 0) 
         {
            ArrayResize(result, i);
            break;
         }
         Print("Error: Corrupted data or incomplete structure read.");
         FileClose(handle);
         return(false);
      }
      
      i++;
   }
   
   FileClose(handle);
   return(true);
}

Key Implementation Details

  1. StringToCharArray: Since we cannot use dynamic string types in a binary structure, we define char symbol[12]. We use StringToCharArray to convert MQL4 strings into this fixed-byte format before saving, and CharArrayToString when reading back for display.
  2. FILE_BIN Flag: This is critical in FileOpen. It tells the system not to treat the data as text lines (looking for line breaks), but as raw bytes.
  3. sizeof(MyTradeData): This operator returns the exact byte size of the structure. This is useful for validating that the data read/written matches the expected structure size.
  4. FileIsEnding: Used in the read loop to determine when the file pointer has reached the end of the file.

Q&A: MQL4 Data Structures & File I/O

Q: Can I save an array of structures in one go without a loop?
A: FileWriteArray can be used for arrays of simple types or structures. However, FileWriteStruct is often safer for explicit control over structure alignment. If using FileWriteArray with structures, ensure the structure contains absolutely no dynamic objects.

Q: What happens if I change the structure definition and try to read an old file?
A: The read will likely fail or produce garbage data. Binary files rely on exact byte alignment. If you add a field to the struct, the size changes, and the old file's byte mapping will no longer align with the new struct definition. You should implement a version header in your file if you plan to update the structure later.

Q: Where are these files saved?
A: By default, they are saved in the MQL4/Files directory of your terminal's data folder. You can access this via File -> Open Data Folder. If you use the FILE_COMMON flag, they are saved in the common data path shared by all terminals installed on the computer.