@mfxhub/sdk 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,10 @@ export interface paths {
10
10
  path?: never;
11
11
  cookie?: never;
12
12
  };
13
- /** List the tenant's terminals, newest first (cursor pagination) */
13
+ /**
14
+ * List the tenant's terminals, newest first (cursor pagination), filtered by label and state
15
+ * @description Every terminal of yours, RETIRED ones included, newest first. `label` and `state` narrow the list in the query, before the page is cut, so a filtered page is never short for being filtered.
16
+ */
14
17
  get: operations["listTerminals"];
15
18
  put?: never;
16
19
  /**
@@ -37,7 +40,10 @@ export interface paths {
37
40
  get: operations["getTerminal"];
38
41
  put?: never;
39
42
  post?: never;
40
- /** Retire a terminal — it drains, then retires; answered with its state now */
43
+ /**
44
+ * Retire a terminal — it drains, then retires; answered with its state now
45
+ * @description A terminal that is ACTIVE, OFFLINE, PARKED or still PROVISIONING (waiting for its login, for example for a credential) drains: its cell stops it and deletes its folder, and it is RETIRED when the cell confirms, or by the platform's deadline when the cell never held it. One that is REQUESTED, FAILED or DRAINED is RETIRED at once. One that is LOGGING_IN drains as PROVISIONING does: its cell stops the login and deletes its folder. A login ends by itself within 15 minutes: FAILED when it has not completed, at once when the broker refuses it. A FAILED terminal is tried again after `terminals.login_cooloff_minutes` (30), at most `terminals.login_cycles_per_day` (3) times a day, never after a refusal; retire the FAILED terminals you no longer need. MFX retires none of yours by itself. One already DRAINING or RETIRED is answered as it is.
46
+ */
41
47
  delete: operations["retireTerminal"];
42
48
  options?: never;
43
49
  head?: never;
@@ -48,6 +54,28 @@ export interface paths {
48
54
  patch: operations["updateTerminal"];
49
55
  trace?: never;
50
56
  };
