Skip to main content

open-api-mt5

MetaTrader 5 API service with FastAPI.

Important limitation

This API controls a single MT5 terminal/session instance per running service process.

  • A single API instance can be connected to only one account at a time.
  • /account/connect switches that single active session.
  • If you run multiple API services, use separate MT5 terminal instances/data folders for reliable isolation.

Setup

  1. Create a virtual environment:
    • Windows PowerShell: python -m venv .venv
  2. Activate it:
    • .\.venv\Scripts\Activate.ps1
  3. Install dependencies:
    • python -m pip install -U pip
    • pip install -e .

MT5 startup config

The API initializes MetaTrader 5 when FastAPI starts and closes it when FastAPI stops. It also checks MT5 connection every 5 seconds and tries to reconnect automatically if disconnected.

You can configure startup with a server_config.json file in the project directory:

{
  "path": "C:\\Program Files\\MetaTrader 5\\terminal64.exe",
  "username": "12345678",
  "password": "your-password",
  "server": "YourBroker-Server",
  "account": "12345678",
  "multiTerminal": true,
  "portable": true,
  "port": 8000,
  "apiKey": "optional-api-key"
}

account is required and is always checked against the connected MT5 account after initialization. The API loads MT5 and server startup settings from the JSON config file only; environment variables are not used for these settings. When the connected account does not match, it restarts the configured terminal path and retries up to 3 times; if the account still does not match, the API exits. If path is set, the API starts MT5 with portable=true by default so separate API instances can attach to separate MT5 terminal folders. Set "portable": false only if you intentionally want the normal shared MT5 data-directory behavior.

When running multiple API instances on the same Windows machine, start each process with its own explicit --config file containing a different path, account, and port. Mode, apiKey, apiSecret, and registryUrl are config-file startup settings and are not stored by /account/connect.

Run

open-api-mt5

Optional flags:

open-api-mt5 --reload
open-api-mt5 --config C:\path\to\server_config.json

Default port is 8000. Set host and port in the config file to change the bind address.

Run unit tests with coverage:

pip install -e ".[test]"
pytest

The calendar, bars, and trade services require 100% statement coverage.

Run only the mocked news-close integration scenarios:

pytest -m integration --no-cov

API docs:

  • Swagger UI: http://127.0.0.1:8000/docs
  • WebSocket docs in Swagger:
    • GET /ws/positions/open/docs

Health endpoint:

  • GET http://127.0.0.1:8000/health

Bars endpoints:

  • GET http://127.0.0.1:8000/bars/{symbol}?timeframe=M1&n=100
  • GET http://127.0.0.1:8000/bars/{symbol}/range?timeframe=M1&fromDate=2026-03-20T08:00:00Z&toDate=2026-03-20T12:00:00Z
  • GET http://127.0.0.1:8000/quotes/{symbol}
  • GET http://127.0.0.1:8000/ticks/{symbol}?count=100
  • GET http://127.0.0.1:8000/market-depth/{symbol}
  • GET http://127.0.0.1:8000/exposure
  • GET http://127.0.0.1:8000/exposure/{symbol}

Account connection endpoints:

  • POST http://127.0.0.1:8000/account/connect
    • Body: username, password, server, optional path, optional portable
    • Example body:
      {
        "username": "12345678",
        "password": "your-password",
        "server": "YourBroker-Server",
        "path": "C:\\Program Files\\MetaTrader 5\\terminal64.exe",
        "portable": true,
        "account": "12345678"
      }
      
  • POST http://127.0.0.1:8000/account/disconnect
  • If registryUrl is set in config, the API sends a POST call to that URL with JSON body fields address, port, apiKey, and accountId

Security modes:

  • Set mode in config to standalone, secure, or secure-client
  • Set apiKey in config and, for secure-client, apiSecret
  • Set the registry target with registryUrl in config
  • standalone: default mode, no X-apiKey or X-apiSecret header checks
  • secure: every HTTP endpoint and the open positions WebSocket require X-apiKey to match the locally stored apiKey
  • secure-client: every HTTP endpoint and the open positions WebSocket require both X-apiKey and X-apiSecret to match the locally stored values

Server info endpoint:

  • GET http://127.0.0.1:8000/api/server/info
  • Returns JSON with the locally stored apiKey and the detected machine ipAddress

