MENU
  • Blog Home
  • Categories
    • DMM & Browser Gaming
    • Business in Japan
    • Development
    • Forex Trading
    • Security
    • Windows Server/VPS Guides
  • Winserver VPS Japan
Windows VPS starting from $9.90
Providing reliable Windows VPS services from Japan since 2004.
Winserver Blog
  • Blog Home
  • Categories
    • DMM & Browser Gaming
    • Business in Japan
    • Development
    • Forex Trading
    • Security
    • Windows Server/VPS Guides
  • Winserver VPS Japan
Winserver Blog
  • Blog Home
  • Categories
    • DMM & Browser Gaming
    • Business in Japan
    • Development
    • Forex Trading
    • Security
    • Windows Server/VPS Guides
  • Winserver VPS Japan
  1. Home
  2. Development
  3. How to Run a Python Trading Bot with MetaTrader 5 on a Windows VPS (2026)

How to Run a Python Trading Bot with MetaTrader 5 on a Windows VPS (2026)

2026 11/10
Development Forex Trading
2026-11-10
Python trading bot connected to MetaTrader 5 running on a Windows VPS

Last updated: November 6, 2026

Python is one of the most popular languages for algorithmic trading, and MetaTrader 5 is one of the most widely used trading platforms. MetaQuotes, the company behind MetaTrader, publishes an official Python package that lets your scripts read live prices and place trades through MT5.

The package only runs on Windows, and your bot only works while the MT5 terminal is running.

This guide shows you how to build a simple Python trading bot with the official MetaTrader5 package and run it 24/7 on a Windows VPS, with copy-paste code and commands.

Quick answer

  • What you need: a Windows computer or VPS, MetaTrader 5 from your broker, and 64-bit Python with the MetaTrader5 package (pip install MetaTrader5).
  • Why Windows: the official package is Windows-only. On Linux or macOS, you need workarounds such as Wine.
  • Why a VPS: the MT5 terminal must stay open for your bot to trade. A VPS keeps both running 24/7, even when your own computer is off.
  • Before going live: test on a demo account. The bot in this guide refuses to trade on a real account until you change one setting.
TABLE OF CONTENTS

How the MetaTrader5 Python Package Works

The MetaTrader5 package doesn’t connect to your broker directly. Instead, it talks to the MetaTrader 5 terminal running on the same Windows machine, and the terminal handles the connection to the broker. Your Python script asks the terminal for prices, account details and positions, and sends orders through it.

That has two practical consequences:

  • It needs Windows. The package is only published for 64-bit Windows. On Linux, community projects run MT5 under Wine and bridge it to Python, but that adds another layer that can break.
  • The terminal must stay open and logged in. If your PC sleeps, restarts or loses its connection, the bot stops trading.

Windows VPS vs your own PC vs Linux

Your own Windows PC Windows VPS Linux server
Official MetaTrader5 package Yes Yes No (Wine workaround)
Runs 24/7 Only if the PC never sleeps or restarts Yes Yes
Affected by your home internet or power Yes No No
Setup difficulty Easy Easy (same as a PC, over Remote Desktop) Harder
Best for Writing and testing your bot Running your bot around the clock Teams already experienced with Wine

A good workflow is to develop and test on your own PC, then move the bot to a VPS when it’s ready to run continuously.

Does server location matter? For a bot like this one, which checks prices every few minutes, a few extra milliseconds make no real difference. If you trade very short-term, choose a VPS close to your broker’s trade server. From our data center in Osaka, we measured about 9 ms to Tokyo, which suits brokers with servers in Japan. For brokers whose servers are in London or New York, a VPS nearer those cities will respond faster and will be a better choice.

FOREX VPS IN JAPAN

Keep your MT5 bot running 24/7

The MetaTrader5 Python package needs Windows, and your bot needs a machine that never sleeps. A Winserver VPS gives you Windows Server with the license included, guaranteed memory, and redundant power in our Osaka data center, so your strategy keeps running while your PC is off.

See Forex VPS Plans →

Recommended: 4 GB plan from $19.80/month · Windows license included · No setup fees

What You’ll Build

