Skip to main content

tickerall

Official Python client for the TickerAll REST + WebSocket API.

Place trades, stream live market data, and manage broker sessions programmatically — without an MT4/MT5 terminal in the path. No Windows VM, no Wine, no MetaTrader5 terminal to babysit, no thread-safety workarounds.

pip install tickerall

Requires Python 3.9+. Depends only on httpx and websocket-client.

Why

The official MetaTrader5 Python package only runs on Windows, drives a local terminal over a single-threaded IPC channel, and falls over under concurrency. TickerAll hosts the broker connection for you and exposes it as a clean HTTP + WebSocket API, so your bot can run anywhere — Linux, macOS, a container, a Raspberry Pi — and stream ticks instead of polling.

MetaTrader5 (local terminal) tickerall
OS Windows only anywhere Python runs
Live ticks poll symbol_info_tick() per symbol push over WebSocket
Concurrency single-threaded IPC, not thread-safe stateless HTTP, thread-safe
Deploy a terminal per account to babysit pip install

Quickstart

from tickerall import Tickerall

client = Tickerall(api_key="cf_live_...")

# Connect a broker account → get a TickerAll account_id
session = client.sessions.start(
    broker="mt5",
    server="Exness-MT5Trial7",
    account=12345678,
    password="...",
)

# Place a market order
order = client.orders.place(
    session.account_id,
    type="market",
    symbol="BTCUSDm",
    side="BUY",
    volume=0.10,
    stop_loss=58000.0,
    take_profit=72000.0,
)
print(order.ticket, order.status)

client.sessions.end(session.account_id)

The client is a context manager too:

with Tickerall(api_key="cf_live_...") as client:
    ...

Terminal type (MOBILE / WEB / CLIENT)

terminal_type picks which client the connection presents AS — "MOBILE" (the default), "WEB", or "CLIENT" (a desktop terminal). All expose the full surface (account, quotes, positions, history). The type sets the broker-assigned order origin (ENUM_DEAL_REASON): "MOBILE" → DEAL_REASON_MOBILE, "WEB" → DEAL_REASON_WEB, "CLIENT" → DEAL_REASON_CLIENT — useful where a venue distinguishes desktop-placed orders (e.g. some prop firms).

"WEB" requires the broker's web-terminal URL (web_terminal_url) — web terminals are per-broker-domain, so the URL must be supplied. "MOBILE" and "CLIENT" take neither web field:

session = client.sessions.start(
    broker="mt5",
    server="YourBroker-Server",
    account=12345678,
    password="...",
    terminal_type="WEB",
    web_terminal_url="https://mt5.yourbroker.com",  # required for WEB
    # web_endpoint="wss://host/path",               # optional WS override (rare)
)

For a desktop-origin (DEAL_REASON_CLIENT) connection — no web URL needed:

session = client.sessions.start(
    broker="mt5",
    server="YourBroker-Server",
    account=12345678,
    password="...",
    terminal_type="CLIENT",
)

Streaming — push, not poll

The stream runs on its own background thread. Register callbacks and go; it heartbeats, reconnects with backoff, and re-subscribes automatically.

client = Tickerall(api_key="cf_live_...")
session = client.sessions.start(broker="mt5", server="Exness-MT5Trial7",
                                account=12345678, password="...")

stream = client.stream.connect()
stream.on("tick", lambda e: print(e.symbol, e.bid, e.ask, e.timestamp))
stream.on("position", lambda e: print(e.event, e.position.ticket, e.position.profit))
stream.subscribe_ticks(session.account_id, ["BTCUSDm", "ETHUSDm"])
stream.subscribe_positions(session.account_id)

# ... your app runs ...
stream.close()

Keep an in-memory tick cache fresh (zero polling)

A common pattern: let the WebSocket fill a dict so price reads are O(1) with no network call — strictly better than polling a terminal per symbol.

latest: dict[str, "TickEvent"] = {}
stream = client.stream.connect()
stream.on("tick", lambda e: latest.__setitem__(e.symbol, e))
stream.subscribe_ticks(session.account_id, ["BTCUSDm", "ETHUSDm", "XAUUSDm"])

# Anywhere in your app — instant, no IPC, no thread-safety dance:
tick = latest.get("BTCUSDm")

Market data & history

# Historical OHLC candles (coarser timeframes reach further back)
bars = client.candles.get(session.account_id, symbol="BTCUSDm", hours=24, timeframe="M5")
for c in bars:
    print(c.timestamp, c.open, c.high, c.low, c.close)

# Closed-trade history (profit, swap, commission per row) — what the broker provides
trades = client.history.get(session.account_id, symbol="BTCUSDm", limit=100)