Trade history endpoint:

  • GET http://127.0.0.1:8000/trades/history
  • Optional query params: fromDate, toDate (ISO datetime, UTC recommended)
  • If omitted, it returns the last 7 days by default

Modify an open position's stop loss / take profit:

  • POST http://127.0.0.1:8000/positions/modify
    • Body: ticket, optional sl, optional tp, optional comment
    • At least one of sl or tp is required. If one is omitted, its current MT5 value is kept.
    • Example body:
      {
        "ticket": 123456789,
        "sl": 1.0825,
        "tp": 1.095
      }
      

Open position details:

  • GET http://127.0.0.1:8000/positions/{ticket}/details
  • Returns entryPrice, stopLossPrice, takeProfitPrice, volume, contractSize, stopLossValue, and takeProfitValue
  • Value formula:
    • Buy stop loss: (entryPrice - stopLossPrice) * volume * contractSize
    • Buy take profit: (takeProfitPrice - entryPrice) * volume * contractSize
    • Sell stop loss: (stopLossPrice - entryPrice) * volume * contractSize
    • Sell take profit: (entryPrice - takeProfitPrice) * volume * contractSize

Adjust an open position's stop loss / take profit by money values:

  • POST http://127.0.0.1:8000/positions/adjust-by-money
    • Body: ticket, optional stopLossValueInMoney, optional takeProfitValueInMoney, optional comment
    • Defaults: stopLossValueInMoney = 10, takeProfitValueInMoney = 30
    • The API converts money values to SL/TP price distances in account currency using MT5 profit calculation when available, falling back to position entry price, volume, and symbol contract size.
    • If the exact value cannot be represented by the symbol price step, the API uses the nearest lower value.
    • Example body:
      {
        "ticket": 123456789,
        "stopLossValueInMoney": 10,
        "takeProfitValueInMoney": 30
      }
      
    • Response includes stopLossPrice, takeProfitPrice, stopLossValueInMoney, and takeProfitValueInMoney after rounding.

Close orders before economic news:

  • POST /orders accepts optional closeOnNews, for example M15_High, M10_Medium, or M5_Low
  • New orders with closeOnNews are rejected with HTTP 409 when a matching event is already inside the configured close window
  • GET /orders/news-close/jobs lists the current tagged pending orders and open positions watched by the news-close checker
  • The policy is stored in a durable SQLite file at .db/news_close_jobs.sqlite3 under the folder where the API process was started
  • Restart the API from the same folder to reuse the existing news-close jobs; set NEWS_CLOSE_DB_PATH only if you need a custom database path
  • High closes only for high-impact events; Medium closes for medium/high; Low closes for low/medium/high
  • Events are matched against the symbol's base, profit, or margin currency
  • Tagged open positions are closed and tagged pending orders are cancelled when an event enters the configured time window
  • Modifying SL/TP values, including money-based adjustments, keeps the stored news policy unchanged
  • The check runs once at startup and every 60 seconds; orders without closeOnNews are unchanged
  • Fair Economy events include an explicit timezone and are compared in UTC. If you test with timezone-less event timestamps, the API also considers the local computer timezone; set LOCAL_TIMEZONE (for example Europe/Paris) only if you need to override the OS timezone.

Calendar events endpoint:

  • GET http://127.0.0.1:8000/calendar/events
  • The default source is the Fair Economy current-week feed, cached in calendar_cache.json
  • The cache is loaded at startup, retried once on startup failure, refreshed every Monday at 00:00 UTC, and loaded lazily when missing or stale
  • Optional query params: source (fairEconomy or mt5), fromDate, toDate (ISO datetime), country/currency (example: USD), and impact (Low, Medium, High, or Holiday)
  • Fair Economy defaults to the current Monday-through-Sunday UTC week
  • Use source=mt5 for the previous MT5 calendar behavior (default range: last 7 days to next 7 days)
  • Optional environment variables: FAIR_ECONOMY_CALENDAR_URL and FAIR_ECONOMY_CALENDAR_CACHE_PATH

WebSocket streams

Open positions stream:

  • ws://127.0.0.1:8000/ws/positions/open
  • Optional query param: intervalSeconds (poll interval, bounded to 0.2..60)
  • Events:
    • subscribed
    • positionsSnapshot
    • error
  • positionsSnapshot includes:
    • positions[].pnl (position PnL, sourced from MT5 profit)
    • totalPnl (sum of all open positions PnL)

