@mathieuc/tradingview 3.5.2 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +112 -51
  2. package/dist/chart/chart-session.d.ts +114 -0
  3. package/dist/chart/chart-session.js +339 -0
  4. package/dist/chart/graphics.d.ts +129 -0
  5. package/dist/chart/graphics.js +153 -0
  6. package/dist/chart/index.d.ts +11 -0
  7. package/dist/chart/index.js +6 -0
  8. package/dist/chart/strategy.d.ts +100 -0
  9. package/dist/chart/strategy.js +51 -0
  10. package/dist/chart/study.d.ts +60 -0
  11. package/dist/chart/study.js +171 -0
  12. package/dist/chart/timeframes.d.ts +7 -0
  13. package/dist/chart/timeframes.js +22 -0
  14. package/dist/chart/types.d.ts +105 -0
  15. package/dist/chart/types.js +9 -0
  16. package/dist/client/client.d.ts +89 -0
  17. package/dist/client/client.js +254 -0
  18. package/dist/client/index.d.ts +4 -0
  19. package/dist/client/index.js +2 -0
  20. package/dist/client/transport.d.ts +27 -0
  21. package/dist/client/transport.js +38 -0
  22. package/dist/data/candles.d.ts +40 -0
  23. package/dist/data/candles.js +101 -0
  24. package/dist/data/history.d.ts +61 -0
  25. package/dist/data/history.js +101 -0
  26. package/dist/data/index.d.ts +28 -0
  27. package/dist/data/index.js +15 -0
  28. package/dist/data/indicators.d.ts +54 -0
  29. package/dist/data/indicators.js +139 -0
  30. package/dist/data/operation.d.ts +71 -0
  31. package/dist/data/operation.js +205 -0
  32. package/dist/data/provider.d.ts +23 -0
  33. package/dist/data/provider.js +11 -0
  34. package/dist/data/quotes.d.ts +33 -0
  35. package/dist/data/quotes.js +117 -0
  36. package/dist/data/symbols.d.ts +9 -0
  37. package/dist/data/symbols.js +19 -0
  38. package/dist/errors.d.ts +53 -0
  39. package/dist/errors.js +24 -0
  40. package/dist/events.d.ts +36 -0
  41. package/dist/events.js +70 -0
  42. package/dist/http/account.d.ts +47 -0
  43. package/dist/http/account.js +127 -0
  44. package/dist/http/index.d.ts +11 -0
  45. package/dist/http/index.js +5 -0
  46. package/dist/http/indicators.d.ts +37 -0
  47. package/dist/http/indicators.js +144 -0
  48. package/dist/http/layouts.d.ts +36 -0
  49. package/dist/http/layouts.js +24 -0
  50. package/dist/http/market.d.ts +45 -0
  51. package/dist/http/market.js +59 -0
  52. package/dist/http/pine-permissions.d.ts +29 -0
  53. package/dist/http/pine-permissions.js +56 -0
  54. package/dist/http/request.d.ts +44 -0
  55. package/dist/http/request.js +83 -0
  56. package/dist/index.d.ts +21 -0
  57. package/dist/index.js +19 -0
  58. package/dist/indicators/builtin-indicator.d.ts +17 -0
  59. package/dist/indicators/builtin-indicator.js +75 -0
  60. package/dist/indicators/index.d.ts +8 -0
  61. package/dist/indicators/index.js +2 -0
  62. package/dist/indicators/pine-indicator.d.ts +63 -0
  63. package/dist/indicators/pine-indicator.js +101 -0
  64. package/dist/protocol/compression.d.ts +12 -0
  65. package/dist/protocol/compression.js +83 -0
  66. package/dist/protocol/framing.d.ts +59 -0
  67. package/dist/protocol/framing.js +68 -0
  68. package/dist/protocol/ids.d.ts +2 -0
  69. package/dist/protocol/ids.js +10 -0
  70. package/dist/protocol/index.d.ts +4 -0
  71. package/dist/protocol/index.js +3 -0
  72. package/dist/quote/fields.d.ts +35 -0
  73. package/dist/quote/fields.js +21 -0
  74. package/dist/quote/index.d.ts +4 -0
  75. package/dist/quote/index.js +2 -0
  76. package/dist/quote/quote-session.d.ts +73 -0
  77. package/dist/quote/quote-session.js +205 -0
  78. package/docs/README.es.md +58 -0
  79. package/docs/README.fr.md +58 -0
  80. package/docs/README.pt.md +58 -0
  81. package/docs/data-api.md +229 -0
  82. package/docs/low-level-api.md +213 -0
  83. package/docs/migration-v4.md +128 -0
  84. package/docs/v4-coverage.md +192 -0
  85. package/docs/v4-reliability.md +33 -0
  86. package/package.json +55 -20
  87. package/.env.sample +0 -2
  88. package/.eslintrc.js +0 -28
  89. package/.gitattributes +0 -2
  90. package/docs/DOCS.md +0 -3
  91. package/examples/AllPrivateIndicators.js +0 -44
  92. package/examples/BuiltInIndicator.js +0 -51
  93. package/examples/CustomChartType.js +0 -123
  94. package/examples/CustomTimeframe.js +0 -31
  95. package/examples/Errors.js +0 -155
  96. package/examples/FakeReplayMode.js +0 -40
  97. package/examples/FromToData.js +0 -35
  98. package/examples/GetDrawings.js +0 -27
  99. package/examples/GraphicIndicator.js +0 -42
  100. package/examples/MultipleSyncFetch.js +0 -44
  101. package/examples/PinePermManage.js +0 -67
  102. package/examples/ReplayMode.js +0 -103
  103. package/examples/Search.js +0 -14
  104. package/examples/SimpleChart.js +0 -63
  105. package/examples/UserLogin.js +0 -16
  106. package/main.js +0 -11
  107. package/src/chart/graphicParser.js +0 -308
  108. package/src/chart/session.js +0 -553
  109. package/src/chart/study.js +0 -435
  110. package/src/classes/BuiltInIndicator.js +0 -137
  111. package/src/classes/PineIndicator.js +0 -132
  112. package/src/classes/PinePermManager.js +0 -159
  113. package/src/client.js +0 -298
  114. package/src/miscRequests.js +0 -609
  115. package/src/protocol.js +0 -60
  116. package/src/quote/market.js +0 -132
  117. package/src/quote/session.js +0 -125
  118. package/src/types.js +0 -36
  119. package/src/utils.js +0 -20
  120. package/vite.config.js +0 -7
