v4.14.0

The symbol-suffix, symbol-import, body-cap, journal-size and log-tail work below was contributed by @Marinski in #18.

- `POST /symbols/import` fills a terminal's symbol cache from a JSON list without calling the MT5 SDK, so a `mode: backtest` terminal can be primed. Each import is merged into the existing cache. `"complete": true` says the list is the broker's full book and replaces the cache instead. A partial import keeps the cache's `updated` stamp, so only a full book restarts its trust window. The body is bounded by `symbol_import_max_body_bytes` (2 MiB, `413` before parsing, `411` without a `Content-Length`), `symbol_import_max_symbols` (20000) and `symbol_import_max_symbol_length` (64). See [Market data](docs/market-data.md).
- Every request body is capped before it is parsed: `max_request_body_bytes` (4 MiB) for JSON, `max_upload_body_bytes` (25 MiB, matching nginx's `client_max_body_size`) for multipart `POST /backtest`. Over the cap is a `413`.

  For all five byte and count settings, zero, a negative or a non-integer falls back to the default with a warning in the API log. There is no value that turns a cap off.
- `scripts/measure-broker-offsets.py` reads each terminal's latest tick and prints the `utc_offset` to set, for re-measuring after a DST change. It only works against `mode: live` terminals. See [Installation and configuration](docs/installation-and-configuration.md).
- The rotator truncates a terminal journal over `MAX_LOG_BYTES` (default 2 GiB) once it has been idle for `IDLE_MINUTES` (default 30). One high-frequency backtest can fill the disk with today's journal long before the `RETAIN_DAYS` age pass reaches it. The file is truncated in place with its mtime kept, because the terminal holds it open. See [Operations](docs/operations.md).
- `tests/integration/test_mcpunifier.py` calls the unifier's `endpoints` tool over MCP and compares the result with the API's Flask routes in both directions.

- **`GET /symbols` returns `409` on a `mode: backtest` terminal.** It used to call `mt5.initialize()` there, which starts `terminal64.exe` and holds the tester's data-dir lock, so every later backtest on that terminal came back empty. Callers that listed symbols on a backtest terminal get a `409` now and should use `POST /symbols/import`. The refusal does not wait for the MT5 lock.
- **Every other MT5 SDK route answers `503` on a `mode: backtest` terminal.** Account, orders, positions, history, market data and terminal info all went through the same reconnect path, which started `terminal64.exe` on a backtest terminal and left later backtests there with an empty report. They now refuse before any SDK call.
- **`start.bat` launches only the terminals in its VM's group.** `config_helper.py terminals` now applies the same `config/vm-group.txt` filter that `check_health.py` and the container healthcheck already use. Before, every VM in a multi-VM install prepared and served every terminal, including another VM's live terminal on the same shared data dir. A single-VM install, or one with no group file, still gets every terminal.

- `symbol_suffix` is no longer appended to a symbol the broker only carries bare. Brokers often suffix part of their book: Eightcap Global has `EURUSD.i` but a bare `XAUUSD`, so every non-FX backtest there asked for a symbol that does not exist. The suffix is skipped only when a complete symbol list, written by an unfiltered `GET /symbols` on a live terminal or by an import with `"complete": true`, has the bare name and not the suffixed one. With no cache, a partial one, or one older than `symbol_cache_max_age` (default `7d`; a bare number is seconds), the suffix is appended as before.
- Log tails (`GET /backtest/<id>/tail`) read at most the last 256 KiB of the file. A multi-gigabyte Tester log used to be read whole on every poll, which held the process long enough to fail `/ping` and the healthcheck.