A simple moving average crossover bot for a demo account:

  • Every 30 seconds, it checks whether a new 15-minute bar has closed on USDJPY.
  • When the 20-bar moving average crosses above the 50-bar average, it buys. When it crosses below, it sells.
  • Before opening a trade, it closes any trade of its own in the opposite direction.
  • Every trade has a stop loss and take profit.
  • It writes everything to a log file, reconnects automatically if the terminal disconnects, and refuses to trade on a real account unless you change one setting.

The above is only an example. You can reuse the structure and customize it with your own logic.

Step 1: Install MetaTrader 5 and Log In to a Demo Account

Connect to your Windows VPS with Remote Desktop, then:

  1. Download MT5 from your broker’s official website and install it. Most brokers, such as Exness and AvaTrade, provide their own MT5 installer that’s already set up with their servers. Avoid MT5 downloads from unofficial sites.
  2. Open MT5 and log in to a demo account. Tick Save password, so the terminal can log in again by itself after a restart.
  3. Click the Algo Trading button in the toolbar so it turns green. Without this, the terminal rejects orders from Python.
  4. Open Tools > Options > Expert Advisors and make sure Allow algorithmic trading is ticked.

Find the path to the terminal. Your Python script needs to know where MT5 is installed. Run this in PowerShell:

Get-ChildItem "C:\Program Files" -Recurse -Filter terminal64.exe -ErrorAction SilentlyContinue | Select-Object FullName

Copy the full path that appears, for example C:\Program Files\MetaTrader 5\terminal64.exe. Many brokers install to a folder with their own name.

If you’re new to Remote Desktop, see our Remote Desktop setup guide for your Windows VPS.

Step 2: Install Python and the MetaTrader5 Package

Install 64-bit Python. In PowerShell:

winget install -e --id Python.Python.3.12

If winget isn’t available, download the Windows installer (64-bit) from python.org and tick Add python.exe to PATH during setup.

Create a project folder and a virtual environment (an isolated Python setup just for the bot), then install the packages. Close and reopen PowerShell first so it picks up the new Python:

New-Item -ItemType Directory -Force -Path C:\bots\mt5bot
py -3.12 -m venv C:\bots\mt5bot\venv
C:\bots\mt5bot\venv\Scripts\python.exe -m pip install --upgrade pip
C:\bots\mt5bot\venv\Scripts\python.exe -m pip install MetaTrader5 pandas

Using the full path to the virtual environment’s python.exe means you don’t need to “activate” it, which avoids PowerShell script permission errors.

Step 3: Test the Connection

Before writing the bot, check that Python can talk to MT5. Create a test script:

notepad C:\bots\mt5bot\check_mt5.py

Paste the following, change TERMINAL_PATH to your path from Step 1, and save:

import MetaTrader5 as mt5

# Path to your broker's MetaTrader 5 terminal
TERMINAL_PATH = r"C:\Program Files\MetaTrader 5\terminal64.exe"
SYMBOL = "USDJPY"

if not mt5.initialize(TERMINAL_PATH):
    print("initialize() failed:", mt5.last_error())
    raise SystemExit(1)

print("MetaTrader5 package version:", mt5.__version__)
print("Terminal version:", mt5.version())

account = mt5.account_info()
if account is None:
    print("Not logged in to a trading account:", mt5.last_error())
else:
    print("Account:", account.login, "on", account.server)
    print("Balance:", account.balance, account.currency)
    print("Demo account:", account.trade_mode == mt5.ACCOUNT_TRADE_MODE_DEMO)

print("Algo Trading enabled in terminal:", mt5.terminal_info().trade_allowed)

if mt5.symbol_select(SYMBOL, True):
    tick = mt5.symbol_info_tick(SYMBOL)
    print(SYMBOL, "bid:", tick.bid, "ask:", tick.ask)
else:
    print(SYMBOL, "not found. Similar symbols:", [s.name for s in mt5.symbols_get("*" + SYMBOL + "*")])

mt5.shutdown()

Run it:

C:\bots\mt5bot\venv\Scripts\python.exe C:\bots\mt5bot\check_mt5.py

You should see your account number, balance, Demo account: True, Algo Trading enabled in terminal: True and a live USDJPY price.