@@ -0,0 +1,205 @@
1
+ import { TradingViewClient } from '../client/client.js';
2
+ import { TradingViewError, toTradingViewError } from '../errors.js';
3
+ export const DEFAULT_TIMEOUT_MS = 15_000;
4
+ function abortError(signal) {
5
+ const { reason } = signal;
6
+ const message = reason instanceof Error ? reason.message : reason ? String(reason) : 'Aborted';
7
+ return new TradingViewError('ABORTED', message, { cause: reason });
8
+ }
9
+ export function validateTimeout(timeoutMs) {
10
+ const value = timeoutMs ?? DEFAULT_TIMEOUT_MS;
11
+ if (!Number.isFinite(value) || value <= 0) {
12
+ throw new TradingViewError('INVALID_ARGUMENT', 'timeoutMs must be a positive number');
13
+ }
14
+ return value;
15
+ }
16
+ export function validateSymbol(symbol) {
17
+ if (typeof symbol !== 'string' || !symbol.trim()) {
18
+ throw new TradingViewError('INVALID_ARGUMENT', 'symbol is required');
19
+ }
20
+ return symbol.trim();
21
+ }
22
+ export function throwIfAborted(signal) {
23
+ if (signal?.aborted)
24
+ throw abortError(signal);
25
+ }
26
+ function connect(options) {
27
+ if (options.client) {
28
+ if (options.client.isClosed)
29
+ throw new TradingViewError('INVALID_STATE', 'The provided client is closed');
30
+ return { client: options.client, release: async () => { } };
31
+ }
32
+ const client = new TradingViewClient({ ...options.clientOptions, credentials: options.credentials });
33
+ return { client, release: () => client.close() };
34
+ }
35
+ /**
36
+ * Runs a one-shot operation with timeout, abort and guaranteed cleanup:
37
+ * sessions are removed and an owned connection is closed before settling.
38
+ */
39
+ export function runOperation(options, description, start) {
40
+ let timeoutMs;
41
+ let connection;
42
+ try {
43
+ timeoutMs = validateTimeout(options.timeoutMs);
44
+ throwIfAborted(options.signal);
45
+ connection = connect(options);
46
+ }
47
+ catch (error) {
48
+ return Promise.reject(toTradingViewError(error, 'INVALID_ARGUMENT'));
49
+ }
50
+ const { client } = connection;
51
+ const { signal } = options;
52
+ return new Promise((resolve, reject) => {
53
+ let settled = false;
54
+ let cleanup;
55
+ const unsubscribers = [];
56
+ const finish = (error, value) => {
57
+ if (settled)
58
+ return;
59
+ settled = true;
60
+ clearTimeout(timer);
61
+ signal?.removeEventListener('abort', onAbort);
62
+ for (const off of unsubscribers)
63
+ off();
64
+ try {
65
+ cleanup?.();
66
+ }
67
+ catch {
68
+ // Cleanup must never hide the result.
69
+ }
70
+ connection.release().catch(() => { }).then(() => {
71
+ if (error)
72
+ reject(error);
73
+ else
74
+ resolve(value);
75
+ });
76
+ };
77
+ const onAbort = () => finish(abortError(signal));
78
+ const timer = setTimeout(() => {
79
+ finish(new TradingViewError('TIMEOUT', `${description} timed out after ${timeoutMs} ms`));
80
+ }, timeoutMs);
81
+ signal?.addEventListener('abort', onAbort, { once: true });
82
+ unsubscribers.push(client.on('error', (error) => finish(error)), client.on('close', () => finish(new TradingViewError('DISCONNECTED', `Connection closed during ${description}`))));
83
+ try {
84
+ cleanup = start({
85
+ client,
86
+ resolve: (value) => finish(undefined, value),
87
+ reject: (error) => finish(toTradingViewError(error)),
88
+ });
89
+ }
90
+ catch (error) {
91
+ finish(toTradingViewError(error, 'INVALID_ARGUMENT'));
92
+ }
93
+ });
94
+ }
95
+ /**
96
+ * Starts a watcher. The returned promise resolves once `ready()` is called
97
+ * and rejects on timeout, abort or failure before that.
98
+ */
99
+ export function startWatcher(options, description, handlers, start, build) {
100
+ let timeoutMs;
101
+ let connection;
102
+ try {
103
+ timeoutMs = validateTimeout(options.timeoutMs);
104
+ throwIfAborted(options.signal);
105
+ connection = connect(options);
106
+ }
107
+ catch (error) {
108
+ return Promise.reject(toTradingViewError(error, 'INVALID_ARGUMENT'));
109
+ }
110
+ const { client } = connection;
111
+ const { signal } = options;
112
+ let active = true;
113
+ let started = false;
114
+ let cleanup;
115
+ let stopPromise;
116
+ let resolveClosed;
117
+ const closed = new Promise((resolve) => { resolveClosed = resolve; });
118
+ const unsubscribers = [];
119
+ let resolveStart;
120
+ let rejectStart;
121
+ const startPromise = new Promise((resolve, reject) => {
122
+ resolveStart = resolve;
123
+ rejectStart = reject;
124
+ });
125
+ const report = (error) => {
126
+ try {
127
+ handlers.onError?.(error);
128
+ }
129
+ catch (listenerError) {
130
+ console.error('[tradingview] onError callback threw:', listenerError);
131
+ }
132
+ };
133
+ const stop = () => {
134
+ if (stopPromise)
135
+ return stopPromise;
136
+ active = false;
137
+ clearTimeout(timer);
138
+ signal?.removeEventListener('abort', onAbort);
139
+ for (const off of unsubscribers)
140
+ off();
141
+ try {
142
+ cleanup?.();
143
+ }
144
+ catch {
145
+ // Ignore cleanup failures.
146
+ }
147
+ stopPromise = connection.release().catch(() => { }).then(() => resolveClosed());
148
+ return stopPromise;
149
+ };
150
+ const fail = (raw) => {
151
+ if (!active)
152
+ return;
153
+ const error = toTradingViewError(raw);
154
+ const closing = stop();
155
+ if (!started)
156
+ closing.then(() => rejectStart(error));
157
+ else
158
+ report(error);
159
+ };
160
+ const watcher = build({
161
+ stop,
162
+ closed,
163
+ get isActive() {
164
+ return active;
165
+ },
166
+ });
167
+ const onAbort = () => fail(abortError(signal));
168
+ const timer = setTimeout(() => {
169
+ if (!started)
170
+ fail(new TradingViewError('TIMEOUT', `${description} timed out after ${timeoutMs} ms`));
171
+ }, timeoutMs);
172
+ signal?.addEventListener('abort', onAbort, { once: true });
173
+ unsubscribers.push(client.on('error', (error) => fail(error)), client.on('close', () => fail(new TradingViewError('DISCONNECTED', `Connection closed during ${description}`))));
174
+ try {
175
+ cleanup = start({
176
+ client,
177
+ ready: () => {
178
+ if (!active || started)
179
+ return;
180
+ started = true;
181
+ clearTimeout(timer);
182
+ resolveStart(watcher);
183
+ },
184
+ fail,
185
+ warn: (error) => {
186
+ if (active)
187
+ report(toTradingViewError(error));
188
+ },
189
+ isActive: () => active,
190
+ });
191
+ }
192
+ catch (error) {
193
+ fail(error);
194
+ }
195
+ return startPromise;
196
+ }
197
+ /** Converts a Date or Unix time (seconds) into Unix seconds. */
198
+ export function toUnixSeconds(value, name) {
199
+ if (value === undefined)
200
+ return undefined;
201
+ const seconds = value instanceof Date ? Math.floor(value.getTime() / 1000) : value;
202
+ if (!Number.isFinite(seconds))
203
+ throw new TradingViewError('INVALID_ARGUMENT', `${name} must be a Date or Unix seconds`);
204
+ return seconds;
205
+ }
@@ -0,0 +1,23 @@
1
+ import type { Candle } from '../chart/types.js';
2
+ import { type CandleHandlers, type WatchCandlesQuery } from './candles.js';
3
+ import type { ConnectionOptions } from './operation.js';
4
+ /** Minimum lifecycle required of a streaming candle source. */
5
+ export interface CandleSourceWorker {
6
+ /** First usable snapshot and subsequent updates, oldest first. */
7
+ readonly latest: readonly Candle[];
8
+ /** Releases resources. Safe to call more than once. */
9
+ stop(): Promise<void>;
10
+ }
11
+ /**
12
+ * Source-neutral candle contract. Applications can provide another market
13
+ * data source without emulating TradingView's charts or websocket protocol.
14
+ */
15
+ export interface MarketDataProvider {
16
+ watchCandles(query: WatchCandlesQuery, handlers: CandleHandlers): Promise<CandleSourceWorker>;
17
+ }
18
+ /** Default candle source backed by TradingView, with optional connection defaults. */
19
+ export declare class TradingViewProvider implements MarketDataProvider {
20
+ private readonly connection;
21
+ constructor(connection?: ConnectionOptions);
22
+ watchCandles(query: WatchCandlesQuery, handlers: CandleHandlers): Promise<CandleSourceWorker>;
23
+ }
@@ -0,0 +1,11 @@
1
+ import { watchCandles } from './candles.js';
2
+ /** Default candle source backed by TradingView, with optional connection defaults. */
3
+ export class TradingViewProvider {
4
+ connection;
5
+ constructor(connection = {}) {
6
+ this.connection = connection;
7
+ }
8
+ watchCandles(query, handlers) {
9
+ return watchCandles({ ...this.connection, ...query }, handlers);
10
+ }
11
+ }
@@ -0,0 +1,33 @@
1
+ import type { TradingSession } from '../chart/types.js';
2
+ import type { QuoteData, QuoteField, QuoteFieldPreset } from '../quote/fields.js';
3
+ import { type OperationOptions, type WatchHandlers, type Watcher } from './operation.js';
4
+ export interface QuoteOptions extends OperationOptions {
5
+ /** `all` (default), `price`, or a list of fields. */
6
+ fields?: QuoteFieldPreset | readonly QuoteField[];
7
+ /** Trading session. Default: `regular`. */
8
+ session?: TradingSession;
9
+ }
10
+ export interface QuoteQuery extends QuoteOptions {
11
+ symbol: string;
12
+ }
13
+ export interface QuotesQuery extends QuoteOptions {
14
+ symbols: readonly string[];
15
+ }
16
+ /** Gets quotes for several symbols at once, keyed by symbol. */
17
+ export declare function getQuotes(query: QuotesQuery | readonly string[]): Promise<Record<string, QuoteData>>;
18
+ /** Gets the current quote of a symbol (last price, change, volume...). */
19
+ export declare function getQuote(query: QuoteQuery | string): Promise<QuoteData>;
20
+ export interface QuoteHandlers extends WatchHandlers {
21
+ /** Receives the merged quote and the fields that changed. */
22
+ onData(symbol: string, quote: QuoteData, changes: QuoteData): void;
23
+ }
24
+ export interface QuoteWatcher extends Watcher {
25
+ /** Latest quote per symbol. */
26
+ readonly latest: Readonly<Record<string, QuoteData>>;
27
+ }
28
+ /**
29
+ * Streams quotes for one or more symbols. Resolves once every symbol has
30
+ * loaded (or failed: failures go to `onError`; if every symbol fails, the
31
+ * promise rejects). Call `stop()` when finished.
32
+ */
33
+ export declare function watchQuotes(query: QuotesQuery, handlers: QuoteHandlers): Promise<QuoteWatcher>;
@@ -0,0 +1,117 @@
1
+ import { TradingViewError } from '../errors.js';
2
+ import { runOperation, startWatcher, validateSymbol, } from './operation.js';
3
+ function validateSymbols(symbols) {
4
+ if (!Array.isArray(symbols) || symbols.length === 0) {
5
+ throw new TradingViewError('INVALID_ARGUMENT', 'symbols must be a non-empty array');
6
+ }
7
+ return [...new Set(symbols.map(validateSymbol))];
8
+ }
9
+ /** Gets quotes for several symbols at once, keyed by symbol. */
10
+ export function getQuotes(query) {
11
+ const options = Array.isArray(query) ? { symbols: query } : query;
12
+ let symbols;
13
+ try {
14
+ symbols = validateSymbols(options.symbols);
15
+ }
16
+ catch (error) {
17
+ return Promise.reject(error);
18
+ }
19
+ return runOperation(options, `getQuotes(${symbols.join(', ')})`, ({ client, resolve, reject }) => {
20
+ const session = client.createQuoteSession({ fields: options.fields });
21
+ const results = {};
22
+ let pending = symbols.length;
23
+ for (const symbol of symbols) {
24
+ const sub = session.subscribe(symbol, { session: options.session });
25
+ sub.on('error', reject);
26
+ sub.once('loaded', () => {
27
+ results[symbol] = sub.data;
28
+ pending -= 1;
29
+ if (pending === 0)
30
+ resolve(results);
31
+ });
32
+ }
33
+ return () => session.delete();
34
+ });
35
+ }
36
+ /** Gets the current quote of a symbol (last price, change, volume...). */
37
+ export async function getQuote(query) {
38
+ const options = typeof query === 'string' ? { symbol: query } : query;
39
+ const symbol = validateSymbol(options.symbol);
40
+ const quotes = await getQuotes({ ...options, symbols: [symbol] });
41
+ return quotes[symbol];
42
+ }
43
+ /**
44
+ * Streams quotes for one or more symbols. Resolves once every symbol has
45
+ * loaded (or failed: failures go to `onError`; if every symbol fails, the
46
+ * promise rejects). Call `stop()` when finished.
47
+ */
48
+ export function watchQuotes(query, handlers) {
49
+ let symbols;
50
+ try {
51
+ if (typeof handlers?.onData !== 'function')
52
+ throw new TradingViewError('INVALID_ARGUMENT', 'onData must be a function');
53
+ symbols = validateSymbols(query.symbols);
54
+ }
55
+ catch (error) {
56
+ return Promise.reject(error);
57
+ }
58
+ const latest = {};
59
+ return startWatcher(query, `watchQuotes(${symbols.join(', ')})`, handlers, (ctx) => {
60
+ const session = ctx.client.createQuoteSession({ fields: query.fields });
61
+ const settled = new Set();
62
+ const errors = [];
63
+ let loaded = 0;
64
+ const settle = (symbol, ok) => {
65
+ if (settled.has(symbol))
66
+ return;
67
+ settled.add(symbol);
68
+ if (ok)
69
+ loaded += 1;
70
+ if (settled.size < symbols.length)
71
+ return;
72
+ if (loaded === 0)
73
+ ctx.fail(errors[0]);
74
+ else {
75
+ for (const error of errors)
76
+ ctx.warn(error);
77
+ ctx.ready();
78
+ }
79
+ };
80
+ const subscriptions = symbols.map((symbol) => {
81
+ const sub = session.subscribe(symbol, { session: query.session });
82
+ sub.on('data', (quote, changes) => {
83
+ latest[symbol] = quote;
84
+ if (!ctx.isActive())
85
+ return;
86
+ try {
87
+ handlers.onData(symbol, quote, changes);
88
+ }
89
+ catch (error) {
90
+ ctx.warn(new TradingViewError('CALLBACK_ERROR', 'onData callback threw', { cause: error }));
91
+ }
92
+ });
93
+ sub.on('loaded', () => settle(symbol, true));
94
+ sub.on('error', (error) => {
95
+ if (error.code === 'DISCONNECTED')
96
+ return; // Reported by the watcher itself.
97
+ if (settled.size < symbols.length && !settled.has(symbol)) {
98
+ errors.push(error);
99
+ settle(symbol, false);
100
+ }
101
+ else
102
+ ctx.warn(error);
103
+ });
104
+ return sub;
105
+ });
106
+ return () => {
107
+ for (const sub of subscriptions)
108
+ sub.close();
109
+ session.delete();
110
+ };
111
+ }, (base) => ({
112
+ stop: base.stop,
113
+ closed: base.closed,
114
+ get isActive() { return base.isActive; },
115
+ get latest() { return { ...latest }; },
116
+ }));
117
+ }
@@ -0,0 +1,9 @@
1
+ import type { SymbolInfo, TradingSession } from '../chart/types.js';
2
+ import { type OperationOptions } from './operation.js';
3
+ export interface SymbolInfoQuery extends OperationOptions {
4
+ symbol: string;
5
+ session?: TradingSession;
6
+ currency?: string;
7
+ }
8
+ /** Resolves a symbol: exchange, currency, session, price scale, type... */
9
+ export declare function getSymbolInfo(query: SymbolInfoQuery | string): Promise<SymbolInfo>;
@@ -0,0 +1,19 @@
1
+ import { runOperation, validateSymbol } from './operation.js';
2
+ /** Resolves a symbol: exchange, currency, session, price scale, type... */
3
+ export function getSymbolInfo(query) {
4
+ const options = typeof query === 'string' ? { symbol: query } : query;
5
+ let symbol;
6
+ try {
7
+ symbol = validateSymbol(options.symbol);
8
+ }
9
+ catch (error) {
10
+ return Promise.reject(error);
11
+ }
12
+ return runOperation(options, `getSymbolInfo(${symbol})`, ({ client, resolve, reject }) => {
13
+ const chart = client.createChart();
14
+ chart.on('error', reject);
15
+ chart.on('symbolLoaded', resolve);
16
+ chart.setMarket(symbol, { count: 1, session: options.session, currency: options.currency });
17
+ return () => chart.delete();
18
+ });
19
+ }
@@ -0,0 +1,53 @@
1
+ /** Machine-readable error categories raised by this library. */
2
+ export type TradingViewErrorCode =
3
+ /** The server rejected a websocket packet and closed the connection. */
4
+ 'PROTOCOL_ERROR'
5
+ /** The websocket could not be opened or failed. */
6
+ | 'CONNECTION_ERROR'
7
+ /** The websocket closed while an operation was still waiting for data. */
8
+ | 'DISCONNECTED'
9
+ /** Credentials were rejected or the account could not be loaded. */
10
+ | 'AUTH_ERROR'
11
+ /** `symbol_error`: the symbol could not be resolved. */
12
+ | 'SYMBOL_ERROR'
13
+ /** `series_error`: the series could not be loaded (permissions, timeframe...). */
14
+ | 'SERIES_ERROR'
15
+ /** `critical_error`: the server rejected a chart or replay command. */
16
+ | 'CRITICAL_ERROR'
17
+ /** `study_error`: an indicator failed on the server. */
18
+ | 'STUDY_ERROR'
19
+ /** A quote symbol returned an error status. */
20
+ | 'QUOTE_ERROR'
21
+ /** A strategy report or compressed payload could not be decoded. */
22
+ | 'PARSE_ERROR'
23
+ /** A method was called in a state where it cannot work. */
24
+ | 'INVALID_STATE'
25
+ /** An argument is invalid. */
26
+ | 'INVALID_ARGUMENT'
27
+ /** An HTTP endpoint answered with an unexpected status or payload. */
28
+ | 'HTTP_ERROR'
29
+ /** The requested resource does not exist or is not accessible. */
30
+ | 'NOT_FOUND'
31
+ /** The server answered but returned no usable data. */
32
+ | 'NO_DATA'
33
+ /** An operation did not complete in time. */
34
+ | 'TIMEOUT'
35
+ /** An operation was cancelled through an `AbortSignal`. */
36
+ | 'ABORTED'
37
+ /** A user-supplied callback threw while processing data. */
38
+ | 'CALLBACK_ERROR';
39
+ export interface TradingViewErrorOptions {
40
+ /** Raw details sent by the server, when available. */
41
+ details?: unknown;
42
+ cause?: unknown;
43
+ }
44
+ /** Every error produced by this library is a `TradingViewError`. */
45
+ export declare class TradingViewError extends Error {
46
+ readonly name = "TradingViewError";
47
+ readonly code: TradingViewErrorCode;
48
+ /** Raw details sent by the server, when available. */
49
+ readonly details: unknown;
50
+ constructor(code: TradingViewErrorCode, message: string, options?: TradingViewErrorOptions);
51
+ }
52
+ /** Normalises any thrown value into a `TradingViewError`. */
53
+ export declare function toTradingViewError(error: unknown, fallbackCode?: TradingViewErrorCode): TradingViewError;
package/dist/errors.js ADDED
@@ -0,0 +1,24 @@
1
+ /** Every error produced by this library is a `TradingViewError`. */
2
+ export class TradingViewError extends Error {
3
+ name = 'TradingViewError';
4
+ code;
5
+ /** Raw details sent by the server, when available. */
6
+ details;
7
+ constructor(code, message, options = {}) {
8
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
9
+ this.code = code;
10
+ this.details = options.details;
11
+ }
12
+ }
13
+ /** Normalises any thrown value into a `TradingViewError`. */
14
+ export function toTradingViewError(error, fallbackCode = 'CONNECTION_ERROR') {
15
+ if (error instanceof TradingViewError)
16
+ return error;
17
+ if (error instanceof Error) {
18
+ if (error.name === 'AbortError') {
19
+ return new TradingViewError('ABORTED', error.message || 'Aborted', { cause: error });
20
+ }
21
+ return new TradingViewError(fallbackCode, error.message, { cause: error });
22
+ }
23
+ return new TradingViewError(fallbackCode, String(error), { details: error });
24
+ }
@@ -0,0 +1,36 @@
1
+ type EventMap = {
2
+ [event: string]: unknown[];
3
+ };
4
+ export type Listener<Args extends unknown[]> = (...args: Args) => void;
5
+ /** Unsubscribes the listener it was returned for. */
6
+ export type Unsubscribe = () => void;
7
+ /** Catch-all listener: receives the event name, then the event arguments. */
8
+ export type AnyListener<Events extends EventMap> = (...args: {
9
+ [K in keyof Events & string]: [event: K, ...args: Events[K]];
10
+ }[keyof Events & string]) => void;
11
+ /**
12
+ * Small typed event emitter shared by clients, sessions and studies.
13
+ *
14
+ * Listener exceptions are reported without interrupting other listeners or
15
+ * crashing the host process. An `error` event without any
16
+ * listener is written to `console.error` instead of being silently dropped.
17
+ */
18
+ export declare class Emitter<Events extends EventMap> {
19
+ #private;
20
+ /** Adds a listener and returns a function that removes it. */
21
+ on<K extends keyof Events & string>(event: K, listener: Listener<Events[K]>): Unsubscribe;
22
+ /** Adds a listener called at most once. */
23
+ once<K extends keyof Events & string>(event: K, listener: Listener<Events[K]>): Unsubscribe;
24
+ /** Removes a listener. */
25
+ off<K extends keyof Events & string>(event: K, listener: Listener<Events[K]>): void;
26
+ /** Listens to every event; receives the event name first. */
27
+ onAny(listener: AnyListener<Events>): Unsubscribe;
28
+ /** Number of listeners for an event (catch-all listeners excluded). */
29
+ listenerCount(event: keyof Events & string): number;
30
+ /** True when the event has a listener, including catch-all listeners. */
31
+ protected hasListeners(event: keyof Events & string): boolean;
32
+ /** Removes every listener. */
33
+ protected removeAllListeners(): void;
34
+ protected emit<K extends keyof Events & string>(event: K, ...args: Events[K]): void;
35
+ }
36
+ export {};
package/dist/events.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Small typed event emitter shared by clients, sessions and studies.
3
+ *
4
+ * Listener exceptions are reported without interrupting other listeners or
5
+ * crashing the host process. An `error` event without any
6
+ * listener is written to `console.error` instead of being silently dropped.
7
+ */
8
+ export class Emitter {
9
+ #listeners = new Map();
10
+ #anyListeners = new Set();
11
+ /** Adds a listener and returns a function that removes it. */
12
+ on(event, listener) {
13
+ let set = this.#listeners.get(event);
14
+ if (!set) {
15
+ set = new Set();
16
+ this.#listeners.set(event, set);
17
+ }
18
+ set.add(listener);
19
+ return () => this.off(event, listener);
20
+ }
21
+ /** Adds a listener called at most once. */
22
+ once(event, listener) {
23
+ const wrapper = (...args) => {
24
+ this.off(event, wrapper);
25
+ listener(...args);
26
+ };
27
+ return this.on(event, wrapper);
28
+ }
29
+ /** Removes a listener. */
30
+ off(event, listener) {
31
+ this.#listeners.get(event)?.delete(listener);
32
+ }
33
+ /** Listens to every event; receives the event name first. */
34
+ onAny(listener) {
35
+ this.#anyListeners.add(listener);
36
+ return () => { this.#anyListeners.delete(listener); };
37
+ }
38
+ /** Number of listeners for an event (catch-all listeners excluded). */
39
+ listenerCount(event) {
40
+ return this.#listeners.get(event)?.size ?? 0;
41
+ }
42
+ /** True when the event has a listener, including catch-all listeners. */
43
+ hasListeners(event) {
44
+ return this.listenerCount(event) > 0 || this.#anyListeners.size > 0;
45
+ }
46
+ /** Removes every listener. */
47
+ removeAllListeners() {
48
+ this.#listeners.clear();
49
+ this.#anyListeners.clear();
50
+ }
51
+ emit(event, ...args) {
52
+ const set = this.#listeners.get(event);
53
+ if (event === 'error' && !set?.size && !this.#anyListeners.size) {
54
+ console.error('[tradingview]', ...args);
55
+ return;
56
+ }
57
+ for (const listener of [...(set ?? [])])
58
+ Emitter.#call(() => listener(...args));
59
+ for (const listener of [...this.#anyListeners])
60
+ Emitter.#call(() => listener(event, ...args));
61
+ }
62
+ static #call(fn) {
63
+ try {
64
+ fn();
65
+ }
66
+ catch (error) {
67
+ console.error('[tradingview] Listener threw:', error);
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,47 @@
1
+ import { type Credentials, type HttpOptions } from './request.js';
2
+ /** A TradingView account. */
3
+ export interface User {
4
+ id: number;
5
+ username: string;
6
+ firstName: string;
7
+ lastName: string;
8
+ reputation: number;
9
+ following: number;
10
+ followers: number;
11
+ notifications: {
12
+ user: number;
13
+ following: number;
14
+ };
15
+ /** `sessionid` cookie. Keep it secret. */
16
+ session: string;
17
+ /** `sessionid_sign` cookie. Keep it secret. */
18
+ signature: string;
19
+ sessionHash: string;
20
+ privateChannel: string;
21
+ /** Websocket auth token. Keep it secret. */
22
+ authToken: string;
23
+ joinDate: Date;
24
+ }
25
+ export interface LoginOptions extends HttpOptions {
26
+ username: string;
27
+ password: string;
28
+ /** Keep the session alive longer. Default: true. */
29
+ remember?: boolean;
30
+ /** Custom user agent prefix. */
31
+ userAgent?: string;
32
+ }
33
+ /**
34
+ * Signs in with a username/email and password and returns the account with
35
+ * its session cookies. Accounts with 2FA or captcha challenges are not supported.
36
+ */
37
+ export declare function loginUser(options: LoginOptions): Promise<User>;
38
+ export interface GetUserOptions extends HttpOptions {
39
+ /** Page used to read the account, for example `https://fr.tradingview.com/`. */
40
+ location?: string;
41
+ /** Maximum number of redirects to follow. Default: 5. */
42
+ maxRedirects?: number;
43
+ }
44
+ /** Parses the account embedded in a TradingView HTML page. */
45
+ export declare function parseUserPage(page: string, credentials: Credentials): User;
46
+ /** Loads the account behind `sessionid` (and `sessionid_sign`) cookies. */
47
+ export declare function getUser(credentials: Credentials, options?: GetUserOptions): Promise<User>;