MFX Hub Python SDK
The typed Python client for the MFX Hub API: hosted trading terminals for your own broker accounts, with market data, execution, account state and a live event stream behind one signed REST and WebSocket API.
pip install mfx-sdk
Python 3.12 or later. Async first, with a thin synchronous client for scripts.
Your first quote and your first order
You need an API key (its id, and the private key shown once when it was issued), your broker's name, your server's name exactly as your trading platform's login window shows it, and your login. The program below finds the ids for those names, asks for a terminal, reads a quote, buys the smallest volume the symbol allows, closes the position and retires the terminal. Use a demo account: the order is real on whatever account you log in to.
import asyncio
import getpass
import os
import uuid
import mfx
API = "https://api.dev.mfxhub.com/v1" # https://api.mfxhub.com/v1 for production
SYMBOL = "EURUSD" # the broker's own symbol name
async def main() -> None:
run = uuid.uuid4().hex[:8] # every write takes an idempotency key; one set per run
key = mfx.SigningKey(os.environ["MFX_PRIVATE_KEY"])
async with mfx.MfxClient(API, os.environ["MFX_KEY_ID"], key) as client:
# 1. The ids for the names you know: a farm your plan allows that executes orders,
# your broker, and your server.
farm = [f async for f in client.farms.list() if "order_execution" in map(str, f.usages)][0]
broker = [
b
async for b in client.catalog.all_brokers()
if os.environ["MFX_BROKER"].lower() in (b.code.lower(), b.display_name.lower())
][0]
server = [
s
for s in await client.catalog.servers(broker.id)
if s.server_name == os.environ["MFX_SERVER"]
][0]
# 2. A terminal for your account, logged in with your password, typed at a prompt.
terminal = await client.terminals.create(
farm_id=farm.id,
broker_id=broker.id,
server_id=server.id,
execution=True,
instruments=[SYMBOL],
idempotency_key=f"terminal-{run}",
)
await client.terminals.submit_credentials(
terminal.id,
login=int(os.environ["MFX_LOGIN"]),
server=server.server_name,
password=mfx.Secret(getpass.getpass("Broker password: ")),
idempotency_key=f"credential-{run}",
)
while not mfx.is_usable(terminal := await client.terminals.get(terminal.id)):
await asyncio.sleep(5)
# 3. A quote, always with its age.
quote = await client.catalog.quote(broker.id, SYMBOL, server_id=server.id)
print(quote.bid, quote.ask, quote.age_s)
# 4. An order, the smallest volume allowed. Execution never raises: every outcome is a
# result, and wait_for returns it once it is final.
spec = await client.catalog.symbol(broker.id, SYMBOL, server_id=server.id)
placed = await client.execution.place_order(
terminal.id,
symbol=SYMBOL,
side="buy",
volume=str(spec.volume_min),
idempotency_key=f"buy-{run}",
)
done = await client.execution.wait_for(placed.command_id)
print(done.state, done.exec_price, done.comment_raw)
# 5. Close the position it opened, and retire the terminal.
closed = await client.execution.close_position(
terminal.id, done.tickets.position, idempotency_key=f"close-{run}"
)
print((await client.execution.wait_for(closed.command_id)).state)
await client.terminals.retire(terminal.id, idempotency_key=f"retire-{run}")
asyncio.run(main())
Run it with MFX_KEY_ID, MFX_PRIVATE_KEY, MFX_BROKER (for example Pepperstone),
MFX_SERVER (for example Pepperstone-Demo) and MFX_LOGIN set. The same program, with every
outcome checked, is samples/first_order.py
in the knowledge base, whose guide
explains each step.
Reference
The documentation has guides and the full reference, generated from this package: client and signing · terminals · execution · accounts and catalogue · market data · stream and live account · errors.
What to know
- Every write takes an
idempotency_key, and none defaults it. Retrying with the same key returns the original answer; a fresh key asks for a second terminal or a second order. - Execution methods never raise. A transport fault, a timeout, a rate limit and a broker
rejection all come back as an
ExecutionResultwith a granularreason, the broker's owncomment_raw, andretryable. A result in stateUNKNOWNmay already be a position: never re-send it under a new key; the platform reconciles it. - Prices, volumes and money are decimal strings, in both directions. Pass
Decimalorstr; afloatis refused, because0.07 * 3is0.21000000000000002. - Reads raise typed errors, mapped from the API's
error.code:NotFoundError,RateLimitErrorwithretry_after_s,UpstreamError(withDependencyUnavailableErrorandEdgeErrorbeneath it),MaintenanceErrorwith the window'sends_at, and the rest. - Credentials cannot be logged by accident.
SigningKeyandSecretrender as***in every repr, f-string, log line and traceback, and cannot be pickled. - Lists follow the cursor. There are no totals and no page numbers;
list()methods iterate every page,page()methods return one. - The stream (
client.stream()) carries market data, candles, order book, account state, results and events, with resumable sequence numbers on the channels that need them.
REST only
Every tenant operation in the API has a typed method here except these, which you call over HTTPS
with the same request signing (mfx.signing). A test keeps this table equal to the API, so it
cannot drift.
| Operation | Route |
|---|---|
cancelTerminalOperation |
DELETE /terminals/{id}/operations/{operation_id} |
createRedisPushConnector |
POST /connectors/redis-push |
updateTerminal |
PATCH /terminals/{id} |
openStream is the WebSocket, opened with client.stream().
How this package is built
src/mfx/_generated is generated from the published MFX Hub API description in spec/openapi.yaml
and is never edited by hand; CI regenerates it and fails on any difference. The hand-written modules
around it (mfx.terminals, mfx.execution, mfx.streaming and the rest) are what you import.
uv sync
uv run pytest
uv run python tools/generate.py # after the API description changes
Support and security
- Documentation: docs.mfxhub.com.
- Questions and bugs: open an issue, or write to support@mfxhub.com.
- Contributing: see CONTRIBUTING.md; every release is in CHANGELOG.md.
- Security reports: see SECURITY.md. Please do not open a public issue for one.
Licence and trademarks
Licensed under the Apache License 2.0.
MFX Hub is an independent service and is not affiliated with, endorsed by or sponsored by MetaQuotes Ltd. MetaTrader is a trademark of MetaQuotes Ltd., used here only to say which trading platform MFX Hub's terminals are compatible with.
Metadata
Release files for mfx-sdk 0.9.18
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| mfx_sdk-0.9.18.tar.gz | 197.9 kB | With provenance | Details for mfx_sdk-0.9.18.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mfx_sdk-0.9.18-py3-none-any.whl | Python 3 | none | any | Details for mfx_sdk-0.9.18-py3-none-any.whl |
Total release size: 621.8 kB
Release files / mfx_sdk-0.9.18.tar.gz
| Download URL | mfx_sdk-0.9.18.tar.gz |
|---|---|
| Size | 197.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f088869f3c731670be5290325073778a78d1eccc6e399f9d66736f453c60ca3f
|
|
BLAKE2b-256 checksum How to use checksums |
596bc8bcb23ce5832a6b3be9f5090d95b5522b5faf5f29c2f03718ba40b313f3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / mfx_sdk-0.9.18-py3-none-any.whl
| Download URL | mfx_sdk-0.9.18-py3-none-any.whl |
|---|---|
| Size | 423.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
796410ecdcbeb7941d7165508fc337f6a2551bbae66d0118590803136920bb37
|
|
BLAKE2b-256 checksum How to use checksums |
b270b7eebf003d747e85887abfd0dcc8e7c83653ad812b6628aa3ad6166e6151
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for mfx-sdk 0.9.18
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for mfx_sdk-0.9.18.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.