Notice there’s no password in the script. Because you saved the password in MT5 in Step 1, initialize() uses the account already logged in. That’s safer than storing your password in a text file.

If the symbol isn’t found, your broker probably uses a different name, such as USDJPYm or USDJPY.a. The script lists similar symbols so you can copy the right one.

Step 4: Create the Trading Bot

Option 1: download it. Download python-mt5-bot.zip and extract it to the bot folder:

Expand-Archive -Path "$env:USERPROFILE\Downloads\python-mt5-bot.zip" -DestinationPath C:\bots\mt5bot -Force

The zip also contains a fresh copy of check_mt5.py, which replaces the one from Step 3. Open both files in Notepad and change TERMINAL_PATH (and SYMBOL if needed):

notepad C:\bots\mt5bot\mt5_bot.py
notepad C:\bots\mt5bot\check_mt5.py

Option 2: create it yourself. Open a new file:

notepad C:\bots\mt5bot\mt5_bot.py

Paste the code below. Change TERMINAL_PATH to your path, and SYMBOL if your broker uses a different name. Then save:

Show the full bot code (mt5_bot.py)
"""
Simple MetaTrader 5 moving average crossover bot (educational example).
Always test on a DEMO account first. This is not financial advice.
"""
import logging
import sys
import time

import MetaTrader5 as mt5
import pandas as pd

# ---------------- Settings ----------------
TERMINAL_PATH = r"C:\Program Files\MetaTrader 5\terminal64.exe"
SYMBOL = "USDJPY"
TIMEFRAME = mt5.TIMEFRAME_M15
FAST_MA = 20           # fast moving average (bars)
SLOW_MA = 50           # slow moving average (bars)
LOT = 0.01             # trade size
SL_POINTS = 300        # stop loss distance in points
TP_POINTS = 600        # take profit distance in points
DEVIATION = 20         # max slippage in points
MAGIC = 20261106       # ID so the bot only manages its own trades
CHECK_SECONDS = 30     # how often to check for a new bar
DEMO_ONLY = True       # refuse to trade on a real account
LOG_FILE = r"C:\bots\mt5bot\bot.log"
# ------------------------------------------

handlers = [logging.FileHandler(LOG_FILE, encoding="utf-8")]
if sys.stderr is not None:  # no console when run with pythonw.exe
    handlers.append(logging.StreamHandler())
