🚀 AI One-Click Generation of mql4 Strategy Code

Mql4 to Python: Real-Time Data Exchange Using Named Pipes

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

Problem Description

How can I send live market data from my trading terminal to a separate Python application running on the same computer?

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:

  1. Python acts as the Server (creates the pipe and waits for data).
  2. 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

  1. Start Python First: Run the Python script. It will display "Waiting for MT4 to connect...".
  2. Start MT4: Open your MetaTrader 4 terminal.
  3. Attach EA: Compile the MQL4 code and drag the EA onto a chart (e.g., EURUSD).
  4. Connection:
    • The Python console should immediately say "MT4 Connected!".
    • You will see live ticks streaming in the Python console.

Key Implementation Details

  1. FileFlush is 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).
  2. Pipe Direction: In this example, the pipe is PIPE_ACCESS_INBOUND for 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.
  3. Reconnect Logic: The Python script is wrapped in a while True loop. If you restart the MT4 terminal or recompile the EA, the pipe connection breaks. The Python script catches the ERROR_BROKEN_PIPE and 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:

  1. Single Pipe: Modify the Python script to allow multiple instances (nMaxInstances in CreateNamedPipe) and use threading to handle connections.
  2. Unique Pipes: Change the PipeName input 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.