Problem Description
Solution
To send live market data from MetaTrader 4 (MQL4) to a Python application running on the same computer, the most efficient and low-latency method is using Named Pipes.
Named Pipes allow two distinct processes (MT4 and Python) to communicate via the computer's memory, avoiding the slowness of disk I/O (reading/writing text files) and the complexity of network sockets.
In this setup:
- Python acts as the Server (creates the pipe and waits for data).
- MT4 acts as the Client (connects to the pipe and pushes tick data).
1. MQL4 Expert Advisor (Sender)
This EA runs on the chart. On every tick, it formats the data into a CSV-style string and pushes it to the pipe.
//+------------------------------------------------------------------+
//| MT4_To_Python_Pipe.mq4|
//| Copyright 2023, Quantitative AI |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input string PipeName = "MT4_Tick_Data"; // Name of the pipe (must match Python)
//--- Global Variables
int pipeHandle = INVALID_HANDLE;
string fullPipeName = "\\\\.\\pipe\\" + PipeName;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Attempt to open the pipe immediately to check availability
OpenPipe();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Close the pipe handle when removing the EA
if(pipeHandle != INVALID_HANDLE)
{
FileClose(pipeHandle);
pipeHandle = INVALID_HANDLE;
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Ensure the pipe is open
if(pipeHandle == INVALID_HANDLE)
{
OpenPipe();
// If still invalid, exit and try next tick
if(pipeHandle == INVALID_HANDLE) return;
}
// 2. Prepare the data string
// Format: Symbol, Bid, Ask, Time (Unix Timestamp)
string data = Symbol() + "," +
DoubleToString(Bid, Digits) + "," +
DoubleToString(Ask, Digits) + "," +
IntegerToString(TimeCurrent()) + "\r\n"; // Newline is crucial for Python readline
// 3. Write to the pipe
// We use FileWriteString for text data
int bytesWritten = FileWriteString(pipeHandle, data);
// 4. Flush the buffer immediately to ensure low latency
FileFlush(pipeHandle);
// 5. Error Handling: If write failed, the pipe might be broken (Python script closed)
if(bytesWritten <= 0)
{
Print("Error writing to pipe. Error code: ", GetLastError());
FileClose(pipeHandle);
pipeHandle = INVALID_HANDLE; // Reset handle to try reconnecting next tick
}
}
//+------------------------------------------------------------------+
//| Helper: Open the Named Pipe |
//+------------------------------------------------------------------+
void OpenPipe()
{
// Reset error state
ResetLastError();
// Open the pipe as a binary file for writing
// Note: In MQL4, pipes are treated like files but with a specific path prefix
pipeHandle = FileOpen(fullPipeName, FILE_WRITE | FILE_BIN | FILE_ANSI);
if(pipeHandle == INVALID_HANDLE)
{
// Only print error occasionally to avoid spamming logs if Python isn't running
if(GetLastError() != 5004) // 5004 = File not found (Pipe not created yet)
{
Print("Waiting for Python Pipe Server... (Error: ", GetLastError(), ")");
}
}
else
{
Print("Successfully connected to Python Pipe: ", fullPipeName);
}
}
2. Python Application (Receiver)
You will need the pywin32 library to access Windows Named Pipes. Install it via pip:
pip install pywin32
import time
import win32pipe
import win32file
import pywintypes
PIPE_NAME = r'\\.\pipe\MT4_Tick_Data'
BUFFER_SIZE = 65536
def create_pipe_server():
print(f"Creating Named Pipe: {PIPE_NAME}")
print("Waiting for MT4 to connect...")
# Create the Named Pipe
pipe = win32pipe.CreateNamedPipe(
PIPE_NAME,
win32pipe.PIPE_ACCESS_INBOUND, # Read-only for Python
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
1, # Number of instances
BUFFER_SIZE, # Output buffer size
BUFFER_SIZE, # Input buffer size
0, # Default timeout
None # Security attributes
)
try:
# Block until the Client (MT4) connects
win32pipe.ConnectNamedPipe(pipe, None)
print("MT4 Connected! Receiving Data...")
while True:
try:
# Read data from the pipe
# win32file.ReadFile returns (error_code, data)
result, data = win32file.ReadFile(pipe, BUFFER_SIZE)
if result == 0:
# Decode bytes to string
message = data.decode('utf-8').strip()
if message:
# Handle multiple ticks arriving in one buffer read
ticks = message.split('\r\n')
for tick in ticks:
if tick:
process_tick_data(tick)
except pywintypes.error as e:
if e.winerror == 109: # ERROR_BROKEN_PIPE
print("MT4 disconnected.")
break
else:
print(f"Pipe error: {e}")
break
except Exception as e:
print(f"General error: {e}")
finally:
win32file.CloseHandle(pipe)
def process_tick_data(data_str):
"""
Parses the CSV string from MT4.
Expected format: Symbol,Bid,Ask,Time
"""
try:
parts = data_str.split(',')
if len(parts) == 4:
symbol = parts[0]
bid = float(parts[1])
ask = float(parts[2])
timestamp = int(parts[3])
print(f"[{timestamp}] {symbol} | Bid: {bid} | Ask: {ask}")
# Add your algo logic here
except ValueError:
print(f"Data parse error: {data_str}")
if __name__ == "__main__":
while True:
create_pipe_server()
time.sleep(1) # Wait before restarting server if connection breaks
How to Run
- Start Python First: Run the Python script. It will display "Waiting for MT4 to connect...".
- Start MT4: Open your MetaTrader 4 terminal.
- Attach EA: Compile the MQL4 code and drag the EA onto a chart (e.g., EURUSD).
- Connection:
- The Python console should immediately say "MT4 Connected!".
- You will see live ticks streaming in the Python console.
Key Implementation Details
FileFlushis Critical: In the MQL4 code,FileFlush(pipeHandle)is called immediately after writing. Without this, MT4 will buffer the data internally and only send it to Python when the buffer is full (causing massive latency).- Pipe Direction: In this example, the pipe is
PIPE_ACCESS_INBOUNDfor Python (Python reads, MT4 writes). If you need to send trading commands back to MT4 (e.g., "Open Buy"), you would need to create a second pipe or configure this one as Duplex. - Reconnect Logic: The Python script is wrapped in a
while Trueloop. If you restart the MT4 terminal or recompile the EA, the pipe connection breaks. The Python script catches theERROR_BROKEN_PIPEand automatically restarts the listener to wait for MT4 to come back.
Q&A: Live Market Data Integration
Q: Can I send data for multiple symbols simultaneously?
A: Yes. You can attach the EA to multiple charts (e.g., EURUSD and GBPUSD). However, since Named Pipes are typically 1-to-1, you have two options:
- Single Pipe: Modify the Python script to allow multiple instances (
nMaxInstancesinCreateNamedPipe) and use threading to handle connections. - Unique Pipes: Change the
PipeNameinput in the EA for each chart (e.g., "MT4_EURUSD", "MT4_GBPUSD") and have Python listen on multiple specific pipes.
Q: Is this fast enough for High-Frequency Trading (HFT)?
A: Named Pipes are extremely fast (microseconds latency on the local machine). The bottleneck will likely be the speed at which MT4's OnTick fires (which depends on the broker's data feed) and Python's processing speed, rather than the data transmission method itself.
Q: Why not use WebRequest in MQL4?
A: WebRequest uses HTTP, which creates a new TCP connection for every request. This introduces significant overhead and latency. Furthermore, WebRequest is synchronous (blocking), meaning if the server is slow, your MT4 terminal will freeze. Pipes are persistent and much lighter.
Q: Does this work on MT5?
A: Yes, the logic is nearly identical for MQL5, though MQL5 has more advanced event handling capabilities. The Python side remains exactly the same.