fxapis 0.1.1__tar.gz

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.
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ .venv-check/
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ __pycache__/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
fxapis-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 El Wizard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fxapis-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.5
2
+ Name: fxapis
3
+ Version: 0.1.1
4
+ Summary: MT5 Python SDK for fxapis, the hosted MetaTrader 5 REST API: trade MT5 accounts with no terminal, VPS or EA.
5
+ Project-URL: Homepage, https://fxapis.com
6
+ Project-URL: Documentation, https://docs.fxapis.com
7
+ Project-URL: API reference, https://docs.fxapis.com/api-reference
8
+ Project-URL: Repository, https://github.com/FXapis/fxapis-python
9
+ Project-URL: Issues, https://github.com/FXapis/fxapis-python/issues
10
+ Project-URL: Examples, https://github.com/FXapis/fxapis-examples
11
+ Project-URL: Changelog, https://docs.fxapis.com/changelog
12
+ Author-email: El Wizard <mago@elwizard.net>
13
+ License-Expression: MIT
14
+ License-File: LICENSE
15
+ Keywords: algorithmic-trading,copy-trading,forex,forex-api,metatrader,metatrader-5,metatrader5,mt5,mt5 python,mt5-api,rest-api,sdk,trade-copier,trading-api
16
+ Classifier: Development Status :: 4 - Beta
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Financial and Insurance Industry
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
+ Classifier: Topic :: Office/Business :: Financial :: Investment
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.10
31
+ Requires-Dist: httpx<1,>=0.25
32
+ Provides-Extra: dev
33
+ Requires-Dist: build>=1.2; extra == 'dev'
34
+ Requires-Dist: mypy>=1.10; extra == 'dev'
35
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
36
+ Requires-Dist: pytest>=8; extra == 'dev'
37
+ Requires-Dist: ruff>=0.6; extra == 'dev'
38
+ Requires-Dist: twine>=5; extra == 'dev'
39
+ Description-Content-Type: text/markdown
40
+
41
+ <div align="center">
42
+
43
+ <img src="https://fxapis.com/logo.png" alt="" width="72" height="72">
44
+
45
+ # fxapis
46
+
47
+ **The official Python SDK for fxapis — the hosted MetaTrader 5 REST API**
48
+
49
+ [Website](https://fxapis.com) · [Docs](https://docs.fxapis.com) · [API Reference](https://docs.fxapis.com/api-reference) · [Status](https://status.fxapis.com) · [Support](mailto:support@fxapis.com)
50
+
51
+ [![License: MIT](https://img.shields.io/badge/license-MIT-22D3D6?style=flat-square)](LICENSE)
52
+ [![ci](https://img.shields.io/github/actions/workflow/status/FXapis/fxapis-python/ci.yml?branch=main&style=flat-square&label=ci)](https://github.com/FXapis/fxapis-python/actions/workflows/ci.yml)
53
+ [![PyPI](https://img.shields.io/pypi/v/fxapis?style=flat-square&color=22D3D6)](https://pypi.org/project/fxapis/)
54
+ [![release](https://img.shields.io/github/v/release/FXapis/fxapis-python?style=flat-square&color=22D3D6)](https://github.com/FXapis/fxapis-python/releases)
55
+ [![Python](https://img.shields.io/badge/python-3.10%E2%80%933.14-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org)
56
+ [![types](https://img.shields.io/badge/types-strict%20mypy-3178C6?style=flat-square)](pyproject.toml)
57
+ [![fxapis status](https://status.fxapis.com/badge.svg)](https://status.fxapis.com)
58
+
59
+ </div>
60
+
61
+ ---
62
+
63
+ Connect an MT5 account once, then place market and pending orders, close and modify positions, read deals and positions, and send one trade to many accounts at once — from Python, over HTTPS.
64
+
65
+ You run **no MetaTrader terminal, no Windows VPS and no EA**. fxapis runs the MT5 terminals in its cloud (EU, Amsterdam) and gives you an API in front of them. That makes it a fit for **MT5 Python** automation on Linux or macOS, **copy trading** and trade copiers, **click-to-trade** signal apps, and anything that manages many MT5 accounts.
66
+
67
+ - **Sync (`Fxapis`) and async (`AsyncFxapis`) clients**, built on [httpx](https://www.python-httpx.org/)
68
+ - **Fully typed** (`py.typed`, `TypedDict` responses with the API's exact field names), checked with `mypy --strict`
69
+ - **Automatic `Idempotency-Key`** on every order, with an override for your own keys
70
+ - **Typed exceptions** for every API error code — and safe retries that never resend an unresolved order
71
+ - **Helpers:** `wait_until_ready()`, `wait_until_resolved()`, `wait_until_settled()`, pagination iterators
72
+
73
+ MT5 only (MT4 is not supported).
74
+
75
+ ## Table of contents
76
+
77
+ - [Installation](#installation)
78
+ - [Quickstart](#quickstart)
79
+ - [Handling order outcomes](#handling-order-outcomes)
80
+ - [Click-to-trade and signals](#click-to-trade-and-signals)
81
+ - [Copy trading: one trade on many accounts](#copy-trading-one-trade-on-many-accounts)
82
+ - [Async](#async)
83
+ - [Reference](#reference)
84
+ - [Good to know](#good-to-know)
85
+ - [Related fxapis repositories](#related-fxapis-repositories)
86
+ - [Contributing](#contributing)
87
+ - [Support](#support)
88
+ - [License](#license)
89
+
90
+ ## Installation
91
+
92
+ ```bash
93
+ pip install fxapis
94
+ ```
95
+
96
+ Python 3.10+. Create an API key in the console at [fxapis.com](https://fxapis.com) and export it:
97
+
98
+ ```bash
99
+ export FXAPIS_API_KEY="fx_test_..."
100
+ ```
101
+
102
+ > [!WARNING]
103
+ > **Test keys reach real brokers.** An `fx_test_` key is a label for your configuration, not a sandbox. Build and test with a **broker demo account**.
104
+
105
+ ## Quickstart
106
+
107
+ ```python
108
+ from fxapis import Fxapis
109
+
110
+ client = Fxapis() # reads FXAPIS_API_KEY
111
+
112
+ # 1. Connect an MT5 account (once). Use the trading password, not the investor password.
113
+ account = client.accounts.connect(
114
+ login="26177561",
115
+ server="VantageMarkets-Demo",
116
+ password="your-mt5-trading-password",
117
+ mode="warm_on_demand", # online when needed, offline after 15 idle minutes
118
+ label="demo — strategy A",
119
+ )
120
+
121
+ # 2. Bring it online and wait (about 10 seconds for a typical broker).
122
+ client.accounts.warm(account["id"])
123
+ client.accounts.wait_until_ready(account["id"])
124
+
125
+ # 3. Trade. Volumes and prices are strings: "0.01", not 0.01.
126
+ order = client.orders.market(
127
+ account["id"],
128
+ symbol="EURUSD",
129
+ side="buy",
130
+ volume="0.01",
131
+ stop_loss="1.12900",
132
+ take_profit="1.14200",
133
+ )
134
+ print(order["state"], order["filledPrice"])
135
+
136
+ # 4. Read positions, then close one.
137
+ for position in client.positions.list(account["id"]):
138
+ print(position["symbol"], position["side"], position["volume"], position["profit"], position["observedAt"])
139
+ client.positions.close(account["id"], position["brokerPositionId"])
140
+ ```
141
+
142
+ The `with Fxapis() as client:` form closes the connection pool for you.
143
+
144
+ ## Handling order outcomes
145
+
146
+ An order can end three ways that must be told apart. The SDK raises a different exception for each:
147
+
148
+ ```python
149
+ from fxapis import (
150
+ Fxapis,
151
+ OrderRejectedError,
152
+ OrderUnresolvedError,
153
+ SendFailedError,
154
+ AccountNotReadyError,
155
+ )
156
+
157
+ client = Fxapis()
158
+ key = f"signal_{signal_id}:member_{member_id}" # your own idempotency key
159
+
160
+ try:
161
+ order = client.orders.market(account_id, symbol="XAUUSD", side="buy", volume="0.05", idempotency_key=key)
162
+ except OrderRejectedError as err:
163
+ # The broker refused it; nothing opened. err.retryable says whether the reason was
164
+ # transient (a requote). A new attempt needs a NEW key — this one now answers with the rejection.
165
+ print("rejected:", err.message)
166
+ except OrderUnresolvedError as err:
167
+ # Nobody knows yet whether it reached the broker. NEVER resend it. Poll until fxapis
168
+ # has confirmed the result with the broker:
169
+ order = client.orders.wait_until_resolved(err.order_id)
170
+ except (SendFailedError, AccountNotReadyError) as err:
171
+ # Nothing was sent. The SDK already retried with the same key (max_retries);
172
+ # retrying later with err.idempotency_key is still safe.
173
+ print("not sent:", err.code)
174
+ ```
175
+
176
+ | Error class | API code | Retried automatically? |
177
+ |---|---|---|
178
+ | `SendFailedError` | `SEND_FAILED` (503) | Yes, same key |
179
+ | `AccountNotReadyError` / `NoRuntimeError` | `ACCOUNT_NOT_READY` / `NO_RUNTIME` (409) | Yes, same key |
180
+ | `IdempotencyInFlightError` | `IDEMPOTENCY_IN_FLIGHT` (409) | Yes, same key |
181
+ | `RateLimitedError` | `RATE_LIMITED` (429) | Yes, after `Retry-After` |
182
+ | `OrderUnresolvedError` | `ORDER_UNRESOLVED` (503) | **Never** — poll the order |
183
+ | `OrderRejectedError` | `ORDER_REJECTED` (422) | Never — needs a new key |
184
+ | `IdempotencyKeyReusedError` | `IDEMPOTENCY_KEY_REUSED` (409) | Never |
185
+ | `QuotaExceededError` / `FeatureNotInPlanError` | 402 | Never |
186
+ | `AuthenticationError`, `PermissionDeniedError`, `InvalidRequestError`, `NotFoundError` | 401 / 403 / 400 / 404 | Never |
187
+ | `APIConnectionError` / `APITimeoutError` | no HTTP answer | Reads, and requests with an idempotency key |
188
+
189
+ Every API error is an `APIStatusError` with `.status`, `.code`, `.message`, `.request_id` (quote it to support), `.details`, `.order_id`, `.idempotency_key` and `.retryable`. Tune retries with `Fxapis(max_retries=0..n)`.
190
+
191
+ **Idempotency keys.** Market orders, pending orders, closes and multi-account orders always carry an `Idempotency-Key` — a UUID unless you pass `idempotency_key=`. The API honours a key for 24 hours: the same key with the same body returns the first answer and never places a second order. Derive your own key from something stable (a signal and a member, a strategy tick) when a double click or a restarted worker must not trade twice.
192
+
193
+ ## Click-to-trade and signals
194
+
195
+ For apps where each member approves a signal with a click: prepare the member's account **when they open the signal**, then place the order with a key made from the signal and the member.
196
+
197
+ ```python
198
+ client.accounts.prepare([member.fxapis_account_id]) # up to 200 accounts per call; returns at once
199
+
200
+ order = client.orders.market(
201
+ member.fxapis_account_id,
202
+ symbol=signal.symbol,
203
+ side=signal.side,
204
+ volume=member.lot_size,
205
+ stop_loss=signal.stop_loss,
206
+ take_profit=signal.take_profit,
207
+ client_order_id=f"signal_{signal.id}",
208
+ idempotency_key=f"signal_{signal.id}:member_{member.id}",
209
+ )
210
+ ```
211
+
212
+ The full walkthrough is in the [signals guide](https://docs.fxapis.com/signals).
213
+
214
+ ## Copy trading: one trade on many accounts
215
+
216
+ A multi-account order ("execution wave") brings every account online, then sends the orders together:
217
+
218
+ ```python
219
+ wave = client.waves.create(
220
+ account_ids=follower_ids, # up to 500
221
+ symbol="EURUSD",
222
+ side="buy",
223
+ volume="0.10",
224
+ weights={big_account_id: "0.50"}, # per-account volume, optional
225
+ barrier_policy="release-ready", # or "all-or-nothing", "wait"
226
+ client_wave_id="master-deal-123456",
227
+ )
228
+ wave = client.waves.wait_until_settled(wave["id"])
229
+ print(wave["summary"], wave["dispatchSpreadMs"])
230
+ ```
231
+
232
+ `client.multi_account_orders` is the same resource under a descriptive name. There are no event webhooks yet: to follow a master account, poll its deals (`client.deals.list(master_id, since=...)`) — see [`copy_trader.py`](https://github.com/FXapis/fxapis-examples/blob/main/python/copy_trader.py).
233
+
234
+ ## Async
235
+
236
+ ```python
237
+ import asyncio
238
+ from fxapis import AsyncFxapis
239
+
240
+
241
+ async def main() -> None:
242
+ async with AsyncFxapis() as client:
243
+ accounts = await client.accounts.list()
244
+ async for order in client.orders.iter(state="unknown"):
245
+ print(order["id"], order["symbol"])
246
+
247
+
248
+ asyncio.run(main())
249
+ ```
250
+
251
+ ## Reference
252
+
253
+ | Resource | Methods |
254
+ |---|---|
255
+ | `client.workspace` | `get()` |
256
+ | `client.accounts` | `connect()`, `list()`, `get()`, `status()`, `warm()`, `prepare()`, `cool()`, `restart()`, `disconnect()`, `delete()`, `set_mode()` (alias `mode()`), `replace_credentials()`, `reconcile()`, `wait_until_ready()`, `bring_online()`, `symbols()`, `instruments()`, `sessions()` (market hours), `sync_symbols()` |
257
+ | `client.instruments` | `list()` — markets named once (`XAUUSD`, `US30`…); pass one as `instrument=` to `orders.market()` / `orders.pending()` to trade each account's own broker symbol for it |
258
+ | `client.orders` | `market()`, `pending()`, `modify()`, `cancel()`, `get()`, `list()`, `iter()`, `deals()`, `wait_until_resolved()` |
259
+ | `client.positions` | `list()`, `close()`, `modify()` |
260
+ | `client.deals` | `list()`, `iter()`, `for_order()` |
261
+ | `client.calculate` | `margin()`, `profit()` |
262
+ | `client.waves` (= `client.multi_account_orders`) | `create()`, `get()`, `list()`, `cancel()`, `wait_until_settled()` |
263
+ | `client.alert_hooks` | `create()`, `list()`, `get()`, `update()`, `rotate()`, `enable()`, `disable()`, `delete()`, `deliveries()` — TradingView alerts to MT5 |
264
+ | `client.usage` | `get()`, `daily()` |
265
+ | `client.plans` | `list()` |
266
+
267
+ Notes:
268
+
269
+ - `orders.list()` and `deals.list()` return a `Page` (`.data`, `.has_more`, `.next_cursor`); `iter()` walks every page for you.
270
+ - `positions.modify(..., stop_loss=None)` **removes** the stop loss; leaving the argument out keeps it. The same holds for `orders.modify`.
271
+ - Times accept `datetime` (sent as RFC 3339 UTC) or strings. Numbers accept `str`, `Decimal`, `int` or `float` (rounded to 8 places) and are always sent as strings.
272
+ - `Fxapis(base_url=..., timeout=..., max_retries=..., http_client=httpx.Client(...))` for proxies, custom transports or tests.
273
+
274
+ Everything in the API — including API keys, members and billing — is in the [API reference](https://docs.fxapis.com/api-reference), with samples in 13 languages.
275
+
276
+ ## Good to know
277
+
278
+ - **Accounts on demand** (`warm_on_demand`) come online for an order or a prepare and go offline after 15 idle minutes. Stop losses and take profits live at the broker and keep working while an account is offline.
279
+ - **Positions are a snapshot.** Each has `observedAt`; call `accounts.reconcile()` for a fresh read while the account is online.
280
+ - **Scoped keys.** Keys can be read-only or reduce-only (can close, cannot open) — give each service the least it needs.
281
+ - **Wrong password?** `accounts.replace_credentials(id, password=...)` fixes it on the same account; connecting a *disconnected* login again brings the same account back. `accounts.delete(id)` removes an account and its history for good.
282
+ - **No event webhooks yet.** Poll `status`, orders and deals. (Incoming TradingView alerts are supported — see `client.alert_hooks`.)
283
+
284
+ ## Related fxapis repositories
285
+
286
+ | Repository | What it is |
287
+ |---|---|
288
+ | [`fxapis-examples`](https://github.com/FXapis/fxapis-examples) | Runnable examples using this SDK (Python, Node/TypeScript, curl) |
289
+ | [`fxapis-typescript`](https://github.com/FXapis/fxapis-typescript) | The official TypeScript/Node.js SDK, same conventions |
290
+ | [`fxapis-mcp-examples`](https://github.com/FXapis/fxapis-mcp-examples) | Connect AI agents (Claude, Cursor, VS Code) over MCP |
291
+ | [`fxapis-integrations`](https://github.com/FXapis/fxapis-integrations) | Postman collection, TradingView payloads, automation templates |
292
+
293
+ ## Contributing
294
+
295
+ ```bash
296
+ pip install -e ".[dev]"
297
+ ruff check .
298
+ mypy
299
+ pytest -q
300
+ ```
301
+
302
+ These are exactly what the `ci` workflow runs, on Python 3.10 through 3.14. Pull requests that fix a
303
+ bug, improve a docstring, or add a test are welcome. A behaviour change to a method's signature or
304
+ return shape should match the TypeScript SDK's equivalent method — both are meant to stay in step.
305
+
306
+ ## Support
307
+
308
+ - **Docs:** [docs.fxapis.com](https://docs.fxapis.com)
309
+ - **Status:** [status.fxapis.com](https://status.fxapis.com)
310
+ - **Bugs:** [open an issue](https://github.com/FXapis/fxapis-python/issues)
311
+ - **Everything else:** [support@fxapis.com](mailto:support@fxapis.com)
312
+
313
+ ## License
314
+
315
+ MIT — see [LICENSE](LICENSE).
316
+
317
+ © 2026 El Wizard
fxapis-0.1.1/README.md ADDED
@@ -0,0 +1,277 @@
1
+ <div align="center">
2
+
3
+ <img src="https://fxapis.com/logo.png" alt="" width="72" height="72">
4
+
5
+ # fxapis
6
+
7
+ **The official Python SDK for fxapis — the hosted MetaTrader 5 REST API**
8
+
9
+ [Website](https://fxapis.com) · [Docs](https://docs.fxapis.com) · [API Reference](https://docs.fxapis.com/api-reference) · [Status](https://status.fxapis.com) · [Support](mailto:support@fxapis.com)
10
+
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-22D3D6?style=flat-square)](LICENSE)
12
+ [![ci](https://img.shields.io/github/actions/workflow/status/FXapis/fxapis-python/ci.yml?branch=main&style=flat-square&label=ci)](https://github.com/FXapis/fxapis-python/actions/workflows/ci.yml)
13
+ [![PyPI](https://img.shields.io/pypi/v/fxapis?style=flat-square&color=22D3D6)](https://pypi.org/project/fxapis/)
14
+ [![release](https://img.shields.io/github/v/release/FXapis/fxapis-python?style=flat-square&color=22D3D6)](https://github.com/FXapis/fxapis-python/releases)
15
+ [![Python](https://img.shields.io/badge/python-3.10%E2%80%933.14-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org)
16
+ [![types](https://img.shields.io/badge/types-strict%20mypy-3178C6?style=flat-square)](pyproject.toml)
17
+ [![fxapis status](https://status.fxapis.com/badge.svg)](https://status.fxapis.com)
18
+
19
+ </div>
20
+
21
+ ---
22
+
23
+ Connect an MT5 account once, then place market and pending orders, close and modify positions, read deals and positions, and send one trade to many accounts at once — from Python, over HTTPS.
24
+
25
+ You run **no MetaTrader terminal, no Windows VPS and no EA**. fxapis runs the MT5 terminals in its cloud (EU, Amsterdam) and gives you an API in front of them. That makes it a fit for **MT5 Python** automation on Linux or macOS, **copy trading** and trade copiers, **click-to-trade** signal apps, and anything that manages many MT5 accounts.
26
+
27
+ - **Sync (`Fxapis`) and async (`AsyncFxapis`) clients**, built on [httpx](https://www.python-httpx.org/)
28
+ - **Fully typed** (`py.typed`, `TypedDict` responses with the API's exact field names), checked with `mypy --strict`
29
+ - **Automatic `Idempotency-Key`** on every order, with an override for your own keys
30
+ - **Typed exceptions** for every API error code — and safe retries that never resend an unresolved order
31
+ - **Helpers:** `wait_until_ready()`, `wait_until_resolved()`, `wait_until_settled()`, pagination iterators
32
+
33
+ MT5 only (MT4 is not supported).
34
+
35
+ ## Table of contents
36
+
37
+ - [Installation](#installation)
38
+ - [Quickstart](#quickstart)
39
+ - [Handling order outcomes](#handling-order-outcomes)
40
+ - [Click-to-trade and signals](#click-to-trade-and-signals)
41
+ - [Copy trading: one trade on many accounts](#copy-trading-one-trade-on-many-accounts)
42
+ - [Async](#async)
43
+ - [Reference](#reference)
44
+ - [Good to know](#good-to-know)
45
+ - [Related fxapis repositories](#related-fxapis-repositories)
46
+ - [Contributing](#contributing)
47
+ - [Support](#support)
48
+ - [License](#license)
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install fxapis
54
+ ```
55
+
56
+ Python 3.10+. Create an API key in the console at [fxapis.com](https://fxapis.com) and export it:
57
+
58
+ ```bash
59
+ export FXAPIS_API_KEY="fx_test_..."
60
+ ```
61
+
62
+ > [!WARNING]
63
+ > **Test keys reach real brokers.** An `fx_test_` key is a label for your configuration, not a sandbox. Build and test with a **broker demo account**.
64
+
65
+ ## Quickstart
66
+
67
+ ```python
68
+ from fxapis import Fxapis
69
+
70
+ client = Fxapis() # reads FXAPIS_API_KEY
71
+
72
+ # 1. Connect an MT5 account (once). Use the trading password, not the investor password.
73
+ account = client.accounts.connect(
74
+ login="26177561",
75
+ server="VantageMarkets-Demo",
76
+ password="your-mt5-trading-password",
77
+ mode="warm_on_demand", # online when needed, offline after 15 idle minutes
78
+ label="demo — strategy A",
79
+ )
80
+
81
+ # 2. Bring it online and wait (about 10 seconds for a typical broker).
82
+ client.accounts.warm(account["id"])
83
+ client.accounts.wait_until_ready(account["id"])
84
+
85
+ # 3. Trade. Volumes and prices are strings: "0.01", not 0.01.
86
+ order = client.orders.market(
87
+ account["id"],
88
+ symbol="EURUSD",
89
+ side="buy",
90
+ volume="0.01",
91
+ stop_loss="1.12900",
92
+ take_profit="1.14200",
93
+ )
94
+ print(order["state"], order["filledPrice"])
95
+
96
+ # 4. Read positions, then close one.
97
+ for position in client.positions.list(account["id"]):
98
+ print(position["symbol"], position["side"], position["volume"], position["profit"], position["observedAt"])
99
+ client.positions.close(account["id"], position["brokerPositionId"])
100
+ ```
101
+
102
+ The `with Fxapis() as client:` form closes the connection pool for you.
103
+
104
+ ## Handling order outcomes
105
+
106
+ An order can end three ways that must be told apart. The SDK raises a different exception for each:
107
+
108
+ ```python
109
+ from fxapis import (
110
+ Fxapis,
111
+ OrderRejectedError,
112
+ OrderUnresolvedError,
113
+ SendFailedError,
114
+ AccountNotReadyError,
115
+ )
116
+
117
+ client = Fxapis()
118
+ key = f"signal_{signal_id}:member_{member_id}" # your own idempotency key
119
+
120
+ try:
121
+ order = client.orders.market(account_id, symbol="XAUUSD", side="buy", volume="0.05", idempotency_key=key)
122
+ except OrderRejectedError as err:
123
+ # The broker refused it; nothing opened. err.retryable says whether the reason was
124
+ # transient (a requote). A new attempt needs a NEW key — this one now answers with the rejection.
125
+ print("rejected:", err.message)
126
+ except OrderUnresolvedError as err:
127
+ # Nobody knows yet whether it reached the broker. NEVER resend it. Poll until fxapis
128
+ # has confirmed the result with the broker:
129
+ order = client.orders.wait_until_resolved(err.order_id)
130
+ except (SendFailedError, AccountNotReadyError) as err:
131
+ # Nothing was sent. The SDK already retried with the same key (max_retries);
132
+ # retrying later with err.idempotency_key is still safe.
133
+ print("not sent:", err.code)
134
+ ```
135
+
136
+ | Error class | API code | Retried automatically? |
137
+ |---|---|---|
138
+ | `SendFailedError` | `SEND_FAILED` (503) | Yes, same key |
139
+ | `AccountNotReadyError` / `NoRuntimeError` | `ACCOUNT_NOT_READY` / `NO_RUNTIME` (409) | Yes, same key |
140
+ | `IdempotencyInFlightError` | `IDEMPOTENCY_IN_FLIGHT` (409) | Yes, same key |
141
+ | `RateLimitedError` | `RATE_LIMITED` (429) | Yes, after `Retry-After` |
142
+ | `OrderUnresolvedError` | `ORDER_UNRESOLVED` (503) | **Never** — poll the order |
143
+ | `OrderRejectedError` | `ORDER_REJECTED` (422) | Never — needs a new key |
144
+ | `IdempotencyKeyReusedError` | `IDEMPOTENCY_KEY_REUSED` (409) | Never |
145
+ | `QuotaExceededError` / `FeatureNotInPlanError` | 402 | Never |
146
+ | `AuthenticationError`, `PermissionDeniedError`, `InvalidRequestError`, `NotFoundError` | 401 / 403 / 400 / 404 | Never |
147
+ | `APIConnectionError` / `APITimeoutError` | no HTTP answer | Reads, and requests with an idempotency key |
148
+
149
+ Every API error is an `APIStatusError` with `.status`, `.code`, `.message`, `.request_id` (quote it to support), `.details`, `.order_id`, `.idempotency_key` and `.retryable`. Tune retries with `Fxapis(max_retries=0..n)`.
150
+
151
+ **Idempotency keys.** Market orders, pending orders, closes and multi-account orders always carry an `Idempotency-Key` — a UUID unless you pass `idempotency_key=`. The API honours a key for 24 hours: the same key with the same body returns the first answer and never places a second order. Derive your own key from something stable (a signal and a member, a strategy tick) when a double click or a restarted worker must not trade twice.
152
+
153
+ ## Click-to-trade and signals
154
+
155
+ For apps where each member approves a signal with a click: prepare the member's account **when they open the signal**, then place the order with a key made from the signal and the member.
156
+
157
+ ```python
158
+ client.accounts.prepare([member.fxapis_account_id]) # up to 200 accounts per call; returns at once
159
+
160
+ order = client.orders.market(
161
+ member.fxapis_account_id,
162
+ symbol=signal.symbol,
163
+ side=signal.side,
164
+ volume=member.lot_size,
165
+ stop_loss=signal.stop_loss,
166
+ take_profit=signal.take_profit,
167
+ client_order_id=f"signal_{signal.id}",
168
+ idempotency_key=f"signal_{signal.id}:member_{member.id}",
169
+ )
170
+ ```
171
+
172
+ The full walkthrough is in the [signals guide](https://docs.fxapis.com/signals).
173
+
174
+ ## Copy trading: one trade on many accounts
175
+
176
+ A multi-account order ("execution wave") brings every account online, then sends the orders together:
177
+
178
+ ```python
179
+ wave = client.waves.create(
180
+ account_ids=follower_ids, # up to 500
181
+ symbol="EURUSD",
182
+ side="buy",
183
+ volume="0.10",
184
+ weights={big_account_id: "0.50"}, # per-account volume, optional
185
+ barrier_policy="release-ready", # or "all-or-nothing", "wait"
186
+ client_wave_id="master-deal-123456",
187
+ )
188
+ wave = client.waves.wait_until_settled(wave["id"])
189
+ print(wave["summary"], wave["dispatchSpreadMs"])
190
+ ```
191
+
192
+ `client.multi_account_orders` is the same resource under a descriptive name. There are no event webhooks yet: to follow a master account, poll its deals (`client.deals.list(master_id, since=...)`) — see [`copy_trader.py`](https://github.com/FXapis/fxapis-examples/blob/main/python/copy_trader.py).
193
+
194
+ ## Async
195
+
196
+ ```python
197
+ import asyncio
198
+ from fxapis import AsyncFxapis
199
+
200
+
201
+ async def main() -> None:
202
+ async with AsyncFxapis() as client:
203
+ accounts = await client.accounts.list()
204
+ async for order in client.orders.iter(state="unknown"):
205
+ print(order["id"], order["symbol"])
206
+
207
+
208
+ asyncio.run(main())
209
+ ```
210
+
211
+ ## Reference
212
+
213
+ | Resource | Methods |
214
+ |---|---|
215
+ | `client.workspace` | `get()` |
216
+ | `client.accounts` | `connect()`, `list()`, `get()`, `status()`, `warm()`, `prepare()`, `cool()`, `restart()`, `disconnect()`, `delete()`, `set_mode()` (alias `mode()`), `replace_credentials()`, `reconcile()`, `wait_until_ready()`, `bring_online()`, `symbols()`, `instruments()`, `sessions()` (market hours), `sync_symbols()` |
217
+ | `client.instruments` | `list()` — markets named once (`XAUUSD`, `US30`…); pass one as `instrument=` to `orders.market()` / `orders.pending()` to trade each account's own broker symbol for it |
218
+ | `client.orders` | `market()`, `pending()`, `modify()`, `cancel()`, `get()`, `list()`, `iter()`, `deals()`, `wait_until_resolved()` |
219
+ | `client.positions` | `list()`, `close()`, `modify()` |
220
+ | `client.deals` | `list()`, `iter()`, `for_order()` |
221
+ | `client.calculate` | `margin()`, `profit()` |
222
+ | `client.waves` (= `client.multi_account_orders`) | `create()`, `get()`, `list()`, `cancel()`, `wait_until_settled()` |
223
+ | `client.alert_hooks` | `create()`, `list()`, `get()`, `update()`, `rotate()`, `enable()`, `disable()`, `delete()`, `deliveries()` — TradingView alerts to MT5 |
224
+ | `client.usage` | `get()`, `daily()` |
225
+ | `client.plans` | `list()` |
226
+
227
+ Notes:
228
+
229
+ - `orders.list()` and `deals.list()` return a `Page` (`.data`, `.has_more`, `.next_cursor`); `iter()` walks every page for you.
230
+ - `positions.modify(..., stop_loss=None)` **removes** the stop loss; leaving the argument out keeps it. The same holds for `orders.modify`.
231
+ - Times accept `datetime` (sent as RFC 3339 UTC) or strings. Numbers accept `str`, `Decimal`, `int` or `float` (rounded to 8 places) and are always sent as strings.
232
+ - `Fxapis(base_url=..., timeout=..., max_retries=..., http_client=httpx.Client(...))` for proxies, custom transports or tests.
233
+
234
+ Everything in the API — including API keys, members and billing — is in the [API reference](https://docs.fxapis.com/api-reference), with samples in 13 languages.
235
+
236
+ ## Good to know
237
+
238
+ - **Accounts on demand** (`warm_on_demand`) come online for an order or a prepare and go offline after 15 idle minutes. Stop losses and take profits live at the broker and keep working while an account is offline.
239
+ - **Positions are a snapshot.** Each has `observedAt`; call `accounts.reconcile()` for a fresh read while the account is online.
240
+ - **Scoped keys.** Keys can be read-only or reduce-only (can close, cannot open) — give each service the least it needs.
241
+ - **Wrong password?** `accounts.replace_credentials(id, password=...)` fixes it on the same account; connecting a *disconnected* login again brings the same account back. `accounts.delete(id)` removes an account and its history for good.
242
+ - **No event webhooks yet.** Poll `status`, orders and deals. (Incoming TradingView alerts are supported — see `client.alert_hooks`.)
243
+
244
+ ## Related fxapis repositories
245
+
246
+ | Repository | What it is |
247
+ |---|---|
248
+ | [`fxapis-examples`](https://github.com/FXapis/fxapis-examples) | Runnable examples using this SDK (Python, Node/TypeScript, curl) |
249
+ | [`fxapis-typescript`](https://github.com/FXapis/fxapis-typescript) | The official TypeScript/Node.js SDK, same conventions |
250
+ | [`fxapis-mcp-examples`](https://github.com/FXapis/fxapis-mcp-examples) | Connect AI agents (Claude, Cursor, VS Code) over MCP |
251
+ | [`fxapis-integrations`](https://github.com/FXapis/fxapis-integrations) | Postman collection, TradingView payloads, automation templates |
252
+
253
+ ## Contributing
254
+
255
+ ```bash
256
+ pip install -e ".[dev]"
257
+ ruff check .
258
+ mypy
259
+ pytest -q
260
+ ```
261
+
262
+ These are exactly what the `ci` workflow runs, on Python 3.10 through 3.14. Pull requests that fix a
263
+ bug, improve a docstring, or add a test are welcome. A behaviour change to a method's signature or
264
+ return shape should match the TypeScript SDK's equivalent method — both are meant to stay in step.
265
+
266
+ ## Support
267
+
268
+ - **Docs:** [docs.fxapis.com](https://docs.fxapis.com)
269
+ - **Status:** [status.fxapis.com](https://status.fxapis.com)
270
+ - **Bugs:** [open an issue](https://github.com/FXapis/fxapis-python/issues)
271
+ - **Everything else:** [support@fxapis.com](mailto:support@fxapis.com)
272
+
273
+ ## License
274
+
275
+ MIT — see [LICENSE](LICENSE).
276
+
277
+ © 2026 El Wizard