Example JavaScript client:

const ws = new WebSocket("ws://127.0.0.1:8000/ws/positions/open?intervalSeconds=1");
ws.onmessage = (event) => {
  const payload = JSON.parse(event.data);
  console.log(payload.event, payload);
};

Build and publish package

Build distribution files locally:

python -m pip install --upgrade build
python -m build

Install from the local build to smoke-test it:

python -m pip install --force-reinstall dist/*.whl
open-api-mt5 --help

Install from PyPI and run:

pip install open-api-mt5
open-api-mt5 --config C:\path\to\server_config.json

Release to PyPI

The repository includes .github/workflows/publish.yml, which runs tests, builds the package, and publishes to PyPI with a PyPI API token. It only runs from release branches named like release/0.5.

One-time PyPI token setup:

  1. Create or open your PyPI account.
  2. Create a PyPI API token. If the project already exists on PyPI, prefer a project-scoped token for open-api-mt5; otherwise create an account-scoped token for the first upload.
  3. In GitHub, open repository Settings -> Secrets and variables -> Actions.
  4. Add a repository secret named PYPI_API_TOKEN with the token value from PyPI. The workflow uses PyPI username __token__ and this secret as the password.
  5. Optional: create a GitHub environment named pypi and add required reviewers if you want manual approval before publishing.

Prepare a release:

  1. Update version in pyproject.toml.
  2. Run tests:
    python -m pip install -e ".[test]"
    python -m pytest
    
  3. Build locally:
    python -m pip install --upgrade build
    python -m build
    
  4. Commit the version change:
    git add pyproject.toml
    git commit -m "Release 0.6.1"
    
  5. Create and push a release branch:
    git checkout -b release/0.6.1
    git push origin release/0.6.1
    
  6. The Publish to PyPI workflow runs on pushes to release/**. You can also run it manually from GitHub Actions, but select a release/** branch such as release/0.6.1.

PyPI rejects reused versions, so every release must have a new pyproject.toml version.

Manual upload with a token, if needed:

python -m pip install --upgrade twine
python -m twine upload dist/* -u __token__ -p "<your-pypi-token>"

Optional: standalone executable (no Python required on target machine)

If you want users to run it without installing Python, build an executable:

python -m pip install pyinstaller
pyinstaller --onefile --name open-api-mt5 app/cli.py

The executable will be in dist/open-api-mt5.exe.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

open_api_mt5-0.8.2.tar.gz (57.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

open_api_mt5-0.8.2-py3-none-any.whl (38.1 kB view details)

Uploaded Python 3

File details

Details for the file open_api_mt5-0.8.2.tar.gz.

File metadata

  • Download URL: open_api_mt5-0.8.2.tar.gz
  • Upload date:
  • Size: 57.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for open_api_mt5-0.8.2.tar.gz
Algorithm Hash digest
SHA256 381f3edb55da3583201d139effb2dd48736163edba91e2e816d09965d0ded286
MD5 48359f5497362e44cf2cb68694176312
BLAKE2b-256 55058f6667e144bd16e6483dbe93940acd1bc3c695fe6e06bb9e73f113fd9d0b

See more details on using hashes here.

File details

Details for the file open_api_mt5-0.8.2-py3-none-any.whl.

File metadata

  • Download URL: open_api_mt5-0.8.2-py3-none-any.whl
  • Upload date:
  • Size: 38.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for open_api_mt5-0.8.2-py3-none-any.whl
Algorithm Hash digest
SHA256 aaeff9c3dd82a2c37d978e273fa3c95947225ccd323c1a66eae7a05bafcb17a8
MD5 ba3dfde910cc67aef465609919c82da3
BLAKE2b-256 9a0f8b1afc93106751a3978540eabb23f9f48a07d8997ba55c6e0e3b85b060d5

See more details on using hashes here.

Release history Release notifications | RSS feed

0.10.2

2 files

0.10.1

2 files

0.10.0

2 files

0.9.2

2 files

0.9.1

2 files

0.9.0

2 files

0.8.4

2 files

0.8.3

2 files

This release

0.8.2 This release

2 files

0.8.1

2 files

0.8.0

2 files

0.7.5

2 files

0.7.4

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page