Unofficial Python client for the PocketOption WebSocket API. Version 2.0 exposes a synchronous high-level class, PocketOption, built on a single WebSocket layer (no separate data-feed module).
- Auto-reconnection —
WebsocketClient.connect()walks the candidate server list with exponential backoff (base 1s, cap 10s) and up tomax_reconnect_attempts(5) before raisingConnectionError. Failed attempts incrementreconnect_attempts; auth failures (41/NotAuthorized) stop the loop immediately. - Backoff on the message loop —
websocket_listeneris wrapped withbackoff(exponential, max 5 tries / 30s) for transientWebSocketException/ConnectionErrorwhile reading frames. - Keepalive — Background
42["ps"]ping every ~20s while connected. - Clean disconnect —
PocketOption.disconnect_websocket()closes the socket on the shared asyncio loop (useful before swapping SSID).
- Demo vs real from SSID — On init,
WebsocketClient._parse_ssid()readsisDemoanduidfrom the wire string and rebuilds auth withjson.dumps(PHP-style session strings stay valid). If parsing fails, it defaults to demo and logs a warning. - Server selection — Demo: EU demo endpoints only, fixed order (
constants.REGION.DEMO_SERVERS). Real: all real endpoints (REAL_SERVERS); if there is more than one URL, the client probes TCP latency in parallel and tries fastest-first (unreachable hosts are still tried last). No manual region configuration is required for real accounts.
- Time synchronization — Initial offset from the HTTP
Dateheader on WebSocket upgrade; refined withupdateStreamticks;TimeSynchronizerexposes synced time and server-native time for history requests. - Historical candles —
loadHistoryPeriodviaGetCandles, optional reconciliation withupdateHistoryNewFastand stream ticks (strip forming bar, replace stale bars, gap fill when tick coverage allows). - Auth handshake — Payload includes
platform: 2,isFastHistory: true,isOptimized: true. The client treats the session as ready onsuccessauth(not on earlier events such asupdateAssets).
- Asset catalog — After
updateAssets, each symbol maps toid,name,category,payout(%),is_available,timeframes, andraw(full server row). Useget_assets(),get_payout(asset). - Balance —
successupdateBalanceupdatesglobal_value.balance/balance_updated; optionalbalance_typemirrors serverisDemo. - Sentiment —
chaformessages fill per-asset values;get_sentiment(asset). - Real-time ticks — Ring buffer per asset (
subscribe→updateStream);get_realtime_ticks. - Orders —
buyviaBuyv3(includes account demo flag from the sameis_demoas SSID);check_win/get_order_resultfor closures.
- Single WebSocket stack —
WebsocketClienthandles Socket.IO frames and binary payloads (451-[type, binary]);PocketOptionAPIholds deques/buffers per asset;PocketOptionis the stable entry point. - Dependencies —
websockets>=12,pandas,tzlocal,backoff(seerequirements.txt). The client usesextra_headerson websockets v12 andadditional_headerson v13+.
The old datafeed module was removed; all streaming logic lives in ws/client.py.
| Layer | Role |
|---|---|
pocketoptionapi.ws.client.WebsocketClient |
Connect, SSID parse, server selection, auth, reconnection, message routing, tick/history handlers. |
pocketoptionapi.api.PocketOptionAPI |
Per-asset stream buffers, updateHistoryNewFast cache, asset catalog, history/order events. |
pocketoptionapi.stable_api.PocketOption |
Public methods: connect, subscribe, get_historical_candles, buy, check_win, etc. |
- Python 3.9+ recommended.
- Install runtime deps:
pip install -r requirements.txtEditable install from this repo:
pip install -e .Note:
examples/ml_trading_bot.pyusesscikit-learnandnumpy, which are not listed inrequirements.txt. Install them separately if you run that example.
The public API is synchronous: connect() runs the WebSocket on a background thread. Use check_connect() and is_time_synced() before requesting candles.
import logging
import time
from pocketoptionapi import PocketOption
logging.basicConfig(level=logging.INFO)
SSID = r'''42["auth",{...}]''' # Your captured Socket.IO auth string
api = PocketOption(SSID)
ok, err = api.connect()
if not ok:
raise RuntimeError(err)
# Wait until socket is up and time sync is ready (required for history)
while not (api.check_connect() and api.is_time_synced()):
time.sleep(0.1)
api.subscribe("EURUSD_otc", period=60)
time.sleep(1)
candles = api.get_historical_candles(
"EURUSD_otc",
period=60,
offset=45000, # Large offset avoids server timeouts on loadHistoryPeriod
count_request=1,
)
if candles:
print(len(candles), "bars")See test.py for a minimal end-to-end script (balance, assets, payout, sentiment, ticks). Set PO_SSID to your captured auth string before running it.
Pass the full wire string you would send after 42, e.g.:
42["auth",{"session":"...","isDemo":1,"uid":123456,"platform":2,...}]
The client extracts session, isDemo, and uid, then builds a fresh auth message with the fields the server expects. Real-mode sessions may be long PHP-serialized strings; do not truncate or manually escape quotes—json.dumps handles them.
- Call
connect()→ returns(True, None)or(False, reason). - Poll until
check_connect()andis_time_synced()are true.get_historical_candlesreturnsNoneif time is not synced. - For candle-aligned logic, use
get_server_datetime()/get_server_timestamp()(synced UTC-based estimates).
| Mechanism | Where | Behavior |
|---|---|---|
| Connect loop | WebsocketClient.connect |
Tries each URL in the current list; on failure, backoff sleep and retry until connected, auth error, or 5 failed rounds across the outer loop. |
| Read loop | websocket_listener |
@backoff.on_exception retries transient read errors before surfacing. |
| Auth failure | 41 / NotAuthorized |
Sets global_value.check_websocket_if_error; no further server attempts for that session. |
To close the socket from your code (e.g. before a new SSID), call disconnect_websocket().
| Method | Description |
|---|---|
connect() |
Start WebSocket thread; wait up to ~30s for connection. Returns (success: bool, error: str | None). |
disconnect_websocket(timeout=12.0) |
Schedule websocket.close() on the client event loop; returns whether a running loop was found. |
check_connect() |
Whether global_value.websocket_is_connected is true. |
is_time_synced() |
Whether TimeSynchronizer has references and offset (needed for history). |
| Method | Description |
|---|---|
get_server_timestamp() |
Unix seconds (synced). |
get_server_datetime() |
Timezone-aware datetime (local zone via tzlocal). |
| Method | Description |
|---|---|
get_balance() |
Last balance if balance_updated, else None. |
get_assets() |
Dict keyed by symbol → id, name, category, payout, is_available, timeframes, raw. |
get_payout(asset) |
Payout % or None. |
get_sentiment(asset) |
Sentiment from chafor cache or None. |
get_open_deals() / get_closed_deals() / get_pending_deals() |
Shallow copies of server-fed lists. |
After successful auth, the server may send an account id; the client stores it in global_value.account_id when present in the successauth payload.
| Method | Description |
|---|---|
subscribe(asset, period=60) |
changeSymbol + subfor for updateStream / updateHistoryNewFast. |
get_realtime_ticks(asset=None, limit=100) |
Recent (timestamp, price) from the in-memory ring buffer. |
| Method | Description |
|---|---|
get_historical_candles(active, period, start_time=None, offset=None, count_request=1) |
Loads via loadHistoryPeriod; merges/reconciles with fast history and stream ticks. |
offset: Sent to the server as the offset field (candle window / count). None sends null. In practice, use a large positive value (e.g. 45000 or 9000) so requests do not time out.
count_request: Multiple paginated rounds; after each response, the end time steps backward from the oldest candle.
After fetch, _reconcile_with_realtime may:
- Remove the forming candle.
- Replace recent server bars with tick-built OHLC when bars look non-finalized and tick coverage is above ~70%.
- Fill gaps using ticks when possible.
| Method | Description |
|---|---|
process_candles_data(data, period) |
Build a pandas DataFrame with local wall-clock time string and numeric timestamp. |
process_candle(candle_data, period) |
Sort, dedupe, forward-fill; returns (df, is_continuous). |
| Method | Description |
|---|---|
buy(amount, active, direction, expiration) |
Sends openOrder (via Buyv3). direction is sent as action (e.g. call / put). expiration is duration in seconds. Returns (order_dict, True) on matching requestId, else ({}, False). |
check_win(id_number, callback=None) |
Queues resolution against successcloseOrder / _order_results; optional callback with deal dict. |
get_order_result(id_number) |
Snapshot of cached result if deals present. |
There is no high-level close() on PocketOption; use disconnect_websocket() or end the process. Lower-level PocketOptionAPI.websocket exposes async helpers if you extend the client.
Defined in pocketoptionapi.ws.client:
WebSocketErrorAuthenticationErrorConnectionErrorMessageError
Connection failures are also surfaced via connect()’s second return value and global_value.websocket_error_reason.
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)Supported bar sizes (seconds) include:
1, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 14400, 28800, 43200, 86400, 604800, 2592000
(see PocketOption.CANDLE_SIZES.)
Forex, crypto, indices, commodities, and stock symbols depend on PocketOption’s catalog. Use get_assets() for live payout, is_available, and timeframes. Static name/id hints remain in pocketoptionapi.constants.ACTIVES.
| File | Description |
|---|---|
test.py |
Connection, balance, assets, subscribe, sentiment, ticks (requires PO_SSID). |
examples/ml_trading_bot.py |
Fetch candles, train a small classifier, loop (educational only). |
examples/real-time-data+chart.py |
Real-time chart demo. |
examples/buy+check.py |
Order placement pattern. |
- Do not commit or share live SSIDs or session strings.
- Prefer environment variables or secret stores for credentials in real apps.
- This library disables TLS verification on the bundled
requestssession inPocketOptionAPI(session.verify = False) for compatibility with some environments—harden this for production if you rely on HTTP helpers.
This is an unofficial API. It is not affiliated with PocketOption. Trading involves risk; the authors are not responsible for financial losses. Examples are for education and testing only.
Contributions are welcome. Please keep code style consistent, add tests when possible, and update this README when public behavior changes.
MIT License.