🚀 AI One-Click Generation of mql4 Strategy Code

Creating and Managing Custom Offline Charts in Mql4 with FileOpenHistory()

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

Problem Description

How can I programmatically create and write data to a custom offline chart file?

Solution

To programmatically create and write data to a custom offline chart in MetaTrader 4, you must generate a binary file with the .hst extension in the terminal's history folder. This file must adhere strictly to the MT4 History File Format (Version 401).

The Strategy

  1. Define Data Structures: Create structs that match the binary layout of the MT4 HST header (148 bytes) and the bar record (60 bytes).
  2. File Operations: Use FileOpenHistory to create the file directly in the correct directory.
  3. Binary Writing: Use FileWriteStruct to write the header once, followed by the bar data in a loop.
  4. Access: Once created, the chart is accessed via File -> Open Offline in the MT4 terminal.

MQL4 Implementation

Here is a complete script. You can run this on any chart (e.g., EURUSD M1), and it will generate an offline file (e.g., !EURUSD_OFFLINEM10.hst) containing the current chart's data.

//+------------------------------------------------------------------+
//|                                        CreateOfflineChart.mq4    |
//|                                   Copyright 2023, QuantitativeAI |
//|                                             https://mql4.com     |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, QuantitativeAI"
#property link      "https://mql4.com"
#property version   "1.00"
#property strict
#property script_show_inputs

//--- Input Parameters
input string   CustomSymbolName = "!EURUSD_OFFLINE"; // Name of the offline symbol
input int      CustomPeriod     = 10;                // Period (e.g., 10 minutes)

//--- MT4 History Header Structure (148 bytes)
struct MqlHistoryHeader
{
   int               version;          // Database version (401)
   uchar             copyright[64];    // Copyright info
   uchar             symbol[12];       // Symbol name
   int               period;           // Timeframe
   int               digits;           // Digits
   int               timesign;         // Time of file creation
   int               last_sync;        // Time of last synchronization
   int               unused[13];       // Reserved for future use
};

//--- MT4 Bar Record Structure (60 bytes)
struct MqlHistoryRate
{
   long              time;             // Bar open time
   double            open;             // Open price
   double            high;             // High price
   double            low;              // Low price
   double            close;            // Close price
   long              tick_volume;      // Tick volume
   int               spread;           // Spread
   long              real_volume;      // Real volume
};

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
   // 1. Construct the file name
   // Format must be: Symbol + Period + ".hst"
   string fileName = CustomSymbolName + IntegerToString(CustomPeriod) + ".hst";
   
   Print("Attempting to create offline chart: ", fileName);

   // 2. Open the file in the History folder
   // FILE_BIN: Binary mode
   // FILE_WRITE: Write mode
   // FILE_SHARE_WRITE/READ: Allows the offline chart to update while open
   int handle = FileOpenHistory(fileName, FILE_BIN | FILE_WRITE | FILE_SHARE_WRITE | FILE_SHARE_READ);

   if(handle == INVALID_HANDLE)
   {
      Print("Error opening file. Code: ", GetLastError());
      return;
   }

   // 3. Prepare and Write the Header
   MqlHistoryHeader header;
   ZeroMemory(header);
   
   header.version = 401;
   StringToCharArray("MetaQuotes", header.copyright);
   StringToCharArray(CustomSymbolName, header.symbol);
   header.period = CustomPeriod;
   header.digits = Digits;
   header.timesign = (int)TimeLocal();
   header.last_sync = 0;
   
   // Write the header structure to the file
   if(FileWriteStruct(handle, header) <= 0)
   {
      Print("Error writing header. Code: ", GetLastError());
      FileClose(handle);
      return;
   }

   // 4. Prepare and Write Bar Data
   // We copy data from the current chart to the offline chart
   int barsCount = Bars;
   int writtenCount = 0;
   
   // MT4 history files store oldest data first. 
   // The Bars array is indexed [0] = newest. 
   // So we loop from Bars-1 down to 0.
   for(int i = barsCount - 1; i >= 0; i--)
   {
      MqlHistoryRate rate;
      
      rate.time        = (long)Time[i];
      rate.open        = Open[i];
      rate.high        = High[i];
      rate.low         = Low[i];
      rate.close       = Close[i];
      rate.tick_volume = (long)Volume[i];
      rate.spread      = 0;              // Optional: Record specific spread if needed
      rate.real_volume = 0;              // Usually 0 for Forex
      
      if(FileWriteStruct(handle, rate) <= 0)
      {
         Print("Error writing bar at index ", i);
         break;
      }
      writtenCount++;
   }

   // 5. Finalize
   FileFlush(handle);
   FileClose(handle);
   
   Print("Successfully wrote ", writtenCount, " bars to ", fileName);
   Print("Go to File -> Open Offline to view the chart.");
}
//+------------------------------------------------------------------+

Key Implementation Details

  1. FileOpenHistory: This function is critical. Unlike FileOpen (which opens in the MQL4/Files sandbox), FileOpenHistory opens files directly in the history/<server_name> folder, which is where MT4 looks for chart data.
  2. Struct Alignment: The MqlHistoryHeader and MqlHistoryRate structures are defined exactly to match the C++ structures used internally by MT4. Using uchar arrays for strings ensures the header is exactly 148 bytes.
  3. Data Ordering: MT4 .hst files require data to be written chronologically (Oldest -> Newest). Since MQL4 arrays (Time[], Open[]) are usually indexed as Series (0 is Newest), the loop iterates backwards: for(int i = barsCount - 1; i >= 0; i--).
  4. Shared Access: The flags FILE_SHARE_WRITE | FILE_SHARE_READ are essential if you plan to update this file continuously (e.g., from an Expert Advisor) while the offline chart is open and being viewed.

How to Use

  1. Compile the script.
  2. Drag it onto any live chart (e.g., EURUSD).
  3. Check the "Experts" tab for the success message.
  4. In MT4, go to File -> Open Offline.
  5. Find !EURUSD_OFFLINE, M10 in the list and click Open.

Q&A

Q: Can I update the offline chart in real-time?
A: Yes. Instead of a script, implement this logic in an Expert Advisor (EA). In OnInit, write the history. In OnTick, seek to the end of the file (FileSeek(handle, 0, SEEK_END)), write the new bar data, and call FileFlush. To make the offline chart refresh visually, you may need to send a window command message to the offline chart window.

Q: Why use uchar arrays for strings in the header?
A: The .hst file format requires fixed-byte fields (64 bytes for copyright, 12 bytes for symbol). Standard MQL4 string types are dynamic pointers. Using uchar arrays ensures the binary structure is written with the exact byte size required by the file format specification.

Q: What happens if I use a standard timeframe ID (e.g., 60 for H1)?
A: You can overwrite standard history data if the filename matches an existing symbol (e.g., EURUSD60.hst). However, the terminal might overwrite your custom data during synchronization with the broker server. It is safer to use a custom symbol name or a non-standard timeframe (e.g., M10, M2, Renko) to prevent conflicts.