⬡ API · WITH CODE · 14 MIN READ

MetaTrader 5 API with Python: automate futures and Forex.

The bridge that connects Python's flexibility to the MT5 terminal. Pull market data, compute signals with your favorite libraries and send orders — all through code. With complete examples.

By the RoboTraderIA Team· updated May 2026· intermediate to advanced level

MQL5 (MetaTrader's native language) is powerful, but Python has an unbeatable ecosystem: pandas, numpy, scikit-learn, ready-made indicator libraries. The official MetaTrader5 library gives you the best of both worlds — you use Python to think and MT5 to execute. And the key point for you: it's the most accessible way to automate index futures and CFDs with Python.

How the bridge works: the MetaTrader5 library is not a standalone web API. It connects your Python script to an MT5 terminal installed and open on Windows. Python sends commands, MT5 executes them at the broker. Think of MT5 as the "engine" and Python as the "brain."

01Installation and prerequisites

You need: Windows, your broker's MT5 terminal installed and logged in, and Python 3.11+. Then:

pip install MetaTrader5 pandas

About the operating system: the library is officially Windows, because it depends on the MT5 terminal. You can run it via Wine on Linux or (most common for a 24/5 bot) on a Windows VPS. On Mac, via a virtual machine. For serious automation, a Windows VPS is the way.

02Connect to the terminal

With MT5 open and logged into your account (use a demo account!), initialize the connection:

# connect.py
import MetaTrader5 as mt5

# initialize the connection with the open MT5 terminal
if not mt5.initialize():
    print("Failed to connect:", mt5.last_error())
    quit()

# account info (confirm it's DEMO)
account = mt5.account_info()
print(f"Account: {account.login} | Balance: {account.balance} | Server: {account.server}")

# always close the connection when done
# mt5.shutdown()

If you need to log in via code (instead of manually in the terminal), pass credentials to initialize:

mt5.initialize(login=12345678, password="your_password", server="ServerName-Demo")

03Pull candles (including index futures)

Here's what connects to your indicators. Pull candles from any asset your broker offers — including index instruments like US500 or US30 if it gives access:

# data.py
import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime

mt5.initialize()

# symbol: e.g. "US500" (S&P 500 index), "US30" (Dow) or "EURUSD"
# the exact symbol name varies by broker — check the Market Watch
symbol = "US500"
timeframe = mt5.TIMEFRAME_M5   # M1, M5, M15, H1, etc.

# get the last 500 candles
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 500)
df = pd.DataFrame(rates)
df["time"] = pd.to_datetime(df["time"], unit="s")
print(df[["time","open","high","low","close","tick_volume"]].tail())

Tip on symbol names: the exact name (e.g. US500, SPX500, US30) varies between brokers. Open the Market Watch in MT5, right-click → "Show All", and copy the exact name that appears. Use mt5.symbol_select(symbol, True) to make sure it's enabled.

04Compute the signal with your indicators

This is where Python shines — use the indicator functions you've already learned in our guides. The DataFrame that came from MT5 plugs straight in:

# reuse the functions from the indicator guides
from indicators import compute_rsi, compute_macd   # your modules

df["rsi"] = compute_rsi(df["close"])
macd, signal, hist = compute_macd(df["close"])

# example of a combined signal
last = df.iloc[-1]
buy_signal = (last["rsi"] < 70) and (macd.iloc[-1] > signal.iloc[-1])

Integration with everything you've seen: the RSI, MACD and ATR functions from our guides work directly on this DataFrame. That's the beauty of using Python: the whole ecosystem is available for any market.

05Send an order

Sending an order in MT5 is more verbose than on Binance, because you build a detailed "request." Example of a market buy with a stop and target:

# executor.py
def buy(symbol, lot, stop_pts, target_pts):
    info = mt5.symbol_info_tick(symbol)
    price = info.ask
    point = mt5.symbol_info(symbol).point

    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": symbol,
        "volume": lot,
        "type": mt5.ORDER_TYPE_BUY,
        "price": price,
        "sl": price - stop_pts * point,   # stop loss
        "tp": price + target_pts * point, # take profit
        "deviation": 20,                   # tolerated slippage
        "magic": 123456,                  # your bot's id
        "type_filling": mt5.ORDER_FILLING_FOK,
    }
    result = mt5.order_send(request)
    if result.retcode != mt5.TRADE_RETCODE_DONE:
        print("Order error:", result.retcode, result.comment)
    return result

The detail that trips up beginners: the type_filling (execution mode) must be compatible with what the asset accepts — FOK, IOC or RETURN. If the order fails with a "filling" error, try the other modes. The magic number identifies your bot's orders (useful for managing only your own).

Already understand a bot's structure?

See the complete bot architecture guide — the same logic from our Binance tutorial applies to MT5.

See the architecture →

06Manage positions

For a complete bot, you need to query and close positions:

# query your bot's open positions (by magic)
positions = mt5.positions_get(symbol="US500")
for p in positions:
    if p.magic == 123456:
        print(f"Position {p.ticket}: profit {p.profit}")

# close a position
def close(position):
    tick = mt5.symbol_info_tick(position.symbol)
    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": position.symbol,
        "volume": position.volume,
        "type": mt5.ORDER_TYPE_SELL if position.type == 0 else mt5.ORDER_TYPE_BUY,
        "position": position.ticket,
        "price": tick.bid if position.type == 0 else tick.ask,
        "deviation": 20,
        "type_filling": mt5.ORDER_FILLING_FOK,
    }
    return mt5.order_send(request)

07The bot loop

Putting it all together, the skeleton of the MT5 bot:

import time

mt5.initialize()
try:
    while True:
        df = pull_candles("US500", mt5.TIMEFRAME_M5)
        signal = compute_signal(df)             # your strategy
        atr = compute_atr(df).iloc[-1]          # dynamic stop

        if signal == "BUY" and not has_position():
            lot = size_position(atr)            # risk management
            buy("US500", lot, stop_pts=2*atr, target_pts=4*atr)

        time.sleep(60)                       # check every minute
except Exception as e:
    print("Error:", e)
finally:
    mt5.shutdown()

Before real money: run on a demo account for weeks. Confirm the symbols, the type_filling and the lot calculation are right for your asset. A volume or point error can send an order of the wrong size. Test, test, test — and only then a live account, with a minimal lot.

08Frequently asked questions

Can you automate futures with Python?

Yes. The official MetaTrader5 library connects to the MT5 terminal, and if your broker offers MT5 with access to index futures or CFDs, you pull data and send orders via Python. It's the most accessible way to automate these markets with Python.

Do you need MT5 installed?

Yes. The library isn't a web API — it's a bridge to the MT5 terminal installed and open on Windows. Python sends commands, MT5 executes them at the broker.

Does it work on Linux or Mac?

Officially it's Windows. You can run it via Wine on Linux or on a Windows VPS (most common for a 24/5 bot). On Mac, via a virtual machine. For serious operation, a Windows VPS.

What's the difference from using MQL5 directly?

MQL5 runs inside MT5 and is more integrated for pure execution. Python via the API wins on ecosystem (pandas, ML, libraries). Many use Python for research/signals and MQL5 for critical execution. To start, Python is more accessible.

The symbol name doesn't work, why?

The exact name varies by broker (US500, SPX500, US30, etc.). Open the Market Watch in MT5, show all symbols and copy the exact name. Use mt5.symbol_select(name, True) to enable it.