@tickerall/sdk
v0.1.9
Published
Official TypeScript client for the TickerAll REST + WebSocket API.
Maintainers
Readme
@tickerall/sdk
Official TypeScript 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.
Install
npm install @tickerall/sdkRequires Node.js 18 or later (native fetch). Bun and Deno are also supported.
Quickstart
import { Tickerall } from '@tickerall/sdk'
const ticker = new Tickerall({ apiKey: process.env.TICKERALL_API_KEY! })
const { accountId } = await ticker.sessions.start({
broker: 'mt5',
server: 'Exness-MT5Trial7',
account: 12345678,
password: process.env.MT5_PASSWORD!,
})
const order = await ticker.orders.place(accountId, {
type: 'market',
symbol: 'EURUSDm',
side: 'BUY',
volume: 0.1,
stopLoss: 1.0800,
takeProfit: 1.1000,
})
await ticker.sessions.end(accountId)Configuration
new Tickerall({
apiKey: 'cf_live_...', // required
baseUrl: 'https://api.tickerall.com', // optional; override for staging
streamUrl: 'wss://api.tickerall.com/v1/stream',
timeout: 30_000, // ms; per-request default
fetch: globalThis.fetch, // injection point for tests
userAgent: 'my-algo/1.0', // tacked onto outgoing UA
})Multiple Tickerall instances are independent — useful for multi-tenant code.
Sessions
const session = await ticker.sessions.start({
broker: 'mt5',
server: 'Exness-MT5Trial7',
account: 12345678,
password: '...',
})
// { accountId, isDemo, status: 'connected', expiresAt }
await ticker.sessions.end(session.accountId)Credentials are sent on each sessions.start. TickerAll never persists broker passwords; a session lives only as long as the underlying broker connection.
Terminal type (MOBILE / WEB)
terminalType picks which client the connection presents AS — 'MOBILE' (the
default) or 'WEB'. Both expose the full surface (account, quotes, positions,
history). 'WEB' requires the broker's web-terminal URL (webTerminalUrl) —
web terminals are per-broker-domain, so the URL must be supplied. TypeScript
enforces it (passing terminalType: 'WEB' without webTerminalUrl is a compile
error):
const session = await ticker.sessions.start({
broker: 'mt5',
server: 'YourBroker-Server',
account: 12345678,
password: '...',
terminalType: 'WEB',
webTerminalUrl: 'https://mt5.yourbroker.com', // required for WEB
// webEndpoint: 'wss://host/path', // optional WS override (rare)
})Accounts
const accounts = await ticker.accounts.list()
const account = await ticker.accounts.get(accountId)
const symbols = await ticker.accounts.symbols(accountId)
// 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.
await ticker.accounts.remove(accountId)If account.status === 'offline', call sessions.start again with credentials to reconnect.
Candles (historical OHLC)
const bars = await ticker.candles.get({ symbol: 'BTCUSDm', hours: 8760, timeframe: 'D1' })
// [{ timestamp, open, high, low, close, bid }, ...]
// timestamp is the bar OPEN time (Unix seconds, UTC); bid mirrors close.Public endpoint — available on every plan (the API key is sent but isn't required). Timeframes: M1 M5 M15 M30 H1 H4 D1 W1 MN1 (defaults to M5). Coarser timeframes reach further back; one request returns as much history as fits in a few seconds, so pass a large hours and take what comes back.
Orders + positions
const order = await ticker.orders.place(accountId, {
type: 'market',
symbol: 'EURUSDm',
side: 'BUY',
volume: 0.1,
})
// { ticket, symbol, side, type, volume, status, timestamp, ... }
const limit = await ticker.orders.place(accountId, {
type: 'limit',
symbol: 'EURUSDm',
side: 'BUY',
volume: 0.1,
price: 1.0800,
})
await ticker.positions.modify(accountId, order.ticket, { stopLoss: 1.0850 })
await ticker.positions.close(accountId, order.ticket)
await ticker.positions.close(accountId, order.ticket, { volume: 0.05 }) // partial closeIdempotency
State-changing endpoints (sessions.start, orders.place, positions.close, positions.modify) auto-generate an Idempotency-Key (UUID v4) per call. Pass your own to deduplicate retries on your side:
await ticker.orders.place(
accountId,
{ type: 'market', symbol: 'EURUSDm', side: 'BUY', volume: 0.1 },
{ idempotencyKey: 'my-strategy-tick-12345' },
)Resilience
TickerAll keeps a fast, always-on connection to your broker, and the SDK is built so a momentary backend blip — a deploy, a restart, a network hiccup — doesn't break an idle app. The stream reconnects silently and re-sends your subscriptions; nothing is thrown unless you actively try to do something during the window.
Trades fail fast by default. If you place an order while TickerAll is momentarily unreachable, you get a TickerallServiceUnavailableError (err.transient === true) right away — so you can re-decide with fresh prices rather than fire a stale order late:
try {
await ticker.orders.place(accountId, params)
} catch (err) {
if (err instanceof TickerallServiceUnavailableError) {
// momentarily unreachable — retry, or skip this tick
}
}Queue-and-replay (opt-in). For calls that aren't price-sensitive (a pending order, an SL/TP edit), pass queueIfReconnecting to have the SDK hold the call and replay it — with its stable idempotency key, so it can't double-execute — until connectivity returns or queueMaxMs (default 60s) elapses:
await ticker.positions.modify(
accountId, ticket, { stopLoss: 1.0850 },
{ queueIfReconnecting: true, queueMaxMs: 30_000 },
)Queued calls replay in submission order. Avoid this for market orders — a delayed fill at a moved price is usually worse than a fast failure.
Keep a session alive (auto re-arm)
For an account you want to stay connected with no manual reconnect, start it with keepAlive instead of start. The SDK caches the broker credentials in your process's memory (never persisted) and transparently re-supplies them — re-arming the session — if the account ever goes cold (e.g. TickerAll restarted and dropped its server-side credentials):
const { accountId } = await ticker.sessions.keepAlive({
broker: 'mt5', server: 'Exness-...', account: 123, password: process.env.MT5_PASSWORD!,
})
// A later call that hits a cold account auto-re-arms and retries — once:
await ticker.orders.place(accountId, { type: 'market', symbol: 'ETHUSDm', side: 'BUY', volume: 0.1 })
ticker.sessions.stopKeepAlive(accountId) // forget the cached credentials- The live stream re-arms kept sessions automatically when it reconnects, so subscriptions resume against a hot account.
- Observe re-arms with the
onRearmhook:new Tickerall({ apiKey, onRearm: id => log('re-armed', id) }). - Proactively recover after a known outage:
await ticker.sessions.rearmPending()re-arms every kept account that TickerAll reports cold (or inspectawait ticker.sessions.pendingRearm()yourself). - Credentials live in RAM only and are dropped on
stopKeepAliveorsessions.end. TickerAll never persists broker passwords — which is also why, after a TickerAll restart, a kept session is recovered by your SDK re-supplying them (automatically), not by us having stored them.
Live stream
const stream = await ticker.stream.connect()
await stream.subscribeTicks(accountId, ['EURUSDm', 'XAUUSDm'])
await stream.subscribePositions(accountId)
await stream.subscribeAccount(accountId)
stream.on('tick', e => console.log(e.symbol, e.bid, e.ask))
stream.on('position', e => console.log(e.event, e.position))
stream.on('account', e => console.log(e.snapshot.balance))
stream.on('error', e => console.error(e.message))
stream.on('reconnect', e => console.log('reconnecting, attempt', e.attempt))
stream.on('close', e => console.log('closed:', e.code, e.reason))
await stream.close()Connection state is queryable any time — never pushed at you, so an idle app stays quiet:
stream.getState() // 'connecting' | 'open' | 'reconnecting' | 'closed'
stream.isConnected() // true only when live
await stream.waitUntilConnected(10_000) // resolve when open, reject on timeoutThe stream:
- Reconnects on disconnect with exponential backoff (1s, 2s, 4s, 8s, 16s, max 30s, with jitter) — silently, so an idle app never breaks on a backend blip.
- Re-sends all known subscriptions on reconnect.
- Sends a heartbeat every 25 seconds; force-reconnects on missed pong.
- A missing
errorlistener never crashes your process; subscribe to'reconnect'/'close'only if you want to observe it. - Does not buffer missed ticks (mirrors the server contract — resubscribe and discard stale state if you reconnect).
Errors
All API errors extend TickerallApiError. Check err.status and err.code:
import {
TickerallApiError,
TickerallAuthError,
TickerallForbiddenError,
TickerallValidationError,
TickerallNotFoundError,
TickerallBrokerError,
TickerallServiceUnavailableError,
} from '@tickerall/sdk'
try {
await ticker.orders.place(accountId, params)
} catch (err) {
if (err instanceof TickerallServiceUnavailableError) {
// TickerAll was momentarily unreachable (network blip, deploy, restart).
// The request never reached a verdict — safe to retry. See "Resilience".
} else if (err instanceof TickerallBrokerError) {
// broker rejected the order, broker is offline, etc.
} else if (err instanceof TickerallForbiddenError && err.code === 'FREE_TIER_LIVE_REJECTED') {
// user is on the free tier — upgrade to use live accounts
} else if (err instanceof TickerallValidationError) {
console.error('bad input:', err.details)
} else if (err instanceof TickerallApiError) {
console.error(err.status, err.code, err.message, err.requestId)
}
}Every error carries a boolean err.transient — true only for TickerallServiceUnavailableError (a connectivity blip you can safely retry), false for everything else (auth, validation, broker rejection — these won't change on retry).
| Error class | Status / code | Notes |
| ---------------------------------- | ----------------------------------- | ----------------------------------------------------------- |
| TickerallAuthError | 401 | invalid or missing API key |
| TickerallForbiddenError | 403 | includes FREE_TIER_LIVE_REJECTED |
| TickerallValidationError | 400 / 422 | bad request body or params |
| TickerallNotFoundError | 404 | unknown account, ticket, etc. |
| TickerallBrokerError | broker-coded (e.g. BROKER_*) | broker rejection or broker connection issues |
| TickerallServiceUnavailableError | network / timeout / bare 502·503 | transient — TickerAll momentarily unreachable; retry |
| TickerallApiError | any | base class; check .code and .transient for specifics |
Timeouts and cancellation
await ticker.accounts.list({ timeout: 5_000 })
const controller = new AbortController()
setTimeout(() => controller.abort(), 1_000)
await ticker.accounts.list({ signal: controller.signal })TypeScript
The SDK ships hand-written .d.ts files. All public types are exported from the root:
import type { PlaceOrderParams, Position, TickEvent } from '@tickerall/sdk'License
MIT
