TickerAll TypeScript SDK
A typed client for the TickerAll REST + WebSocket API — place trades, stream live market data, and manage broker sessions, with idempotency keys and automatic reconnects built in. Requires Node 18+.
Install & connect
One client object holds your API key and exposes every namespace. The same key can drive many broker accounts at once, each addressed by its accountId.
Add the package. Requires Node 18+ / Python 3.9+; no native dependencies.
npm install @tickerall/sdkCreate a client. Only apiKey is required; baseUrl / streamUrl / timeout / userAgent / onRearm are optional.
import { Tickerall } from '@tickerall/sdk'
const client = new Tickerall({
apiKey: process.env.TICKERALL_API_KEY!,
// baseUrl?: 'https://api.tickerall.com'
// streamUrl?: 'wss://api.tickerall.com/v1/stream'
// timeout?: 30_000 (ms, per request)
})Sessions
Connect a broker account and get its accountId. keepAlive caches the credentials in this process’s memory (never persisted) so the client transparently re-arms an account that goes cold — e.g. after a service restart.
Connect a broker account → returns its accountId, isDemo, status, expiresAt. Pass terminalType to pick the terminal type (MOBILE default, or WEB — WEB requires the broker’s webTerminalUrl). Choosing WEB or CLIENT requires a Pro or Enterprise plan.
const session = await client.sessions.start({
broker: 'mt5',
server: 'Exness-MT5Trial14',
account: 12345678,
password: process.env.MT5_PASSWORD!,
// Optional terminal type — 'MOBILE' (default) or 'WEB'. Omit for MOBILE.
terminalType: 'MOBILE',
})
// session.accountId, session.isDemo
// For WEB, the broker's web-terminal URL is required (TypeScript enforces it):
// const web = await client.sessions.start({
// broker: 'mt5', server: 'YourBroker-Server', account: 12345678,
// password: process.env.MT5_PASSWORD!,
// terminalType: 'WEB', webTerminalUrl: 'https://mt5.yourbroker.com',
// })Start a session AND keep it alive — credentials cached in RAM for transparent re-arm.
const session = await client.sessions.keepAlive({
broker: 'mt5', server: 'Exness-MT5Trial14',
account: 12345678, password: '...',
})
// later calls re-arm automatically if the account went coldStop auto-re-arming an account and drop its cached credentials.
client.sessions.stopKeepAlive(accountId)Manually re-arm a kept account now (normally unnecessary — the client does it on demand).
await client.sessions.rearm(accountId)Always-hot accounts that currently have no live connection (need a credentials refresh).
const pending = await client.sessions.pendingRearm()Re-arm every pending account you hold kept credentials for. Returns the re-armed IDs.
const ids = await client.sessions.rearmPending()Disconnect a session (also stops keeping it alive).
await client.sessions.end(accountId)Accounts
List and inspect the broker accounts linked to your key — balances, open positions, tradeable symbols and their volume specs.
All broker accounts linked to your API key.
const accounts = await client.accounts.list()Full snapshot — balance/leverage info and open positions when online.
const detail = await client.accounts.get(accountId)
for (const p of detail.positions) {
console.log(p.ticket, p.symbol, p.side, p.volume, p.profit)
}The broker-native symbol names this account can trade.
const symbols = await client.accounts.symbols(accountId)Per-symbol volume specs (min / max / step) + trade mode + the base, profit (quote) and margin currency — validate an order size and denote each instrument in its real currency, not the account currency (MT5 only).
const specs = await client.accounts.symbolSpecs(accountId)
// each: { name, volumeMin, volumeMax, volumeStep, specSource, tradeMode, baseCurrency, profitCurrency, marginCurrency }Remove a broker account from your roster — disconnects it and drops it from your list and billing. Does not touch the broker account or its open positions; reconnect with sessions.start to re-add it.
await client.accounts.remove(accountId)Switch the terminal type (MOBILE↔WEB) on a live account. Open positions/orders/balance are preserved (they live broker-side). The switch is zero-gap — the new transport is warmed before the old is dropped — and runs only when the account is idle. Returns status:"noop" if already on `to`.
const res = await client.accounts.migrate(accountId, 'WEB')
// res.terminalType, res.status ('noop' | 'migrated')Orders
Place market and pending (limit / stop) orders, and manage resting pending orders. State-changing calls carry a stable idempotency key so a retry can’t double-execute.
Place a market / limit / stop order. price is required for limit & stop.
const order = await client.orders.place(accountId, {
type: 'market',
symbol: 'BTCUSD',
side: 'BUY',
volume: 0.1,
stopLoss: 58000,
takeProfit: 72000,
})
// order.ticket, order.status ('open' | 'pending')List resting pending orders (LIMIT / STOP). MT5 only — MT4 returns an empty list.
const pending = await client.orders.listPending(accountId)
// each: { ticket, symbol, type, side, orderType, volume, price, ... }Cancel a resting pending order by its ticket.
await client.orders.cancelPending(accountId, 4521969907)Change a pending order’s trigger price / SL / TP. Omitted fields are preserved.
await client.orders.modifyPending(accountId, 4521969907, {
price: 60000, stopLoss: 58000, takeProfit: 72000,
})Positions
Close (fully or partially) and modify the stop-loss / take-profit on open positions.
Close a position fully, or partially when volume is given.
await client.positions.close(accountId, 70001) // full
await client.positions.close(accountId, 70001, { volume: 0.05 }) // partialSet or change the SL / TP on an open position. Provide at least one.
await client.positions.modify(accountId, 70001, {
stopLoss: 60000, takeProfit: 72000,
})Bulk operations
Execute one action across many accounts in a single call — place, close, modify, and cancel over a set of accounts, or read live state for your whole roster at once. Each method returns a per-account results array plus a summary tally, so a partial success is explicit. Bulk operations require a Pro or Enterprise plan.
Place an order on many accounts (1–50) in one request. Each entry mirrors an orders.place body plus its target accountId.
const res = await client.bulk.place([
{ accountId: 'acc_8Kd3...', symbol: 'BTCUSD', side: 'BUY', volume: 0.1 },
{ accountId: 'acc_9Lm4...', type: 'limit', symbol: 'BTCUSD', side: 'BUY', volume: 0.1, price: 60000 },
])
// res.summary -> { total, filled, failed }
// res.results[] -> { accountId, status: 'filled' | 'failed', ticket?, price?, code?, reason? }Close positions across accounts — an explicit list of positions, or an intent that flattens whole accounts (optionally by symbol / side).
// explicit list (omit volume for a full close, pass a smaller volume to scale out)
await client.bulk.closePositions({
items: [
{ accountId: 'acc_8Kd3...', ticket: 4072808150 },
{ accountId: 'acc_9Lm4...', ticket: 4072809002, volume: 0.05 },
],
})
// intent: flatten these accounts (optionally one symbol / side)
await client.bulk.closePositions({ targets: [{ accountId: 'acc_8Kd3...', symbol: 'BTCUSD' }, { accountId: 'acc_9Lm4...', symbol: 'BTCUSD' }] })
// res.results[] -> { accountId, ticket, status: 'ok' | 'failed', closed, ... }Set or change the SL / TP on positions across accounts. Provide at least one of stopLoss / takeProfit per item.
await client.bulk.modifyPositions([
{ accountId: 'acc_8Kd3...', ticket: 4072808150, stopLoss: 72000, takeProfit: 85000 },
{ accountId: 'acc_9Lm4...', ticket: 4072809002, takeProfit: 84000 },
])Cancel resting pending orders across accounts — an explicit list, or an intent that cancels every pending order on whole accounts (optionally one symbol).
// explicit list
await client.bulk.cancelPending({
items: [
{ accountId: 'acc_8Kd3...', ticket: 4072809988 },
{ accountId: 'acc_9Lm4...', ticket: 4072809991 },
],
})
// intent: cancel every pending order on these accounts (optionally one symbol)
await client.bulk.cancelPending({ targets: [{ accountId: 'acc_8Kd3...' }, { accountId: 'acc_9Lm4...' }] })Change the trigger price / SL / TP on pending orders across accounts. Provide at least one of price / stopLoss / takeProfit per item.
await client.bulk.modifyPending([
{ accountId: 'acc_8Kd3...', ticket: 4072809988, price: 60000, stopLoss: 58000 },
{ accountId: 'acc_9Lm4...', ticket: 4072809991, takeProfit: 72000 },
])Read live state (status, account financials, open positions) for many accounts in one call. Omit ids to read your whole roster; use include to trim the payload.
const state = await client.bulk.readAccounts({
ids: ['acc_8Kd3...', 'acc_9Lm4...'], // omit for every account on your key
include: ['account', 'positions'], // default both
})
for (const a of state.accounts) {
console.log(a.id, a.status, a.account?.balance)
}
// state.summary -> { total, online, offline }Copy Trading
Mirror one master account's trades to many followers — each scaled, symbol-mapped, and risk-clamped to its own size. Create a set, tune each follower, arm it, and the master's trades follow. Read a set's stats + log. A Pro or Enterprise feature.
Create a copy set — a master account plus optional followers, each with its own sizing + risk config. Arm it to start mirroring.
const set = await client.copy.createSet({
name: 'My desk',
masterAccountId: 'acc_master',
followers: [
{ followerAccountId: 'acc_1', sizingMethod: 'proportional' }, // by equity ratio
{ followerAccountId: 'acc_2', sizingMethod: 'multiplier', sizingValue: 0.5 },
],
})
await client.copy.arm(set.id) // start mirroring (pause with client.copy.pause)Attach or tune a follower — sizing, lot clamps, symbol allow/block, reverse copy, slippage guard, and per-broker symbol overrides.
await client.copy.addFollower(set.id, { followerAccountId: 'acc_3', reverse: true, maxSlippagePips: 3 })
await client.copy.updateFollower(set.id, 'follower_id', { maxLot: 1, symbolBlock: ['XAUUSD'] })
await client.copy.removeFollower(set.id, 'follower_id')Read a set’s dashboard stats (totals, replication rate, per-follower rollups) and the copy log (every mirrored action, paginated).
const stats = await client.copy.getStats(set.id)
// stats.totals -> { ok, skipped, failed, replicationRate }
const page = await client.copy.getLog(set.id, { limit: 50 })
// page.entries[] -> { action, followerAccountId, result, reason?, ... }; page.nextBeforeCandles
Historical OHLC candles from a connected account, across all nine timeframes. Bound the series by a look-back window (hours) or an exact date range (from + to, ISO-8601). Coarser timeframes reach further back; deep look-backs ride an isolated history connection so they don’t disturb your live tick stream.
Fetch OHLC bars for a symbol — by look-back window (hours) or by exact date range (from + to, ISO-8601). Returns the bars, oldest first.
// look-back window
const bars = await client.candles.get(accountId, {
symbol: 'BTCUSD', hours: 8760, timeframe: 'D1',
})
// exact date range — pass BOTH from and to (ISO-8601)
const ranged = await client.candles.get(accountId, {
symbol: 'BTCUSD', from: '2026-01-01T00:00:00Z', to: '2026-03-01T00:00:00Z', timeframe: 'H1',
})
// each: { timestamp, open, high, low, close, bid, tickVolume?, spread? }
// tickVolume/spread on recent bars; deep-history bars are bid-onlyTrade history
Closed-trade history — executed deals paired into round-trips, from the broker’s recent deal window plus any closes seen live this session.
Closed round-trips, optionally filtered by symbol / from / to / limit.
const trades = await client.history.get(accountId, {
symbol: 'BTCUSD', limit: 100,
})
// each: { ticket, side, volume, openPrice, closePrice, profit, ... }Realtime stream
A single self-healing WebSocket pushes live ticks, position events, and account snapshots. It heartbeats, reconnects with backoff, and re-subscribes automatically — you register callbacks and go.
Open the stream, register callbacks, and subscribe to topics.
const stream = await client.stream.connect()
stream.on('tick', (e) => console.log(e.symbol, e.bid, e.ask, e.timestamp))
stream.on('position', (e) => console.log(e.event, e.position.ticket, e.position.profit))
stream.on('account', (e) => console.log(e.snapshot.balance))
stream.on('reconnect', (e) => console.log('reconnecting', e.attempt))
await stream.subscribeTicks(accountId, ['BTCUSD', 'ETHUSD'])
await stream.subscribePositions(accountId)
await stream.subscribeAccount(accountId)Let the WebSocket fill a dict/map so price reads are instant with no polling.
const latest = new Map<string, TickEvent>()
stream.on('tick', (e) => latest.set(e.symbol, e))
// latest.get('BTCUSD') — instant, no networkQuery connection state, wait for it, unsubscribe, and close.
stream.getState() // 'connecting' | 'open' | 'reconnecting' | 'closed'
stream.isConnected()
await stream.waitUntilConnected(30_000)
await stream.unsubscribeTicks(accountId, ['ETHUSD'])
await stream.close()Advanced hook — runs after a reconnect, just before subscriptions are re-sent. Use it to re-arm kept sessions so the stream transparently survives a backend restart.
stream.setBeforeResubscribe(async () => {
await client.sessions.rearmPending()
})Errors
Every failure is a typed error carrying status / code / requestId / details / transient. Catch the base class, or a narrow subclass. transient marks a momentary connectivity issue that is safe to retry.
TickerallApiError (base) → Auth (401), Forbidden (403), Validation (400/422), NotFound (404), Broker (broker rejection), ServiceUnavailable (transient).
import {
TickerallApiError,
TickerallServiceUnavailableError,
TickerallBrokerError,
} from '@tickerall/sdk'
try {
await client.orders.place(accountId, { type: 'market', symbol: 'BTCUSD', side: 'BUY', volume: 0.1 })
} catch (err) {
if (err instanceof TickerallServiceUnavailableError) {
// transient — TickerAll momentarily unreachable; safe to retry
} else if (err instanceof TickerallBrokerError) {
// broker rejected the order — err.code, err.message
} else if (err instanceof TickerallApiError) {
console.error(err.status, err.code, err.requestId, err.transient)
}
}Reliability
State-changing calls (sessions.start, orders.place, positions.close / modify, pending cancel / modify) carry a stable Idempotency-Key, so a retried call can’t double-execute. By default a transient failure fails fast so you can re-decide with fresh prices.
Auto-generated per call; supply your own to make a retry safe across process restarts.
await client.orders.place(
accountId,
{ type: 'market', symbol: 'BTCUSD', side: 'BUY', volume: 0.1 },
{ idempotencyKey: 'order-2026-06-02-001' },
)For price-insensitive orders (pending, SL/TP edits), opt into queue-and-replay: held in order and retried with the stable key until connectivity returns.
await client.orders.place(
accountId,
{ type: 'limit', symbol: 'BTCUSD', side: 'BUY', volume: 0.1, price: 60000 },
{ queueIfReconnecting: true, queueMaxMs: 60_000 },
)For an always-on connection, keepAlive caches credentials so the client transparently re-arms after a service restart — no manual reconnect.
await client.sessions.keepAlive({ broker: 'mt5', server, account, password })
// later calls just work; the client re-arms under the hood