Why Automate File Uploads from MT5?
If you're running multiple trading robots or managing client accounts, you've probably needed to get trade logs or reports off your local machine and onto a remote server. Maybe you want to centralize performance data, feed a custom dashboard, or just keep backups off-site. Manually copying CSV files every day gets old fast — especially when you're juggling ten EAs across multiple charts.
MetaTrader 5 includes a built-in FTP client that most traders never touch. It's tucked away in the platform settings under Tools > Options > FTP, and the MQL5 documentation for the FTP functions is sparse — a single paragraph and one code example that barely works. But once you set it up properly, you can have your Expert Advisor upload files automatically after every trade, at market close, or on any schedule you define. This guide walks you through exactly that process — from enabling the FTP client in MT5 to writing the MQL5 code that pushes your files to a remote server.
I'll assume you know basic MQL5 — declaring variables, using OnTick() or OnTimer(), writing to files. If you're brand new to MQL5, you'll still be able to follow the setup steps and use the code examples as templates. Just don't expect to understand every line of the EA integration part.
One thing I've learned the hard way: MT5's FTP implementation is basic. It only supports plain FTP (no FTPS or SFTP). If your server requires encrypted transfers, you'll need a workaround — more on that later. Also, the FTP client runs in the terminal's context, not as a separate process, so large file uploads can briefly freeze the platform.
Prerequisites
Before you start, make sure you have the following:
- MetaTrader 5 build 2000 or newer — older builds may have slightly different FTP settings. Check Help > About. Build 2000 was released in 2019, so unless you're running something ancient, you're fine. I've tested this on build 4000+ and it works identically.
- An FTP server — this can be a shared hosting account, a VPS with FTP enabled, or a local FTP server on your network. You'll need the server address, port (usually 21), username, and password. Free options like FileZilla Server (for Windows) or the FTP service on a cheap VPS work fine.
- An MQL5 Expert Advisor or Script that you can modify. I'll provide a complete example, but you'll need to integrate it with your own file-writing logic.
- Windows firewall or antivirus may block outgoing FTP connections — you might need to allow MetaTrader 5 through. I've seen Norton and McAfee silently block FTP traffic without any notification.
One important note: MT5's FTP client only supports plain FTP (no FTPS or SFTP). If your server requires encrypted transfers, you'll need a workaround like uploading to a local folder and syncing with a separate tool like WinSCP or rsync. I've also seen people use a free FTP-to-SFTP bridge running on localhost, but that's overkill for most setups. If encryption is a hard requirement, consider using the WebRequest() function with HTTPS instead — but that's a different guide entirely.
Step-by-Step: Enabling and Using the MT5 FTP Client
Step 1: Configure FTP Server Settings in MT5
Open MetaTrader 5 and go to Tools > Options, or press Ctrl+O. Click the FTP tab — it's usually the last one on the right, after Email. You'll see a list of FTP profiles. By default it's empty, with just an "Add" button staring at you.
Click Add to create a new profile. Fill in the fields carefully — one typo in the server address and you'll waste an hour debugging. Here's what each field means:
| Field | Example Value | Notes |
|---|---|---|
| Server | ftp.yourserver.com | No "ftp://" prefix — just the hostname or IP address. I've seen people include the protocol and wonder why it fails. Also avoid trailing slashes. |
| Port | 21 | Standard FTP port. Change if your server uses a non-default port like 2100. Most hosting providers stick with 21. |
| Login | my_trader_user | Your FTP username. Some hosts use your full email address here — check your hosting control panel. |
| Password | •••••••• | Stored in the terminal's encrypted configuration, not in your MQL5 code. This is a security feature — use it. Don't hardcode passwords in your EA. |
| Remote Path | /tradelogs/ | Directory on the FTP server. Must already exist — MT5 won't create it for you. Include trailing slash. Relative paths like "tradelogs/" also work but I prefer absolute for clarity. |
| Passive Mode | Checked | Almost always required for modern networks and firewalls. If the test fails, try toggling this first. Active mode rarely works behind NAT. |
Leave the Connection Timeout at 30 seconds unless you're on a slow link or satellite internet. Click OK to save the profile. You'll see it listed with a name like "Profile1". You can rename it by double-clicking the name column — I usually rename mine to something descriptive like "VPS_Reports" or "Client_Dashboard". This name is critical because you'll reference it in your MQL5 code later.
Step 2: Test the FTP Connection Manually
Before writing any code, verify the connection works. In the same FTP tab, select your profile and click Test. MT5 will attempt to connect and list the remote directory. If it succeeds, you'll see a green "Connection established" message and a list of files/folders in the bottom pane.
If it fails, here's a troubleshooting checklist I've built from experience:
- Server address and port — try pinging the host from Command Prompt:
ping ftp.yourserver.com. If ping fails, the server might be down or blocked by your ISP. Also trytelnet ftp.yourserver.com 21to check if the port is reachable. - Firewall on your PC or server — port 21 outbound must be open. Temporarily disable Windows Firewall to test, then create an exception for MetaTrader 5. If you're on a corporate network, the IT admin might block FTP entirely.
- Passive mode — toggle it on/off and test again. Some older FTP servers require active mode. If you're behind a NAT router, passive mode is almost mandatory. If both fail, your ISP might be blocking FTP — try port 2121 or contact support.
- Remote path — the directory must exist on the server. If you set it to "/tradelogs/" but that folder doesn't exist, the test will fail. Create it first via FileZilla or your hosting control panel. Some shared hosts use a path like "/public_html/tradelogs/" — check your hosting setup.
- Username/password — double-check for typos. FTP passwords are case-sensitive. If your hosting uses cPanel, the FTP username might be "[email protected]" format. I've wasted hours on this one.
I've had cases where the test worked but the actual upload failed — usually because the remote path had a trailing space I didn't notice. Be meticulous. Also, MT5's test only checks connectivity and directory listing — it doesn't verify write permissions. To test write access, create a dummy file manually via FileZilla first.
Step 3: Write MQL5 Code to Upload a File
The key MQL5 function is FileCopy(), but it's not the one you think. FileCopy() normally copies files between local folders. For FTP uploads, you use the same FileCopy() with a special destination path format that tells MT5 to use the FTP client. Here's the syntax:
bool FileCopy(
const string src_name, // local file path in the Files folder
int common_flag, // 0 for local terminal folder, FILE_COMMON for common folder
const string dst_name, // destination path — use "ftp://" prefix
int reserve_flags // reserved, must be 0
);
The destination path must start with ftp:// followed by the FTP profile name (as it appears in the FTP tab), then the remote path. For example:
ftp://Profile1/tradelogs/daily_report.csvNotice the profile name is case-sensitive. If you renamed it to "VPS_Reports", use exactly that. Also, the remote path in the destination must match what you set in the FTP profile — if your profile has a remote path of "/tradelogs/", then the destination path should be ftp://Profile1/tradelogs/daily_report.csv. If you omit the remote path in the destination, MT5 uses the profile's default remote path.
Here's a complete MQL5 script that writes a simple trade report and uploads it. You can attach this as a Script to any chart, or call the UploadTradeReport() function from your EA.
//+------------------------------------------------------------------+
//| FTP_Upload_Example.mq5 |
//| (c) TradingBotMaker.com |
//+------------------------------------------------------------------+
#property copyright "TradingBotMaker.com"
#property version "1.00"
#property script_show_inputs
input string FTPProfile = "Profile1"; // FTP profile name in MT5
input string RemoteDir = "/tradelogs/"; // Remote directory (must exist)
input bool UploadOnClose = true; // Upload after each trade close
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
// Generate a local CSV report
string localFile = "TradeReport_" + TimeToString(TimeCurrent(), TIME_DATE) + ".csv";
string localPath = "Files\\" + localFile; // relative to Terminal\Files folder
if(!WriteReportToFile(localPath))
{
Print("Failed to write local report file: ", localPath);
return;
}
// Upload via FTP
string remotePath = "ftp://" + FTPProfile + RemoteDir + localFile;
if(FileCopy(localPath, 0, remotePath, 0))
{
Print("Upload successful: ", remotePath);
}
else
{
Print("Upload failed. Error: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Write a simple CSV report |
//+------------------------------------------------------------------+
bool WriteReportToFile(string filePath)
{
int handle = FileOpen(filePath, FILE_WRITE|FILE_CSV|FILE_ANSI, ",");
if(handle == INVALID_HANDLE)
return false;
// Header
FileWrite(handle, "Ticket", "Symbol", "Type", "Volume", "OpenPrice", "ClosePrice", "Profit");
// Iterate through closed positions for today
HistorySelect(TimeCurrent() - 86400, TimeCurrent()); // last 24 hours
int total = HistoryDealsTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket > 0)
{
string symbol = HistoryDealGetString(ticket, DEAL_SYMBOL);
ENUM_DEAL_TYPE type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE);
double volume = HistoryDealGetDouble(ticket, DEAL_VOLUME);
double openPrice = HistoryDealGetDouble(ticket, DEAL_PRICE);
double closePrice = HistoryDealGetDouble(ticket, DEAL_PRICE); // For simplicity
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
FileWrite(handle, ticket, symbol, type, volume, openPrice, closePrice,