57
+ "/terminals/{id}/journal": {
58
+ parameters: {
59
+ query?: never;
60
+ header?: never;
61
+ path: {
62
+ id: components["parameters"]["terminalId"];
63
+ };
64
+ cookie?: never;
65
+ };
66
+ /**
67
+ * The terminal's MetaTrader journal, newest first - read live while it runs, its last lines for 24 hours after it stops
68
+ * @description While the terminal runs, the lines are read from its cell on request (`from: live`, within 10 s, as a quote is). When it has stopped, failed or been retired, the answer is the last lines its cell kept at the stop (`from: last_words`), kept 24 hours. Account numbers are masked (`****` and the last four digits, yours included) and the cell's file paths are removed; MetaTrader writes no password to its journal. Expert advisor logs are not included. Every read is audited. Another tenant's terminal is 404.
69
+ */
70
+ get: operations["getTerminalJournal"];
71
+ put?: never;
72
+ post?: never;
73
+ delete?: never;
74
+ options?: never;
75
+ head?: never;
76
+ patch?: never;
77
+ trace?: never;
78
+ };
51
79
  "/terminals/{id}/instruments": {
52
80
  parameters: {
53
81
  query?: never;
@@ -92,6 +120,28 @@ export interface paths {
92
120
  patch?: never;
93
121
  trace?: never;
94
122
  };
123
+ "/terminals/{id}/ha": {
124
+ parameters: {
125
+ query?: never;
126
+ header?: never;
127
+ path: {
128
+ id: components["parameters"]["terminalId"];
129
+ };
130
+ cookie?: never;
131
+ };
132
+ get?: never;
133
+ put?: never;
134
+ /**
135
+ * Turn MFX Hub HA on or off for a terminal, or change its mode
136
+ * @description On, MFX Hub creates a second terminal on the same broker login, on separate hardware in the terminal's farm, and keeps it ready (`ha.fallback_state`); the terminal's id answers as before. Off, the second terminal is retired. Sent again, the same ask changes nothing. Refused: a plan that does not include HA (403 `forbidden`, `details.reason` `ha_not_in_plan`); a terminal that is not REQUESTED, PROVISIONING, LOGGING_IN, ACTIVE or OFFLINE (409 `state_error`); a terminal carrying an expert advisor, or on a farm that runs customer code (409 `conflict`, `details.reason` `ea_attached` or `customer_code_farm`); a login whose broker is not known to allow one more session (409 `placement_error`, `details.reason` `login_session_limit`); warm mode, not offered yet (422 `unprocessable`). While HA is on, attaching an expert advisor is 409 `conflict` with `details.reason` `ha_on`.
137
+ */
138
+ post: operations["setTerminalHa"];
139
+ delete?: never;
140
+ options?: never;
141
+ head?: never;
142
+ patch?: never;
143
+ trace?: never;
144
+ };
95
145
  "/terminals/{id}/resume": {
96
146
  parameters: {
97
147
  query?: never;
@@ -184,6 +234,32 @@ export interface paths {
184
234
  patch?: never;
185
235
  trace?: never;
186
236
  };
237
+ "/terminals/{id}/expert-advisor/parameters": {
238
+ parameters: {
239
+ query?: never;
240
+ header?: never;
241
+ path: {
242
+ id: components["parameters"]["terminalId"];
243
+ };
244
+ cookie?: never;
245
+ };
246
+ /**
247
+ * The newest parameter set and the version your EA reports it runs
248
+ * @description 404 when no set was ever sent for the terminal, or for another tenant's terminal.
249
+ */
250
+ get: operations["getExpertAdvisorParameters"];
251
+ /**
252
+ * Send your EA a new parameter set - the platform numbers it, delivers it to the terminal, and keeps it for every later copy of the terminal
253
+ * @description The set replaces the one before. The platform gives it the next `version` (1 for the first), answers at once, and delivers it to the terminal's EA, which reads it from its parameter file. You never send a version: ordering is the platform's, so a retry or a race can never put an older set back. The newest set is also part of the terminal's desired state, so a terminal copied again (detach and attach, or a move to another cell) starts with it, not with the EA's defaults. `applied_version` on getExpertAdvisorParameters says when your EA runs it: your EA echoes the version it runs in every heartbeat line. Refused: 409 `no_ea_attached` when the terminal carries no expert; 400 `validation_error` when the set is not an object, when the file it makes would exceed 256 KiB (`details.bytes`, `details.max`), or when a key anywhere in it is named like a credential (`details.key`) - your EA can read this file, so it must never hold one; 404 for another tenant's terminal. The same Idempotency-Key with the same body answers the same version again; with another body it is 409 `conflict` with `details.reason = idempotency_key_reused`. What the cell answered is kept on the platform: read the outcome on getExpertAdvisorParameters (`version`, `applied_version`, `converged`); getCommand shows only the command's own state.
254
+ */
255
+ put: operations["putExpertAdvisorParameters"];
256
+ post?: never;
257
+ delete?: never;
258
+ options?: never;
259
+ head?: never;
260
+ patch?: never;
261
+ trace?: never;
262
+ };
187
263
  "/terminals/{id}/operations/{operation_id}": {
188
264
  parameters: {
189
265
  query?: never;
@@ -404,7 +480,7 @@ export interface paths {
404
480
  };
405
481
  /**
406
482
  * Command state + result — mt5_retcode, retcode_text, reason (granular), comment_raw, tickets, exec_price, exec_volume, broker_latency_ms, agent_latency_ms, terminal_state
407
- * @description UNKNOWN is visible and not final; it resolves to SUCCEEDED or FAILED by reconciliation. Another tenant's command is 404.
483
+ * @description UNKNOWN is visible and not final; the platform asks the terminal again until `resolve_by` and resolves it to SUCCEEDED, FAILED or REJECTED (command.unknown, then command.resolved). Another tenant's command is 404.
408
484
  */
409
485
  get: operations["getCommand"];
410
486
  put?: never;
@@ -475,7 +551,9 @@ export interface paths {
475
551
  "/terminals/{id}/history/deals": {
476
552
  parameters: {
477
553
  query: {
554
+ /** @description A UTC instant; the broker's own clock is applied on the way to MT5 */
478
555
  from: string;
556
+ /** @description A UTC instant, as from */
479
557
  to: string;
480
558
  /** @description Only the deals of this position (Deal.position): its opening deal, any partial closes and its closing deal, inside from/to. A position open longer than 31 days is read window by window */
481
559
  position?: number;
@@ -490,7 +568,7 @@ export interface paths {
490
568
  };
491
569
  /**
492
570
  * Deals in a window, newest first by (time, deal), optionally of one position
493
- * @description Read from the broker on demand with a read-only request through the terminal, bounded at 10 s. Nothing is stored: the broker is the book of record for history. `to - from` is at most 31 days; `next_cursor` continues the window before the oldest deal returned. With `position`, only that position's deals are returned: the window is still read from the broker and the other deals are left out, so a page can hold fewer than `limit`, and even none, while `next_cursor` is not null. Keep asking until it is null.
571
+ * @description Read from the broker on demand with a read-only request through the terminal, bounded at 10 s. Nothing is stored: the broker is the book of record for history. `to - from` is at most 31 days; `next_cursor` continues the window before the oldest deal returned. With `position`, only that position's deals are returned: the window is still read from the broker and the other deals are left out, so a page can hold fewer than `limit`, and even none, while `next_cursor` is not null. Keep asking until it is null. Every time is UTC: MT5 stamps deals on the broker server's own clock (UTC+2 or UTC+3 on many servers, moving with daylight saving), and the platform converts each deal, and the window, with that server's measured clock. When no clock has been measured for the server yet the read is refused rather than guessed (503 `broker_clock_unknown`); it answers once a terminal on the server has received a price.
494
572
  */
495
573
  get: operations["listDeals"];
496
574
  put?: never;
@@ -559,6 +637,28 @@ export interface paths {
559
637
  patch?: never;
560
638
  trace?: never;
561
639
  };
640
+ "/brokers/{id}/catalogues": {
641
+ parameters: {
642
+ query?: never;
643
+ header?: never;
644
+ path: {
645
+ id: components["parameters"]["pathId"];
646
+ };
647
+ cookie?: never;
648
+ };
649
+ /**
650
+ * One row per server and account type you hold a catalogue for, with a hash of the whole list - compare it to re-read only what changed
651
+ * @description `catalogue_hash` changes when any symbol is added, removed or comes back, or any symbol's spec_hash, description or identity fields change; never on a quote figure. A tenant that holds no catalogue for this broker gets an empty list.
652
+ */
653
+ get: operations["listCatalogues"];
654
+ put?: never;
655
+ post?: never;
656
+ delete?: never;
657
+ options?: never;
658
+ head?: never;
659
+ patch?: never;
660
+ trace?: never;
661
+ };
562
662
  "/brokers/{id}/symbols": {
563
663
  parameters: {
564
664
  query?: {
@@ -580,7 +680,7 @@ export interface paths {
580
680
  };
581
681
  /**
582
682
  * The tenant's symbol catalogue for one server and account type — spec, trade_mode (quotable ≠ tradeable), pip_value_per_lot, spec_hash, trade sessions and derived status
583
- * @description Read from the tenant's own terminals on that server. MFX holds no broker accounts and never shares one tenant's catalogue with another. A server where the tenant runs no terminal answers 409 (`state_error`, details.reason `no_terminal_on_server`), never an empty list.
683
+ * @description Read from the tenant's own terminals on that server. MFX holds no broker accounts and never shares one tenant's catalogue with another. A server and account type where your terminals reported a catalogue and none runs any more is answered from the last report, with `source: last_known` and each row's `refreshed_at`. A server you have never run a terminal on answers 409 (`state_error`, details.reason `no_terminal_on_server`), never an empty list: start a Read only terminal there with your own login.
584
684
  */
585
685
  get: operations["listSymbols"];
586
686
  put?: never;
@@ -699,7 +799,7 @@ export interface paths {
699
799
  path?: never;
700
800
  cookie?: never;
701
801
  };
702
- /** Historical candles by bar time, read through one of your own terminals on the server; at most 1000 bars per call, the newest of the window */
802
+ /** Historical candles by bar time, from the platform's store when it holds the window complete, else read through one of your own terminals on the server; at most 1000 bars per call, the newest of the window */
703
803
  get: operations["getCandles"];
704
804
  put?: never;
705
805
  post?: never;
@@ -779,7 +879,28 @@ export interface paths {
779
879
  path?: never;
780
880
  cookie?: never;
781
881
  };
782
- /** Quota consumption and remaining budget per metric (commands/s, md msgs/s, ws conns) */
882
+ /**
883
+ * The calling key's and its tenant's limits, where each comes from, and how much is in use
884
+ * @description Every limit that applies to the calling key and its tenant, so an integration paces itself
885
+ * below them. Per-key figures come from the tenant's plan unless the tenant or the key has
886
+ * its own (the key's beats the tenant's, which beats the plan's), and say whether they are
887
+ * applied yet (`enforced`).
888
+ *
889
+ * Defaults per plan, per API key:
890
+ *
891
+ * | Limit | Standard | Enterprise | System |
892
+ * |---|---|---|---|
893
+ * | Commands per second, steady | 5 | 20 | 50 |
894
+ * | Commands, burst | 10 | 50 | 100 |
895
+ * | Market-data messages per second | 200 | 2,000 | 5,000 |
896
+ * | Stream connections | 3 | 10 | 20 |
897
+ * | Account channels per connection | 50 | 500 | 1,000 |
898
+ * | Terminals per tenant | 20 | 200 | 500 |
899
+ * | Terminals per broker login | the broker's own limit | the broker's own limit | the broker's own limit |
900
+ *
901
+ * The terminals-per-tenant figures apply together with per-key enforcement; until then the
902
+ * tenant's terminal limit is the one `tenant.terminals` serves.
903
+ */
783
904
  get: operations["getUsage"];
784
905
  put?: never;
785
906
  post?: never;
@@ -926,7 +1047,7 @@ export interface paths {
926
1047
  };
927
1048
  /**
928
1049
  * WebSocket upgrade (wss://ws.mfxhub.com/v1/stream; dev wss://ws.dev.mfxhub.com/v1/stream) — Ed25519 signature at upgrade (mfxSignature); channels md · candles · events · results · account · ea · book; 24 h max lifetime; drop-oldest ticks per slow client
929
- * @description Control messages are JSON: `StreamClientMessage` in, `StreamServerMessage` out. Channels are keyed by id (`StreamChannel`). Results and events carry a per-channel `seq` and are replayed on `resume` for up to 15 minutes; ticks and candles are not replayed. Account state travels on the terminal's own `account:{terminal_id}` channel, not on `events`: `account.snapshot` on every subscribe and resume (a snapshot, never a replay), when the position set changes and every 60 s, and `account.marks` in between. Account messages are samples: a slow client loses them before any event, and a `seq` gap means resume. `position.*` and `account.profile_changed` stay on `events`; fills stay on `results:{terminal_id}`. Market depth travels on `book:{server_id}:{symbol}` as `md.book`: the newest book on subscribe, then each change. While a planned maintenance window suspends the tenant's market data, its `md:`, `candles:` and `book:` channels carry an `md.status` of `maintenance` and no data, then `live` when it ends (`MdStatusMessage`). The upgrade request is signed as any call is (`mfxSignature`, Ed25519): over `GET`, the path with its query and an empty body. Each event's `data` is described by the schema `x-event-payloads` names for its type; on `events` it also carries the `type`. Close codes are listed in `x-close-codes`.
1050
+ * @description Control messages are JSON: `StreamClientMessage` in, `StreamServerMessage` out. Channels are keyed by id (`StreamChannel`). Results and events carry a per-channel `seq` and are replayed on `resume` for up to 15 minutes; ticks and candles are not replayed. Account state travels on the terminal's own `account:{terminal_id}` channel, not on `events`: `account.snapshot` on every subscribe and resume (a snapshot, never a replay), when the position set changes and every 60 s, and `account.marks` in between. Account messages are samples: a slow client loses them before any event, and a `seq` gap means resume. `position.*`, `order.*` and `account.profile_changed` stay on `events`; fills stay on `results:{terminal_id}`. Market depth travels on `book:{server_id}:{symbol}` as `md.book`: the newest book on subscribe, then each change. While a planned maintenance window suspends the tenant's market data, its `md:`, `candles:` and `book:` channels carry an `md.status` of `maintenance` and no data, then `live` when it ends (`MdStatusMessage`). The upgrade request is signed as any call is (`mfxSignature`, Ed25519): over `GET`, the path with its query and an empty body. Each event's `data` is described by the schema `x-event-payloads` names for its type; on `events` it also carries the `type`. Close codes are listed in `x-close-codes`.
930
1051
  */
931
1052
  get: operations["openStream"];
932
1053
  put?: never;
@@ -1157,7 +1278,7 @@ export interface components {
1157
1278
  * @description The operation that replaced this one; set only on SUPERSEDED
1158
1279
  */
1159
1280
  superseded_by?: string | null;
1160
- /** @description key:<key id>, admin.<email> for an operator, or system.healing for an automatic recovery step */
1281
+ /** @description key:<key id>, admin.<email> for an operator, system.healing for an automatic recovery step, or system.maintenance for a maintenance window's restart */
1161
1282
  requested_by: string;
1162
1283
  reason: string;
1163
1284
  /** @description True when the terminal would not close and its job was stopped; null until the cell says */
@@ -1236,16 +1357,41 @@ export interface components {
1236
1357
  [key: string]: string;
1237
1358
  };
1238
1359
  /**
1360
+ * @description The older word for `ha`: active_active asks for HA in hot mode and active_passive in warm mode. Sent with `ha` saying something else is 422
1239
1361
  * @default none
1240
1362
  * @enum {string}
1241
1363
  */
1242
1364
  redundancy: "none" | "active_passive" | "active_active";
1365
+ ha?: components["schemas"]["TerminalHa"];
1243
1366
  /**
1244
1367
  * @description The terminal's refresh rate in milliseconds: how often it sweeps prices and, while positions are open, reads the account. Omitted, the farm's rate applies. Faster than the farm's rate is 422 with details.allowed. With no farm named, only farms that allow the rate are considered, and none with room is 409 no_farm (why no_room). A flat account is read every 60 s whatever this says
1245
1368
  * @enum {integer}
1246
1369
  */
1247
1370
  refresh_ms?: 5000 | 4000 | 3000 | 2000 | 1000 | 500 | 250 | 200;
1248
1371
  };
1372
+ /** @description MFX Hub HA for one terminal. On, MFX Hub runs a second terminal on the same broker login, on separate hardware, ready to take over; you keep this terminal's id for everything and never see the second one. It is not in your terminal quota, your usage or your terminals per login, and it is not billed while HA is not priced. It is one more session on the login, so the broker's limit of sessions applies. */
1373
+ TerminalHa: {
1374
+ enabled: boolean;
1375
+ /**
1376
+ * @description hot: the second terminal stays logged in and takes over at once. warm: it waits on reserved capacity without a broker session and takes over after one login; not offered yet (422 `unprocessable`). Omitted: the mode this terminal already has, else hot.
1377
+ * @enum {string|null}
1378
+ */
1379
+ mode?: "hot" | "warm" | null;
1380
+ };
1381
+ /** @description This terminal's MFX Hub HA. `enabled: false`, `mode: null` and `fallback_state: off` when HA is not on */
1382
+ TenantTerminalHa: {
1383
+ enabled: boolean;
1384
+ /**
1385
+ * @description The mode in effect; null when HA is off
1386
+ * @enum {string|null}
1387
+ */
1388
+ mode: "hot" | "warm" | null;
1389
+ /**
1390
+ * @description Where the second terminal stands. off: HA is not on. starting: it is being created, placed or logged in. ready: it is logged in and healthy. waiting_for_capacity: there is no separate hardware in the farm to place it on yet. repairing: it exists and is not healthy (offline, failed, degraded or being moved).
1391
+ * @enum {string}
1392
+ */
1393
+ fallback_state: "off" | "starting" | "ready" | "waiting_for_capacity" | "repairing";
1394
+ };
1249
1395
  /** @description What a tenant may change on a running terminal. Send only the fields to change */
1250
1396
  TerminalUpdate: {
1251
1397
  /**
@@ -1269,6 +1415,11 @@ export interface components {
1269
1415
  farm: components["schemas"]["Ref"];
1270
1416
  broker: components["schemas"]["Ref"];
1271
1417
  server: components["schemas"]["Ref"];
1418
+ /**
1419
+ * @description The kind of the terminal's server as the operator recorded it, the `kind` of `GET /brokers/{id}/servers`. Known before any login, so it answers demo or live while a login is refused. Once the terminal has logged in, `GET /terminals/{id}/account` → `profile.account_type` is the account's own word (demo, live or contest) and wins.
1420
+ * @enum {string|null}
1421
+ */
1422
+ server_kind?: "demo" | "live" | null;
1272
1423
  /** @description e.g. ****3456; null until a credential is submitted */
1273
1424
  login_masked?: string | null;
1274
1425
  streaming: boolean;
@@ -1304,6 +1455,13 @@ export interface components {
1304
1455
  * @enum {string|null}
1305
1456
  */
1306
1457
  attributed_to?: "broker" | null;
1458
+ /** @description As `Condition.broker_words`: the broker's own words when it refused the login */
1459
+ broker_words?: string | null;
1460
+ /**
1461
+ * @description As `Condition.refusal_kind`
1462
+ * @enum {string|null}
1463
+ */
1464
+ refusal_kind?: "invalid_account" | "other" | null;
1307
1465
  };
1308
1466
  };
1309
1467
  instruments: string[];
@@ -1316,6 +1474,7 @@ export interface components {
1316
1474
  redundancy?: "none" | "active_passive" | "active_active";
1317
1475
  /** @enum {string|null} */
1318
1476
  replica_role?: "writer" | "standby" | null;
1477
+ ha?: components["schemas"]["TenantTerminalHa"];
1319
1478
  terminal_build?: number | null;
1320
1479
  /** Format: date-time */
1321
1480
  last_tick_at?: string | null;
@@ -1507,7 +1666,30 @@ export interface components {
1507
1666
  /** @description The Idempotency-Key of the platform command that closed it (closePosition or closePositionBy): the newest such command on this terminal whose outcome is recorded SUCCEEDED, names this position and was recorded within the last 30 s. Absent for a close nobody asked the platform for (sl, tp, stop-out, a person at the terminal), and when the command's outcome was not yet recorded when the close was told; `magic` and `comment` here are the position's own */
1508
1667
  idempotency_key?: string | null;
1509
1668
  };
1510
- /** @description `data` of a `position.opened` message on the `events` channel: sent once when a position opens on the terminal's account, by anyone (MFX Hub, an expert advisor, the broker's own apps). The terminal reads its positions and tells what changed since it last read them, so a position that opened and closed between two reads is told as `position.opened` then `position.closed`. On the stream every value is text, as on every event, and a field with no value is left out. */
1669
+ /**
1670
+ * @description `data` of a `position.opened` message on the `events` channel: sent once when a position opens on the terminal's account, by anyone (MFX Hub, an expert advisor, the broker's own apps). The terminal reads its positions and tells what changed since it last read them, so a position that opened and closed between two reads is told as `position.opened` then `position.closed`. On the stream every value is text, as on every event, and a field with no value is left out.
1671
+ * @example {
1672
+ * "schema_version": "1",
1673
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
1674
+ * "event_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101:58190233:open",
1675
+ * "ticket": "58190233",
1676
+ * "symbol": "EURUSD",
1677
+ * "side": "buy",
1678
+ * "volume": "0.10",
1679
+ * "open_price": "1.08412",
1680
+ * "price_current": "1.08420",
1681
+ * "sl": "1.08100",
1682
+ * "tp": "1.09000",
1683
+ * "profit": "0.80",
1684
+ * "swap": "0.00",
1685
+ * "open_time": "2026-10-23T09:15:02Z",
1686
+ * "magic": "0",
1687
+ * "comment": "",
1688
+ * "ts": "2026-10-23T09:15:02Z",
1689
+ * "idempotency_key": "client-order-0001",
1690
+ * "order_ticket": "58190233"
1691
+ * }
1692
+ */
1511
1693
  PositionOpened: {
1512
1694
  /** @description The payload's version; 1 */
1513
1695
  schema_version: string;
@@ -1553,8 +1735,31 @@ export interface components {
1553
1735
  ts: string;
1554
1736
  /** @description The Idempotency-Key of the platform command that opened it (placeOrder): the newest such command on this terminal recorded SUCCEEDED naming this position within the last 30 s. Absent for a position the platform did not open (an expert advisor, the broker's apps) or whose command outcome was not yet recorded */
1555
1737
  idempotency_key?: string;
1738
+ /** @description The MT5 ticket of the order whose deal opened the position, an integer as text: for a position opened by a pending order, that order's ticket, the same order `position_ticket` names on its `order.closed`. Absent from a terminal whose agent does not report it yet */
1739
+ order_ticket?: string;
1556
1740
  };
1557
- /** @description `data` of a `position.modified` message on the `events` channel: sent once each time the terminal reads an open position with a different stop loss, take profit or volume than it last read - set by anyone, a partial close and each move of a trailing stop included. Each message is the position as it now stands, not the difference. On the stream every value is text, as on every event, and a field with no value is left out: a stop loss or take profit that was removed is absent. */
1741
+ /**
1742
+ * @description `data` of a `position.modified` message on the `events` channel: sent once each time the terminal reads an open position with a different stop loss, take profit or volume than it last read - set by anyone, a partial close and each move of a trailing stop included. Each message is the position as it now stands, not the difference. On the stream every value is text, as on every event, and a field with no value is left out: a stop loss or take profit that was removed is absent.
1743
+ * @example {
1744
+ * "schema_version": "1",
1745
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
1746
+ * "event_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101:58190233:modify:1.08412:1.09000:0.05",
1747
+ * "ticket": "58190233",
1748
+ * "symbol": "EURUSD",
1749
+ * "side": "buy",
1750
+ * "volume": "0.05",
1751
+ * "open_price": "1.08412",
1752
+ * "price_current": "1.08455",
1753
+ * "sl": "1.08412",
1754
+ * "tp": "1.09000",
1755
+ * "profit": "2.15",
1756
+ * "swap": "0.00",
1757
+ * "open_time": "2026-10-23T09:15:02Z",
1758
+ * "magic": "0",
1759
+ * "comment": "",
1760
+ * "ts": "2026-10-23T09:42:10Z"
1761
+ * }
1762
+ */
1558
1763
  PositionModified: {
1559
1764
  /** @description The payload's version; 1 */
1560
1765
  schema_version: string;
@@ -1601,7 +1806,242 @@ export interface components {
1601
1806
  /** @description The Idempotency-Key of the platform command that changed it (modifyPosition, closePositionPartial, a trailing stop): the newest such command on this terminal recorded SUCCEEDED naming this position within the last 30 s. Absent for a change the platform did not make */
1602
1807
  idempotency_key?: string;
1603
1808
  };
1604
- /** @description `data` of a `terminal.state_changed` message on the `events` channel: sent once for each move of a terminal's lifecycle state, after the move is recorded, whoever made it (you, an operator or the platform). `transition_id` names the move in the terminal's history. On the stream every value is text, as on every event. */
1809
+ /**
1810
+ * @description `data` of an `order.placed` message on the `events` channel: sent once when a pending order (a buy or sell limit, stop or stop-limit order) appears on the terminal's account, placed by anyone (MFX Hub, an expert advisor, the broker's own apps). The order in the words of `PendingOrder`. On the stream every value is text, as on every event, and a field with no value is left out.
1811
+ * @example {
1812
+ * "schema_version": "1",
1813
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
1814
+ * "event_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101:58190240:order:placed",
1815
+ * "ticket": "58190240",
1816
+ * "symbol": "EURUSD",
1817
+ * "side": "buy",
1818
+ * "order_type": "limit",
1819
+ * "volume": "0.10",
1820
+ * "price": "1.08000",
1821
+ * "sl": "1.07700",
1822
+ * "tp": "1.08600",
1823
+ * "placed_time": "2026-10-23T09:20:00Z",
1824
+ * "expires_at": "2026-10-23T21:00:00Z",
1825
+ * "magic": "0",
1826
+ * "comment": "",
1827
+ * "ts": "2026-10-23T09:20:00Z",
1828
+ * "idempotency_key": "client-order-0002"
1829
+ * }
1830
+ */
1831
+ OrderPlaced: {
1832
+ /** @description The payload's version; 1 */
1833
+ schema_version: string;
1834
+ /**
1835
+ * Format: uuid
1836
+ * @description The join key: the terminal whose account holds the order
1837
+ */
1838
+ terminal_id: string;
1839
+ /** @description Deterministic per order: a replay after a cell restart carries the same one and is sent once */
1840
+ event_id: string;
1841
+ /** @description The order's MT5 ticket, an integer as text: the key the pending list and the other order events carry for it */
1842
+ ticket: string;
1843
+ symbol: string;
1844
+ /** @enum {string} */
1845
+ side: "buy" | "sell";
1846
+ /** @enum {string} */
1847
+ order_type: "limit" | "stop" | "stop_limit";
1848
+ /** @description Lots still to fill, a decimal as text */
1849
+ volume: string;
1850
+ /** @description The order's price, a decimal as text; on a stop-limit order the stop price */
1851
+ price: string;
1852
+ /** @description A stop-limit order's second price, the limit placed when the market reaches `price`; absent on every other order */
1853
+ stop_limit_price?: string;
1854
+ /** @description The stop loss; absent when none */
1855
+ sl?: string;
1856
+ /** @description The take profit; absent when none */
1857
+ tp?: string;
1858
+ /**
1859
+ * Format: date-time
1860
+ * @description When it was placed, UTC
1861
+ */
1862
+ placed_time: string;
1863
+ /**
1864
+ * Format: date-time
1865
+ * @description When it expires, UTC; absent when good till cancelled
1866
+ */
1867
+ expires_at?: string;
1868
+ /** @description The MT5 magic number, an integer as text; 0 when none */
1869
+ magic: string;
1870
+ /** @description The MT5 comment; empty when none */
1871
+ comment: string;
1872
+ /**
1873
+ * Format: date-time
1874
+ * @description When it was placed, UTC: the same instant as `placed_time`
1875
+ */
1876
+ ts: string;
1877
+ /** @description The Idempotency-Key of the platform command that placed it (placeOrder): the newest such command on this terminal recorded SUCCEEDED naming this order within the last 30 s. Absent for an order the platform did not place, or whose command outcome was not yet recorded */
1878
+ idempotency_key?: string;
1879
+ };
1880
+ /**
1881
+ * @description `data` of an `order.modified` message on the `events` channel: sent once each time the terminal reads a pending order with a different price, stop-limit price, stop loss, take profit, expiry or volume than it last read, set by anyone; a partial fill that leaves the rest pending is one. Each message is the order as it now stands, not the difference. On the stream every value is text, and a field with no value is left out.
1882
+ * @example {
1883
+ * "schema_version": "1",
1884
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
1885
+ * "event_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101:58190240:order:modify:1.07950:1.07700:1.08600:2026-10-23T21:00:00Z:0.10",
1886
+ * "ticket": "58190240",
1887
+ * "symbol": "EURUSD",
1888
+ * "side": "buy",
1889
+ * "order_type": "limit",
1890
+ * "volume": "0.10",
1891
+ * "price": "1.07950",
1892
+ * "sl": "1.07700",
1893
+ * "tp": "1.08600",
1894
+ * "placed_time": "2026-10-23T09:20:00Z",
1895
+ * "expires_at": "2026-10-23T21:00:00Z",
1896
+ * "magic": "0",
1897
+ * "comment": "",
1898
+ * "ts": "2026-10-23T09:31:12Z",
1899
+ * "idempotency_key": "client-modify-0001"
1900
+ * }
1901
+ */
1902
+ OrderModified: {
1903
+ /** @description The payload's version; 1 */
1904
+ schema_version: string;
1905
+ /**
1906
+ * Format: uuid
1907
+ * @description The join key: the terminal whose account holds the order
1908
+ */
1909
+ terminal_id: string;
1910
+ /** @description Deterministic per change (the ticket with its new price, stop loss, take profit, expiry and volume): a replay after a cell restart carries the same one and is sent once */
1911
+ event_id: string;
1912
+ /** @description The order's MT5 ticket, an integer as text: the key the pending list and the other order events carry for it */
1913
+ ticket: string;
1914
+ symbol: string;
1915
+ /** @enum {string} */
1916
+ side: "buy" | "sell";
1917
+ /** @enum {string} */
1918
+ order_type: "limit" | "stop" | "stop_limit";
1919
+ /** @description Lots still to fill, a decimal as text */
1920
+ volume: string;
1921
+ /** @description The order's price, a decimal as text; on a stop-limit order the stop price */
1922
+ price: string;
1923
+ /** @description A stop-limit order's second price, the limit placed when the market reaches `price`; absent on every other order */
1924
+ stop_limit_price?: string;
1925
+ /** @description The stop loss; absent when none */
1926
+ sl?: string;
1927
+ /** @description The take profit; absent when none */
1928
+ tp?: string;
1929
+ /**
1930
+ * Format: date-time
1931
+ * @description When it was placed, UTC
1932
+ */
1933
+ placed_time: string;
1934
+ /**
1935
+ * Format: date-time
1936
+ * @description When it expires, UTC; absent when good till cancelled
1937
+ */
1938
+ expires_at?: string;
1939
+ /** @description The MT5 magic number, an integer as text; 0 when none */
1940
+ magic: string;
1941
+ /** @description The MT5 comment; empty when none */
1942
+ comment: string;
1943
+ /**
1944
+ * Format: date-time
1945
+ * @description When the terminal saw the change, UTC
1946
+ */
1947
+ ts: string;
1948
+ /** @description The Idempotency-Key of the platform command that changed it (modifyOrder): the newest such command on this terminal recorded SUCCEEDED naming this order within the last 30 s. Absent for a change the platform did not make */
1949
+ idempotency_key?: string;
1950
+ };
1951
+ /**
1952
+ * @description `data` of an `order.closed` message on the `events` channel: sent once when a pending order leaves the account's pending list, for whatever reason. `reason` is the order's own state in the terminal's order history (MT5 `ORDER_STATE`): `filled`, `cancelled`, `expired`, `rejected`; `unknown` when the terminal did not find it in its history within 60 s, or MT5 gave a state outside that list. The platform never infers it from the expiry or from a nearby position. A fill carries `position_ticket`, the position it opened or, on a netting account, went into; `position.opened` carries the same order's ticket as `order_ticket`. The order as it stood when it left. On the stream every value is text, and a field with no value is left out.
1953
+ * @example {
1954
+ * "schema_version": "1",
1955
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
1956
+ * "event_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101:58190240:order:closed",
1957
+ * "ticket": "58190240",
1958
+ * "symbol": "EURUSD",
1959
+ * "side": "buy",
1960
+ * "order_type": "limit",
1961
+ * "volume": "0.10",
1962
+ * "price": "1.07950",
1963
+ * "sl": "1.07700",
1964
+ * "tp": "1.08600",
1965
+ * "placed_time": "2026-10-23T09:20:00Z",
1966
+ * "expires_at": "2026-10-23T21:00:00Z",
1967
+ * "magic": "0",
1968
+ * "comment": "",
1969
+ * "reason": "filled",
1970
+ * "order_state_raw": "4",
1971
+ * "position_ticket": "58190240",
1972
+ * "ts": "2026-10-23T10:02:47Z"
1973
+ * }
1974
+ */
1975
+ OrderClosed: {
1976
+ /** @description The payload's version; 1 */
1977
+ schema_version: string;
1978
+ /**
1979
+ * Format: uuid
1980
+ * @description The join key: the terminal whose account holds the order
1981
+ */
1982
+ terminal_id: string;
1983
+ /** @description Deterministic per order: a replay after a cell restart carries the same one and is sent once */
1984
+ event_id: string;
1985
+ /** @description The order's MT5 ticket, an integer as text: the key the pending list and the other order events carry for it */
1986
+ ticket: string;
1987
+ symbol: string;
1988
+ /** @enum {string} */
1989
+ side: "buy" | "sell";
1990
+ /** @enum {string} */
1991
+ order_type: "limit" | "stop" | "stop_limit";
1992
+ /** @description Lots still to fill, a decimal as text */
1993
+ volume: string;
1994
+ /** @description The order's price, a decimal as text; on a stop-limit order the stop price */
1995
+ price: string;
1996
+ /** @description A stop-limit order's second price, the limit placed when the market reaches `price`; absent on every other order */
1997
+ stop_limit_price?: string;
1998
+ /** @description The stop loss; absent when none */
1999
+ sl?: string;
2000
+ /** @description The take profit; absent when none */
2001
+ tp?: string;
2002
+ /**
2003
+ * Format: date-time
2004
+ * @description When it was placed, UTC
2005
+ */
2006
+ placed_time: string;
2007
+ /**
2008
+ * Format: date-time
2009
+ * @description When it expires, UTC; absent when good till cancelled
2010
+ */
2011
+ expires_at?: string;
2012
+ /** @description The MT5 magic number, an integer as text; 0 when none */
2013
+ magic: string;
2014
+ /** @description The MT5 comment; empty when none */
2015
+ comment: string;
2016
+ /**
2017
+ * @description Why it left the pending list: MT5 ORDER_STATE in plain words; unknown when it could not be read
2018
+ * @enum {string}
2019
+ */
2020
+ reason: "filled" | "cancelled" | "expired" | "rejected" | "unknown";
2021
+ /** @description MT5's own ORDER_STATE code, an integer as text; absent when the order was not found in the history */
2022
+ order_state_raw?: string;
2023
+ /** @description On a fill, the MT5 ticket of the position it opened or went into, an integer as text; absent otherwise */
2024
+ position_ticket?: string;
2025
+ /**
2026
+ * Format: date-time
2027
+ * @description When it left, UTC: the order's time done in the history, or when the terminal saw it gone
2028
+ */
2029
+ ts: string;
2030
+ /** @description On a cancel, the Idempotency-Key of the platform command that cancelled it (cancelOrder): the newest such command on this terminal recorded SUCCEEDED naming this order within the last 30 s. Absent on a fill, an expiry or a rejection, and on a cancel the platform did not make */
2031
+ idempotency_key?: string;
2032
+ };
2033
+ /**
2034
+ * @description `data` of a `terminal.state_changed` message on the `events` channel: sent once for each move of a terminal's lifecycle state, after the move is recorded, whoever made it (you, an operator or the platform). `transition_id` names the move in the terminal's history. On the stream every value is text, as on every event.
2035
+ * @example {
2036
+ * "schema_version": "1",
2037
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
2038
+ * "from": "LOGGING_IN",
2039
+ * "to": "ACTIVE",
2040
+ * "reason": "account verified, broker connected",
2041
+ * "transition_id": "5b1d7e0a-2c44-4f0e-9a51-7d3b8c6e1f20",
2042
+ * "ts": "2026-10-23T09:02:41Z"
2043
+ * }
2044
+ */
1605
2045
  TerminalStateChanged: {
1606
2046
  /** @description The payload's version; 1 */
1607
2047
  schema_version: string;
@@ -1630,7 +2070,18 @@ export interface components {
1630
2070
  */
1631
2071
  ts: string;
1632
2072
  };
1633
- /** @description `data` of a `terminal.condition_changed` message on the `events` channel: sent once when one of a terminal's observed conditions changes status, and when `TicksFresh` moves between its two named `failed` reasons, `slow_feed` and `broker_quiet`. A reason reworded under a status that stands sends nothing. The fields are those of `Condition`. On the stream every value is text, as on every event, and a value that does not exist is empty. */
2073
+ /**
2074
+ * @description `data` of a `terminal.condition_changed` message on the `events` channel: sent once when one of a terminal's observed conditions changes status, and when `TicksFresh` moves between its two named `failed` reasons, `slow_feed` and `broker_quiet`. A reason reworded under a status that stands sends nothing. The fields are those of `Condition`. On the stream every value is text, as on every event, and a value that does not exist is empty.
2075
+ * @example {
2076
+ * "schema_version": "1",
2077
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
2078
+ * "condition": "BrokerConnected",
2079
+ * "status": "failed",
2080
+ * "reason": "no connection to the broker's server",
2081
+ * "since": "2026-10-23T10:05:13Z",
2082
+ * "attributed_to": "broker"
2083
+ * }
2084
+ */
1634
2085
  TerminalConditionChanged: {
1635
2086
  /** @description The payload's version; 1 */
1636
2087
  schema_version: string;
@@ -1651,8 +2102,28 @@ export interface components {
1651
2102
  * @enum {string}
1652
2103
  */
1653
2104
  attributed_to: "broker" | "";
2105
+ /** @description As `Condition.broker_words`; empty unless the reason is `login_refused` */
2106
+ broker_words?: string;
2107
+ /**
2108
+ * @description As `Condition.refusal_kind`; empty unless the reason is `login_refused`
2109
+ * @enum {string}
2110
+ */
2111
+ refusal_kind?: "invalid_account" | "other" | "";
1654
2112
  };
1655
- /** @description `data` of a `terminal.operation_changed` message on the `events` channel: sent when a governed operation on a terminal (`TerminalOperation`: a restart, a rebuild, a move, a retire, a retried login, an attach or detach, a drain) is queued, when it starts, and when it ends: settled (SUCCEEDED or FAILED) or ended without running (NOT_STARTED, EXPIRED, CANCELLED, SUPERSEDED). The steps in between are not sent; read `GET /terminals/{id}/operations/{operation_id}` for its timeline. On the stream every value is text, as on every event, and a value that does not exist is empty. */
2113
+ /**
2114
+ * @description `data` of a `terminal.operation_changed` message on the `events` channel: sent when a governed operation on a terminal (`TerminalOperation`: a restart, a rebuild, a move, a retire, a retried login, an attach or detach, a drain) is queued, when it starts, and when it ends: settled (SUCCEEDED or FAILED) or ended without running (NOT_STARTED, EXPIRED, CANCELLED, SUPERSEDED). The steps in between are not sent; read `GET /terminals/{id}/operations/{operation_id}` for its timeline. On the stream every value is text, as on every event, and a value that does not exist is empty.
2115
+ * @example {
2116
+ * "schema_version": "1",
2117
+ * "operation_id": "9c2e4b71-0d3a-4e8f-b6a2-1f5c7d9e3a40",
2118
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
2119
+ * "kind": "restart",
2120
+ * "state": "SUCCEEDED",
2121
+ * "step": "RELAUNCHING",
2122
+ * "forced_stop": "false",
2123
+ * "cooldown_until": "2026-10-23T10:20:00Z",
2124
+ * "finished_at": "2026-10-23T10:15:31Z"
2125
+ * }
2126
+ */
1656
2127
  TerminalOperationChanged: {
1657
2128
  /** @description The payload's version; 1 */
1658
2129
  schema_version: string;
@@ -1679,7 +2150,50 @@ export interface components {
1679
2150
  /** @description UTC date-time when it ended; empty while it runs */
1680
2151
  finished_at: string;
1681
2152
  };
1682
- /** @description `data` of an `account.profile_changed` message on the `events` channel: the terminal reads its account's slow facts (`AccountProfile`) every 60 seconds and sends this when any of them differs from what it last read, and when it has no earlier reading to compare with. Every fact is sent as it now stands; `changed` names the ones that moved. A fact the terminal could not read is left out. On the stream every value is text, as on every event. */
2153
+ /**
2154
+ * @description `data` of a `terminal.ha_switched` message on the `events` channel: MFX Hub HA moved the terminal to its second terminal, which now carries its orders, quotes and account. The terminal's id is the same before and after; nothing about the second terminal is sent. On the stream every value is text, as on every event.
2155
+ * @example {
2156
+ * "schema_version": "1",
2157
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
2158
+ * "reason": "broker_lost",
2159
+ * "at": "2026-11-01T22:04:31.120Z",
2160
+ * "took_ms": "40"
2161
+ * }
2162
+ */
2163
+ TerminalHaSwitched: {
2164
+ /** @description The payload's version; 1 */
2165
+ schema_version: string;
2166
+ /**
2167
+ * Format: uuid
2168
+ * @description The terminal, as you address it
2169
+ */
2170
+ terminal_id: string;
2171
+ /**
2172
+ * @description Why it switched. order: an order arrived while the first terminal could not carry it, and it was placed through the second. broker_lost: the first terminal lost its broker connection. unresponsive: the first terminal stopped reporting. terminal_failed and process_gone: the first terminal failed or its MetaTrader 5 process ended. operator: MFX Hub support switched it.
2173
+ * @enum {string}
2174
+ */
2175
+ reason: "order" | "broker_lost" | "unresponsive" | "terminal_failed" | "process_gone" | "operator";
2176
+ /** @description UTC date-time of the switch, to the millisecond */
2177
+ at: string;
2178
+ /** @description Milliseconds the switch took, an integer as text */
2179
+ took_ms: string;
2180
+ };
2181
+ /**
2182
+ * @description `data` of an `account.profile_changed` message on the `events` channel: the terminal reads its account's slow facts (`AccountProfile`) every 60 seconds and sends this when any of them differs from what it last read, and when it has no earlier reading to compare with. Every fact is sent as it now stands; `changed` names the ones that moved. A fact the terminal could not read is left out. On the stream every value is text, as on every event.
2183
+ * @example {
2184
+ * "schema_version": "1",
2185
+ * "terminal_id": "0e6a51a8-65f1-4f4c-9c53-3d6a2f6a0101",
2186
+ * "currency": "USD",
2187
+ * "leverage": "500",
2188
+ * "company": "Example Broker Ltd",
2189
+ * "account_type": "demo",
2190
+ * "margin_mode": "hedging",
2191
+ * "trade_allowed": "true",
2192
+ * "expert_allowed": "true",
2193
+ * "changed": "[\"leverage\"]",
2194
+ * "observed_at": "2026-10-23T09:00:00Z"
2195
+ * }
2196
+ */
1683
2197
  AccountProfileChanged: {
1684
2198
  /** @description The payload's version; 1 */
1685
2199
  schema_version: string;
@@ -1728,7 +2242,7 @@ export interface components {
1728
2242
  comment?: string;
1729
2243
  };
1730
2244
  /**
1731
- * @description Final states are SUCCEEDED, FAILED, EXPIRED and REJECTED. UNKNOWN is visible and NOT final: the order may have reached the broker and its outcome could not be read (a timeout after send, a lost connection). Reconciliation against the terminal's deal history resolves it to SUCCEEDED or FAILED on its own. A client must not re-send an order whose command is UNKNOWN, because that is how one fill becomes two. A NOT_EXECUTED answer from the terminal reads REJECTED with its reason. SUCCEEDED means the broker did what the command asked: for a market order, filled; for a pending order (limit, stop, stop_limit), accepted onto the broker's book with its ticket in `tickets.order` - not filled (OrderPlace).
2245
+ * @description Final states are SUCCEEDED, FAILED, EXPIRED and REJECTED. UNKNOWN is visible and NOT final: the order may have reached the broker and its outcome could not be read (a timeout after send, a lost connection). The platform asks the terminal that ran it to read its deal and order history again, every minute, until `resolve_by` (15 minutes after it became UNKNOWN by default), and the answer resolves it to SUCCEEDED, FAILED or REJECTED. `command.unknown` is sent when it becomes UNKNOWN and `command.resolved` when it is resolved. One still UNKNOWN at `resolve_by` stays UNKNOWN, because it may have run, and is resolved by an operator with the tenant. A client must not re-send an order whose command is UNKNOWN, because that is how one fill becomes two. A NOT_EXECUTED answer from the terminal reads REJECTED with its reason. SUCCEEDED means the broker did what the command asked: for a market order, filled; for a pending order (limit, stop, stop_limit), accepted onto the broker's book with its ticket in `tickets.order` - not filled (OrderPlace).
1732
2246
  * @enum {string}
1733
2247
  */
1734
2248
  CommandState: "ACCEPTED" | "ENQUEUED" | "DELIVERED" | "EXECUTING" | "SUCCEEDED" | "FAILED" | "EXPIRED" | "REJECTED" | "UNKNOWN";
@@ -1744,7 +2258,7 @@ export interface components {
1744
2258
  /** @description What happened at the broker, once the command has an outcome. `retryable` is set on every reason, so an SDK decides from the result and not from a table that can drift. It is written once, from the broker's answer to the command: for a pending order_place that is the placement (`mt5_retcode` 10008, `tickets.order` set, no deal, no `exec_price`), and the later fill never rewrites it (OrderPlace says where the fill is seen). */
1745
2259
  CommandResult: {
1746
2260
  ok: boolean;
1747
- /** @description Granular reason; null when ok */
2261
+ /** @description Granular reason; null when ok. Every reason, with whether it is retryable, its typical cause and what to do, is in the error catalogue at the root of this document */
1748
2262
  reason: string | null;
1749
2263
  /** @description Whether sending the same order again, under a NEW Idempotency-Key, can succeed. Always false while the state is UNKNOWN */
1750
2264
  retryable: boolean;
@@ -1795,6 +2309,11 @@ export interface components {
1795
2309
  expires_at?: string | null;
1796
2310
  /** @description Null until the command has an outcome */
1797
2311
  result: components["schemas"]["CommandResult"] | null;
2312
+ /**
2313
+ * Format: date-time
2314
+ * @description While the command is UNKNOWN: until when the platform asks the terminal that ran it to read the outcome again. After it, an operator resolves it with the tenant. Null in every other state
2315
+ */
2316
+ resolve_by?: string | null;
1798
2317
  };
1799
2318
  /** @description `data` of a `command.completed` or `command.failed` message on the `events` channel and on `results:{terminal_id}`: sent once when a command reaches an outcome, `command.completed` for SUCCEEDED and `command.failed` for every other end (FAILED, EXPIRED, REJECTED). It carries what `GET /commands/{id}` serves as `result`. On the stream every value is text, `tickets` and `latencies` are JSON objects as text, and a value that does not exist is empty. */
1800
2319
  CommandOutcome: {
@@ -1827,6 +2346,78 @@ export interface components {
1827
2346
  /** @description CommandResult.comment: the comment the command sent; empty where it is null or the order was placed without one. Always sent */
1828
2347
  comment?: string;
1829
2348
  };
2349
+ /** @description `data` of a `command.unknown` message on the `events` channel: sent when a command becomes UNKNOWN, from any state. Its outcome could not be read and it may have reached the broker, so it must not be sent again. The platform asks the terminal that ran it to read the outcome again until `resolve_by`; `command.resolved` follows when it is resolved. Values are text as in CommandOutcome; `state` is UNKNOWN. */
2350
+ CommandUnknown: {
2351
+ schema_version: string;
2352
+ /** Format: uuid */
2353
+ command_id: string;
2354
+ idempotency_key: string;
2355
+ /**
2356
+ * Format: uuid
2357
+ * @description The join key: the terminal the command ran on
2358
+ */
2359
+ terminal_id: string;
2360
+ state: components["schemas"]["CommandState"];
2361
+ /** @description As CommandOutcome.retcode */
2362
+ retcode: string;
2363
+ /** @description As CommandOutcome.reason */
2364
+ reason: string;
2365
+ /** @description CommandTickets as JSON text */
2366
+ tickets: string;
2367
+ /** @description Decimal as a string; empty when nothing filled */
2368
+ exec_price: string;
2369
+ /** @description Lots, decimal as a string; empty when nothing filled */
2370
+ exec_volume: string;
2371
+ /** @description JSON text: {"broker_ms": integer or null, "agent_ms": integer or null} */
2372
+ latencies: string;
2373
+ /** @description As CommandOutcome.terminal_state */
2374
+ terminal_state: string;
2375
+ /** @description As CommandOutcome.magic */
2376
+ magic?: string;
2377
+ /** @description As CommandOutcome.comment */
2378
+ comment?: string;
2379
+ /**
2380
+ * Format: date-time
2381
+ * @description Until when the platform asks the terminal again (the moment it became UNKNOWN plus the resolve window, 15 minutes by default). Still UNKNOWN after it, an operator resolves it with the tenant, and a later answer still sends command.resolved
2382
+ */
2383
+ resolve_by: string;
2384
+ };
2385
+ /** @description `data` of a `command.resolved` message on the `events` channel: sent when an UNKNOWN command reaches its outcome (SUCCEEDED, FAILED or REJECTED), beside the `command.completed` or `command.failed` that outcome sends as always. Values are text as in CommandOutcome and describe the outcome. */
2386
+ CommandResolved: {
2387
+ schema_version: string;
2388
+ /** Format: uuid */
2389
+ command_id: string;
2390
+ idempotency_key: string;
2391
+ /**
2392
+ * Format: uuid
2393
+ * @description The join key: the terminal the command ran on
2394
+ */
2395
+ terminal_id: string;
2396
+ state: components["schemas"]["CommandState"];
2397
+ /** @description As CommandOutcome.retcode */
2398
+ retcode: string;
2399
+ /** @description As CommandOutcome.reason */
2400
+ reason: string;
2401
+ /** @description CommandTickets as JSON text */
2402
+ tickets: string;
2403
+ /** @description Decimal as a string; empty when nothing filled */
2404
+ exec_price: string;
2405
+ /** @description Lots, decimal as a string; empty when nothing filled */
2406
+ exec_volume: string;
2407
+ /** @description JSON text: {"broker_ms": integer or null, "agent_ms": integer or null} */
2408
+ latencies: string;
2409
+ /** @description As CommandOutcome.terminal_state */
2410
+ terminal_state: string;
2411
+ /** @description As CommandOutcome.magic */
2412
+ magic?: string;
2413
+ /** @description As CommandOutcome.comment */
2414
+ comment?: string;
2415
+ /**
2416
+ * Format: date-time
2417
+ * @description When the command became UNKNOWN
2418
+ */
2419
+ unknown_since: string;
2420
+ };
1830
2421
  Position: {
1831
2422
  /** Format: int64 */
1832
2423
  ticket: number;
@@ -1925,7 +2516,10 @@ export interface components {
1925
2516
  sl?: string | null;
1926
2517
  /** @description As `sl`, for the take profit */
1927
2518
  tp?: string | null;
1928
- /** Format: date-time */
2519
+ /**
2520
+ * Format: date-time
2521
+ * @description When the broker executed the deal, in UTC (converted from the broker server's own clock)
2522
+ */
1929
2523
  time: string;
1930
2524
  magic: number;
1931
2525
  comment: string;
@@ -1933,20 +2527,61 @@ export interface components {
1933
2527
  * @description MT5 DEAL_REASON; null on an entry deal
1934
2528
  * @enum {string|null}
1935
2529
  */
1936
- close_reason: "client" | "expert" | "sl" | "tp" | "stop_out" | "rollover" | "vmargin" | "split" | "mobile" | "web" | null;
1937
- };
1938
- /** @description Newest first by (time, deal), inside the requested from/to */
1939
- DealPage: {
1940
- /** @description Opaque (created_at, id) cursor for the following page; null on the last page */
1941
- next_cursor: string | null;
1942
- /** @description How many items are in THIS page; never a total */
1943
- returned?: number;
1944
- items: components["schemas"]["Deal"][];
2530
+ close_reason: "client" | "expert" | "sl" | "tp" | "stop_out" | "rollover" | "vmargin" | "split" | "mobile" | "web" | null;
2531
+ };
2532
+ /** @description Newest first by (time, deal), inside the requested from/to */
2533
+ DealPage: {
2534
+ /** @description Opaque (created_at, id) cursor for the following page; null on the last page */
2535
+ next_cursor: string | null;
2536
+ /** @description How many items are in THIS page; never a total */
2537
+ returned?: number;
2538
+ items: components["schemas"]["Deal"][];
2539
+ };
2540
+ Candle: {
2541
+ /**
2542
+ * Format: date-time
2543
+ * @description Bar open time, UTC
2544
+ */
2545
+ ts: string;
2546
+ o: string;
2547
+ h: string;
2548
+ l: string;
2549
+ c: string;
2550
+ /** @description Tick volume */
2551
+ v: number;
2552
+ };
2553
+ CandleList: {
2554
+ /** @description Oldest first by bar time; at most 1000 bars per call */
2555
+ items: components["schemas"]["Candle"][];
2556
+ /**
2557
+ * @description Where the bars came from: the platform's store (no cell), one of your terminals, or closed bars from the store and the forming bar from a terminal
2558
+ * @enum {string}
2559
+ */
2560
+ source?: "store" | "terminal" | "store+terminal";
2561
+ /**
2562
+ * Format: date-time
2563
+ * @description For an answer from the store: the oldest bar of the span the platform vouches holds every bar
2564
+ */
2565
+ complete_from?: string | null;
2566
+ /**
2567
+ * Format: date-time
2568
+ * @description For an answer from the store: the newest bar of that span. Holding bars up to it, you are missing none. Older than the last closed boundary while a gap is repaired, with Retry-After
2569
+ */
2570
+ complete_to?: string | null;
1945
2571
  };
1946
- Candle: {
2572
+ /** @description md.candle_closed on candles:{server_id}:{symbol}:{tf}: a closed bar, once, read by one of your own terminals. Never replayed; prev_ts tells you whether you missed one */
2573
+ MdCandleClosed: {
2574
+ /** @example 1 */
2575
+ schema_version: string;
2576
+ /** Format: uuid */
2577
+ server_id: string;
2578
+ /** @description The broker's own name */
2579
+ symbol: string;
2580
+ /** @enum {string} */
2581
+ timeframe: "M1" | "M5" | "M15" | "M30" | "H1" | "H4" | "D1" | "W1" | "MN1";
1947
2582
  /**
1948
2583
  * Format: date-time
1949
- * @description Bar open time, UTC
2584
+ * @description Bar open time, UTC; a D1 bar opens at the broker's midnight
1950
2585
  */
1951
2586
  ts: string;
1952
2587
  o: string;
@@ -1955,10 +2590,11 @@ export interface components {
1955
2590
  c: string;
1956
2591
  /** @description Tick volume */
1957
2592
  v: number;
1958
- };
1959
- CandleList: {
1960
- /** @description Oldest first by bar time; at most 1000 bars per call */
1961
- items: components["schemas"]["Candle"][];
2593
+ /**
2594
+ * Format: date-time
2595
+ * @description Open time of the bar before this one in the terminal's history. Not the ts of the last bar you hold: you missed bars; read GET /v1/md/candles from your last ts
2596
+ */
2597
+ prev_ts: string;
1962
2598
  };
1963
2599
  Tick: {
1964
2600
  /**
@@ -2272,28 +2908,114 @@ export interface components {
2272
2908
  */
2273
2909
  as_of: string;
2274
2910
  };
2275
- /** @description One metric's consumption in one period (recorded hourly). Quantities are decimals as strings. */
2276
- UsageRow: {
2277
- /** @enum {string} */
2278
- metric: "terminal_hours" | "md_msgs" | "commands" | "ws_minutes";
2279
- quantity: string;
2280
- /** Format: date-time */
2281
- period_start: string;
2282
- /** Format: date-time */
2283
- period_end: string;
2284
- };
2285
- /** @description What this tenant has consumed and what the calling key may still spend. The limits are the key's own, not the tenant's, so one runaway integration cannot starve a tenant's production traffic; `budget_source` says whether `budget_remaining` is a measurement or the configured allowance, exactly as the `X-MFX-Budget-Source` header does. */
2911
+ /** @description One limit: the figure in force, where it comes from, whether the platform applies it yet, and how much of it is in use where that is measured. Under `key` it is the figure for the calling key; under `tenant` the ceiling for all the tenant's keys together. */
2912
+ UsageLimit: {
2913
+ /** @description Under `key`, for commands_per_s, commands_burst and ws_conns: the lower of the key's cap and the tenant's ceiling (a key with no cap may use the whole ceiling). A key whose commands_per_s cap applies and that has no commands_burst cap of its own bursts to twice that cap, never past the tenant's burst (source key_override, key_cap null). Otherwise the figure itself. */
2914
+ limit: number;
2915
+ /**
2916
+ * @description Where `limit` comes from. A tenant ceiling is the tenant's own figure, else the plan's. Under `key`, key_override when the key's own cap or figure is what applies.
2917
+ * @enum {string}
2918
+ */
2919
+ source: "plan" | "tenant_override" | "key_override";
2920
+ /** @description Under `key`, for commands_per_s, commands_burst and ws_conns only: the key's own cap, null when the key has none (the default). Absent elsewhere. */
2921
+ key_cap?: number | null;
2922
+ /** @description False while the figure is published and not yet applied to this tenant: a request over it is not refused. True once it is: an execution command over its rate is 429 `rate_limited` with `Retry-After`, and the stream refuses, drops or closes as its limit says, naming the limit in `details.limit`. */
2923
+ enforced: boolean;
2924
+ /** @description Current use where the platform measures it; null where it does not yet */
2925
+ used: number | null;
2926
+ };
2927
+ /** @description The calling key's and its tenant's limits, each with where it comes from and how much of it is in use. Read it to pace an integration below the limits rather than finding them through 429s. */
2286
2928
  Usage: {
2287
- items: components["schemas"]["UsageRow"][];
2288
- /** @description The calling key's ceilings. */
2289
- limits: {
2290
- commands_per_s?: number | null;
2291
- md_msgs_per_s?: number | null;
2292
- ws_conns?: number | null;
2293
- };
2294
- budget_remaining: number;
2295
- /** @enum {string} */
2296
- budget_source: "counted" | "configured" | "unconfigured";
2929
+ /**
2930
+ * @description The tenant's plan, whose defaults apply where nothing is overridden
2931
+ * @enum {string}
2932
+ */
2933
+ plan: "standard" | "enterprise" | "system";
2934
+ /** @description The limits as they apply to the calling key. For commands_per_s, commands_burst and ws_conns that is the tenant's ceiling, lowered by the key's own cap where it has one. */
2935
+ key: {
2936
+ key_id: string;
2937
+ /** @description Execution commands per second, steady */
2938
+ commands_per_s: components["schemas"]["UsageLimit"];
2939
+ /** @description The most execution commands accepted at once; the steady rate refills it */
2940
+ commands_burst: components["schemas"]["UsageLimit"];
2941
+ /** @description Market-data messages per second on this key's stream connections */
2942
+ md_msgs_per_s: components["schemas"]["UsageLimit"];
2943
+ /** @description Stream connections open at once */
2944
+ ws_conns: components["schemas"]["UsageLimit"];
2945
+ /** @description `account:{terminal_id}` channels one connection may hold at once */
2946
+ account_channels_per_conn: components["schemas"]["UsageLimit"];
2947
+ };
2948
+ tenant: {
2949
+ /** @description Execution commands per second, steady, all the tenant's keys together */
2950
+ commands_per_s: components["schemas"]["UsageLimit"];
2951
+ /** @description The most execution commands accepted at once, all keys together; the steady rate refills it */
2952
+ commands_burst: components["schemas"]["UsageLimit"];
2953
+ /** @description Stream connections open at once, all keys together */
2954
+ ws_conns: components["schemas"]["UsageLimit"];
2955
+ /** @description Terminals the tenant may have, not counting retired ones. A request over it is 409 `quota_exceeded`. */
2956
+ terminals: {
2957
+ limit: number;
2958
+ used: number;
2959
+ /** @enum {string} */
2960
+ source: "tenant_override" | "platform_default";
2961
+ };
2962
+ };
2963
+ /** @description The farms the tenant may place a terminal on, each with what one of its cells holds and how many terminals are on it now. */
2964
+ farms: {
2965
+ farm: components["schemas"]["Ref"];
2966
+ /** @description Terminals one cell of the farm holds, not the farm's total: a farm adds cells by itself as it fills, so it has no total of its own. Null where no cap is set */
2967
+ cap: number | null;
2968
+ /** @description Terminals on the whole farm now, not counting retired ones. It may be more than `cap` once the farm has more than one cell */
2969
+ used: number;
2970
+ }[];
2971
+ /** @description How many terminals may run on one broker login. */
2972
+ per_login: {
2973
+ /** @description 0: only the broker server's own limit applies */
2974
+ plan_limit: number;
2975
+ /** @description Each enabled broker server, with the number of sessions it is known to allow on one login. */
2976
+ servers: {
2977
+ broker: components["schemas"]["Ref"];
2978
+ server: components["schemas"]["Ref"];
2979
+ /** @description Null: not known yet */
2980
+ max_sessions: number | null;
2981
+ /**
2982
+ * @description `probed`: seen to hold this many, a lower bound; `limit`: the ceiling found; `operator`: entered, final
2983
+ * @enum {string|null}
2984
+ */
2985
+ max_sessions_source: "probed" | "limit" | "operator" | null;
2986
+ }[];
2987
+ };
2988
+ /** @description The rules a terminal operation (restart, rebuild, move, retire, retry, attach, detach, drain) is refused by. */
2989
+ lifecycle: {
2990
+ /** @description Seconds a terminal rests after an operation of this kind; refused 429 `cooldown` */
2991
+ cooldown_s: {
2992
+ [key: string]: number;
2993
+ };
2994
+ /** @description Operations of this kind and its kin per tenant per hour; refused 429 `tenant_rate` */
2995
+ tenant_rate_per_hour: {
2996
+ [key: string]: number;
2997
+ };
2998
+ /** @description Operations of this kind and its kin a tenant may have open at once; refused 429 `concurrency` */
2999
+ concurrency: {
3000
+ [key: string]: number;
3001
+ };
3002
+ /** @description The share of a farm's terminals that may be in an operation at once (never fewer than one); refused 429 `farm_capacity` */
3003
+ farm_share: number;
3004
+ /** @description How long a refused operation may wait its turn */
3005
+ queue_max_wait_s: number;
3006
+ };
3007
+ webhooks: {
3008
+ endpoints: {
3009
+ limit: number;
3010
+ used: number;
3011
+ };
3012
+ timeout_ms: number;
3013
+ /** @description The waits before each retry of a failed delivery */
3014
+ retry_after_seconds: number[];
3015
+ pause_after_hours: number;
3016
+ failure_days: number;
3017
+ in_flight_per_endpoint: number;
3018
+ };
2297
3019
  /** Format: date-time */
2298
3020
  as_of: string;
2299
3021
  };
@@ -2333,10 +3055,10 @@ export interface components {
2333
3055
  */
2334
3056
  state: "pending" | "streaming" | "degraded" | "stopped";
2335
3057
  /**
2336
- * @description Why the subscription is `degraded`; null in every other state. `symbol_removed`: the broker no longer lists the symbol. `subscription_refused`: the broker refused it on every one of your ACTIVE terminals on the server that carries it. `terminal_disconnected`: you have no ACTIVE terminal on the server and no report of the symbol has arrived for the unknown window. `report_stale`: a terminal is ACTIVE and still none has arrived, for instance because none of them carries the symbol any more. `no_ticks_in_session`: the symbol's own hours say it is trading and it has not ticked for the silence window (measured at the newest report); it recovers on two reports in a row that each bring a new tick inside the window, or when its session closes. Nothing is degraded for silence outside the symbol's trading session.
3058
+ * @description Why the subscription is `degraded`; null in every other state. `symbol_removed`: the broker no longer lists the symbol. `subscription_refused`: the broker refused it on every one of your ACTIVE terminals on the server that carries it. `terminal_disconnected`: you have no ACTIVE terminal on the server and no report of the symbol has arrived for the unknown window. `report_stale`: a terminal is ACTIVE and still none has arrived, for instance because none of them carries the symbol any more. `no_ticks_in_session`: the symbol's own hours say it is trading and it has not ticked for the silence window (measured at the newest report); it recovers on two reports in a row that each bring a new tick inside the window, or when its session closes. Nothing is degraded for silence outside the symbol's trading session. `bars_unwatched`: the subscription asks for timeframes and none of your terminals on the server watches the symbol, so no closed bar can be sent. `bars_gap`: a closed-bar series has a gap the platform could not repair (no terminal to read it through); GET says how far it is complete.
2337
3059
  * @enum {string|null}
2338
3060
  */
2339
- degraded_reason?: "symbol_removed" | "subscription_refused" | "terminal_disconnected" | "report_stale" | "no_ticks_in_session" | null;
3061
+ degraded_reason?: "symbol_removed" | "subscription_refused" | "terminal_disconnected" | "report_stale" | "no_ticks_in_session" | "bars_unwatched" | "bars_gap" | null;
2340
3062
  /**
2341
3063
  * Format: date-time
2342
3064
  * @description When it became degraded; null in every other state
@@ -2545,6 +3267,101 @@ export interface components {
2545
3267
  */
2546
3268
  last_tick_at?: string;
2547
3269
  };
3270
+ /**
3271
+ * @description `data` of a `maintenance.window_changed` message on the `events` channel, and the same event to webhooks: sent when a window is announced to you, starts, its expected end or its message changes, ends or is cancelled. The fields are those of `TenantMaintenance`, as the window stands after the change; `GET /maintenance` answers the same. On the stream every value is text, as on every event.
3272
+ * @example {
3273
+ * "schema_version": "1",
3274
+ * "window_id": "5b1d7e0a-2c44-4f0e-9a51-7d3b8c6e1f20",
3275
+ * "label": "MW-0031",
3276
+ * "state": "in_progress",
3277
+ * "starts_at": "2026-11-05T10:00:00Z",
3278
+ * "ends_at": "2026-11-05T12:00:00Z",
3279
+ * "message": "Planned maintenance on 3 of your terminals until about 12:00 UTC",
3280
+ * "terminals": "3",
3281
+ * "change": "eta_moved"
3282
+ * }
3283
+ */
3284
+ MaintenanceWindowChanged: {
3285
+ /** @description The payload's version; 1 */
3286
+ schema_version: string;
3287
+ /** Format: uuid */
3288
+ window_id: string;
3289
+ /** @description MW-0031 */
3290
+ label: string;
3291
+ /** @enum {string} */
3292
+ state: "scheduled" | "in_progress" | "completed" | "cancelled";
3293
+ /** Format: date-time */
3294
+ starts_at: string;
3295
+ /**
3296
+ * Format: date-time
3297
+ * @description The current expected end: the ETA while in progress
3298
+ */
3299
+ ends_at: string;
3300
+ /** @description The notice as sent to you, with your own counts; empty when the notice is off */
3301
+ message: string;
3302
+ /** @description How many of your terminals are in the window; GET /maintenance/{id}/terminals lists them, each with its own expected time */
3303
+ terminals: string;
3304
+ /**
3305
+ * @description What moved this time
3306
+ * @enum {string}
3307
+ */
3308
+ change: "announced" | "started" | "eta_moved" | "message_changed" | "ended" | "cancelled";
3309
+ };
3310
+ TerminalJournal: {
3311
+ /** Format: uuid */
3312
+ terminal_id: string;
3313
+ /**
3314
+ * @description live: read from the running terminal now. last_words: kept by its cell when it stopped
3315
+ * @enum {string}
3316
+ */
3317
+ from: "live" | "last_words";
3318
+ /**
3319
+ * Format: date-time
3320
+ * @description When the cell read the journal: now for live, the stop for last_words
3321
+ */
3322
+ read_at: string;
3323
+ /**
3324
+ * @description For last_words, why the cell kept them; null for live
3325
+ * @enum {string|null}
3326
+ */
3327
+ stopped?: "stopped" | "failed" | "retired" | null;
3328
+ /** @description Newest first */
3329
+ lines: components["schemas"]["JournalLine"][];
3330
+ };
3331
+ JournalLine: {
3332
+ /**
3333
+ * Format: date-time
3334
+ * @description UTC: the journal file's date and the line's time, converted from the cell's clock
3335
+ */
3336
+ ts: string;
3337
+ /** @description The journal's own source column as MetaTrader writes it, e.g. Network, Terminal, Trades */
3338
+ source: string;
3339
+ /** @description The line as written, account numbers masked, file paths removed, at most 200 characters. MetaTrader's journal has no level column, so none is given */
3340
+ message: string;
3341
+ };
3342
+ BrokerCatalogue: {
3343
+ /** Format: uuid */
3344
+ server_id: string;
3345
+ /** @description The broker's symbol variant, as on GET /brokers/{id}/symbols */
3346
+ account_type: string;
3347
+ /**
3348
+ * @description As SymbolPage.source
3349
+ * @enum {string}
3350
+ */
3351
+ source: "live" | "last_known";
3352
+ /** @description sha256 over every listed symbol's name, spec_hash, description and identity fields, in name order */
3353
+ catalogue_hash: string;
3354
+ /** @description Symbols listed now (removed ones excluded) */
3355
+ symbols: number;
3356
+ /**
3357
+ * Format: date-time
3358
+ * @description The newest refreshed_at of its rows
3359
+ */
3360
+ refreshed_at: string;
3361
+ };
3362
+ BrokerCatalogueList: {
3363
+ items: components["schemas"]["BrokerCatalogue"][];
3364
+ };
2548
3365
  /** @description `data` of a `symbol.spec_changed` message on the `events` channel: sent once for each change a tenant's terminal reported to a symbol on its own catalogue (one server and account type) - the specification a program sizes a trade by, what trading the broker allows, the trade sessions, or the symbol's removal. A pip value that moves with the exchange rate is not a change and sends nothing; read it on the catalogue. On the stream every value is text, and `before` and `after` are JSON objects as text. */
2549
3366
  SymbolSpecChanged: {
2550
3367
  schema_version: string;
@@ -2559,8 +3376,11 @@ export interface components {
2559
3376
  server_id: string;
2560
3377
  account_type: string;
2561
3378
  symbol: string;
2562
- /** @enum {string} */
2563
- change: "spec" | "trading" | "sessions" | "removed";
3379
+ /**
3380
+ * @description added: new on the catalogue; came_back: listed again after a removal; description: the broker's long name changed; identity: path, isin, exchange, category, currency_base, basis or calc_mode changed. MetaTrader has no rename: a renamed symbol is removed and added, and isin is how the two may be matched
3381
+ * @enum {string}
3382
+ */
3383
+ change: "spec" | "trading" | "sessions" | "removed" | "added" | "came_back" | "description" | "identity";
2564
3384
  /** @description The spec before (digits, contract_size, volume_min, volume_step, volume_max, trade_mode, margin_currency, pip_value_per_lot, trade_sessions, description), decimals as strings */
2565
3385
  before: Record<string, never> | null;
2566
3386
  /** @description The spec after, same shape; null for a removal */
@@ -2713,8 +3533,27 @@ export interface components {
2713
3533
  /** Format: date-time */
2714
3534
  refreshed_at: string;
2715
3535
  status: components["schemas"]["SymbolStatus"];
3536
+ /** @description Where the broker files the symbol (SYMBOL_PATH), e.g. Shares\US\NVDA. Null when the broker leaves it empty; never inferred */
3537
+ path?: string | null;
3538
+ /** @description SYMBOL_ISIN, as the broker fills it; null when empty */
3539
+ isin?: string | null;
3540
+ /** @description SYMBOL_EXCHANGE; null when empty */
3541
+ exchange?: string | null;
3542
+ /** @description SYMBOL_CATEGORY, the broker's own free text; null when empty */
3543
+ category?: string | null;
3544
+ /** @description SYMBOL_CURRENCY_BASE; null when empty */
3545
+ currency_base?: string | null;
3546
+ /** @description SYMBOL_BASIS, the underlying asset of a derivative; null when empty */
3547
+ basis?: string | null;
3548
+ /** @description SYMBOL_TRADE_CALC_MODE as MetaTrader names it, lower-case without its prefix (forex, futures, cfd, cfdindex, cfdleverage, forex_no_leverage, exch_stocks, exch_futures, exch_options, exch_options_margin, exch_bonds, exch_stocks_moex, exch_bonds_moex, serv_collateral); a number without a name is given as its digits */
3549
+ calc_mode?: string | null;
2716
3550
  };
2717
3551
  SymbolPage: {
3552
+ /**
3553
+ * @description live: a terminal of yours runs on this server and account type. last_known: none does any more; the rows are the last your terminals reported, each with its refreshed_at
3554
+ * @enum {string}
3555
+ */
3556
+ source?: "live" | "last_known";
2718
3557
  /** @description Opaque (created_at, id) cursor for the following page; null on the last page */
2719
3558
  next_cursor: string | null;
2720
3559
  /** @description How many items are in THIS page; never a total */
@@ -2983,6 +3822,37 @@ export interface components {
2983
3822
  symbol: string;
2984
3823
  timeframe: string;
2985
3824
  };
3825
+ /** @description A parameter set for the terminal's EA. Your keys go in `params`; the platform writes them, with `enabled` and the version it assigns, to the EA's parameter file (`MQL5/Files/mfx/params.json`), so they never collide with the platform's own keys. */
3826
+ ExpertAdvisorParametersPut: {
3827
+ /** @description False asks your EA to stop trading (a soft pause): it is honoured by the EA, and the hard stop stays the platform's */
3828
+ enabled: boolean;
3829
+ /** @description Your EA's settings, any JSON object. With the platform's keys the file is at most 256 KiB and 32 levels deep; no key anywhere may be named like a credential (password, secret, token, api key, private key, login) */
3830
+ params: {
3831
+ [key: string]: unknown;
3832
+ };
3833
+ };
3834
+ /** @description The newest parameter set for a terminal, and how far its EA has taken it up */
3835
+ ExpertAdvisorParameters: {
3836
+ /** Format: uuid */
3837
+ terminal_id: string;
3838
+ /** @description Assigned by the platform; one more than the set before */
3839
+ version: number;
3840
+ enabled: boolean;
3841
+ params: {
3842
+ [key: string]: unknown;
3843
+ };
3844
+ /** Format: date-time */
3845
+ accepted_at: string;
3846
+ /** @description The version your EA last said it runs, from its heartbeat line as the terminal reported it; null until it has said */
3847
+ applied_version: number | null;
3848
+ /**
3849
+ * Format: date-time
3850
+ * @description When the platform first heard that applied_version
3851
+ */
3852
+ applied_seen_at: string | null;
3853
+ /** @description True when applied_version equals version. False past the grace window (60 s unless an operator changed it) raises the terminal condition EaParamsNotApplied */
3854
+ converged: boolean;
3855
+ };
2986
3856
  TenantExpertAdvisorPage: {
2987
3857
  /** @description Opaque (created_at, id) cursor for the following page; null on the last page */
2988
3858
  next_cursor: string | null;
@@ -3149,6 +4019,10 @@ export interface operations {
3149
4019
  query?: {
3150
4020
  cursor?: components["parameters"]["cursor"];
3151
4021
  limit?: components["parameters"]["limit"];
4022
+ /** @description key:value, the label's exact key and value, case-sensitive. Repeat it to need several: a terminal is listed only when it carries every one. A value without a colon is 400 validation_error */
4023
+ label?: string[];
4024
+ /** @description one state, or several comma-separated */
4025
+ state?: ("REQUESTED" | "PROVISIONING" | "LOGGING_IN" | "ACTIVE" | "OFFLINE" | "PARKED" | "DRAINING" | "DRAINED" | "RETIRED" | "FAILED")[];
3152
4026
  };
3153
4027
  header?: never;
3154
4028
  path?: never;
@@ -3313,6 +4187,50 @@ export interface operations {
3313
4187
  default: components["responses"]["Error"];
3314
4188
  };
3315
4189
  };
4190
+ getTerminalJournal: {
4191
+ parameters: {
4192
+ query?: {
4193
+ /** @description How many lines, newest first; 1 to 200, default 100 */
4194
+ lines?: number;
4195
+ };
4196
+ header?: never;
4197
+ path: {
4198
+ id: components["parameters"]["terminalId"];
4199
+ };
4200
+ cookie?: never;
4201
+ };
4202
+ requestBody?: never;
4203
+ responses: {
4204
+ /** @description the lines */
4205
+ 200: {
4206
+ headers: {
4207
+ [name: string]: unknown;
4208
+ };
4209
+ content: {
4210
+ "application/json": components["schemas"]["TerminalJournal"];
4211
+ };
4212
+ };
4213
+ /** @description state_error - the terminal is not running and no last lines are held (details.reason not_running), for example before its first launch or more than 24 hours after it stopped */
4214
+ 409: {
4215
+ headers: {
4216
+ [name: string]: unknown;
4217
+ };
4218
+ content: {
4219
+ "application/json": components["schemas"]["Error"];
4220
+ };
4221
+ };
4222
+ /** @description upstream_error - the cell did not answer within 10 s (details.reason terminal_timeout), or could not read the journal (journal_unreadable) */
4223
+ 503: {
4224
+ headers: {
4225
+ [name: string]: unknown;
4226
+ };
4227
+ content: {
4228
+ "application/json": components["schemas"]["Error"];
4229
+ };
4230
+ };
4231
+ default: components["responses"]["Error"];
4232
+ };
4233
+ };
3316
4234
  replaceTerminalInstruments: {
3317
4235
  parameters: {
3318
4236
  query?: never;
@@ -3393,6 +4311,36 @@ export interface operations {
3393
4311
  default: components["responses"]["Error"];
3394
4312
  };
3395
4313
  };
4314
+ setTerminalHa: {
4315
+ parameters: {
4316
+ query?: never;
4317
+ header: {
4318
+ /** @description A key is remembered for 24 hours per key-holder. The same key with an identical body returns the original answer with `duplicate: true`. The same key with a DIFFERENT body is 409 `conflict` with `details.reason = idempotency_key_reused` and `details.original_command_id`: silently answering a different order with the first order's result is the failure that loses money unnoticed. The body is compared as its canonical JSON, and the SDK does this for the caller. */
4319
+ "Idempotency-Key": components["parameters"]["idempotencyKey"];
4320
+ };
4321
+ path: {
4322
+ id: components["parameters"]["terminalId"];
4323
+ };
4324
+ cookie?: never;
4325
+ };
4326
+ requestBody: {
4327
+ content: {
4328
+ "application/json": components["schemas"]["TerminalHa"];
4329
+ };
4330
+ };
4331
+ responses: {
4332
+ /** @description the terminal as it now is, with `ha` */
4333
+ 200: {
4334
+ headers: {
4335
+ [name: string]: unknown;
4336
+ };
4337
+ content: {
4338
+ "application/json": components["schemas"]["TenantTerminal"];
4339
+ };
4340
+ };
4341
+ default: components["responses"]["Error"];
4342
+ };
4343
+ };
3396
4344
  resumeTerminal: {
3397
4345
  parameters: {
3398
4346
  query?: never;
@@ -3537,6 +4485,59 @@ export interface operations {
3537
4485
  default: components["responses"]["Error"];
3538
4486
  };
3539
4487
  };
4488
+ getExpertAdvisorParameters: {
4489
+ parameters: {
4490
+ query?: never;
4491
+ header?: never;
4492
+ path: {
4493
+ id: components["parameters"]["terminalId"];
4494
+ };
4495
+ cookie?: never;
4496
+ };
4497
+ requestBody?: never;
4498
+ responses: {
4499
+ /** @description the newest set */
4500
+ 200: {
4501
+ headers: {
4502
+ [name: string]: unknown;
4503
+ };
4504
+ content: {
4505
+ "application/json": components["schemas"]["ExpertAdvisorParameters"];
4506
+ };
4507
+ };
4508
+ default: components["responses"]["Error"];
4509
+ };
4510
+ };
4511
+ putExpertAdvisorParameters: {
4512
+ parameters: {
4513
+ query?: never;
4514
+ header: {
4515
+ /** @description A key is remembered for 24 hours per key-holder. The same key with an identical body returns the original answer with `duplicate: true`. The same key with a DIFFERENT body is 409 `conflict` with `details.reason = idempotency_key_reused` and `details.original_command_id`: silently answering a different order with the first order's result is the failure that loses money unnoticed. The body is compared as its canonical JSON, and the SDK does this for the caller. */
4516
+ "Idempotency-Key": components["parameters"]["idempotencyKey"];
4517
+ };
4518
+ path: {
4519
+ id: components["parameters"]["terminalId"];
4520
+ };
4521
+ cookie?: never;
4522
+ };
4523
+ requestBody: {
4524
+ content: {
4525
+ "application/json": components["schemas"]["ExpertAdvisorParametersPut"];
4526
+ };
4527
+ };
4528
+ responses: {
4529
+ /** @description accepted, numbered and on its way to the terminal */
4530
+ 202: {
4531
+ headers: {
4532
+ [name: string]: unknown;
4533
+ };
4534
+ content: {
4535
+ "application/json": components["schemas"]["ExpertAdvisorParameters"];
4536
+ };
4537
+ };
4538
+ default: components["responses"]["Error"];
4539
+ };
4540
+ };
3540
4541
  getTerminalOperation: {
3541
4542
  parameters: {
3542
4543
  query?: never;
@@ -3954,7 +4955,9 @@ export interface operations {
3954
4955
  listDeals: {
3955
4956
  parameters: {
3956
4957
  query: {
4958
+ /** @description A UTC instant; the broker's own clock is applied on the way to MT5 */
3957
4959
  from: string;
4960
+ /** @description A UTC instant, as from */
3958
4961
  to: string;
3959
4962
  /** @description Only the deals of this position (Deal.position): its opening deal, any partial closes and its closing deal, inside from/to. A position open longer than 31 days is read window by window */
3960
4963
  position?: number;
@@ -3987,7 +4990,7 @@ export interface operations {
3987
4990
  "application/json": components["schemas"]["Error"];
3988
4991
  };
3989
4992
  };
3990
- /** @description upstream_error - terminal_timeout (no answer within 10 s) or deals_unreadable */
4993
+ /** @description upstream_error - terminal_timeout (no answer within 10 s), deals_unreadable, or broker_clock_unknown (no clock measured for the server yet: retry once a terminal on it has a price) */
3991
4994
  503: {
3992
4995
  headers: {
3993
4996
  [name: string]: unknown;
@@ -4075,6 +5078,29 @@ export interface operations {
4075
5078
  default: components["responses"]["Error"];
4076
5079
  };
4077
5080
  };
5081
+ listCatalogues: {
5082
+ parameters: {
5083
+ query?: never;
5084
+ header?: never;
5085
+ path: {
5086
+ id: components["parameters"]["pathId"];
5087
+ };
5088
+ cookie?: never;
5089
+ };
5090
+ requestBody?: never;
5091
+ responses: {
5092
+ /** @description the catalogues */
5093
+ 200: {
5094
+ headers: {
5095
+ [name: string]: unknown;
5096
+ };
5097
+ content: {
5098
+ "application/json": components["schemas"]["BrokerCatalogueList"];
5099
+ };
5100
+ };
5101
+ default: components["responses"]["Error"];
5102
+ };
5103
+ };
4078
5104
  listSymbols: {
4079
5105
  parameters: {
4080
5106
  query?: {
@@ -4319,16 +5345,28 @@ export interface operations {
4319
5345
  };
4320
5346
  requestBody?: never;
4321
5347
  responses: {
4322
- /** @description candles, oldest first */
5348
+ /** @description candles, oldest first; Retry-After when the store is still repairing the window (complete_to says how far it is whole) */
4323
5349
  200: {
4324
5350
  headers: {
5351
+ /** @description Seconds; only while the store repairs a gap in the window */
5352
+ "Retry-After"?: number;
4325
5353
  [name: string]: unknown;
4326
5354
  };
4327
5355
  content: {
4328
5356
  "application/json": components["schemas"]["CandleList"];
4329
5357
  };
4330
5358
  };
4331
- /** @description upstream_error — terminal_timeout (no answer within 10 s) or the cell's word */
5359
+ /** @description reads_busy — your reads through terminals on this server are at their limit in flight (md.candle_reads_in_flight); ask again after Retry-After */
5360
+ 429: {
5361
+ headers: {
5362
+ "Retry-After"?: number;
5363
+ [name: string]: unknown;
5364
+ };
5365
+ content: {
5366
+ "application/json": components["schemas"]["Error"];
5367
+ };
5368
+ };
5369
+ /** @description upstream_error — terminal_timeout (no answer within 10 s), broker_clock_unknown (no clock measured for the server yet), or the cell's word */
4332
5370
  503: {
4333
5371
  headers: {
4334
5372
  [name: string]: unknown;
@@ -4365,7 +5403,7 @@ export interface operations {
4365
5403
  "application/json": components["schemas"]["TickList"];
4366
5404
  };
4367
5405
  };
4368
- /** @description upstream_error — terminal_timeout (no answer within 10 s) or the cell's word */
5406
+ /** @description upstream_error — terminal_timeout (no answer within 10 s), broker_clock_unknown (no clock measured for the server yet), or the cell's word */
4369
5407
  503: {
4370
5408
  headers: {
4371
5409
  [name: string]: unknown;
@@ -4433,7 +5471,7 @@ export interface operations {
4433
5471
  };
4434
5472
  requestBody?: never;
4435
5473
  responses: {
4436
- /** @description Consumption per metric, the calling key's limits, and the budget left */
5474
+ /** @description The limits, their sources and their use */
4437
5475
  200: {
4438
5476
  headers: {
4439
5477
  "X-MFX-Budget-Remaining": components["headers"]["BudgetRemaining"];
@@ -4643,7 +5681,7 @@ export interface operations {
4643
5681
  cursor?: components["parameters"]["cursor"];
4644
5682
  limit?: components["parameters"]["limit"];
4645
5683
  /** @description one state, or several comma-separated. completed lists the last 30 days */
4646
- state?: "scheduled" | "in_progress" | "completed" | "cancelled";
5684
+ state?: ("scheduled" | "in_progress" | "completed" | "cancelled")[];
4647
5685
  };
4648
5686
  header?: never;
4649
5687
  path?: never;
@@ -4705,7 +5743,7 @@ export interface operations {
4705
5743
  };
4706
5744
  content?: never;
4707
5745
  };
4708
- /** @description over WS connection quota */
5746
+ /** @description Over the tenant's connection ceiling or the key's cap (`ws_conns`, `details.scope`). Not sent as an HTTP status: the upgrade succeeds, one `error` op with `rate_limited` follows, and the connection closes with 4029. */
4709
5747
  429: {
4710
5748
  headers: {
4711
5749
  "Retry-After": components["headers"]["RetryAfter"];
@@ -4921,7 +5959,7 @@ export interface operations {
4921
5959
  parameters: {
4922
5960
  query?: {
4923
5961
  /** @description one value, or several comma-separated */
4924
- state?: "retrying" | "delivered" | "dead";
5962
+ state?: ("retrying" | "delivered" | "dead")[];
4925
5963
  cursor?: components["parameters"]["cursor"];
4926
5964
  limit?: components["parameters"]["limit"];
4927
5965
  };