Command-line client
mt4-quote-bridge is QuoteBridge's command-line client (repository
mt4-quote-bridge-cli). Each command calls one or more
REST API v1 operations; this page lists them all. Trading rules are in Trading. To let an
AI agent run it for you, from this PC or another computer, see AI agents and remote access.
Install it by adding its agent skill (npx skills@latest add https://github.com/ross-jill-ws/mt4-quote-bridge-cli),
which installs the CLI the first time the agent uses it, or on its own with Node.js 22.12 or later:
npm pack github:ross-jill-ws/mt4-quote-bridge-cli
npm install --global ./mt4-quote-bridge-cli-<version>.tgz
mt4-quote-bridge <command> <subcommand> [options]
Every command has --help with its options, output and exit codes.
Sign-in, scopes and what stays in the app window
| Command | Does | API |
|---|---|---|
auth server [url] |
Saves the server URL after checking it (default http://127.0.0.1:5580) |
GET /api/v1/info |
auth login [--api-key-stdin] |
Saves an API key after checking it | GET /api/auth/session |
auth status |
Shows the key's name, scope and expiry, checked with the app | GET /api/v1/info, GET /api/auth/session |
auth logout |
Forgets the saved key locally (revoke it in the app to disable it) | — |
config show |
Shows the effective server, config file and whether a key is saved (never the key) | — |
- The app answers on this computer only (
127.0.0.1,localhostor[::1]), on the HTTP port in Settings › Connection. From another computer, go through a tunnel such asngrok http 5580 --host-header=rewriteand pass its address toauth server; see AI agents and remote access. - Create keys in the app window › Settings › API keys.
readonlyreads everything but screenshots;traderalso takes screenshots and previews, executes and reviews requests. Areadonlykey's screenshot, preview, execute or review answers exit 4 (insufficient-scope). - Installation and setup, API keys, network settings, recording, Demo trading, Real-account trading, the lot cap and slippage are the app window's alone. No command changes them.
- The key is saved in
~/.mt4-quote-bridge/config.json(owner-only).MT4QB_SERVER,MT4QB_API_KEYandMT4QB_CONFIGoverride the saved values.
Identities
- Market: a broker server and a symbol, both exact and case-sensitive. Name it
SYMBOL --broker <broker server>or--market-id <id>.SYMBOLalone works only when one broker server has it. - Account: a broker server and a login. Name it
--account <account-id>, or--account <login> --broker <broker server>; a bare login only when one broker server has it. There is no default, saved or active account. Where a command takes both, the account fixes the broker server the symbol is looked up on. - Tickets are MetaTrader's and unique only within an account, so every ticket command takes
--account. - Ambiguity lists the candidates (
error.details.candidates) and exits 6. Nothing is matched by substring, case or alias, and there is no interactive picker.
Resolution reads GET /markets?symbol=…[&brokerServer=…] and GET /accounts, following every cursor.
Commands
Markets
| Command | API |
|---|---|
markets list [--broker S] [--symbol S] |
GET /markets?brokerServer&symbol |
markets show SYMBOL |
GET /markets/{marketId} |
markets quote SYMBOL |
GET /markets/{marketId}/quote |
markets candles SYMBOL --timeframe TF [--from] [--to] [--limit] |
GET /markets/{marketId}/candles |
markets samples SYMBOL [--from] [--to] |
GET /markets/{marketId}/samples |
markets history SYMBOL |
GET /markets/{marketId}/history |
markets export SYMBOL --timeframe TF --format csv|hst --output FILE [--force] [--from] [--to] |
GET /markets/{marketId}/export |
markets indicator PACKAGE SYMBOL --timeframe TF [--param id=value]… [--from] [--to] [--limit] |
GET /markets/{marketId}/indicators/{packageId} |
TF is M1 M5 M15 M30 H1 H4 D1 W1 MN1. Candles and indicator values are the newest --limit bars within
--from/--to; the table output prints the --to of the page before on stderr.
Exports write the file named by --output (required). An existing file is never replaced unless --force is
given (exit 13 otherwise); an interrupted or refused export leaves no file. With --json, stdout gets the file's
metadata, never its bytes:
{ "data": { "path": "C:\\data\\EURUSD60.csv", "bytes": 1234, "contentType": "text/csv; charset=utf-8", "marketId": "…", "brokerServer": "EasyForex-Demo", "symbol": "EURUSD", "timeframe": "H1", "format": "csv", "from": null, "to": null }, "meta": {} }
Accounts
| Command | API |
|---|---|
accounts list |
GET /accounts |
accounts show --account A |
GET /accounts/{accountId} |
accounts funds --account A |
GET /accounts/{accountId}/funds |
accounts conditions SYMBOL --account A (or --market-id) |
GET /accounts/{accountId}/markets/{marketId}/conditions |
Positions and pending orders
| Command | API |
|---|---|
positions list --account A |
GET /accounts/{accountId}/positions |
positions show TICKET --account A |
GET /accounts/{accountId}/positions/{ticket} |
positions modify TICKET --account A [--sl P|none] [--tp P|none] |
POST /requests/preview (modify) |
positions close TICKET --account A |
POST /requests/preview (close) |
orders list --account A |
GET /accounts/{accountId}/orders |
orders show TICKET --account A |
GET /accounts/{accountId}/orders/{ticket} |
orders preview --account A --symbol S --side buy|sell --type market|limit|stop --lots L [--price] [--expiry] [--sl] [--tp] |
POST /requests/preview (place) |
orders preview … --risk-amount|--risk-percent|--risk-points X --reward-ratio R |
POST /requests/preview (place with risk) |
orders preview … [--sl-points N] [--tp-points N] |
POST /requests/preview (place with distance) |
orders modify TICKET --account A [--price] [--expiry T|none] [--sl P|none] [--tp P|none] |
POST /requests/preview (modify) |
orders cancel TICKET --account A |
POST /requests/preview (cancel) |
While an account's terminal is offline, its positions and orders are its last snapshot (meta.state stale), or
none when no terminal of it has answered since the app started (unavailable); the table output says so on stderr.
Options left out of a modify stay as they are; none clears a stop or an expiry (sent as null; zero is not a
clear). There are no lot changes or partial closes.
Requests
| Command | API |
|---|---|
requests list [--account A] |
GET /requests?accountId |
requests show ID |
GET /requests/{requestId} |
requests execute ID --confirm [--wait S] |
POST /requests/{requestId}/execute once, then GET /requests/{requestId} |
requests review ID --note TEXT --acknowledge |
POST /requests/{requestId}/review |
Indicators and terminals
| Command | API |
|---|---|
indicators list / indicators show PACKAGE |
GET /indicators[/{packageId}] |
terminals list / terminals show ID |
GET /terminals[/{terminalId}] |
terminals feeds ID [FEED] |
GET /terminals/{terminalId}/feeds[/{feedId}] |
Terminals are diagnostics only; they never route trading.
Screenshots
| Command | API |
|---|---|
screenshot feeds [--output FILE [--force]] [--json] |
GET /screenshots/feeds |
screenshot chart SYMBOL [--broker S | --market-id ID] [--output FILE [--force]] [--json] |
GET /screenshots/charts/{marketId} |
A screenshot checks that the app looks healthy. It switches your own app window to
Feeds, or to the market's chart from its best source feed, and leaves it there; a minimised or hidden window is
captured without being shown. It needs a trader key. SYMBOL is named as for every market command.
The CLI always asks for JSON. By default it prints the window's health, never the image:
View: chart
Market: EURUSD · EasyForex-Demo
Window: hidden
Live: live
Size: 1800 × 1200
Scale: 1.5
Time: 2027-01-15T08:00:00.000Z
--json prints the app's answer, the PNG in base64 included. --output FILE writes the PNG on the CLI's own computer
under the export rules: an existing file is never replaced without --force (exit 13, and the window is not
touched), and a refused screenshot leaves no file. With --json, stdout then gets the health fields plus path and
bytes, without the base64.
| Refusal | Exit |
|---|---|
insufficient-scope: a readonly key |
4 |
window-busy: a dialog is open in the app window; nothing was switched or taken |
7 |
no-feed: no terminal streams the market (chart only) |
7 |
window-unavailable: no window to capture (the app is starting, or its window is being recreated) |
9 |
screenshot-timeout: the window was not captured within ten seconds |
9 |
Trading from the command line
- Every mutation command previews:
orders preview|modify|cancel,positions modify|close. Nothing is sent; the preview lasts 30 seconds and prints its request ID and the execute command. requests execute ID --confirmis the only command that sends anything. There is no--yes, no prompt to skip and no confirmation by default.--confirmstates that the account holder approved that exact preview.- Execute posts once and never retries. It then only reads the request until a definitive result or
--waitseconds (25 by default, 0–60). A lost answer, a timeout or an unknown outcome is answered withrequests show ID, never with another execute or preview. When the app cannot be reached at all (the connection is refused) or no key is saved, execute says so and that nothing was sent. - An unknown outcome blocks its account, previews included, until a result arrives or you review it after checking MetaTrader.
The CLI's agent skill requires the user's explicit approval of each preview; agents never execute on their own initiative.
Output, numbers and times
- Tables for people.
--jsonprints the API's envelope exactly:{ "data": …, "meta": … }.--allon a collection prints one envelope with every item and the last page'smeta(such as an account'scurrencyandstate), itscountandlimitreplaced bycount,pagesandnextCursor: null. - Errors and hints go to stderr only; the table output prints an error's
details.reason(an indicator's or the EA's) on the line below its message. With--json, an error is{ "error": { "code", "message", "details" } }on stderr: the app's error code, or the CLI's ownusage,ambiguous-market,ambiguous-account,unknown-market,unknown-account,output-exists,not-signed-in,invalid-key(not aqbk_key, exit 3),unreachable,redirect(the server redirects elsewhere) orcli-error(anything else, such as a server that is not QuoteBridge). - Prices, lots and money are decimal strings, as the API gives them. Pass decimals as plain digits (
0.10,1.08500). - Times are UTC ISO-8601 with a
brokerTimesibling.--from/--totakeYYYY-MM-DDTHH:MM:SS[.sss]Z, or a dateYYYY-MM-DD(that UTC day's start for--from, its last second for--to).--expirytakes UTC whole seconds. - Collections are cursor-paged:
--limit,--cursor(the previous page'smeta.nextCursor) or--all. Table output prints the next--cursoron stderr.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. For requests show/execute: succeeded, reviewed, or a valid preview |
| 1 | Other error: the app is unreachable or answered unexpectedly (500) |
| 2 | Usage error, or a malformed value (400) |
| 3 | Not signed in, or the key is invalid or revoked (401) |
| 4 | Forbidden (403): readonly key, Demo trading off, a real account with Real-account trading off (real-trading-disabled), symbol not tradable |
| 5 | Not found (404) |
| 6 | Ambiguous symbol or login |
| 7 | Conflict (409): cross-broker market, preview expired or invalidated, account unresolved, a real account with no EnableRealTrading chart (executor-real-account), no single ready executor, a dialog open in the app window (window-busy), no feed for a chart (no-feed) |
| 8 | Invalid trading or indicator values (422) |
| 9 | Source or app window unavailable, the terminal did not answer in five seconds, or a screenshot in ten (503, 504); not an order failure |
| 10 | The request's outcome is unknown and not reviewed: never retry |
| 11 | The request failed definitively |
| 12 | The request was sent and has no definitive result yet |
| 13 | The output file of an export or screenshot exists (no --force) |
This page is docs/cli.md in the app's repository.View on GitHub