Why Export Indicator Buffers to CSV?
At some point, every serious MetaTrader 5 user hits the same wall: the built-in Data Window and chart tooltips are fine for eyeballing a few values, but they're useless when you need to run statistics, feed a Python script, or build a custom report in Excel. The terminal simply doesn't give you a "Save buffer as CSV" button. You either copy values by hand — which is madness past fifty bars — or you write a tiny MQL5 script that dumps the data for you.
That's exactly what this guide covers. You'll end up with a reusable script that reads any indicator's buffers and writes them to a CSV file in the MQL5\Files folder. No external libraries, no DLLs, just the standard FileWrite functions built into the language. I'll also show you the common pitfalls — because CopyBuffer() has a few sharp edges that will bite you the first time.
This is a practical coding workflow, not a strategy discussion. You should already be comfortable opening MetaEditor and compiling a script. If you've never written a line of MQL5, you'll still follow along, but you'll get more out of it if you've at least tinkered with an Expert Advisor before.
Prerequisites
Before we start, make sure you have the following:
- MetaTrader 5 build 2000 or newer — older builds work, but the file functions have been stable for years, so this is more about having a current terminal than a hard requirement.
- Any live or demo account — you don't need a funded account; a demo login works fine for this. The script runs on whatever chart you attach it to.
- An indicator with at least one buffer — the built-in MACD or RSI are perfect for testing. Custom indicators work too, as long as they use
SetIndexBuffer()properly. - MetaEditor — press F4 in the terminal or click the IDE button in the toolbar.
That's it. No special permissions, no external tools. The CSV file lands in C:\Users\<YourName>\AppData\Roaming\MetaQuotes\Terminal\<InstanceID>\MQL5\Files on Windows. You can find this folder quickly by going to File → Open Data Folder in the terminal, then navigating to MQL5 → Files.
Step-by-Step: Building the Export Script
We'll write a Script rather than an Expert Advisor or indicator. Scripts run once and exit, which is exactly what we want for a one-shot data dump. Here's the full plan:
- Create a new script in MetaEditor.
- Define input parameters for the indicator name, timeframe, buffer index, and bar count.
- Use
iCustom()to get a handle to the indicator. - Call
CopyBuffer()to pull data into a dynamic array. - Open a file with
FileOpen(), write a header row, then loop through the data withFileWrite(). - Close the file and confirm the path to the user.
Let's walk through each step.
Step 1: Create the Script
Open MetaEditor (F4), then go to File → New → Script. Name it ExportBufferToCSV. MetaEditor generates a template with OnStart() — that's where all our code goes.
Step 2: Define Inputs
Inputs let you reuse the script without recompiling. You'll change the indicator name or bar count from the script's properties dialog (right-click the script in the Navigator → Properties). Here's a sensible set:
input string InpIndicatorName = "MACD"; // Indicator short name
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Timeframe
input int InpBufferIndex = 0; // Buffer index (0 = first)
input int InpBarsToExport = 500; // Number of bars
input string InpFileName = "indicator_data.csv"; // Output file name
input bool InpIncludeHeader = true; // Write header row?
Note the InpIndicatorName — this must match the indicator's short name as it appears in the Navigator (without the .mq5 extension). For custom indicators with input parameters, you'll need to pass them to iCustom() as well. We'll keep it simple here and assume a no-input indicator like RSI.
Step 3: Get the Indicator Handle
Every indicator in MQL5 is accessed through a handle, not directly. iCustom() creates that handle for custom indicators, while built-ins have their own functions like iRSI() or iMACD(). For a generic script that works with anything, use iCustom():
int handle = iCustom(_Symbol, InpTimeframe, InpIndicatorName, 0);
if(handle == INVALID_HANDLE)
{
Print("Failed to create indicator handle. Error: ", GetLastError());
return;
}
For built-in indicators, you can use their dedicated functions — iRSI(_Symbol, InpTimeframe, 14, PRICE_CLOSE) — but that makes the script less flexible. The iCustom() approach works for both, as long as you use the correct short name.
Step 4: Copy Buffer Data
Now the tricky part. CopyBuffer() doesn't return the data directly — it fills an array you pass in, and you must size that array first. The function returns the number of copied elements or -1 on failure. Here's the correct pattern:
double buffer[];
ArraySetAsSeries(buffer, true); // index 0 = current bar
int copied = CopyBuffer(handle, InpBufferIndex, 0, InpBarsToExport, buffer);
if(copied <= 0)
{
Print("Failed to copy buffer. Error: ", GetLastError());
IndicatorRelease(handle);
return;
}
Print("Copied ", copied, " values.");
Two things to note here. First, ArraySetAsSeries(buffer, true) makes index 0 the most recent bar, which matches chart display order. If you skip this, index 0 is the oldest bar — your CSV will be reversed, and you'll wonder why your data looks scrambled. Second, CopyBuffer() can fail silently if the indicator hasn't calculated enough bars yet. On a live chart this is rarely an issue, but in the Strategy Tester you may need to call it after OnCalculate() has run a few times.
Step 5: Write the CSV File
Now we open a file and write the data. The FILE_WRITE flag creates or overwrites the file. Add FILE_CSV to get automatic delimiter handling, or use FILE_TXT and specify the delimiter yourself. I prefer FILE_CSV with a comma delimiter — it's what Excel expects by default.
string filePath = InpFileName;
int fileHandle = FileOpen(filePath, FILE_WRITE | FILE_CSV, ',');
if(fileHandle == INVALID_HANDLE)
{
Print("Failed to open file. Error: ", GetLastError());
IndicatorRelease(handle);
return;
}
// Write header
if(InpIncludeHeader)
{
FileWrite(fileHandle, "Time", "BufferValue");
}
// Write data
for(int i = 0; i < copied; i++)
{
datetime barTime = iTime(_Symbol, InpTimeframe, i);
FileWrite(fileHandle, TimeToString(barTime), DoubleToString(buffer[i], 8));
}
FileClose(fileHandle);
IndicatorRelease(handle);
Print("Data exported to ", filePath);
Notice I used DoubleToString(buffer[i], 8) instead of feeding the raw double to FileWrite(). This avoids locale issues — some systems use a comma as the decimal separator, which would corrupt your CSV. Eight decimal places is overkill for most indicators, but it doesn't hurt and preserves precision.
Also, iTime() gets the bar's opening time. This is essential — without a timestamp column, your CSV is just a list of numbers with no context. If you're exporting multiple buffers, add more columns in the header and write each buffer value in the loop.
Step 6: Run the Script
Compile with F7 (or the Compile button). Fix any errors — the most common one is a typo in the function name or a missing semicolon. Once it compiles clean, you'll see the script in the Navigator under Scripts.
Drag it onto a chart. A dialog pops up where you can adjust the input parameters — set the indicator name, buffer index, and bar count. Click OK. The script runs instantly and prints a confirmation message in the Experts tab. Open the data folder (File → Open Data Folder → MQL5 → Files) and you'll find your CSV.
Tips and Best Practices from Experience
After doing this dozens of times, a few habits save real headaches:
- Always include a timestamp column. Even if you think you don't need it now, you will later. Matching CSV rows to chart bars without timestamps is miserable.
- Use
DoubleToString()for all floating-point output. The terminal's defaultDoubleToString()with no precision uses up to 16 digits, which is ugly in Excel. Stick with 5-8 digits unless you're exporting something like tick volumes that need integers. - Test with a small bar count first. Export 50 bars, verify the file looks right, then scale up. Debugging a 100,000-row CSV is no fun.
- Close the file handle even on errors. MQL5 leaks file handles if you
returnwithoutFileClose(). Wrap your logic so the close always runs. - For multiple buffers, write them in one loop. Don't call
CopyBuffer()separately for each buffer unless you have to — it's slower and the data may not align perfectly if the indicator recalculates between calls.
One more thing: if you're exporting from the Strategy Tester, the file path is different. The tester uses a sandboxed data folder, so your CSV won't appear in the regular Files folder. Look for MQL5\Files\Tester inside the data folder instead. Many traders panic when their file "disappears" after a backtest — it's just in the tester subfolder.
Common Mistakes and Troubleshooting
Here are the failures I've seen most often, both in my own code and from traders who've asked me for help.
Error 4801 or "Failed to create indicator handle"
This means iCustom() couldn't find the indicator. Check the short name — it's not always the filename. Open the indicator's source in MetaEditor and look at the #property indicator_chart_window or the IndicatorSetString(INDICATOR_SHORTNAME, ...) line. Use that exact string. Also, custom indicators with input parameters require you to pass those parameters in iCustom() — the function signature must match the indicator's inputs exactly.
CopyBuffer returns -1
This usually means the buffer index is wrong or the indicator hasn't calculated data yet. Buffer indices start at 0, but some indicators have hidden buffers (used for internal calculations) that aren't displayed on the chart. Check the indicator's SetIndexBuffer() calls to see which index maps to which visual line. If you're running this on a fresh chart, give the terminal a second to calculate — place a Sleep(100) before calling CopyBuffer() if you're automating this in a loop.
The CSV has "NaN" or empty values
This happens when the indicator hasn't calculated values for all bars — usually at the start of the chart's history. CopyBuffer() fills uncalculated positions with the EMPTY_VALUE (which is DBL_MAX) or NaN. In your loop, check for empty values and either skip the row or write a blank. I prefer skipping the row entirely — a CSV with gaps is harder to analyze than one with fewer rows.
if(buffer[i] != EMPTY_VALUE)
{
FileWrite(fileHandle, TimeToString(barTime), DoubleToString(buffer[i], 8));
}
Excel shows dates as numbers
That's because TimeToString() returns a string like "2024.01.15 12:00", which Excel may misinterpret. Use TimeToString(barTime, TIME_DATE | TIME_MINUTES) to get a clean "2024.01.15 12:00" format. If Excel still mangles it, write the timestamp as a Unix epoch integer ((long)barTime) and convert it in Excel with a formula. That's more robust but less readable.
Performance Considerations for Large Exports
Exporting 500 bars is instant. Exporting 5 million bars of tick data is not. Here's what matters when you scale up.
First, FileWrite() is fast, but each call has overhead. For very large exports, consider building a string buffer and writing once at the end. Something like:
string data = "";
for(int i = 0; i < copied; i++)
{
data += TimeToString(iTime(_Symbol, InpTimeframe, i)) + "," + DoubleToString(buffer[i], 8) + "\n";
}
FileWriteString(fileHandle, data);
This trades memory for speed. A million rows of ~30 characters is about 30 MB of RAM — fine on any modern machine. If you're exporting millions of rows, this is the way to go.
Second, the CopyBuffer() call itself is the bottleneck. It copies all requested bars into memory at once. Requesting 5 million bars means a 40 MB array (8 bytes per double). That's fine, but be aware that CopyBuffer() with a huge count parameter can freeze the terminal briefly. If you see UI stuttering, request data in chunks of 100,000 bars and write each chunk to the file before pulling the next.
Finally, avoid exporting more history than you actually need. Most analysis doesn't require every tick since 2010. Be honest about your data requirements — it'll save you time and disk space.
Going Further: Automating the Export
The script approach is perfect for one-off exports. But if you find yourself exporting the same indicator every day, consider turning it into an Expert Advisor that runs on a timer and appends new bars to the CSV. The core logic is identical — you just move the CopyBuffer() and FileWrite() calls into OnTick() or OnTimer() and track the last exported bar with a static variable.
Another option: use the Strategy Tester to export data over historical periods. Run the script as an Expert Advisor in the tester, and it'll execute on every tick, writing each bar's values to a tester-specific CSV. This is handy for generating training data for machine learning models, though you'll need to handle the tester's file sandboxing.
If you're comfortable with Python, you can skip the CSV entirely and use the MetaTrader5 Python package to pull indicator data directly. But that's a different workflow — the CSV approach here has the advantage of working with any indicator, including ones you didn't write, and doesn't require a Python environment.
Summary and Next Steps
You now have a working script that exports any MQL5 indicator buffer to CSV. The key steps are:






