Skip to main content

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 ExecutionResult with a granular reason, the broker's own comment_raw, and retryable. A result in state UNKNOWN may 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 Decimal or str; a float is refused, because 0.07 * 3 is 0.21000000000000002.
  • Reads raise typed errors, mapped from the API's error.code: NotFoundError, RateLimitError with retry_after_s, UpstreamError (with DependencyUnavailableError and EdgeError beneath it), MaintenanceError with the window's ends_at, and the rest.
  • Credentials cannot be logged by accident. SigningKey and Secret render 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

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 registered 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.20

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mfx-sdk 0.9.20
File Size Uploaded Provenance Actions
mfx_sdk-0.9.20.tar.gz 199.7 kB With provenance Details for mfx_sdk-0.9.20.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for mfx-sdk 0.9.20
File Interpreter ABI Platform Actions
mfx_sdk-0.9.20-py3-none-any.whl Python 3 none any Details for mfx_sdk-0.9.20-py3-none-any.whl

Total release size: 624.7 kB

Release files / mfx_sdk-0.9.20.tar.gz

Download URL mfx_sdk-0.9.20.tar.gz
Size 199.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6a913f7ab703f1c5e3b7ca95638004bdb5e6a790bf9f00ac97afd70ee68e1bdb
BLAKE2b-256 checksum
How to use checksums
c110909ea580abfc126597f595ce1b1a64ed5e6a842283d0423028ce8beb729a
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 Nov 1, 2026.

Transparency log

Release files / mfx_sdk-0.9.20-py3-none-any.whl

Download URL mfx_sdk-0.9.20-py3-none-any.whl
Size 425.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4ceed793eeb2863921f165e3ba4e878ec3f4e4fb97fac9757556e28f309d9de0
BLAKE2b-256 checksum
How to use checksums
fd3c243c3dc233dd5925c3e560b59cff7f50224c9b0b637f28e74e8c26ccd87d
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 Nov 1, 2026.

Transparency log

Security for mfx-sdk 0.9.20

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.

Select from 2 files

Attestation for mfx_sdk-0.9.20.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 Nov 1, 2026.

Transparency log

A 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.

Release history Release notifications | RSS feed

This release

0.9.20 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page