UNPKG

tradingview-api-adapter

Version:

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

290 lines (276 loc) 12.1 kB
import { u as SymbolBatcherExecutor, f as RateLimitOptions, v as Session, e as SessionManager, w as CandlesUpdate, x as CandleTick, q as TvSymbolError, b as Timeframe, C as Candle } from './errors-B_SFtuRv.cjs'; export { y as BackoffOptions, B as BarTick, z as CloseInfo, D as DEFAULT_BACKOFF, A as DEFAULT_RATE_LIMIT, F as FullQuoteSnapshot, d as QuoteErrorInfo, Q as QuoteField, h as QuoteFieldTypeMap, g as QuoteSession, E as QuoteSessionOptions, c as QuoteSnapshot, a as QuoteUpdate, i as RawTimeframe, R as ReconnectOptions, G as SessionManagerOptions, H as SessionManagerState, S as SymbolInfo, j as TIMEFRAME_ALIASES, k as TimeframeAlias, T as TradeTick, I as Transport, J as TransportOptions, K as TransportState, l as TvConnectionError, m as TvError, n as TvErrorOptions, o as TvProtocolError, p as TvSessionError, r as TvTimeoutError, L as calculateBackoff, s as normalizeTimeframe, M as resolveRateLimit, t as symbolInfoFromRaw } from './errors-B_SFtuRv.cjs'; import debug from 'debug'; /** * Types for the TradingView wire protocol. * * TradingView frames incoming data as `~m~<length>~m~<payload>` where * `<length>` is the decimal character length of `<payload>`. A single * WebSocket message may contain one or more back-to-back frames. * * Payloads come in three flavours: * * 1. Heartbeat: `~h~<number>` — server pings, client echoes back * 2. Hello: a JSON object without an `m` field, sent once after * the connection is established (server/session info) * 3. Message: a JSON object of the form `{ "m": <method>, "p": <params> }` */ type ProtocolMessage = HeartbeatMessage | HelloMessage | CommandMessage; interface HeartbeatMessage { type: 'heartbeat'; /** Sequence number sent by the server; must be echoed back unchanged. */ id: number; } interface HelloMessage { type: 'hello'; /** Arbitrary JSON payload from the initial server handshake. */ data: unknown; } interface CommandMessage { type: 'message'; /** The `m` field of the inbound JSON — e.g. `'qsd'`, `'quote_completed'`. */ method: string; /** The `p` field of the inbound JSON. */ params: unknown[]; } /** * TradingView wire protocol — pure encode/decode functions. * * This module has no side effects and no I/O. It only translates between * raw strings received from the WebSocket and strongly typed messages. * * See `protocol.types.ts` for a description of the frame format. */ /** * Wrap a raw payload in the TradingView frame header. * * The payload length is measured in character-count (UTF-16 code units), * matching TradingView's own behaviour. */ declare function encodeFrame(payload: string | object): string; /** Build a heartbeat echo frame for a given server-provided id. */ declare function encodeHeartbeat(id: number): string; /** Build a command frame like `{ m: "quote_add_symbols", p: [...] }`. */ declare function encodeCommand(method: string, params: unknown[]): string; /** * Decode a raw WebSocket message (which may contain multiple concatenated * frames) into an array of typed `ProtocolMessage`s. * * Throws `TvProtocolError` on malformed input. */ declare function decodeFrames(raw: string): ProtocolMessage[]; /** * SymbolBatcher — batching + chunking + dedup for quote add/remove ops. * * See `rate-limiter.types.ts` for a description of the semantics. */ declare class SymbolBatcher { private readonly executor; private readonly opts; private pendingAdd; private pendingRemove; private flushTimer; private inFlight; private disposed; constructor(executor: SymbolBatcherExecutor, opts?: RateLimitOptions); /** * Queue a symbol for addition. If the same symbol has a pending * removal in the current window, the two cancel out and nothing is * dispatched. */ add(symbol: string): void; /** * Queue a symbol for removal. If the same symbol has a pending * addition in the current window, the two cancel out and nothing is * dispatched. */ remove(symbol: string): void; /** Number of pending operations (both adds and removes). */ get pendingCount(): number; /** * Force an immediate flush of any pending operations, bypassing the * batch window timer. Resolves once all resulting chunks have been * dispatched through the executor. */ flushNow(): Promise<void>; /** * Destroy the batcher. Any pending operations are dropped — the * caller is responsible for calling `flushNow()` first if that's * undesired. */ destroy(): void; private scheduleFlush; private flush; private dispatchChunks; } /** * ChartSession — manages a TradingView `chart_create_session` container. * * This session type is used for historical candle data and live bar * updates. A typical flow: * * 1. `chart_create_session` to open the container * 2. `resolve_symbol` to bind a symbol key (e.g. `sym_1`) to a market pair * 3. `create_series` to request N bars for that symbol at a timeframe * 4. Receive `timescale_update` (initial bars) and subsequent `du` * (data update) messages as bars tick * 5. `chart_delete_session` on teardown * * This class wraps all of that into a simple callback-driven API. A * higher-level layer (Phase 4 `Symbol.candles()`) will consume it via * a promise-returning helper. */ interface SeriesRequest { /** Full TradingView pair, e.g. `'BINANCE:BTCUSDT'`. */ symbol: string; /** Candle timeframe (TradingView native, e.g. `'60'` for 1h, `'1D'` for daily). */ timeframe: Timeframe; /** Number of bars to request on initial load. */ barCount: number; } interface ChartSessionOptions { manager: SessionManager; /** Called when a batch of bars arrives on a series (initial load). */ onCandles?: (update: CandlesUpdate) => void; /** Called when a single bar ticks live after the initial load. */ onTick?: (tick: CandleTick) => void; /** Called when TradingView rejects a symbol or a series. */ onError?: (err: TvSymbolError) => void; } declare class ChartSession implements Session { readonly id: string; private readonly manager; private readonly series; private readonly seriesBySymbolKey; private nextSymbolSeq; private nextSeriesSeq; private created; private disposed; private readonly onCandlesCb?; private readonly onTickCb?; private readonly onErrorCb?; private readonly pendingResolves; private readonly pendingCandles; constructor(opts: ChartSessionOptions); /** * Request a series of bars for a symbol. The `onCandles` callback is * invoked once the initial batch arrives; subsequent bar updates come * through `onTick`. * * Returns the generated `seriesId`, which can be used with * `requestMore()` to fetch additional historical bars. */ requestSeries(request: SeriesRequest): string; /** Request additional historical bars for an existing series. */ requestMore(seriesId: string, additionalBars: number): void; /** Remove a series (stop receiving updates for it). */ removeSeries(seriesId: string): void; /** All active series keyed by `seriesId`. */ getSeries(): ReadonlyMap<string, { symbol: string; timeframe: string; }>; /** * Promise-based helper: resolve a symbol without creating a series. * Returns the raw `symbol_resolved` payload from TradingView. * * Useful for fetching symbol metadata (description, exchange, type, * session hours, etc.) without paying for a candle subscription. */ resolvePair(pair: string, timeoutMs?: number): Promise<Record<string, unknown>>; /** * Promise-based helper: fetch a historical candle window in one call. * Creates a temporary series, waits for the initial backfill, then * removes the series and returns the candles. */ fetchCandlesOnce(symbol: string, opts: { timeframe: Timeframe; barCount: number; }, timeoutMs?: number): Promise<Candle[]>; /** Close the chart session on the server and release local state. */ delete(): Promise<void>; handleMessage(method: string, params: unknown[]): void; handleDisconnect(): void; replay(): void; private sendCreate; private handleSymbolResolved; private handleSymbolError; private handleSeriesError; /** * Handle `timescale_update` and `du` (data update). Both carry a * payload of the form: * * { sds_1: { s: [ { i, v: [time, open, high, low, close, volume] }, ... ] } } * * `timescale_update` is typically the initial historical dump; * subsequent `du` updates usually contain only the last tick. */ private handleSeriesData; } /** * Convert `kebab-case` and `snake_case` strings to `camelCase`. * * TradingView's wire format mixes both conventions: `series-key`, * `base_name`, `session-regular-display`, `is_tradable`, etc. Our * public API exposes everything as camelCase — this helper is the * single conversion point. * * Examples: * kebabToCamel('series-key') → 'seriesKey' * kebabToCamel('base_name') → 'baseName' * kebabToCamel('session-regular-display') → 'sessionRegularDisplay' * kebabToCamel('is_tradable') → 'isTradable' * kebabToCamel('has-no-bbo') → 'hasNoBbo' * * Edge behaviour: * - Already-camelCase strings pass through unchanged. * - Numeric segments are kept: `rt-lag` → `rtLag`, `price_52_week_high` → `price52WeekHigh`. * - Leading separators are preserved (`-foo` → `-foo`, unlikely to appear in TV payloads). * - Consecutive separators collapse: `a--b` → `aB`. */ declare function kebabToCamel(key: string): string; /** * Shallow-transform the top-level keys of an object from kebab/snake to * camelCase. Values are passed through unchanged — nested objects keep * their original key casing, because TradingView nests things like * `local_popularity: { US: 123, DE: 45 }` where the inner keys are * country codes and must NOT be lowercased. * * If you need recursive transformation, do it explicitly at each level * where it makes sense. */ declare function transformKeys<T = Record<string, unknown>>(obj: Record<string, unknown>): T; /** * Random alphanumeric id generator. * * Used for session identifiers (quote session, chart session) sent to * TradingView. Not cryptographically secure — just collision-resistant * enough for in-flight session multiplexing. */ declare function randomId(length?: number, random?: () => number): string; /** * Debug logging wrapper. * * Usage: * const log = createLogger('transport') * log('connect() → %s', url) * * Enable at runtime via the `DEBUG` environment variable: * DEBUG=tradingview-adapter:* (all namespaces) * DEBUG=tradingview-adapter:transport (one namespace) * DEBUG=tradingview-adapter:transport,tradingview-adapter:protocol */ type Logger = debug.Debugger; declare function createLogger(namespace: string): Logger; /** * Stable TradingView protocol constants. * * These values come from reverse-engineering the public TradingView * widget WebSocket — they may change without notice if TradingView * updates its infrastructure. */ /** WebSocket endpoint for quote/chart sessions. */ declare const TV_WS_URL = "wss://widgetdata.tradingview.com/socket.io/websocket"; /** Origin header required by TradingView when connecting from Node. */ declare const TV_ORIGIN = "https://s.tradingview.com"; export { Candle, CandleTick, CandlesUpdate, ChartSession, type ChartSessionOptions, type CommandMessage, type HeartbeatMessage, type HelloMessage, type Logger, type ProtocolMessage, RateLimitOptions, type SeriesRequest, Session, SessionManager, SymbolBatcher, SymbolBatcherExecutor, TV_ORIGIN, TV_WS_URL, Timeframe, TvSymbolError, createLogger, decodeFrames, encodeCommand, encodeFrame, encodeHeartbeat, kebabToCamel, randomId, transformKeys };