MQL5 is fast and deeply integrated with MT5. But it's not built for data analysis. When we wanted to calculate rolling 20-day volatility across three years of tick data for six currency pairs, the MQL5 version was 200+ lines of nested loops and manual array management. The Pandas version was 3 lines. That's when we started building hybrid systems.
MetaQuotes ships an official Python library called MetaTrader5 (install via pip install MetaTrader5) that lets Python scripts communicate directly with the MT5 terminal through local IPC. Pull real-time quotes, query account info, send orders, access historical data — all from Python. Then use Pandas, NumPy, Scikit-learn, or any other library to process it.
You don't have to choose between Python and MQL5. We use both, and most of our research pipeline runs on Python while every production EA ships as native MQL5.
Two architecture patterns for hybrid systems
Pattern 1: Python controls everything. Python script connects to MT5, pulls data, analyzes with Pandas, generates signals, and sends orders through the MetaTrader5 library. All logic lives in Python.
We use Pattern 1 for our internal research pipeline — scanning 28 forex pairs for correlation shifts, regime changes, and statistical anomalies. It's not customer-facing; it feeds insights into our EA development process.
- Pros: single codebase, easy debugging, full access to Python's data science ecosystem
- Cons: Python script must run continuously, order execution is slower than native EA
Pattern 2: MQL5 EA executes, Python thinks. The MQL5 EA runs on the chart for real-time monitoring and order execution. Python runs in the background doing heavy computation. They communicate via files or sockets.
One of our EAs uses a Python backend to calculate cross-pair correlation matrices every 4 hours across 15 pairs. The computation takes about 8 seconds in Python with Pandas — doable in MQL5, but it would have taken us weeks to write and debug instead of an afternoon.
- Pros: execution speed of native MQL5, analytical power of Python
- Cons: more complex architecture, two processes to manage, more failure points
For most traders, Pattern 1 is enough unless you need tick-level response speed. We only use Pattern 2 when an EA needs sub-10ms order execution AND complex multi-pair analysis that would be painful in pure MQL5.
Setup and first connection (5 minutes)
pip install MetaTrader5 pandas numpy matplotlib
We tested this on Python 3.11 and 3.12 with MetaTrader5 library version 5.0.4424. Older Python versions (3.7 and below) may have compatibility issues with the library's binary components.
Basic connection test:
import MetaTrader5 as mt5
# Initialize connection to the running MT5 terminal
if not mt5.initialize():
print("MT5 initialization failed:", mt5.last_error())
quit()
print("Connected! MT5 version:", mt5.version())
account = mt5.account_info()
print("Balance:", account.balance, "Leverage:", account.leverage)
mt5.shutdown()
MT5 must be running and logged in on the same machine. The library communicates via local IPC — no network involved. Windows only; Mac/Linux requires Wine or a VM.
One gotcha we hit early: if you have multiple MT5 terminals installed (we run two — one for live, one for testing), mt5.initialize() connects to the last one that was active. Use mt5.initialize(path="C:/path/to/terminal64.exe") to target a specific installation.
Pulling market data into Pandas
This is where the hybrid approach really pays off. Getting data into a Pandas DataFrame unlocks groupby, resample, rolling windows, merge, correlation matrices — operations that would take hundreds of MQL5 lines.
import MetaTrader5 as mt5
import pandas as pd
mt5.initialize()
# Pull 500 daily bars for EURUSD
rates = mt5.copy_rates_from_pos("EURUSD", mt5.TIMEFRAME_D1, 0, 500)
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df.set_index('time', inplace=True)
# Three lines that would be 50+ in MQL5
df['ma20'] = df['close'].rolling(20).mean()
df['returns'] = df['close'].pct_change()
df['volatility'] = df['returns'].rolling(20).std() * (252 ** 0.5)
# Find the 10 most volatile trading days
print(df.nlargest(10, 'volatility')[['close', 'volatility']])
mt5.shutdown()
When we built our correlation scanner, this pattern — pull data, convert to DataFrame, compute — cut development time from weeks to days. The scanner checks correlation coefficients between 15 pairs on three timeframes, and the entire computation runs in under 2 seconds on a mid-range VPS. Writing the equivalent in MQL5 would have been roughly 800 lines of array management code.
For more advanced backtesting workflows, libraries like vectorbt and backtrader can work directly with DataFrames pulled this way.
Placing orders from Python (with latency reality check)
import MetaTrader5 as mt5
mt5.initialize()
symbol = "EURUSD"
tick = mt5.symbol_info_tick(symbol)
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": 0.1,
"type": mt5.ORDER_TYPE_BUY,
"price": tick.ask,
"sl": tick.ask - 0.0050,
"tp": tick.ask + 0.0100,
"deviation": 20, # slippage tolerance in points
"magic": 123456, # unique ID to tag orders from this script
"comment": "Python signal",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
result = mt5.order_send(request)
if result.retcode == mt5.TRADE_RETCODE_DONE:
print("Order placed. Ticket:", result.order)
else:
print("Order failed. Code:", result.retcode)
mt5.shutdown()
Test on demo before live. The type_filling parameter varies by broker — wrong value means instant rejection with error code 10030. Check with your broker which filling modes they support. We've seen ICMarkets accept ORDER_FILLING_FOK, while Pepperstone requires ORDER_FILLING_IOC on the same instrument.
Latency benchmarks from our testing: We benchmarked Python order execution on our London VPS (Contabo, 4 cores, 8GB RAM) by sending 200 market orders on a demo account across different times of day. Average round-trip from order_send() call to confirmed fill: 45ms. Compare that to native MQL5 EA execution on the same VPS: 2ms. The 43ms difference is the IPC overhead between Python and the MT5 terminal.
For H1 or H4 strategies, 45ms is invisible. For M1, it's still fine — price moves less than 0.1 pip in that window during normal conditions. For tick-level scalping, it matters. That's why all our production EAs ship as native MQL5 — we use Python for the research, not the execution.
When to use Python vs pure MQL5
| Task | Best tool | Why |
|---|---|---|
| Data analysis and research | Python + Pandas | Rolling stats, correlation, groupby in 1-3 lines vs 50+ in MQL5 |
| Machine learning model training | Python + scikit-learn | MQL5 has no native ML libraries; ONNX import is possible but training must happen externally |
| Custom backtesting | Python + vectorbt or backtrader | Portfolio-level backtests, walk-forward analysis, Monte Carlo simulation |
| Data visualization | Python + matplotlib | Equity curves, drawdown charts, heatmaps in a few lines |
| Multi-pair correlation analysis | Python + Pandas + NumPy | Correlation matrices across 28 pairs take 3 seconds, not 3 hours of coding |
| Production EA execution | MQL5 | Most stable, lowest latency (2ms), managed by MT5 process |
| Tick-by-tick processing | MQL5 | OnTick() fires with zero overhead; Python polling can't match this |
| 24/7 unattended operation on VPS | MQL5 | MT5 auto-restarts EAs after crash; Python needs external supervision |
| Simple indicator-based strategies | MQL5 | If it's just MA crossover + RSI filter, Python adds complexity for no gain |
Our rule of thumb: if the strategy logic fits in under 300 lines of MQL5 and doesn't need external data, keep it pure MQL5. If you're doing anything with multiple DataFrames, statistical tests, or ML — Python saves you days.
Hybrid communication: file-based signal passing
The simplest reliable method for Pattern 2. Python writes signals to a JSON file, MQL5 EA reads it on a timer.
We use this approach in production. It's unglamorous but it works — we've had a file-based hybrid system running for over 8 months without a single missed signal.
Python side — write the signal:
import json, time
signal = {
"symbol": "EURUSD",
"direction": "BUY",
"lot": 0.1,
"sl": 1.0850,
"tp": 1.0950,
"timestamp": time.time()
}
# Write to MT5's common files directory for cross-process access
with open("C:/Users/[user]/AppData/Roaming/MetaQuotes/Terminal/Common/Files/trade_signal.json", "w") as f:
json.dump(signal, f)
MQL5 side — read in OnTimer():
void OnTimer()
{
// Read from the Common files directory
int file = FileOpen("trade_signal.json", FILE_READ|FILE_COMMON|FILE_TXT);
if(file != INVALID_HANDLE)
{
string content = "";
while(!FileIsEnding(file))
content += FileReadString(file);
FileClose(file);
// Parse JSON fields and execute trade
// After execution, delete or rename the file to avoid re-processing
}
}
Important detail we learned the hard way: use FILE_COMMON so both Python and MQL5 access the same directory (Terminal/Common/Files/). Without it, MQL5 looks in the terminal-specific data folder, which is a different path from what Python writes to. This mismatch silently fails — no errors, just no signals getting through. We spent two days debugging this on our first hybrid build.
Latency with this approach: 200ms to 1 second depending on the OnTimer() interval. Fine for H1+ strategies. For lower latency, socket communication (via localhost TCP) brings it down to 5-15ms, but the code complexity doubles and you need to handle connection drops, reconnection, and message framing.
Limitations and gotchas from production use
Python scripts can't attach to MT5 charts as EAs. They run as external programs that need separate process management. We use a simple while True loop with try/except for our research scripts, and Windows Task Scheduler for anything that needs to survive reboots.
MT5 must be running. The Python library uses local IPC — no MT5 terminal means no connection. If MT5 crashes or restarts for an update, the Python script loses its connection. We check mt5.terminal_info() every 60 seconds and call mt5.initialize() again if it returns None.
Latency is higher than native EA. As we measured above: ~45ms for Python vs ~2ms for native MQL5 on the same VPS. Acceptable for anything above M5 timeframe. Not ideal for scalping strategies.
Windows only. The MetaTrader5 Python library doesn't support Mac or Linux natively. We've seen people run it under Wine on Linux, but it's fragile and we don't recommend it for anything beyond experimentation.
Python processes need a watchdog. Unlike EAs which MT5 manages and auto-restarts, a Python script that crashes stays crashed. For our production hybrid systems, we run the Python component as a Windows Service using NSSM with automatic restart on failure. It's solved every reliability issue we've had.
Memory leaks on long-running scripts. We noticed that copy_rates_from_pos() called in a tight loop for hours can gradually increase memory usage. Our fix: call mt5.shutdown() and mt5.initialize() once every 6 hours to reset the connection. Ugly but effective.
Essential Python libraries for trading
| Library | Purpose | Our take |
|---|---|---|
| pandas | Data analysis, time series, statistics | Core of everything we do. Start here. |
| numpy | Numerical computation, vectorized operations | Used implicitly by Pandas; explicit use for matrix math |
| matplotlib | Charts: price, indicators, equity curves | Good enough for research; not pretty for reports |
| scikit-learn | Machine learning: random forest, SVM, clustering | We use it for feature selection; model training before ONNX export |
| TA-Lib | 150+ technical indicators, one function per indicator | Saves time but installation on Windows can be tricky (needs C library) |
| statsmodels | Cointegration, GARCH, time series decomposition | Essential for pairs trading research |
| vectorbt | Vectorized backtesting, portfolio simulation | Fast alternative to MT5's strategy tester for Python-based strategies |
Start with pandas and numpy. They cover 80% of what you'll need. Add others as specific needs arise.
FAQ
Can Python scripts and MQL5 EAs run simultaneously?
Yes. They're separate processes and don't conflict. But if both place orders on the same pair, you'll get duplicate positions. Use magic numbers to distinguish orders from different sources. We assign magic number ranges: 100000-199999 for Python scripts, 200000+ for native EAs.
How do I backtest a Python strategy?
MT5's strategy tester doesn't support Python scripts. Pull historical data from MT5 into Python, then write your own backtest logic or use frameworks like backtrader or vectorbt. More work upfront, but Python backtesting is far more flexible — we can run walk-forward optimization, Monte Carlo analysis, and portfolio-level tests that MT5's tester simply can't do.
Does the MetaTrader5 library work with MT4?
No. MT4 requires different solutions: DLL calls or third-party bridge tools. The official MetaTrader5 Python library is MT5 only.
Is Python fast enough for live trading?
For strategies on H1 or higher timeframes, absolutely — our 45ms measurement is irrelevant at that scale. For M1, usually fine. For tick-level high frequency, Python's latency may not be competitive with pure MQL5 (see our benchmark numbers above). Match the tool to the strategy's timeframe. Browse the FXTool marketplace for EAs built in pure MQL5 for maximum execution speed, or explore our AI-assisted coding guide for using Python in the development process.
What Python version should I use?
We run Python 3.11 and 3.12 without issues. The MetaTrader5 library requires Python 3.6+ and a 64-bit installation. Check the official library page for the latest compatibility notes.
About the author: The FXTool team builds and tests MetaTrader trading tools daily. We run every EA we sell on live accounts and publish the results. This guide reflects what we've learned from building 50+ EAs and working with thousands of retail traders.
Forex trading involves significant risk and may result in total loss of capital. This article is for educational purposes only and is not investment advice. Understand the risks and consider your financial situation before trading.