Metadata-Version: 2.4
Name: tickerall
Version: 0.1.5
Summary: Official Python client for the TickerAll REST + WebSocket API — place trades, stream live market data, and manage broker sessions without an MT4/MT5 terminal in the path.
Project-URL: Homepage, https://tickerall.com
Project-URL: Documentation, https://tickerall.com/docs
Project-URL: Repository, https://github.com/TickerAll/tickerall
Author: Miguel Santos
License: MIT
License-File: LICENSE
Keywords: algo,api,broker,forex,mt4,mt5,sdk,tickerall,trading,websocket
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25
Requires-Dist: websocket-client>=1.6
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# tickerall

Official Python client for the [TickerAll](https://tickerall.com) 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.

```bash
pip install tickerall
```

Requires Python 3.9+. Depends only on [`httpx`](https://www.python-httpx.org/) and [`websocket-client`](https://github.com/websocket-client/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

```python
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:

```python
with Tickerall(api_key="cf_live_...") as 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.

```python
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.

```python
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

```python
# 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 (recent broker window)
trades = client.history.get(session.account_id, symbol="BTCUSDm", limit=100)

# Tradeable symbols and their volume specs
symbols = client.accounts.symbols(session.account_id)
specs = client.accounts.symbol_specs(session.account_id)  # min / max / step per symbol
```

## Positions

```python
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
```

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

```python
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:

```python
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:

```python
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`:

```python
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
