UNPKG

tradingview-api-adapter

Version:

Real-time market data from TradingView via WebSocket: quotes, candles, symbol info, streaming, groups.

503 lines (485 loc) 19.9 kB
import { Q as QuoteField, F as FullQuoteSnapshot, T as TradeTick, B as BarTick, a as QuoteUpdate, b as Timeframe, c as QuoteSnapshot, S as SymbolInfo, C as Candle, d as QuoteErrorInfo, e as SessionManager, R as ReconnectOptions, f as RateLimitOptions, g as QuoteSession } from './errors-B_SFtuRv.js'; export { h as QuoteFieldTypeMap, i as RawTimeframe, j as TIMEFRAME_ALIASES, k as TimeframeAlias, l as TvConnectionError, m as TvError, n as TvErrorOptions, o as TvProtocolError, p as TvSessionError, q as TvSymbolError, r as TvTimeoutError, s as normalizeTimeframe, t as symbolInfoFromRaw } from './errors-B_SFtuRv.js'; /** * Stream — typed event emitter returned by `TvSymbol.stream()`. * * A `Stream` is a thin wrapper around a `TvSymbol`'s ongoing quote * subscription. It exposes strongly-typed events and a closable * lifecycle, plus an `AsyncIterator` interface so you can consume * updates with `for await`. * * All event handlers receive a single object argument (never positional * arguments) so adding new fields in the future is a non-breaking * change. */ interface StreamEventMap { /** Fires on every quote delta. `data` is the full accumulated snapshot. */ update: { symbol: string; data: FullQuoteSnapshot; }; /** Fires whenever the last traded price (`lp`) changes. */ price: { price: number; }; /** Fires whenever `ch`/`chp` change (both present in the accumulated snapshot). */ change: { value: number; percent: number; }; /** Fires when a new bar arrives (`trade`, `minute-bar`, or `daily-bar`). */ bar: { bar: TradeTick | BarTick; }; /** Fires on per-symbol errors reported by TradingView. */ error: Error; } type StreamEventName = keyof StreamEventMap; type Handler$2<E extends StreamEventName> = (data: StreamEventMap[E]) => void; declare class Stream { readonly tvSymbol: TvSymbol; /** Fields this stream was opened with. Currently informational only. */ readonly fields: readonly QuoteField[]; private readonly listeners; private closed; private iteratorQueue; private iteratorResolve; constructor(tvSymbol: TvSymbol, /** Fields this stream was opened with. Currently informational only. */ fields: readonly QuoteField[]); /** Register a listener for a specific stream event. */ on<E extends StreamEventName>(event: E, handler: Handler$2<E>): this; /** Remove a previously registered listener. */ off<E extends StreamEventName>(event: E, handler: Handler$2<E>): this; /** Close the stream and release its resources. */ close(): void; [Symbol.dispose](): void; /** * Async iterator interface — iterate over `update` events with * `for await`. Breaking out of the loop automatically closes the * stream. */ [Symbol.asyncIterator](): AsyncIterator<StreamEventMap['update']>; /** @internal */ _dispatch(u: QuoteUpdate, snapshot: FullQuoteSnapshot): void; /** @internal */ _dispatchError(err: Error): void; /** @internal — used by TvSymbol when the parent client shuts down. */ _closeFromSymbol(): void; private emit; private pushToIterator; private flushIteratorOnClose; } /** * TvSymbol — the user-facing handle for a single market pair. * * Users never construct this directly. They call `client.symbol(pair)` * which lazily creates (or retrieves) a pooled instance. * * Responsibilities: * - Aggregate every quote field any caller has asked about for this pair * - Maintain a local accumulated snapshot of the latest values * - Dispatch updates to all active Streams on the pair * - Provide one-shot promise helpers: `price`, `snapshot`, `info`, `candles` * * `info()` and `candles()` use a fresh short-lived `ChartSession` per * call; `price()`, `snapshot()`, and `stream()` share the parent * Client's pooled quote session. */ /** Default fields used for `price()`, `snapshot()` without args, and `stream()`. */ declare const DEFAULT_STREAM_FIELDS: readonly QuoteField[]; interface CandlesOptions { timeframe: Timeframe; /** Number of bars to fetch. */ count: number; } declare class TvSymbol { readonly client: Client; readonly pair: string; private readonly streams; private readonly snapshotState; private readonly fields; private loaded; private loadWaiters; private subscribed; private disposed; constructor(client: Client, pair: string); /** The last traded price, resolving as soon as it is available. */ price(): Promise<number>; /** * Resolve a typed snapshot for a specific subset of fields. Passing * no argument returns every field currently accumulated for this * symbol (type `FullQuoteSnapshot`). */ snapshot<const F extends readonly QuoteField[]>(fields: F): Promise<QuoteSnapshot<F>>; snapshot(): Promise<FullQuoteSnapshot>; /** Fetch full symbol metadata via a one-shot chart session resolve. */ info(): Promise<SymbolInfo>; /** Fetch historical candles via a one-shot chart session. */ candles(opts: CandlesOptions): Promise<Candle[]>; /** Open a live quote stream for this symbol. */ stream(fields?: readonly QuoteField[]): Stream; /** Current list of fields this symbol is subscribed to. */ get subscribedFields(): readonly QuoteField[]; /** @internal */ _onUpdate(update: QuoteUpdate): void; /** @internal */ _onComplete(): void; /** @internal */ _onError(info: QuoteErrorInfo): void; /** @internal */ _removeStream(stream: Stream): void; /** @internal */ _dispose(): void; private assertAlive; private ensureSubscribed; private waitForLoad; } /** * MultiStream — a multi-symbol stream used by `Portfolio`, `Group`, * and `Client.stream()`. * * Internally, a `MultiStream` composes one single-symbol `Stream` per * participating `TvSymbol`. Updates from each child stream are * forwarded to the multi-stream's listeners with the `symbol` field * included on every event, so consumers can distinguish which pair * ticked. * * Dedup: because `Client.symbol(pair)` returns a pooled instance, two * groups that share a pair share the same child `TvSymbol`. When the * client-level aggregate stream iterates `symbolCache.values()`, each * pair appears exactly once, so listeners receive one event per tick * regardless of how many groups reference the pair. */ interface MultiStreamEventMap { /** Fires on every quote delta, with the symbol and accumulated snapshot. */ update: { symbol: string; data: FullQuoteSnapshot; }; /** Fires when `lp` changes, with the owning symbol. */ price: { symbol: string; price: number; }; /** Fires when both `ch` and `chp` are present in the accumulated snapshot. */ change: { symbol: string; value: number; percent: number; }; /** Fires when a new bar tick arrives. */ bar: { symbol: string; bar: TradeTick | BarTick; }; /** Fires for per-symbol errors from any child stream. */ error: Error; } type MultiEventName = keyof MultiStreamEventMap; type Handler$1<E extends MultiEventName> = (data: MultiStreamEventMap[E]) => void; interface MultiStreamOptions { /** Called when this MultiStream is closed (used by parent Group to untrack). */ onClose?: () => void; } declare class MultiStream { readonly fields: readonly QuoteField[]; private readonly opts; private readonly children; private readonly listeners; private closed; private iteratorQueue; private iteratorResolve; constructor(symbols: Iterable<TvSymbol>, fields: readonly QuoteField[], opts?: MultiStreamOptions); /** Register a listener for a specific stream event. */ on<E extends MultiEventName>(event: E, handler: Handler$1<E>): this; /** Remove a previously registered listener. */ off<E extends MultiEventName>(event: E, handler: Handler$1<E>): this; /** Close the stream, all of its child streams, and release resources. */ close(): void; [Symbol.dispose](): void; /** * Async iterator interface — yields `update` events across all * attached symbols. Breaking out of the `for await` loop closes the * stream automatically. */ [Symbol.asyncIterator](): AsyncIterator<MultiStreamEventMap['update']>; /** Currently attached pairs. */ get pairs(): readonly string[]; /** Number of currently attached child streams. */ get size(): number; /** Whether this multi-stream has been closed. */ get isClosed(): boolean; /** @internal */ _attachSymbol(sym: TvSymbol): void; /** @internal */ _detachSymbol(pair: string): void; private attachSymbol; private emit; private pushToIterator; private flushIteratorOnClose; } /** * Portfolio — a lightweight, ad-hoc collection of symbols. * * Created via `client.symbols([...])`. Unlike `Group`, a portfolio is * immutable: the pairs you pass at construction are the only pairs it * tracks. Use it for quick, one-off queries ("what are the prices of * these 5 tickers right now?") or short-lived streaming sessions. * * For long-lived, mutable collections with a name, use `Group`. */ declare class Portfolio { readonly client: Client; readonly tvSymbols: readonly TvSymbol[]; constructor(client: Client, pairs: readonly string[]); /** Pairs currently tracked (order preserved from construction). */ get pairs(): readonly string[]; /** Number of symbols in the portfolio. */ get size(): number; /** Last prices keyed by pair. Missing prices are omitted. */ prices(): Promise<Record<string, number>>; /** Typed snapshots keyed by pair. */ snapshot<const F extends readonly QuoteField[]>(fields: F): Promise<Record<string, QuoteSnapshot<F>>>; snapshot(): Promise<Record<string, FullQuoteSnapshot>>; /** Open a multi-symbol live stream over every pair in the portfolio. */ stream(fields?: readonly QuoteField[]): MultiStream; } /** * Group — a named, mutable collection of symbols. * * Unlike `Portfolio`, a `Group` is long-lived: it has a name, is * tracked by the parent `Client` via `GroupRegistry`, and supports * mutation (`add`, `remove`, `clear`). Active streams stay in sync * with the group: adding a pair attaches a child stream, removing a * pair detaches it. * * Lifecycle: * const crypto = client.createGroup('crypto', ['BINANCE:BTCUSDT']) * crypto.add('BINANCE:ETHUSDT') * const stream = crypto.stream() * crypto.add('BINANCE:DOGEUSDT') // ← joins the active stream * crypto.remove('BINANCE:BTCUSDT') // ← detaches from the active stream * await crypto.delete() // ← close all streams, remove from registry */ declare class Group { readonly client: Client; readonly name: string; private readonly members; private readonly activeStreams; private disposed; constructor(client: Client, name: string, pairs: readonly string[]); /** Symbols currently in the group. */ get pairs(): readonly string[]; /** Live `TvSymbol` handles for every pair. */ get tvSymbols(): readonly TvSymbol[]; /** Number of pairs in the group. */ get size(): number; /** Whether a specific pair is in the group. */ has(pair: string): boolean; /** Add a pair. Active streams automatically pick it up. Returns `this` for chaining. */ add(pair: string): this; /** Add many pairs at once. */ addAll(pairs: Iterable<string>): this; /** Remove a pair. Returns `true` if it was present. */ remove(pair: string): boolean; /** Remove many pairs at once. Returns the count of removed pairs. */ removeAll(pairs: Iterable<string>): number; /** Remove every pair. */ clear(): this; /** Last prices keyed by pair. Missing prices are omitted. */ prices(): Promise<Record<string, number>>; /** Typed snapshots keyed by pair. */ snapshot<const F extends readonly QuoteField[]>(fields: F): Promise<Record<string, QuoteSnapshot<F>>>; snapshot(): Promise<Record<string, FullQuoteSnapshot>>; /** Open a multi-symbol live stream that stays in sync with the group. */ stream(fields?: readonly QuoteField[]): MultiStream; /** * Delete the group: close every active stream and remove it from the * parent client's group registry. */ delete(): Promise<void>; private assertAlive; } /** * GroupRegistry — the `Map`-like container for a `Client`'s groups. * * Accessible via `client.groups`. Supports lookup, iteration, * deletion, and presence checks. Creation goes through * `client.createGroup(name, pairs)` (delegated here) to ensure names * are unique per client. */ declare class GroupRegistry { private readonly client; private readonly groups; constructor(client: Client); /** Create a new group. Throws if a group with the same name already exists. */ create(name: string, pairs?: readonly string[]): Group; /** Retrieve a group by name. */ get(name: string): Group | undefined; /** Check whether a group exists. */ has(name: string): boolean; /** Delete a group by name. Returns `true` if it existed. */ delete(name: string): Promise<boolean>; /** List of group names. */ get list(): readonly string[]; /** Number of groups. */ get size(): number; /** Iterate over all groups. */ [Symbol.iterator](): IterableIterator<Group>; /** @internal — called from `Group.delete()` during self-unregistration. */ _unregister(name: string): void; /** @internal — called from `Client.disconnect()` to clean up all groups. */ _disposeAll(): Promise<void>; } /** * Client — the public entry point for the library. * * A `Client` owns a `SessionManager` (and therefore a `Transport`) and * acts as a registry/pool for `TvSymbol` instances. It also lazily * creates a single shared `QuoteSession` that carries every symbol's * live subscription, so one open TradingView session services an * entire portfolio. * * Create one via the `tv(options)` factory — a simple function call is * more idiomatic than `new Client()` and keeps the public surface tiny. */ /** * Authentication options. * * TradingView recognises two cooperating pieces of auth state: * * 1. The `sessionid` + `sessionid_sign` cookies from an active * tradingview.com login. These are passed as a `Cookie` header on * the WebSocket handshake (Node only — browsers don't allow * custom handshake headers). * 2. A per-session `authToken` sent via the `set_auth_token` message * after the server hello. This is a JWT-like token you normally * obtain by POSTing `sessionid` to `https://www.tradingview.com/…` * ahead of time. * * This library does **not** fetch `authToken` for you — if you need * premium features, obtain the token through your own auth flow and * pass it here. Without an `authToken`, public quotes and candles * still work through the implicit `"unauthorized_user_token"` default. */ interface AuthOptions { /** TradingView auth token. Omit for public/anonymous access. */ authToken?: string; /** `sessionid` cookie from tradingview.com (Node only). */ sessionid?: string; /** `sessionid_sign` cookie from tradingview.com (Node only). */ sessionidSign?: string; } interface ClientOptions { /** WebSocket URL. Defaults to TradingView widget endpoint. */ url?: string; /** Origin header (Node only). Defaults to the TradingView origin. */ origin?: string; /** HTTP/SOCKS agent for proxy support (Node only). */ agent?: unknown; /** Authentication credentials (optional). */ auth?: AuthOptions; /** Locale to advertise to TradingView (`[language, country]`). Defaults to `['en', 'US']`. */ locale?: [language: string, country: string]; /** Reconnect behaviour for the underlying transport. */ reconnect?: ReconnectOptions; /** Symbol add/remove rate limiting. */ rateLimit?: RateLimitOptions; /** Abort signal — disconnects the client when fired. */ signal?: AbortSignal; } interface ClientEventMap { open: void; close: void; reconnect: { attempt: number; delayMs: number; }; error: Error; } type ClientEventName = keyof ClientEventMap; type Handler<E extends ClientEventName> = (data: ClientEventMap[E]) => void; /** * Factory: `tv(options)` → `Client`. * * Idiomatic entry point. Equivalent to `new Client(options)`. */ declare function tv(options?: ClientOptions): Client; declare class Client { readonly manager: SessionManager; readonly groups: GroupRegistry; private quotePool; private readonly symbolCache; private readonly aggregatedFields; private readonly listeners; private disposed; constructor(opts?: ClientOptions); /** * Get the `TvSymbol` handle for a market pair. Repeated calls for * the same pair return the same instance, so subscriptions pool * naturally across callers. */ symbol(pair: string): TvSymbol; /** * Build an ad-hoc `Portfolio` over the given pairs. Unlike * `createGroup`, a portfolio is not tracked by the client — it only * lives as long as the caller holds the reference. */ symbols(pairs: readonly string[]): Portfolio; /** * Create a named `Group` and register it with `client.groups`. Use * groups for long-lived, mutable collections — e.g. a watchlist the * user can edit at runtime. */ createGroup(name: string, pairs?: readonly string[]): Group; /** * Aggregate multi-symbol stream across every symbol currently * registered on this client. De-duplicates naturally: a pair that * belongs to multiple groups appears once in the client's symbol * cache, so listeners receive one event per tick regardless of how * many groups reference it. */ stream(fields?: readonly QuoteField[]): MultiStream; /** Open the underlying transport and wait until TradingView is ready. */ connect(): Promise<void>; /** Close everything: groups, pool session, all symbols, transport. */ disconnect(): Promise<void>; /** Register a client-level event listener. */ on<E extends ClientEventName>(event: E, handler: Handler<E>): this; /** Remove a previously registered event listener. */ off<E extends ClientEventName>(event: E, handler: Handler<E>): this; /** Async disposer support (`using client = tv()`). */ [Symbol.asyncDispose](): Promise<void>; /** @internal */ _getQuotePool(): QuoteSession; /** @internal */ _requestFields(fields: readonly QuoteField[]): void; /** @internal — exposed primarily for tests and advanced users. */ _getSymbolCache(): ReadonlyMap<string, TvSymbol>; private dispatchUpdate; private dispatchError; private dispatchComplete; private emit; } /** * tradingview-api-adapter * * Real-time market data from TradingView via WebSocket. * * Quick start: * * import { tv } from 'tradingview-api-adapter' * * const client = tv() * const btc = client.symbol('BINANCE:BTCUSDT') * * console.log(await btc.price()) * * const stream = btc.stream() * stream.on('price', ({ price }) => console.log(price)) * * await client.disconnect() */ declare const version = "2.0.0"; export { type AuthOptions, BarTick, Candle, type CandlesOptions, Client, type ClientEventMap, type ClientOptions, DEFAULT_STREAM_FIELDS, FullQuoteSnapshot, Group, GroupRegistry, MultiStream, type MultiStreamEventMap, type MultiStreamOptions, Portfolio, QuoteField, QuoteSnapshot, Stream, type StreamEventMap, SymbolInfo, Timeframe, TradeTick, TvSymbol, tv, version };