Generate code in 3 minutes
Enter your idea and AI generates runnable code.
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).
structs that match the binary layout of the MT4 HST header (148 bytes) and the bar record (60 bytes).FileOpenHistory to create the file directly in the correct directory.FileWriteStruct to write the header once, followed by the bar data in a loop.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.");
}
//+------------------------------------------------------------------+
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.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..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--).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.!EURUSD_OFFLINE, M10 in the list and click Open.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.