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 is issued), the id of a farm
your plan allows (client.farms.list() shows them and what each offers), and a broker account on
a server MFX Hub supports. The documentation walks through each step,
and the SDK reference lists every method.
import os
import anyio
import mfx
API = "https://api.mfxhub.com/v1" # https://api.dev.mfxhub.com/v1 for development
async def main() -> None:
async with mfx.MfxClient(API, os.environ["MFX_KEY_ID"], os.environ["MFX_PRIVATE_KEY"]) as client:
# 1. A terminal for your broker account, on a farm your plan allows, asking only for what
# that farm offers.
farm = await client.farms.get(os.environ["MFX_FARM_ID"])
offers = {str(usage) for usage in farm.usages}
terminal = await client.terminals.create(
farm_id=farm.id,
broker_id=os.environ["MFX_BROKER_ID"],
server_id=os.environ["MFX_SERVER_ID"],
streaming="quote_streaming" in offers,
execution="order_execution" in offers,
instruments=["EURUSD"], # the broker's own symbol names
idempotency_key="my-first-terminal",
)
await client.terminals.submit_credentials(
terminal.id,
login=int(os.environ["BROKER_LOGIN"]),
password=mfx.Secret(os.environ["BROKER_PASSWORD"]),
server=os.environ["BROKER_SERVER"],
idempotency_key="my-first-credential",
)
while not mfx.is_usable(terminal := await client.terminals.get(terminal.id)):
await anyio.sleep(5)
# 2. A quote, always with its age.
quote = await client.catalog.quote(os.environ["MFX_BROKER_ID"], "EURUSD")
print(quote.bid, quote.ask, quote.age_s)
# 3. An order. Execution never raises: every outcome is a result.
placed = await client.execution.place_order(
terminal.id, symbol="EURUSD", side="buy", volume="0.01",
idempotency_key="my-first-order",
)
done = await client.execution.wait_for(placed.command_id)
print(done.state, done.reason, done.exec_price)
anyio.run(main)
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.15
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.15.tar.gz | 195.3 kB | With provenance | Details for mfx_sdk-0.9.15.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mfx_sdk-0.9.15-py3-none-any.whl | Python 3 | none | any | Details for mfx_sdk-0.9.15-py3-none-any.whl |
Total release size: 617.2 kB
Release files / mfx_sdk-0.9.15.tar.gz
| Download URL | mfx_sdk-0.9.15.tar.gz |
|---|---|
| Size | 195.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6875244e30fac88ac61d0ff1393da8ba8871aa82f4b27c8b07e7a2425276a293
|
|
BLAKE2b-256 checksum How to use checksums |
878573fe5fe2ebed7d762e6a4b0286ca87c2e32d303857d1b8490f6b4355ef61
|
| 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 22, 2026.
Transparency logRelease files / mfx_sdk-0.9.15-py3-none-any.whl
| Download URL | mfx_sdk-0.9.15-py3-none-any.whl |
|---|---|
| Size | 421.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
efbc3c4cdaa8be3e75a337cf927fe9fe0c8995ea245b43d9651a5d2375ec4401
|
|
BLAKE2b-256 checksum How to use checksums |
c09cc4e992aa0f12b45cfc48d0640c8400d24eae47342ba4d58d28aed48c92a2
|
| 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 22, 2026.
Transparency logSecurity for mfx-sdk 0.9.15
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.15.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 22, 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.