Metadata-Version: 2.4
Name: open-api-mt5
Version: 0.5.0
Summary: REST and WebSocket API project for MetaTrader 5
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: fastapi<1.0.0,>=0.116.0
Requires-Dist: MetaTrader5<6.0.0,>=5.0.0
Requires-Dist: uvicorn[standard]<1.0.0,>=0.35.0

# 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.

Set these environment variables in PowerShell before running:

```powershell
$env:MT5_PATH = "C:\Program Files\MetaTrader 5\terminal64.exe"
$env:MT5_LOGIN = "12345678"
$env:MT5_PASSWORD = "your-password"
$env:MT5_SERVER = "YourBroker-Server"
```

`MT5_PATH` is optional if MT5 is already discoverable, but setting it is recommended.
Mode, `apiKey`, `apiSecret`, and `registryUrl` are startup settings and are not stored by `/account/connect`.

## Run

```bash
open-api-mt5
```

Optional flags:

```bash
open-api-mt5 --port 9000
open-api-mt5 --host 0.0.0.0 --port 8000
open-api-mt5 --mode standalone
open-api-mt5 --mode secure --api-key your-api-key
open-api-mt5 --mode secure-client --api-key your-api-key --api-secret your-api-secret
open-api-mt5 --registry-url https://example.com/registry
open-api-mt5 --reload
```

Default port is `8000`.

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`
  - Example body:
    ```json
    {
      "username": "12345678",
      "password": "your-password",
      "server": "YourBroker-Server",
      "path": "C:\\Program Files\\MetaTrader 5\\terminal64.exe"
    }
    ```
- `POST http://127.0.0.1:8000/account/disconnect`
- If `--registry-url` is set at startup, the API sends a `POST` call to that URL with JSON body fields `address`, `port`, `apiKey`, and `accountId`

Security modes:
- Set the mode when starting the app with `--mode standalone`, `--mode secure`, or `--mode secure-client`
- Set the headers credentials when starting the app with `--api-key` and, for `secure-client`, `--api-secret`
- Set the registry target when starting the app with `--registry-url`
- `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:
    ```json
    {
      "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 using the 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:
    ```json
    {
      "ticket": 123456789,
      "stopLossValueInMoney": 10,
      "takeProfitValueInMoney": 30
    }
    ```
  - Response includes `stopLossPrice`, `takeProfitPrice`, `stopLossValueInMoney`, and `takeProfitValueInMoney` after rounding.

Calendar events endpoint:
- `GET http://127.0.0.1:8000/calendar/events`
- Optional query params: `fromDate`, `toDate` (ISO datetime), `country` (example: `US`), `currency` (example: `USD`)
- Default range when omitted: last 7 days to next 7 days

## 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:

```javascript
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

1. Build distribution files:

```bash
python -m pip install --upgrade build twine
python -m build
```

2. Upload to PyPI:

```bash
python -m twine upload dist/*
```

3. Install from PyPI and run:

```bash
pip install open-api-mt5
open-api-mt5 --port 8000
```

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

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

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

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