logging.basicConfig(level=logging.INFO, handlers=handlers,
                    format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("mt5bot")


def connect():
    """Connect to the terminal and check the account is safe to use."""
    if not mt5.initialize(TERMINAL_PATH):
        log.error("initialize() failed: %s", mt5.last_error())
        return False
    account = mt5.account_info()
    if account is None:
        log.error("Not logged in to a trading account: %s", mt5.last_error())
        mt5.shutdown()
        return False
    if DEMO_ONLY and account.trade_mode != mt5.ACCOUNT_TRADE_MODE_DEMO:
        log.error("Account %s is not a demo account. Stopping.", account.login)
        mt5.shutdown()
        raise SystemExit(1)
    if not mt5.terminal_info().trade_allowed:
        log.warning("Algo Trading is off in the terminal. Orders will be rejected.")
    if not mt5.symbol_select(SYMBOL, True):
        log.error("Symbol %s not found. Check your broker's symbol name.", SYMBOL)
        mt5.shutdown()
        raise SystemExit(1)
    log.info("Connected: account %s on %s", account.login, account.server)
    return True


def get_signal():
    """Return ("buy" | "sell" | None, time of the last closed bar)."""
    # start_pos=1 skips the bar that is still forming
    rates = mt5.copy_rates_from_pos(SYMBOL, TIMEFRAME, 1, SLOW_MA + 2)
    if rates is None or len(rates) < SLOW_MA + 2:
        log.warning("Not enough price data: %s", mt5.last_error())
        return None, None
    df = pd.DataFrame(rates)
    df["fast"] = df["close"].rolling(FAST_MA).mean()
    df["slow"] = df["close"].rolling(SLOW_MA).mean()
    prev, last = df.iloc[-2], df.iloc[-1]
    bar_time = int(last["time"])
    if prev["fast"] <= prev["slow"] and last["fast"] > last["slow"]:
        return "buy", bar_time
    if prev["fast"] >= prev["slow"] and last["fast"] < last["slow"]:
        return "sell", bar_time
    return None, bar_time


def choose_filling(request):
    """Pick a filling mode the broker accepts for this symbol."""
    for filling in (mt5.ORDER_FILLING_FOK, mt5.ORDER_FILLING_IOC, mt5.ORDER_FILLING_RETURN):
        request["type_filling"] = filling
        check = mt5.order_check(request)
        if check is not None and check.retcode == 0:
            return
    # None passed the check: send anyway so the broker's reason is logged


def send_order(order_type, volume, position=None):
    """Open a new trade, or close an existing one when position is given."""
    tick = mt5.symbol_info_tick(SYMBOL)
    info = mt5.symbol_info(SYMBOL)
    if tick is None or info is None:
        log.error("No price for %s: %s", SYMBOL, mt5.last_error())
        return False
    is_buy = order_type == mt5.ORDER_TYPE_BUY
    price = tick.ask if is_buy else tick.bid
    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": SYMBOL,
        "volume": volume,
        "type": order_type,
        "price": price,
        "deviation": DEVIATION,
        "magic": MAGIC,
        "comment": "python bot",
        "type_time": mt5.ORDER_TIME_GTC,
    }
    if position is None:
        sl_gap, tp_gap = SL_POINTS * info.point, TP_POINTS * info.point
        request["sl"] = round(price - sl_gap if is_buy else price + sl_gap, info.digits)
        request["tp"] = round(price + tp_gap if is_buy else price - tp_gap, info.digits)
    else:
        request["position"] = position
    choose_filling(request)
    result = mt5.order_send(request)
    if result is None:
        log.error("order_send() failed: %s", mt5.last_error())
        return False
    if result.retcode != mt5.TRADE_RETCODE_DONE:
        log.error("Order rejected: retcode=%s %s", result.retcode, result.comment)
        return False
    action = "Closed" if position else ("Bought" if is_buy else "Sold")
    log.info("%s %s %s at %s (order %s)", action, volume, SYMBOL, result.price, result.order)
    return True


def act_on(signal):
    """Close opposite trades, then open one in the signal's direction."""
    want = mt5.POSITION_TYPE_BUY if signal == "buy" else mt5.POSITION_TYPE_SELL
    mine = [p for p in (mt5.positions_get(symbol=SYMBOL) or []) if p.magic == MAGIC]
    for p in mine:
        if p.type != want:
            close_type = mt5.ORDER_TYPE_SELL if p.type == mt5.POSITION_TYPE_BUY else mt5.ORDER_TYPE_BUY
            send_order(close_type, p.volume, position=p.ticket)
    if not any(p.type == want for p in mine):
        send_order(mt5.ORDER_TYPE_BUY if signal == "buy" else mt5.ORDER_TYPE_SELL, LOT)


def main():
    log.info("Bot starting: %s, MA %s/%s", SYMBOL, FAST_MA, SLOW_MA)
    connected = False
    last_bar = None
    while True:
        try:
            if not connected or mt5.terminal_info() is None:
                connected = connect()
                if not connected:
                    time.sleep(30)
                    continue
            signal, bar_time = get_signal()
            if bar_time is not None and bar_time != last_bar:
                last_bar = bar_time
                if signal:
                    log.info("Signal: %s", signal)
                    act_on(signal)
        except SystemExit:
            raise
        except Exception:
            log.exception("Unexpected error. Reconnecting.")
            mt5.shutdown()
            connected = False
        time.sleep(CHECK_SECONDS)


if __name__ == "__main__":
    main()

