{"id":1472,"date":"2026-11-09T15:00:43","date_gmt":"2026-11-09T06:00:43","guid":{"rendered":"https:\/\/www.winserver.net\/blog\/?p=1472"},"modified":"2026-11-10T15:08:46","modified_gmt":"2026-11-10T06:08:46","slug":"python-metatrader5-trading-bot-windows-vps","status":"publish","type":"post","link":"https:\/\/www.winserver.net\/blog\/python-metatrader5-trading-bot-windows-vps\/","title":{"rendered":"How to Run a Python Trading Bot with MetaTrader 5 on a Windows VPS (2026)"},"content":{"rendered":"<p><em>Last updated: November 6, 2026<\/em><\/p>\n<p>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.<\/p>\n<p><strong>The package only runs on Windows, and your bot only works while the MT5 terminal is running.<\/strong><\/p>\n<p>This guide shows you how to build a simple Python trading bot with the official <code>MetaTrader5<\/code> package and run it 24\/7 on a Windows VPS, with copy-paste code and commands.<\/p>\n<div style=\"border: 2px solid #045FA4; border-radius: 8px; background: #F4F8FC; padding: 16px 20px; margin: 0 0 24px;\">\n<p style=\"margin: 0 0 8px; font-weight: bold; color: #394f6d;\">Quick answer<\/p>\n<ul style=\"margin: 0; padding-left: 20px;\">\n<li><strong>What you need:<\/strong> a Windows computer or VPS, MetaTrader 5 from your broker, and 64-bit Python with the <code>MetaTrader5<\/code> package (<code>pip install MetaTrader5<\/code>).<\/li>\n<li><strong>Why Windows:<\/strong> the official package is Windows-only. On Linux or macOS, you need workarounds such as Wine.<\/li>\n<li><strong>Why a VPS:<\/strong> 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.<\/li>\n<li><strong>Before going live:<\/strong> test on a demo account. The bot in this guide refuses to trade on a real account until you change one setting.<\/li>\n<\/ul>\n<\/div>\n<h2>How the MetaTrader5 Python Package Works<\/h2>\n<p>The <code>MetaTrader5<\/code> package doesn&#8217;t connect to your broker directly. Instead, it talks to the <strong>MetaTrader 5 terminal running on the same Windows machine<\/strong>, 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.<\/p>\n<p>That has two practical consequences:<\/p>\n<ul>\n<li><strong>It needs Windows.<\/strong> 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.<\/li>\n<li><strong>The terminal must stay open and logged in.<\/strong> If your PC sleeps, restarts or loses its connection, the bot stops trading.<\/li>\n<\/ul>\n<h3>Windows VPS vs your own PC vs Linux<\/h3>\n<div style=\"overflow-x: auto;\">\n<table>\n<thead>\n<tr>\n<th><\/th>\n<th>Your own Windows PC<\/th>\n<th>Windows VPS<\/th>\n<th>Linux server<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Official MetaTrader5 package<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>No (Wine workaround)<\/td>\n<\/tr>\n<tr>\n<td>Runs 24\/7<\/td>\n<td>Only if the PC never sleeps or restarts<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Affected by your home internet or power<\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>Setup difficulty<\/td>\n<td>Easy<\/td>\n<td>Easy (same as a PC, over Remote Desktop)<\/td>\n<td>Harder<\/td>\n<\/tr>\n<tr>\n<td>Best for<\/td>\n<td>Writing and testing your bot<\/td>\n<td>Running your bot around the clock<\/td>\n<td>Teams already experienced with Wine<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>A good workflow is to <strong>develop and test on your own PC, then move the bot to a VPS<\/strong> when it&#8217;s ready to run continuously.<\/p>\n<p><strong>Does server location matter?<\/strong> 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&#8217;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.<\/p>\n<div class=\"ws-plan-box\" style=\"border: 2px solid #045FA4; border-radius: 10px; padding: 20px 24px; margin: 32px 0; background: #f5f9fd;\">\n<p style=\"margin: 0 0 6px; font-size: 0.85em; font-weight: bold; color: #0189ff; letter-spacing: 0.05em;\">FOREX VPS IN JAPAN<\/p>\n<p style=\"margin: 0 0 10px; font-size: 1.25em; font-weight: bold; color: #394f6d;\">Keep your MT5 bot running 24\/7<\/p>\n<p style=\"margin: 0 0 16px; color: #394f6d;\">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.<\/p>\n<p><a class=\"ws-plan-btn\" href=\"https:\/\/www.winserver.net\/fx_trading\" style=\"display: inline-block; background: #045FA4; color: #fff; padding: 10px 22px; border-radius: 6px; font-weight: bold; text-decoration: none;\">See Forex VPS Plans \u2192<\/a><\/p>\n<p style=\"margin: 10px 0 0; font-size: 0.8em; color: #6b7c93;\">Recommended: 4 GB plan from $19.80\/month \u00b7 Windows license included \u00b7 No setup fees<\/p>\n<\/div>\n<h2>What You&#8217;ll Build<\/h2>\n<p>A simple <strong>moving average crossover bot<\/strong> for a demo account:<\/p>\n<ul>\n<li>Every 30 seconds, it checks whether a new 15-minute bar has closed on USDJPY.<\/li>\n<li>When the 20-bar moving average crosses <strong>above<\/strong> the 50-bar average, it buys. When it crosses <strong>below<\/strong>, it sells.<\/li>\n<li>Before opening a trade, it closes any trade of its own in the opposite direction.<\/li>\n<li>Every trade has a stop loss and take profit.<\/li>\n<li>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.<\/li>\n<\/ul>\n<p>The above is only an example. You can reuse the structure and customize it with your own logic.<\/p>\n<h2>Step 1: Install MetaTrader 5 and Log In to a Demo Account<\/h2>\n<p>Connect to your Windows VPS with Remote Desktop, then:<\/p>\n<ol>\n<li><strong>Download MT5 from your broker&#8217;s official website<\/strong> and install it. Most brokers, such as Exness and AvaTrade, provide their own MT5 installer that&#8217;s already set up with their servers. Avoid MT5 downloads from unofficial sites.<\/li>\n<li>Open MT5 and log in to a <strong>demo account<\/strong>. Tick <strong>Save password<\/strong>, so the terminal can log in again by itself after a restart.<\/li>\n<li>Click the <strong>Algo Trading<\/strong> button in the toolbar so it turns green. Without this, the terminal rejects orders from Python.<\/li>\n<li>Open <strong>Tools &gt; Options &gt; Expert Advisors<\/strong> and make sure <strong>Allow algorithmic trading<\/strong> is ticked.<\/li>\n<\/ol>\n<p><strong>Find the path to the terminal.<\/strong> Your Python script needs to know where MT5 is installed. Run this in PowerShell:<\/p>\n<pre><code class=\"language-powershell\">Get-ChildItem \"C:\\Program Files\" -Recurse -Filter terminal64.exe -ErrorAction SilentlyContinue | Select-Object FullName<\/code><\/pre>\n<p>Copy the full path that appears, for example <code>C:\\Program Files\\MetaTrader 5\\terminal64.exe<\/code>. Many brokers install to a folder with their own name.<\/p>\n<p>If you&#8217;re new to Remote Desktop, see our <span style=\"color: #000000;\"><strong><a href=\"https:\/\/www.winserver.net\/blog\/windows-vps-remote-desktop-rdp-guide\/\" style=\"color: #000000;\">Remote Desktop setup guide for your Windows VPS<\/a>.<\/strong><\/span><\/p>\n<h2>Step 2: Install Python and the MetaTrader5 Package<\/h2>\n<p><strong>Install 64-bit Python.<\/strong> In PowerShell:<\/p>\n<pre><code class=\"language-powershell\">winget install -e --id Python.Python.3.12<\/code><\/pre>\n<p>If <code>winget<\/code> isn&#8217;t available, download the <strong>Windows installer (64-bit)<\/strong> from python.org and tick <strong>Add python.exe to PATH<\/strong> during setup.<\/p>\n<p><strong>Create a project folder and a virtual environment<\/strong> (an isolated Python setup just for the bot), then install the packages. Close and reopen PowerShell first so it picks up the new Python:<\/p>\n<pre><code class=\"language-powershell\">New-Item -ItemType Directory -Force -Path C:\\bots\\mt5bot\r\npy -3.12 -m venv C:\\bots\\mt5bot\\venv\r\nC:\\bots\\mt5bot\\venv\\Scripts\\python.exe -m pip install --upgrade pip\r\nC:\\bots\\mt5bot\\venv\\Scripts\\python.exe -m pip install MetaTrader5 pandas<\/code><\/pre>\n<p>Using the full path to the virtual environment&#8217;s <code>python.exe<\/code> means you don&#8217;t need to &#8220;activate&#8221; it, which avoids PowerShell script permission errors.<\/p>\n<h2>Step 3: Test the Connection<\/h2>\n<p>Before writing the bot, check that Python can talk to MT5. Create a test script:<\/p>\n<pre><code class=\"language-powershell\">notepad C:\\bots\\mt5bot\\check_mt5.py<\/code><\/pre>\n<p>Paste the following, change <code>TERMINAL_PATH<\/code> to your path from Step 1, and save:<\/p>\n<pre><code class=\"language-python\">import MetaTrader5 as mt5\r\n\r\n# Path to your broker's MetaTrader 5 terminal\r\nTERMINAL_PATH = r\"C:\\Program Files\\MetaTrader 5\\terminal64.exe\"\r\nSYMBOL = \"USDJPY\"\r\n\r\nif not mt5.initialize(TERMINAL_PATH):\r\n    print(\"initialize() failed:\", mt5.last_error())\r\n    raise SystemExit(1)\r\n\r\nprint(\"MetaTrader5 package version:\", mt5.__version__)\r\nprint(\"Terminal version:\", mt5.version())\r\n\r\naccount = mt5.account_info()\r\nif account is None:\r\n    print(\"Not logged in to a trading account:\", mt5.last_error())\r\nelse:\r\n    print(\"Account:\", account.login, \"on\", account.server)\r\n    print(\"Balance:\", account.balance, account.currency)\r\n    print(\"Demo account:\", account.trade_mode == mt5.ACCOUNT_TRADE_MODE_DEMO)\r\n\r\nprint(\"Algo Trading enabled in terminal:\", mt5.terminal_info().trade_allowed)\r\n\r\nif mt5.symbol_select(SYMBOL, True):\r\n    tick = mt5.symbol_info_tick(SYMBOL)\r\n    print(SYMBOL, \"bid:\", tick.bid, \"ask:\", tick.ask)\r\nelse:\r\n    print(SYMBOL, \"not found. Similar symbols:\", [s.name for s in mt5.symbols_get(\"*\" + SYMBOL + \"*\")])\r\n\r\nmt5.shutdown()<\/code><\/pre>\n<p>Run it:<\/p>\n<pre><code class=\"language-powershell\">C:\\bots\\mt5bot\\venv\\Scripts\\python.exe C:\\bots\\mt5bot\\check_mt5.py<\/code><\/pre>\n<p>You should see your account number, balance, <code>Demo account: True<\/code>, <code>Algo Trading enabled in terminal: True<\/code> and a live USDJPY price.<\/p>\n<p><strong>Notice there&#8217;s no password in the script.<\/strong> Because you saved the password in MT5 in Step 1, <code>initialize()<\/code> uses the account already logged in. That&#8217;s safer than storing your password in a text file.<\/p>\n<p>If the symbol isn&#8217;t found, your broker probably uses a different name, such as <code>USDJPYm<\/code> or <code>USDJPY.a<\/code>. The script lists similar symbols so you can copy the right one.<\/p>\n<h2>Step 4: Create the Trading Bot<\/h2>\n<p><strong>Option 1: download it.<\/strong> <span style=\"color: #0189ff;\"><strong><a href=\"https:\/\/blog.winserver.net\/wp-content\/uploads\/2026\/11\/python-mt5-bot.zip\" style=\"color: #0189ff;\">Download python-mt5-bot.zip<\/a><\/strong><\/span> and extract it to the bot folder:<\/p>\n<pre><code class=\"language-powershell\">Expand-Archive -Path \"$env:USERPROFILE\\Downloads\\python-mt5-bot.zip\" -DestinationPath C:\\bots\\mt5bot -Force<\/code><\/pre>\n<p>The zip also contains a fresh copy of <code>check_mt5.py<\/code>, which replaces the one from Step 3. Open both files in Notepad and change <code>TERMINAL_PATH<\/code> (and <code>SYMBOL<\/code> if needed):<\/p>\n<pre><code class=\"language-powershell\">notepad C:\\bots\\mt5bot\\mt5_bot.py\r\nnotepad C:\\bots\\mt5bot\\check_mt5.py<\/code><\/pre>\n<p><strong>Option 2: create it yourself.<\/strong> Open a new file:<\/p>\n<pre><code class=\"language-powershell\">notepad C:\\bots\\mt5bot\\mt5_bot.py<\/code><\/pre>\n<p>Paste the code below. Change <code>TERMINAL_PATH<\/code> to your path, and <code>SYMBOL<\/code> if your broker uses a different name. Then save:<\/p>\n<details style=\"border: 1px solid #045FA4; border-radius: 8px; padding: 12px 16px; margin: 0 0 16px;\">\n<summary style=\"cursor: pointer; font-weight: bold; color: #045fa4;\">Show the full bot code (mt5_bot.py)<\/summary>\n<pre><code class=\"language-python\">\"\"\"\r\nSimple MetaTrader 5 moving average crossover bot (educational example).\r\nAlways test on a DEMO account first. This is not financial advice.\r\n\"\"\"\r\nimport logging\r\nimport sys\r\nimport time\r\n\r\nimport MetaTrader5 as mt5\r\nimport pandas as pd\r\n\r\n# ---------------- Settings ----------------\r\nTERMINAL_PATH = r\"C:\\Program Files\\MetaTrader 5\\terminal64.exe\"\r\nSYMBOL = \"USDJPY\"\r\nTIMEFRAME = mt5.TIMEFRAME_M15\r\nFAST_MA = 20           # fast moving average (bars)\r\nSLOW_MA = 50           # slow moving average (bars)\r\nLOT = 0.01             # trade size\r\nSL_POINTS = 300        # stop loss distance in points\r\nTP_POINTS = 600        # take profit distance in points\r\nDEVIATION = 20         # max slippage in points\r\nMAGIC = 20261106       # ID so the bot only manages its own trades\r\nCHECK_SECONDS = 30     # how often to check for a new bar\r\nDEMO_ONLY = True       # refuse to trade on a real account\r\nLOG_FILE = r\"C:\\bots\\mt5bot\\bot.log\"\r\n# ------------------------------------------\r\n\r\nhandlers = [logging.FileHandler(LOG_FILE, encoding=\"utf-8\")]\r\nif sys.stderr is not None:  # no console when run with pythonw.exe\r\n    handlers.append(logging.StreamHandler())\r\nlogging.basicConfig(level=logging.INFO, handlers=handlers,\r\n                    format=\"%(asctime)s %(levelname)s %(message)s\")\r\nlog = logging.getLogger(\"mt5bot\")\r\n\r\n\r\ndef connect():\r\n    \"\"\"Connect to the terminal and check the account is safe to use.\"\"\"\r\n    if not mt5.initialize(TERMINAL_PATH):\r\n        log.error(\"initialize() failed: %s\", mt5.last_error())\r\n        return False\r\n    account = mt5.account_info()\r\n    if account is None:\r\n        log.error(\"Not logged in to a trading account: %s\", mt5.last_error())\r\n        mt5.shutdown()\r\n        return False\r\n    if DEMO_ONLY and account.trade_mode != mt5.ACCOUNT_TRADE_MODE_DEMO:\r\n        log.error(\"Account %s is not a demo account. Stopping.\", account.login)\r\n        mt5.shutdown()\r\n        raise SystemExit(1)\r\n    if not mt5.terminal_info().trade_allowed:\r\n        log.warning(\"Algo Trading is off in the terminal. Orders will be rejected.\")\r\n    if not mt5.symbol_select(SYMBOL, True):\r\n        log.error(\"Symbol %s not found. Check your broker's symbol name.\", SYMBOL)\r\n        mt5.shutdown()\r\n        raise SystemExit(1)\r\n    log.info(\"Connected: account %s on %s\", account.login, account.server)\r\n    return True\r\n\r\n\r\ndef get_signal():\r\n    \"\"\"Return (\"buy\" | \"sell\" | None, time of the last closed bar).\"\"\"\r\n    # start_pos=1 skips the bar that is still forming\r\n    rates = mt5.copy_rates_from_pos(SYMBOL, TIMEFRAME, 1, SLOW_MA + 2)\r\n    if rates is None or len(rates) &lt; SLOW_MA + 2:\r\n        log.warning(\"Not enough price data: %s\", mt5.last_error())\r\n        return None, None\r\n    df = pd.DataFrame(rates)\r\n    df[\"fast\"] = df[\"close\"].rolling(FAST_MA).mean()\r\n    df[\"slow\"] = df[\"close\"].rolling(SLOW_MA).mean()\r\n    prev, last = df.iloc[-2], df.iloc[-1]\r\n    bar_time = int(last[\"time\"])\r\n    if prev[\"fast\"] &lt;= prev[\"slow\"] and last[\"fast\"] &gt; last[\"slow\"]:\r\n        return \"buy\", bar_time\r\n    if prev[\"fast\"] &gt;= prev[\"slow\"] and last[\"fast\"] &lt; last[\"slow\"]:\r\n        return \"sell\", bar_time\r\n    return None, bar_time\r\n\r\n\r\ndef choose_filling(request):\r\n    \"\"\"Pick a filling mode the broker accepts for this symbol.\"\"\"\r\n    for filling in (mt5.ORDER_FILLING_FOK, mt5.ORDER_FILLING_IOC, mt5.ORDER_FILLING_RETURN):\r\n        request[\"type_filling\"] = filling\r\n        check = mt5.order_check(request)\r\n        if check is not None and check.retcode == 0:\r\n            return\r\n    # None passed the check: send anyway so the broker's reason is logged\r\n\r\n\r\ndef send_order(order_type, volume, position=None):\r\n    \"\"\"Open a new trade, or close an existing one when position is given.\"\"\"\r\n    tick = mt5.symbol_info_tick(SYMBOL)\r\n    info = mt5.symbol_info(SYMBOL)\r\n    if tick is None or info is None:\r\n        log.error(\"No price for %s: %s\", SYMBOL, mt5.last_error())\r\n        return False\r\n    is_buy = order_type == mt5.ORDER_TYPE_BUY\r\n    price = tick.ask if is_buy else tick.bid\r\n    request = {\r\n        \"action\": mt5.TRADE_ACTION_DEAL,\r\n        \"symbol\": SYMBOL,\r\n        \"volume\": volume,\r\n        \"type\": order_type,\r\n        \"price\": price,\r\n        \"deviation\": DEVIATION,\r\n        \"magic\": MAGIC,\r\n        \"comment\": \"python bot\",\r\n        \"type_time\": mt5.ORDER_TIME_GTC,\r\n    }\r\n    if position is None:\r\n        sl_gap, tp_gap = SL_POINTS * info.point, TP_POINTS * info.point\r\n        request[\"sl\"] = round(price - sl_gap if is_buy else price + sl_gap, info.digits)\r\n        request[\"tp\"] = round(price + tp_gap if is_buy else price - tp_gap, info.digits)\r\n    else:\r\n        request[\"position\"] = position\r\n    choose_filling(request)\r\n    result = mt5.order_send(request)\r\n    if result is None:\r\n        log.error(\"order_send() failed: %s\", mt5.last_error())\r\n        return False\r\n    if result.retcode != mt5.TRADE_RETCODE_DONE:\r\n        log.error(\"Order rejected: retcode=%s %s\", result.retcode, result.comment)\r\n        return False\r\n    action = \"Closed\" if position else (\"Bought\" if is_buy else \"Sold\")\r\n    log.info(\"%s %s %s at %s (order %s)\", action, volume, SYMBOL, result.price, result.order)\r\n    return True\r\n\r\n\r\ndef act_on(signal):\r\n    \"\"\"Close opposite trades, then open one in the signal's direction.\"\"\"\r\n    want = mt5.POSITION_TYPE_BUY if signal == \"buy\" else mt5.POSITION_TYPE_SELL\r\n    mine = [p for p in (mt5.positions_get(symbol=SYMBOL) or []) if p.magic == MAGIC]\r\n    for p in mine:\r\n        if p.type != want:\r\n            close_type = mt5.ORDER_TYPE_SELL if p.type == mt5.POSITION_TYPE_BUY else mt5.ORDER_TYPE_BUY\r\n            send_order(close_type, p.volume, position=p.ticket)\r\n    if not any(p.type == want for p in mine):\r\n        send_order(mt5.ORDER_TYPE_BUY if signal == \"buy\" else mt5.ORDER_TYPE_SELL, LOT)\r\n\r\n\r\ndef main():\r\n    log.info(\"Bot starting: %s, MA %s\/%s\", SYMBOL, FAST_MA, SLOW_MA)\r\n    connected = False\r\n    last_bar = None\r\n    while True:\r\n        try:\r\n            if not connected or mt5.terminal_info() is None:\r\n                connected = connect()\r\n                if not connected:\r\n                    time.sleep(30)\r\n                    continue\r\n            signal, bar_time = get_signal()\r\n            if bar_time is not None and bar_time != last_bar:\r\n                last_bar = bar_time\r\n                if signal:\r\n                    log.info(\"Signal: %s\", signal)\r\n                    act_on(signal)\r\n        except SystemExit:\r\n            raise\r\n        except Exception:\r\n            log.exception(\"Unexpected error. Reconnecting.\")\r\n            mt5.shutdown()\r\n            connected = False\r\n        time.sleep(CHECK_SECONDS)\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    main()<\/code><\/pre>\n<\/details>\n<h3>How the code works<\/h3>\n<ul>\n<li><strong>Settings at the top:<\/strong> 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).<\/li>\n<li><strong><code>connect()<\/code><\/strong> starts or attaches to the terminal, checks it&#8217;s logged in, and stops the bot if the account isn&#8217;t a demo account while <code>DEMO_ONLY = True<\/code>.<\/li>\n<li><strong><code>get_signal()<\/code><\/strong> reads the last closed bars with <code>copy_rates_from_pos()<\/code>, skipping the bar still forming so signals don&#8217;t flicker, and checks for a crossover.<\/li>\n<li><strong><code>choose_filling()<\/code><\/strong> tests which order filling mode your broker accepts with <code>order_check()<\/code>. Brokers differ here, and the wrong mode is a common cause of rejected orders.<\/li>\n<li><strong><code>send_order()<\/code><\/strong> places the trade with <code>order_send()<\/code> and logs the result, including the broker&#8217;s reason if it&#8217;s rejected.<\/li>\n<li><strong>The <code>MAGIC<\/code> number<\/strong> tags the bot&#8217;s trades, so it never touches trades you open by hand or trades from other bots.<\/li>\n<li><strong>The main loop<\/strong> only acts once per new bar, and reconnects if anything goes wrong instead of crashing.<\/li>\n<\/ul>\n<p><strong>Test it.<\/strong> Run the bot in PowerShell and watch the messages. Press <strong>Ctrl+C<\/strong> to stop it:<\/p>\n<pre><code class=\"language-powershell\">C:\\bots\\mt5bot\\venv\\Scripts\\python.exe C:\\bots\\mt5bot\\mt5_bot.py<\/code><\/pre>\n<p>A crossover might not happen for a while. To see a trade sooner while testing, temporarily set <code>TIMEFRAME = mt5.TIMEFRAME_M1<\/code>, <code>FAST_MA = 3<\/code> and <code>SLOW_MA = 5<\/code>.<\/p>\n<h2>Step 5: Run the Bot 24\/7 on Your VPS<\/h2>\n<p>MT5 is a desktop application, so it needs a <strong>signed-in Windows session<\/strong>. That affects how you keep it running:<\/p>\n<ul>\n<li><strong>Close the Remote Desktop window instead of signing out.<\/strong> Closing the window (or clicking Disconnect) leaves your session and programs running. Signing out closes MT5 and stops the bot.<\/li>\n<li><strong>Start the bot automatically when you sign in.<\/strong> This command creates a scheduled task that starts the bot at sign-in, with no time limit and no console window:<\/li>\n<\/ul>\n<pre><code class=\"language-powershell\">$action = New-ScheduledTaskAction -Execute \"C:\\bots\\mt5bot\\venv\\Scripts\\pythonw.exe\" `\r\n  -Argument \"C:\\bots\\mt5bot\\mt5_bot.py\" -WorkingDirectory \"C:\\bots\\mt5bot\"\r\n$trigger = New-ScheduledTaskTrigger -AtLogOn -User \"$env:USERDOMAIN\\$env:USERNAME\"\r\n$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable\r\nRegister-ScheduledTask -TaskName \"MT5 Python Bot\" -Action $action -Trigger $trigger -Settings $settings<\/code><\/pre>\n<p><code>pythonw.exe<\/code> runs Python without a console window, so the bot can&#8217;t be closed by accident. The bot starts MT5 itself if it isn&#8217;t already running.<\/p>\n<p><strong>After a VPS restart,<\/strong> 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 <span style=\"color: #000000;\"><a href=\"https:\/\/www.winserver.net\/blog\/mt4-mt5-auto-start-vps-restart\/\" style=\"color: #000000;\">how to start applications automatically after a VPS restart<\/a><\/span>. Our <span style=\"color: #000000;\"><strong><a href=\"https:\/\/www.winserver.net\/blog\/mt4-mt5-auto-start-vps-restart\/\" style=\"color: #000000;\">guide to starting MT4\/MT5 automatically after a restart<\/a> <\/strong><\/span>covers the trading terminal side.<\/p>\n<p><strong>Manage the task:<\/strong><\/p>\n<pre><code class=\"language-powershell\"># Start the bot now\r\nStart-ScheduledTask -TaskName \"MT5 Python Bot\"\r\n\r\n# Check when it last ran\r\nGet-ScheduledTask -TaskName \"MT5 Python Bot\" | Get-ScheduledTaskInfo\r\n\r\n# Stop the bot\r\nStop-ScheduledTask -TaskName \"MT5 Python Bot\"<\/code><\/pre>\n<h2>Step 6: Monitor Your Bot<\/h2>\n<p>Everything the bot does is written to <code>C:\\bots\\mt5bot\\bot.log<\/code>. To watch it live:<\/p>\n<pre><code class=\"language-powershell\">Get-Content C:\\bots\\mt5bot\\bot.log -Tail 20 -Wait<\/code><\/pre>\n<p>Press <strong>Ctrl+C<\/strong> to stop watching (this doesn&#8217;t stop the bot). Trades also appear in MT5&#8217;s <strong>Toolbox &gt; Trade<\/strong> and <strong>History<\/strong> tabs, with the comment &#8220;python bot&#8221;.<\/p>\n<p>For a quick check from your phone, the MetaTrader 5 mobile app can log in to the same account and show the bot&#8217;s trades and positions.<\/p>\n<h2>Troubleshooting<\/h2>\n<div style=\"overflow-x: auto;\">\n<table>\n<thead>\n<tr>\n<th>Problem<\/th>\n<th>Likely cause<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>pip<\/code> says &#8220;No matching distribution found for MetaTrader5&#8221;<\/td>\n<td>Not Windows, 32-bit Python, or a Python version too new for the package<\/td>\n<td>Use 64-bit Python 3.12 on Windows, as in Step 2.<\/td>\n<\/tr>\n<tr>\n<td><code>initialize() failed<\/code> with &#8220;IPC timeout&#8221;<\/td>\n<td>Python can&#8217;t reach the terminal<\/td>\n<td>Check <code>TERMINAL_PATH<\/code>, make sure MT5 can open normally, and run Python as the same Windows user as MT5.<\/td>\n<\/tr>\n<tr>\n<td>&#8220;Authorization failed&#8221; or &#8220;Not logged in&#8221;<\/td>\n<td>The terminal isn&#8217;t logged in<\/td>\n<td>Log in to MT5 manually once and tick <strong>Save password<\/strong>.<\/td>\n<\/tr>\n<tr>\n<td>Order rejected with retcode <strong>10027<\/strong><\/td>\n<td>Algo Trading is off in the terminal<\/td>\n<td>Click the <strong>Algo Trading<\/strong> button so it&#8217;s green (Step 1).<\/td>\n<\/tr>\n<tr>\n<td>Retcode <strong>10030<\/strong> (unsupported filling mode)<\/td>\n<td>The broker doesn&#8217;t accept the order&#8217;s filling mode<\/td>\n<td>The bot&#8217;s <code>choose_filling()<\/code> handles this. If you write your own code, test the modes with <code>order_check()<\/code>.<\/td>\n<\/tr>\n<tr>\n<td>Retcode <strong>10016<\/strong> (invalid stops)<\/td>\n<td>Stop loss or take profit too close to the price<\/td>\n<td>Increase <code>SL_POINTS<\/code> and <code>TP_POINTS<\/code>.<\/td>\n<\/tr>\n<tr>\n<td>Retcode <strong>10018<\/strong> (market closed)<\/td>\n<td>Weekend or holiday<\/td>\n<td>Normal. The bot carries on when the market opens.<\/td>\n<\/tr>\n<tr>\n<td>&#8220;Symbol not found&#8221;<\/td>\n<td>Your broker uses a different symbol name<\/td>\n<td>Run <code>check_mt5.py<\/code> to list similar symbols, then update <code>SYMBOL<\/code>.<\/td>\n<\/tr>\n<tr>\n<td>Bot stopped after closing Remote Desktop<\/td>\n<td>You signed out instead of disconnecting<\/td>\n<td>Close the Remote Desktop window rather than signing out.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<h2>Keep Your Trading VPS Safe and Stable<\/h2>\n<ul>\n<li><strong>Stay on demo until you trust the bot.<\/strong> Only set <code>DEMO_ONLY = False<\/code> after testing it for long enough to see how it behaves in different market conditions.<\/li>\n<li><strong>Protect Remote Desktop:<\/strong> use a strong password and limit who can connect. See <span style=\"color: #000000;\"><strong><a href=\"https:\/\/www.winserver.net\/blog\/windows-vps-firewall-rules-remote-access\/\" style=\"color: #000000;\">how to configure firewall rules on a Windows VPS without breaking remote access<\/a>.<\/strong><\/span><\/li>\n<li><strong>Control Windows Update restarts:<\/strong> set active hours, or schedule updates for the weekend when forex markets are closed, so a restart doesn&#8217;t interrupt the bot mid-trade.<\/li>\n<li><strong>Don&#8217;t store passwords in scripts:<\/strong> let the terminal remember the login, as in this guide.<\/li>\n<li><strong>Give it enough memory:<\/strong> Windows Server, MT5 and Python run comfortably on 4 GB of RAM. If you plan to run several terminals or bots, choose more.<\/li>\n<\/ul>\n<h2>Where to Go Next<\/h2>\n<ul>\n<li><strong>Use your own strategy:<\/strong> replace <code>get_signal()<\/code> with your own logic. The connection, order handling and logging stay the same.<\/li>\n<li><strong>Run several accounts:<\/strong> install a separate MT5 terminal for each account in its own folder and point each bot&#8217;s <code>TERMINAL_PATH<\/code> at its own terminal.<\/li>\n<li><strong>Get alerts:<\/strong> send a message to Telegram, Slack or email from <code>send_order()<\/code> when a trade opens or fails.<\/li>\n<li><strong>Automate updates:<\/strong> deploy new versions of your bot from GitHub. See <span style=\"color: #000000;\"><strong><a href=\"https:\/\/www.winserver.net\/blog\/github-actions-self-hosted-runner-windows-vps\/\" style=\"color: #000000;\">how to set up a GitHub Actions self-hosted runner on a Windows VPS<\/a>.<\/strong><\/span><\/li>\n<li><strong>Write code with AI:<\/strong> an AI coding assistant can help you extend the bot. See <span style=\"color: #000000;\"><strong><a href=\"https:\/\/www.winserver.net\/blog\/claude-code-vps\/\" style=\"color: #000000;\">how to run Claude Code 24\/7 on a Windows VPS<\/a>.<\/strong><\/span><\/li>\n<\/ul>\n<h2>FAQ<\/h2>\n<h3>Q1. Is the MetaTrader5 Python package free?<\/h3>\n<p>Yes. It&#8217;s published by MetaQuotes and installed with <code>pip install MetaTrader5<\/code>. You also need MT5 from a broker, which is free with a demo or live account.<\/p>\n<h3>Q2. Does the MetaTrader5 package work on Linux or macOS?<\/h3>\n<p>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.<\/p>\n<h3>Q3. Can I use Python with MT4?<\/h3>\n<p>Not with this package. The official <code>MetaTrader5<\/code> package only works with MetaTrader 5. MT4 has no official Python package, so most people automating MT4 use Expert Advisors written in MQL4.<\/p>\n<h3>Q4. Does the MT5 terminal need to stay open?<\/h3>\n<p>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&#8217;t running.<\/p>\n<h3>Q5. Do I need a VPS to run a Python trading bot?<\/h3>\n<p>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.<\/p>\n<h3>Q6. Which Python version should I use?<\/h3>\n<p>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&#8217;t supported.<\/p>\n<h3>Q7. Can I run bots for several accounts on one VPS?<\/h3>\n<p>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.<\/p>\n<h3>Q8. Does my broker allow automated trading?<\/h3>\n<p>Most brokers that offer MT5 allow algorithmic trading, but rules differ, so check your broker&#8217;s terms before running a bot on a live account.<\/p>\n<p><strong>Sources:<\/strong> MQL5 documentation: <a href=\"https:\/\/www.mql5.com\/en\/docs\/python_metatrader5\" target=\"_blank\" rel=\"noopener\">MetaTrader for Python<\/a>, <a href=\"https:\/\/www.mql5.com\/en\/docs\/python_metatrader5\/mt5initialize_py\" target=\"_blank\" rel=\"noopener\">initialize<\/a>, <a href=\"https:\/\/www.mql5.com\/en\/docs\/python_metatrader5\/mt5ordersend_py\" target=\"_blank\" rel=\"noopener\">order_send<\/a><\/p>\n<section class=\"winserver-cta-section\">\n<h2>Run Your Trading Bot Around the Clock<\/h2>\n<p>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.<\/p>\n<div class=\"winserver-cta-button-wrapper\"><span style=\"color: #ffffff;\"><a href=\"https:\/\/www.winserver.net\/#pricing\" class=\"winserver-cta-button\" target=\"_blank\" rel=\"noopener\" style=\"color: #ffffff;\">View Windows VPS Plans<\/a><\/span><\/div>\n<\/section>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1476,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"swell_btn_cv_data":"","footnotes":""},"categories":[372,352],"tags":[391,393,390,389,354,388,392,38],"_links":{"self":[{"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/posts\/1472"}],"collection":[{"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/comments?post=1472"}],"version-history":[{"count":5,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/posts\/1472\/revisions"}],"predecessor-version":[{"id":1510,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/posts\/1472\/revisions\/1510"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/media\/1476"}],"wp:attachment":[{"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/media?parent=1472"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/categories?post=1472"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.winserver.net\/blog\/wp-json\/wp\/v2\/tags?post=1472"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}