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
text/typescript
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 };