# Deposits, withdrawals, credits… kept separate from trades (oldest-first)
ops = client.history.balance_operations(session.account_id, limit=100)
# each: ticket, type ("deposit" | "withdrawal" | ...), deal_type, amount, time, account
# The stream's "account" channel pushes an update on every balance change
# (trade settlement, deposit, withdrawal) — call this on that push to learn what moved it.

# Tradeable symbols and their volume specs
symbols = client.accounts.symbols(session.account_id)
specs = client.accounts.symbol_specs(session.account_id)  # volume min/max/step + base/quote/margin currency (MT5)

# Remove an account from your roster (disconnects it + drops it from your list
# and billing; broker account and open positions are untouched). Reversible —
# reconnect the same login with sessions.start to re-add it.
client.accounts.remove(session.account_id)

Positions

detail = client.accounts.get(session.account_id)
for p in detail.positions:
    print(p.ticket, p.symbol, p.side, p.volume, p.profit)

client.positions.modify(session.account_id, ticket=p.ticket, stop_loss=60000.0)
client.positions.close(session.account_id, ticket=p.ticket)          # full close
client.positions.close(session.account_id, ticket=p.ticket, volume=0.05)  # partial

Bulk operations

Execute one action across many of your accounts in a single request. Bulk requires an eligible plan — a request without it returns 403 BULK_REQUIRES_PRO; see pricing for what's included. A bulk call spans N broker sessions and is not atomic — partial success is the contract: each account's outcome is in results, and a mix of success/failure still returns (nothing is raised).

Bulk trading (place/close/modify/cancel) currently runs on demo accounts; live trading is coming soon. The bulk read works on all your accounts, demo or live.

# Place the same order across several accounts
placed = client.bulk.place([
    {"account_id": "acc_1", "type": "market", "symbol": "EURUSDm", "side": "BUY", "volume": 0.1},
    {"account_id": "acc_2", "type": "market", "symbol": "EURUSDm", "side": "BUY", "volume": 0.2},
])
# placed.results -> [BulkPlaceResult(account_id, status='filled'|'failed', ticket, price, ...)]
# placed.summary -> BulkPlaceSummary(total, filled, failed)

# Close positions — explicit tickets, or by intent (flatten all / by symbol+side)
client.bulk.close_positions(items=[{"account_id": "acc_1", "ticket": 4072808150}])
client.bulk.close_positions(targets=[{"account_id": "exness_acc", "symbol": "EURUSDm"}, {"account_id": "xm_acc", "symbol": "EURUSD"}])  # each account, its own broker-native symbol
client.bulk.close_positions(targets=[{"account_id": "acc_1"}, {"account_id": "acc_2"}])                                                # no symbol on a target → flatten that account

# Modify SL/TP, cancel + modify pending orders
client.bulk.modify_positions([{"account_id": "acc_1", "ticket": 4072808150, "stop_loss": 1.0850}])
client.bulk.cancel_pending(targets=[{"account_id": "acc_1", "symbol": "EURUSDm"}])
client.bulk.modify_pending([{"account_id": "acc_1", "ticket": 4072808151, "price": 1.0805}])

# Read live state for many accounts at once (balance/equity/margin + open positions)
roster = client.bulk.read_accounts()                                            # your whole roster
some = client.bulk.read_accounts(ids=["acc_1", "acc_2"], include=["account", "positions"])
# roster.accounts -> [BulkAccountState(id, status='online'|'offline', account, positions, ...)]
# roster.summary  -> BulkAccountsSummary(total, online, offline)

In close/cancel by intent, each target carries its own broker-native symbol — so a mixed-broker roster (Exness EURUSDm + XM EURUSD) is handled in one call. Omit a target's symbol to flatten every position on that account; a target whose symbol matches nothing simply reports no matching open positions.

Every write method auto-generates an idempotency key (pass idempotency_key= to dedupe retries), like the single-account methods.

Copy Trading

Mirror one master account's trades to many follower accounts — each scaled, symbol-mapped, and risk-clamped to its own size. Create a set, tune each follower, arm it, and the moment the master trades (through TickerAll) the followers follow. All accounts are your own. Copy Trading requires an eligible plan — a request without it returns 403 COPY_REQUIRES_PRO; see pricing.

Copy trading currently mirrors to demo followers; live is coming soon. Managing sets + reading stats works on all accounts.

# Create a set: one master, followers each with their own sizing + risk
s = client.copy.create_set(
    "My desk", "acc_master",
    followers=[
        {"follower_account_id": "acc_1", "sizing_method": "proportional"},                     # scale by equity ratio
        {"follower_account_id": "acc_2", "sizing_method": "multiplier", "sizing_value": 0.5},   # half the master's size
        {"follower_account_id": "acc_3", "sizing_method": "fixed", "sizing_value": 0.01, "symbol_block": ["XAUUSD"]},
    ],
)