How the code works

  • Settings at the top: symbol, timeframe, moving average lengths, trade size, and stop loss and take profit in points. On USDJPY, 300 points is 0.3 yen (30 pips).
  • connect() starts or attaches to the terminal, checks it’s logged in, and stops the bot if the account isn’t a demo account while DEMO_ONLY = True.
  • get_signal() reads the last closed bars with copy_rates_from_pos(), skipping the bar still forming so signals don’t flicker, and checks for a crossover.
  • choose_filling() tests which order filling mode your broker accepts with order_check(). Brokers differ here, and the wrong mode is a common cause of rejected orders.
  • send_order() places the trade with order_send() and logs the result, including the broker’s reason if it’s rejected.
  • The MAGIC number tags the bot’s trades, so it never touches trades you open by hand or trades from other bots.
  • The main loop only acts once per new bar, and reconnects if anything goes wrong instead of crashing.

Test it. Run the bot in PowerShell and watch the messages. Press Ctrl+C to stop it:

C:\bots\mt5bot\venv\Scripts\python.exe C:\bots\mt5bot\mt5_bot.py

A crossover might not happen for a while. To see a trade sooner while testing, temporarily set TIMEFRAME = mt5.TIMEFRAME_M1, FAST_MA = 3 and SLOW_MA = 5.

Step 5: Run the Bot 24/7 on Your VPS

MT5 is a desktop application, so it needs a signed-in Windows session. That affects how you keep it running:

  • Close the Remote Desktop window instead of signing out. Closing the window (or clicking Disconnect) leaves your session and programs running. Signing out closes MT5 and stops the bot.
  • Start the bot automatically when you sign in. This command creates a scheduled task that starts the bot at sign-in, with no time limit and no console window:
