Short answer: The official MT5 Python package, MetaTrader5 5.0.6231 on PyPI (27 September 2026), is a 32-function connector to a MetaTrader 5 terminal on the same Windows machine, and PyPI lists Windows x64 wheels only. Read the symbol's execution and filling modes before order_send, because a filling type the symbol does not allow returns retcode 10030. Results are polled, not pushed, so check positions_get and history_deals_get before you resend anything.
Work out lots, units and pip values with your own figures: Forex position size calculator and Pip calculator.
The package was installed and inspected on Windows on 5 November 2026. No MetaTrader 5 terminal or broker account was available, so nothing here placed an order: live calls are labeled as not run, and the logic ran on synthetic data. Use a demo account first; this guide gives no strategy or settings.
For the wider map of MT5 routes, see MT5 API; for chart alerts, see TradingView to MT5.
Install MetaTrader 5 Python: what the MT5 Python API really is
Run pip install MetaTrader5 on Windows with a terminal installed: the package only talks to that terminal, by what the MQL5 reference calls interprocessor communication.
| Item | Value | Where it comes from |
|---|---|---|
| Version | 5.0.6231, uploaded 27 September 2026; 11 of PyPI's 39 releases came in 2026 | PyPI |
| Wheels | 9, all win_amd64, CPython 3.6 to 3.14; no source distribution | PyPI |
| Functions | 32 in the reference; the module exposes the same 32 lowercase names | MQL5 reference, dir() |
initialize() starts the terminal if needed, with a default 60,000 ms timeout, per the reference. With no terminal on the test machine it returned False at once with (-10003, 'IPC initialize failed, MetaTrader 5 x64 not found'). Before that call, positions_get() returned None with (-10004, 'No IPC connection'), and version() returned (0, 0, ''), not the None its reference page gives for errors, so test its contents rather than its truth value.
Two terminal settings can stop orders when the code is right: the automated trading button and the option Disable automatic trading through the external Python API. MetaTrader 5's help says Python trading is disabled by default and must be enabled there (read 5 November 2026). terminal_info() reports them as trade_allowed and tradeapi_disabled.
MT5 unsupported filling mode: why order_send returns 10030
Retcode 10030, Invalid order filling type, means the request's type_filling is not allowed for that symbol, and the reference's own order_send example can cause it.
That example hard-codes ORDER_FILLING_RETURN, which Order Properties disables under Market Execution; the reference's symbol_info sample shows an Instant Execution symbol (trade_exemode=1), where Return is allowed.
trade_exemode | FOK | IOC | Return |
|---|---|---|---|
| Request (0), Instant (1) | Yes, whatever the symbol says | Yes, whatever the symbol says | Always |
| Market (2) | If set in the symbol | If set in the symbol | Disabled |
| Exchange (3) | If set in the symbol | If set in the symbol | Always |
Read filling_mode as bit flags: 1 for FOK, 2 for IOC. Package 5.0.6231 has no SYMBOL_FILLING_FOK or SYMBOL_FILLING_IOC names (hasattr returned False), so snippets that use them raise AttributeError. Do not confuse the flags with the order values: ORDER_FILLING_FOK is 0 and ORDER_FILLING_IOC is 1.
import MetaTrader5 as mt5 # 5.0.6231; this test uses only its constants
FOK_FLAG, IOC_FLAG = 1, 2 # SYMBOL_FILLING_* values from the MQL5 reference
def pick_filling(exemode, flags):
"""Filling type for a market order, following the Order Properties table."""
if flags & FOK_FLAG: # FOK before IOC is a choice, not advice
return mt5.ORDER_FILLING_FOK
if flags & IOC_FLAG:
return mt5.ORDER_FILLING_IOC
if exemode == mt5.SYMBOL_TRADE_EXECUTION_MARKET:
raise ValueError("Market Execution with no FOK or IOC flag: ask the broker")
return mt5.ORDER_FILLING_RETURN
names = {mt5.ORDER_FILLING_FOK: "FOK", mt5.ORDER_FILLING_IOC: "IOC", mt5.ORDER_FILLING_RETURN: "RETURN"}
for mode in ("REQUEST", "INSTANT", "MARKET", "EXCHANGE"):
row = []
for flags in (0, 1, 2, 3):
try:
row.append(names[pick_filling(getattr(mt5, "SYMBOL_TRADE_EXECUTION_" + mode), flags)])
except ValueError:
row.append("error")
print(f"{mode:9}", *row)
# Live use, demo first; not run here:
# info = mt5.symbol_info(symbol) # None: read last_error() and stop
# request["type_filling"] = pick_filling(info.trade_exemode, info.filling_mode)
# check = mt5.order_check(request) # retcode 0 passes in the docs' sample
# result = mt5.order_send(request) # None: read last_error() and state before any resendOutput, using the package's constants:
REQUEST RETURN FOK IOC FOK
INSTANT RETURN FOK IOC FOK
MARKET error FOK IOC FOK
EXCHANGE RETURN FOK IOC FOKHypothetical example. A symbol reports trade_exemode=2 and filling_mode=2. By the documented rule, a request copied from the reference gets 10030, and pick_filling returns IOC. If only 0.04 of a 0.10 lot order can fill, IOC fills 0.04 and cancels the rest.
The documents conflict here. The order_check page says Return applies only to symbols with Market or Exchange execution, while Order Properties and Symbol Properties disable it under Market Execution. The function follows those two pages; a demo account settles it for your broker. For pending orders, the reference says to use Return whatever the execution mode.
MT5 Python return codes and what to do with each
Check result.retcode after every send: an object back from order_send says nothing about success.
| Code | Official description | What to do |
|---|---|---|
| 10009 | Request completed | Confirm fills with history_deals_get; do not resend |
| 10004 | Requote | Fresh price, limited tries |
| 10006 | Request rejected | Read comment and retcode_external; no blind resend |
| 10014 | Invalid volume in the request | Check volume_min, volume_step, volume_max |
| 10016 | Invalid stops in the request | Check trade_stops_level and the side of the price |
| 10018 | Market is closed | Your rules decide |
| 10019 | There is not enough money to complete the request | Use order_check; do not resend |
| 10027 | Autotrading disabled by client terminal | Fix the two settings above |
| 10030 | Invalid order filling type | Use pick_filling |
order_send can also return None: here it did, with -10004 without a terminal and -2 for a non-integer action. Call last_error() and read state before any resend. The package's undocumented helpers retry only on 10004 and 10021 (no quotes), up to 10 times.
Orders are polled: reconcile with positions_get and history_deals_get
The package has no event callback, so a bot must read terminal state after each send and restart.
The OrderSend reference warns that a successful reply does not mean execution, since trades can happen after the response; an Expert Advisor hears of them in OnTradeTransaction, which has no Python counterpart.
from types import SimpleNamespace as NS
def order_state(api, ticket, wanted):
"""Read one order's state; never resend while the answer is unknown."""
if not ticket: # no order ticket
return "unknown: no ticket, do not resend"
replies = []
for call in (api.orders_get, api.history_orders_get, api.history_deals_get):
reply = call(ticket=ticket)
if reply is None and api.last_error()[0] < 0: # check before the next call
return "unknown: do not resend"
replies.append(reply or ())
working, ended, deals = replies
filled = round(sum(d.volume for d in deals), 8)
if filled >= wanted:
return "filled"
if working:
return f"working, {filled} filled"
return f"ended, {filled} of {wanted} filled" if ended else "unknown: read again later"
class Terminal: # synthetic; every call sets the error
def __init__(self, deals=(), working=(), ended=(), broken=None):
self.data, self.broken = {"deals": deals, "working": working, "ended": ended}, broken
def _reply(self, key):
self.err = (-10004, "No IPC connection") if key == self.broken else (1, "Success")
return None if key == self.broken else self.data[key]
history_deals_get = lambda self, ticket: self._reply("deals")
orders_get = lambda self, ticket: self._reply("working")
history_orders_get = lambda self, ticket: self._reply("ended")
last_error = lambda self: self.err
part, order = NS(volume=0.04), (NS(ticket=555),)
print(order_state(Terminal(deals=(part, part, NS(volume=0.02)), ended=order), 555, 0.1))
print(order_state(Terminal(deals=(part,), working=order), 555, 0.1))
print(order_state(Terminal(deals=(part,), ended=order), 555, 0.1))
print(order_state(Terminal(ended=order, broken="deals"), 555, 0.1))
print(order_state(Terminal(), 555, 0.1))
print(order_state(Terminal(), 0, 0.1))Output:
filled
working, 0.04 filled
ended, 0.04 of 0.1 filled
unknown: do not resend
unknown: read again later
unknown: no ticket, do not resendNone means an error, per the reference, so read last_error() before the next call can replace it. Deals are read last; read first, a fill between two calls gave "ended, 0 of 0.1 filled" in a mock, inviting a resend.
Bookkeeping depends on account_info().margin_mode, which your broker sets: 0 netting, 1 exchange, 2 hedging. Under hedging, which allows several positions per symbol, the MqlTradeRequest page (opens in a new tab) says to put the position ticket in a closing request. Limits belong in trading bot risk management, and the general order state model is in broker API integration.
MT5 Python lib timestamp and time zone: UTC or server time?
Build request datetimes in UTC as the reference says, then measure your broker's stamps, because the documents do not settle it.
The pages for copy_rates_from and copy_ticks_from say MetaTrader 5 stores tick and bar open times in UTC and that a naive datetime uses your local zone. The MQL5 reference describes TimeCurrent() as server time formed on the trade server. Which clock your broker's stamps follow could not be tested here.
def clock_gap(tick_time, now_utc):
"""Whole hours between a tick stamp and real UTC; None if the tick looks stale."""
seconds = tick_time - now_utc
hours = round(seconds / 3600)
return hours if abs(hours) <= 14 and abs(seconds - hours * 3600) <= 120 else None
now = 1_793_880_000 # 5 November 2026 12:00 UTC, synthetic
print(clock_gap(now - 3, now)) # stamps are real UTC
print(clock_gap(now + 2 * 3600 - 3, now)) # stamps run two hours ahead
print(clock_gap(now - 25 * 60, now)) # last tick is 25 minutes old
print(clock_gap(now - 3600 - 30, now)) # an hour-old tick reads as -1
print(clock_gap(0, now)) # zero stamp
# Live, on a demo while quoting; not run here:
# tick = mt5.symbol_info_tick(symbol) # None: read last_error()
# clock_gap(tick.time, time.time())Output on synthetic stamps:
0
2
None
-1
NoneRun it on a demo just after the tick time changes, since a stale tick can land on a whole hour; repeat it around daylight-saving changes. MetaTrader 5's MCP capabilities page (opens in a new tab) also lists a get_time_information tool for UTC, server time and the offset. A backtest loaded from these bars inherits the offset; backtesting Python libraries covers what comes next.
MetaTrader 5 Python on Linux and on a VPS
Windows is the documented answer: PyPI has no Linux or macOS wheel, and MetaQuotes' Linux page covers the terminal under Wine and says nothing about Python.
On 5 November 2026, pip download MetaTrader5 --no-deps --only-binary=:all: --python-version 3.12 with --platform manylinux2014_x86_64, and with macosx_11_0_arm64, ended in No matching distribution found.
| Setup | Status |
|---|---|
| Windows desktop or VPS, terminal logged in | Matches the wheels; not run here (no terminal or account) |
| Terminal under Wine on Linux | Documented by MetaQuotes for Ubuntu, Debian, Linux Mint and Fedora; terminal only |
| Windows Python inside Wine | Not documented, not tested here; treat as unsupported |
| Linux application calling a Windows worker | Ordinary design; you own the API and its security |
| MetaQuotes' built-in virtual hosting | Migration page covers Expert Advisors and indicators; scripts are not moved, DLL calls are forbidden, and Python is not mentioned |
On your own Windows VPS, keep the terminal logged in, call initialize() again after a disconnect and reconcile before sending. The module has no client object, so plan on one terminal per process, with path choosing it; that is an inference, not a documented limit. Keep the password out of source and logs.
Where the MT5 Python documents and the package differ
Two more differences, besides the filling flags:
| Topic | Documents | Package |
|---|---|---|
| IPC error code | RES_E_INTERNAL_FAIL_CONNECT listed as -10003 | Constant is -10004, the value seen in the test above |
| Helpers | Not among the 32 functions | Buy, Sell, Close in __init__.py, deviation 10, no type_filling |
Symbol Properties says a request without a filling type gets Return, so by the documented rule those helpers fail with 10030 on Market Execution symbols; that was read from source, not run.
Leveraged forex carries high risk, and automated orders can lose money quickly. Rules depend on your country, account types and API access vary by broker, and no broker is recommended, ranked or linked with a referral here. For readers in Turkey, separate rules apply to leveraged FX with foreign firms: the Capital Markets Board (SPK) said on 23 February 2023 (opens in a new tab) that residents may trade leveraged FX only through firms it has authorized (Law No. 6362 (opens in a new tab) Articles 99(4) and 109(2)). For integration work in general, see algorithmic trading; naming MetaTrader 5 here does not mean Fincode integrates it or has built on it.
Frequently asked questions
Does the MetaTrader5 Python package work on Linux or Mac?
Not officially: PyPI lists only Windows x64 wheels for 5.0.6231, and pip found nothing for Linux or macOS on 5 November 2026. MetaQuotes documents only the terminal under Wine.
Why does mt5.initialize() return False?
Call mt5.last_error() to see why. With no terminal installed it returned (-10003, 'IPC initialize failed, MetaTrader 5 x64 not found') in a test on 5 November 2026; the documented codes also include authorization failures.
How do I fix unsupported filling mode in MT5 Python?
Read symbol_info(symbol).filling_mode and trade_exemode, then set type_filling from them: FOK if flag 1 is set, IOC if flag 2 is set, and Return only where the execution mode allows it.
Are MT5 Python timestamps in UTC?
The reference says so, but it could not be tested here without a terminal. Measure it on a demo by comparing a fresh tick's time with real UTC.
Sources
- PyPI: MetaTrader5 5.0.6231, files and metadata (opens in a new tab)
- MQL5 Reference: Python Integration (function list, interprocessor communication) (opens in a new tab)
- MQL5 Reference: initialize (terminal launch, 60,000 ms timeout) (opens in a new tab)
- MQL5 Reference: order_send (example with ORDER_FILLING_RETURN) (opens in a new tab)
- MQL5 Reference: order_check (filling type table, retcode sample) (opens in a new tab)
- MQL5 Reference: copy_rates_from (UTC note, timeframes) (opens in a new tab)
- MQL5 Reference: Order Properties (filling policies by execution mode) (opens in a new tab)
- MQL5 Reference: Symbol Properties (SYMBOL_FILLING flags, default filling) (opens in a new tab)
- MQL5 Reference: Trade Server Return Codes (opens in a new tab)
- MQL5 Reference: OrderSend (a successful reply is not an execution) (opens in a new tab)
- MetaTrader 5 Help: How to install the platform on Linux (opens in a new tab)
- MetaTrader 5 Help: Platform settings (external Python API option) (opens in a new tab)
- MetaTrader 5 Help: Virtual hosting, migration features (opens in a new tab)
- SPK: Press notice on unauthorized leveraged trading, 23 February 2023 (Turkish) (opens in a new tab)
- Capital Markets Law No. 6362, Articles 99 and 109 (Turkish, consolidated text) (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.