client.copy.arm(s.id)   # start mirroring  (pause with client.copy.pause(s.id))

# Manage followers
client.copy.add_follower(s.id, "acc_4", config={"reverse": True, "max_slippage_pips": 3})
client.copy.update_follower(s.id, "follower_id", {"max_lot": 1, "min_master_lot": 0.05})

# Stats + the copy log
stats = client.copy.get_stats(s.id)          # totals, replication rate, per-follower rollups
page = client.copy.get_log(s.id, limit=50)   # every mirrored action; page with before=

Config keys are accepted in snake_case or camelCase. Sizing per follower: proportional (by equity ratio — a small account gets proportionally small trades), multiplier, fixed, or risk_percent. Per-follower controls include lot clamps, exposure caps, symbol allow/block lists, a daily-loss stop, reverse (inverse) copy, a slippage guard, a min-master-lot filter, and per-broker symbol_overrides. A follower's symbol auto-resolves from the master's (suffix-normalized); set symbol_overrides for anything cross-broker that doesn't.

Always-hot sessions & transparent re-arm

For connections that must stay up across restarts, use keep_alive. The credentials live in this process's memory only (never persisted); if the account goes cold (e.g. TickerAll restarted), the next call transparently re-supplies them and retries once.

session = client.sessions.keep_alive(broker="mt5", server="Exness-MT5Trial7",
                                     account=12345678, password="...")
# ... later, after an outage, this just works — the client re-arms under the hood:
client.accounts.get(session.account_id)

# Stop keeping it alive (drops the cached credentials):
client.sessions.stop_keep_alive(session.account_id)

Reliability — idempotency & queue-and-replay

State-changing calls (sessions.start, orders.place, positions.close, positions.modify) carry a stable Idempotency-Key, so a retried call can't double-execute. By default a transient connectivity failure (TickerallServiceUnavailableError, .transient == True) fails fast so you can re-decide with fresh prices:

from tickerall import TickerallServiceUnavailableError

try:
    client.orders.place(account_id, type="market", symbol="BTCUSDm", side="BUY", volume=0.1)
except TickerallServiceUnavailableError:
    ...  # momentary blip — safe to retry

For price-insensitive orders (pending orders, SL/TP edits) you can instead queue-and-replay until connectivity returns:

client.orders.place(
    account_id, type="limit", symbol="BTCUSDm", side="BUY", volume=0.1, price=60000.0,
    queue_if_reconnecting=True, queue_max_s=60.0,
)

Errors

All errors derive from TickerallApiError and carry .status, .code, .request_id, .details, and .transient:

Class When
TickerallAuthError 401 — bad/missing API key
TickerallForbiddenError 403 — plan limit / reserved resource
TickerallValidationError 400 / 422 — malformed request
TickerallNotFoundError 404 — account / position not found
TickerallBrokerError broker rejected or could not satisfy the request
TickerallServiceUnavailableError transient — TickerAll momentarily unreachable (safe to retry)

Using it from an async app

REST methods are synchronous and thread-safe, so call them from an event loop via asyncio.to_thread:

detail = await asyncio.to_thread(client.accounts.get, account_id)

The stream is already non-blocking (its own thread) — callbacks fire as events arrive.

License

MIT © Miguel Santos

Release files for tickerall 0.8.0

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

Source distribution (sdist)

Source distribution for tickerall 0.8.0
File Size Uploaded
tickerall-0.8.0.tar.gz 37.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tickerall 0.8.0
File Interpreter ABI Platform
tickerall-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 84.4 kB

Release files / tickerall-0.8.0.tar.gz

Download URL tickerall-0.8.0.tar.gz
Size 37.9 kB
Tags Source
SHA-256 checksum
How to use checksums
662b51404b944e36a4d13446dc69f0963756ddb639ef3bc1e234846858840751
BLAKE2b-256 checksum
How to use checksums
f23b5b9452789b2d052ecaa8c6af87ce0167803dacbd5f0f71f15a9043a0bd9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.2

Release files / tickerall-0.8.0-py3-none-any.whl

Download URL tickerall-0.8.0-py3-none-any.whl
Size 46.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c05c84af9cf8c967dbf90470b8a75329286f99dc2da438bf202b4cc51d897ba1
BLAKE2b-256 checksum
How to use checksums
8e6a05a181abd2184f6a13a709436e0b0557b56834552e91affbb14544bf97da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.2

Release history Release notifications | RSS feed

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

This release

0.8.0 This release

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.16

2 release files

0.1.12

2 release files

0.1.11

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

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