$action = New-ScheduledTaskAction -Execute "C:\bots\mt5bot\venv\Scripts\pythonw.exe" `
  -Argument "C:\bots\mt5bot\mt5_bot.py" -WorkingDirectory "C:\bots\mt5bot"
$trigger = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable
Register-ScheduledTask -TaskName "MT5 Python Bot" -Action $action -Trigger $trigger -Settings $settings

pythonw.exe runs Python without a console window, so the bot can’t be closed by accident. The bot starts MT5 itself if it isn’t already running.

After a VPS restart, Windows waits at the sign-in screen, so nothing runs until someone signs in. To make the server sign in automatically after a restart, see how to start applications automatically after a VPS restart. Our guide to starting MT4/MT5 automatically after a restart covers the trading terminal side.

Manage the task:

# Start the bot now
Start-ScheduledTask -TaskName "MT5 Python Bot"

# Check when it last ran
Get-ScheduledTask -TaskName "MT5 Python Bot" | Get-ScheduledTaskInfo

# Stop the bot
Stop-ScheduledTask -TaskName "MT5 Python Bot"

Step 6: Monitor Your Bot

Everything the bot does is written to C:\bots\mt5bot\bot.log. To watch it live:

Get-Content C:\bots\mt5bot\bot.log -Tail 20 -Wait

Press Ctrl+C to stop watching (this doesn’t stop the bot). Trades also appear in MT5’s Toolbox > Trade and History tabs, with the comment “python bot”.

For a quick check from your phone, the MetaTrader 5 mobile app can log in to the same account and show the bot’s trades and positions.

Troubleshooting

Problem Likely cause Fix
pip says “No matching distribution found for MetaTrader5” Not Windows, 32-bit Python, or a Python version too new for the package Use 64-bit Python 3.12 on Windows, as in Step 2.
initialize() failed with “IPC timeout” Python can’t reach the terminal Check TERMINAL_PATH, make sure MT5 can open normally, and run Python as the same Windows user as MT5.
“Authorization failed” or “Not logged in” The terminal isn’t logged in Log in to MT5 manually once and tick Save password.
Order rejected with retcode 10027 Algo Trading is off in the terminal Click the Algo Trading button so it’s green (Step 1).
Retcode 10030 (unsupported filling mode) The broker doesn’t accept the order’s filling mode The bot’s choose_filling() handles this. If you write your own code, test the modes with order_check().
Retcode 10016 (invalid stops) Stop loss or take profit too close to the price Increase SL_POINTS and TP_POINTS.
Retcode 10018 (market closed) Weekend or holiday Normal. The bot carries on when the market opens.
“Symbol not found” Your broker uses a different symbol name Run check_mt5.py to list similar symbols, then update SYMBOL.
Bot stopped after closing Remote Desktop You signed out instead of disconnecting Close the Remote Desktop window rather than signing out.

Keep Your Trading VPS Safe and Stable

  • Stay on demo until you trust the bot. Only set DEMO_ONLY = False after testing it for long enough to see how it behaves in different market conditions.
  • Protect Remote Desktop: use a strong password and limit who can connect. See how to configure firewall rules on a Windows VPS without breaking remote access.
  • Control Windows Update restarts: set active hours, or schedule updates for the weekend when forex markets are closed, so a restart doesn’t interrupt the bot mid-trade.
  • Don’t store passwords in scripts: let the terminal remember the login, as in this guide.
  • Give it enough memory: Windows Server, MT5 and Python run comfortably on 4 GB of RAM. If you plan to run several terminals or bots, choose more.

Where to Go Next

  • Use your own strategy: replace get_signal() with your own logic. The connection, order handling and logging stay the same.
  • Run several accounts: install a separate MT5 terminal for each account in its own folder and point each bot’s TERMINAL_PATH at its own terminal.
  • Get alerts: send a message to Telegram, Slack or email from send_order() when a trade opens or fails.
  • Automate updates: deploy new versions of your bot from GitHub. See how to set up a GitHub Actions self-hosted runner on a Windows VPS.
  • Write code with AI: an AI coding assistant can help you extend the bot. See how to run Claude Code 24/7 on a Windows VPS.

FAQ

Q1. Is the MetaTrader5 Python package free?

Yes. It’s published by MetaQuotes and installed with pip install MetaTrader5. You also need MT5 from a broker, which is free with a demo or live account.

Q2. Does the MetaTrader5 package work on Linux or macOS?

Not officially. The package is only published for 64-bit Windows. Community projects run MT5 under Wine and connect it to Python on Linux, but they add complexity. A Windows PC or Windows VPS is the simplest option.

Q3. Can I use Python with MT4?

Not with this package. The official MetaTrader5 package only works with MetaTrader 5. MT4 has no official Python package, so most people automating MT4 use Expert Advisors written in MQL4.

Q4. Does the MT5 terminal need to stay open?

Yes. The package communicates through the running terminal, so MT5 must be open and logged in. The bot in this guide starts the terminal automatically if it isn’t running.

Q5. Do I need a VPS to run a Python trading bot?

Not to build or test it, which you can do on your own PC. For trading around the clock, a VPS keeps the terminal and bot running even when your computer is off, asleep or restarting for updates.

Q6. Which Python version should I use?

64-bit Python 3.12 is a safe choice. The package is built for specific Python versions, so a newly released version may not be supported straight away, and 32-bit Python isn’t supported.

Q7. Can I run bots for several accounts on one VPS?

Yes. Install a separate MT5 terminal for each account in its own folder, and give each bot the path to its own terminal. Each terminal uses memory, so choose a plan with enough RAM.

Q8. Does my broker allow automated trading?

Most brokers that offer MT5 allow algorithmic trading, but rules differ, so check your broker’s terms before running a bot on a live account.

Sources: MQL5 documentation: MetaTrader for Python, initialize, order_send

Run Your Trading Bot Around the Clock

Winserver offers Windows VPS plans from our own data center in Osaka, Japan, with the Windows Server license included, full administrator access and English support. Install MT5 and Python, start your bot, and let it run 24/7.

View Windows VPS Plans
Development Forex Trading
Algorithmic Trading Automation Forex MetaTrader 5 MT5 Python Trading Bot windows vps
  • How to Set Up a GitHub Actions Self-Hosted Runner on a Windows VPS (Step by Step)

Browse by Category

  • Business in Japan
  • Development
  • DMM & Browser Gaming
  • Forex Trading
  • Security
  • Windows Server/VPS Guides

Browse by Month

Your subscription could not be saved. Please try again.
Your subscription has been successful!

New Guides,

Straight to Your Inbox!

Join our newsletter for the latest articles, VPS tips, and updates!

Winserver.net

Windows VPS starting from $9.90/mon



View Plans Winserver.net on ServerVerify

© Winserver All Rights Reserved.

TABLE OF CONTENTS

Winserver Blog

Development