@mathieuc/tradingview 3.5.1 → 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 -294
  114. package/src/miscRequests.js +0 -607
  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,229 @@
1
+ # Data API guide
2
+
3
+ [README](../README.md) · [Low-level API](low-level-api.md) · [Migrating from v3](migration-v4.md) · [Examples](../examples)
4
+
5
+ **V4 beta is currently available from this repository only; the latest npm release is still v3.** Build this checkout before running the examples below.
6
+
7
+ The data API is the simplest way to use this library, in any application. Each function opens what it needs, waits for complete data, and releases every websocket session (and the connection it opened) before returning, including on errors, timeouts and cancellation.
8
+
9
+ ```ts
10
+ import { getCandles, watchQuotes } from '@mathieuc/tradingview/data';
11
+ ```
12
+
13
+ Everything here is also exported from the root entry (`@mathieuc/tradingview`).
14
+
15
+ | Function | Returns |
16
+ | --- | --- |
17
+ | `getCandles(query)` | Candles, oldest first |
18
+ | `watchCandles(query, handlers)` | A watcher streaming candle snapshots |
19
+ | `getQuote(query)` / `getQuotes(query)` | Current quote(s) |
20
+ | `watchQuotes(query, handlers)` | A watcher streaming quotes |
21
+ | `getSymbolInfo(query)` | Symbol metadata (exchange, currency, session...) |
22
+ | `getIndicatorData(query)` | Indicator values, drawings and strategy report |
23
+ | `watchIndicator(query, handlers)` | A watcher streaming indicator results |
24
+ | `searchMarkets(text, options?)` | Markets matching a text |
25
+ | `searchIndicators(text, options?)` | Built-in and community indicators |
26
+ | `getTechnicalAnalysis(symbol, options?)` | TradingView technical ratings |
27
+
28
+ ## Candles
29
+
30
+ ```ts
31
+ const candles = await getCandles({ symbol: 'BINANCE:BTCUSDT', timeframe: '60', count: 500 });
32
+ const last = candles.at(-1); // { time, open, high, low, close, volume }
33
+ ```
34
+
35
+ `time` is the bar open time in Unix **seconds**. `getCandles('BINANCE:BTCUSDT')` is a shorthand for 100 daily candles.
36
+
37
+ | Option | Default | Description |
38
+ | --- | --- | --- |
39
+ | `symbol` | required | Exchange-qualified symbol: `BINANCE:BTCUSDT`, `NASDAQ:AAPL`, `FX:EURUSD`... Use `searchMarkets` to find one. |
40
+ | `timeframe` | `'D'` | `1`, `5`, `15`, `60`, `240` (minutes), `D`, `W`, `M`, `1S` (seconds)... |
41
+ | `count` | `100` | Number of most recent bars. Larger counts are loaded in batches automatically (deep history). |
42
+ | `from` | | `Date` or Unix seconds. Loads every bar from this time (deep history); `count` is then ignored. |
43
+ | `to` | now | `Date` or Unix seconds: newest bar time. |
44
+ | `maxCount` | `20000` | Safety cap for `from` ranges. |
45
+ | `chartType` | | `HeikinAshi`, `Renko`, `LineBreak`, `Kagi`, `PointAndFigure` or `Range`. |
46
+ | `chartInputs` | | Inputs for `chartType`, e.g. `{ boxSize: 3, style: 'ATR', atrLength: 14 }`. |
47
+ | `currency` | | Convert prices, e.g. `EUR`. |
48
+ | `session` | | `regular` or `extended`. |
49
+ | `adjustment` | `'splits'` | `splits`, `dividends` or `none`. |
50
+ | `backAdjustment` | | Back-adjust continuous futures. |
51
+ | `timezone` | | Chart timezone (affects daily and longer bar alignment). |
52
+ | `credentials`, `client`, `clientOptions`, `timeoutMs`, `signal` | | See [Connections, timeouts and cancellation](#connections-timeouts-and-cancellation). |
53
+
54
+ History stops early, without error, when the server has no more bars (`series_completed` reports `data_completed: "end"` or `"limit"`). Anonymous access is limited: in our tests, about 7 000 one-minute bars, and reference times (`to`) in the past were capped. An empty result rejects with `NO_DATA`; when the server reported a limit, the message says so.
55
+
56
+ ## Watching candles
57
+
58
+ ```ts
59
+ const watcher = await watchCandles({ symbol: 'BINANCE:BTCUSDT', timeframe: '1', count: 50 }, {
60
+ onData(candles) {
61
+ console.log(candles.at(-1)?.close);
62
+ },
63
+ onError(error) {
64
+ console.error(error.code, error.message);
65
+ },
66
+ });
67
+
68
+ // ...later
69
+ await watcher.stop(); // Idempotent
70
+ ```
71
+
72
+ - The promise resolves once the initial history is loaded; `onData` has already received it.
73
+ - Each snapshot is a frozen array of the `count` most recent bars, oldest first.
74
+ - `watcher.latest` holds the last snapshot, `watcher.symbolInfo` the symbol metadata.
75
+ - `watcher.closed` resolves when the watcher stops: after `stop()`, an aborted `signal`, or a fatal error.
76
+ - Errors before start-up reject the promise. Afterwards they go to `onError`; fatal ones (disconnection, symbol or series errors) also stop the watcher. There is no automatic reconnection: start a new watcher when `closed` resolves if you need one.
77
+
78
+ `watchCandles` accepts the same chart options as `getCandles`, except `from`, `to` and `maxCount`.
79
+
80
+ ## Quotes
81
+
82
+ ```ts
83
+ const quote = await getQuote('BINANCE:BTCUSDT');
84
+ console.log(quote.lp, quote.chp); // Last price, change in percent
85
+
86
+ const quotes = await getQuotes({ symbols: ['NASDAQ:AAPL', 'FX:EURUSD'], fields: 'price' });
87
+ console.log(quotes['NASDAQ:AAPL'].lp);
88
+ ```
89
+
90
+ | Option | Default | Description |
91
+ | --- | --- | --- |
92
+ | `symbol` / `symbols` | required | One symbol, or a non-empty list (duplicates are ignored). |
93
+ | `fields` | `'all'` | `all`, `price` (`lp` only), or a list such as `['lp', 'bid', 'ask', 'volume']`. |
94
+ | `session` | `'regular'` | `regular` or `extended`. |
95
+
96
+ A quote contains the fields TradingView sent: `lp`, `lp_time`, `ch`, `chp`, `bid`, `ask`, `volume`, `open_price`, `high_price`, `low_price`, `prev_close_price`, `description`, `currency_code`... An unknown symbol rejects with `QUOTE_ERROR`.
97
+
98
+ ```ts
99
+ const watcher = await watchQuotes({ symbols: ['BINANCE:BTCUSDT', 'BINANCE:ETHUSDT'] }, {
100
+ onData(symbol, quote, changes) {
101
+ console.log(symbol, quote.lp, changes);
102
+ },
103
+ onError: console.error,
104
+ });
105
+ console.log(watcher.latest); // Latest quote per symbol
106
+ await watcher.stop();
107
+ ```
108
+
109
+ `watchQuotes` resolves once every symbol has loaded or failed. Failing symbols are reported to `onError` and the others keep streaming; if all fail, the promise rejects.
110
+
111
+ ## Symbol information
112
+
113
+ ```ts
114
+ const info = await getSymbolInfo('NASDAQ:AAPL');
115
+ console.log(info.description, info.currency_code, info.timezone, info.session, info.pricescale);
116
+ ```
117
+
118
+ Anonymous sessions may be served by a substitute feed: for `NASDAQ:AAPL`, `full_name` can be `BATS:AAPL` while `pro_name` stays `NASDAQ:AAPL`.
119
+
120
+ ## Indicators and strategies
121
+
122
+ ```ts
123
+ const rsi = await getIndicatorData({
124
+ symbol: 'BINANCE:BTCEUR',
125
+ timeframe: '60',
126
+ indicator: 'STD;RSI',
127
+ inputs: { Length: 21 },
128
+ credentials, // Pine scripts need an account
129
+ });
130
+ console.log(rsi.values.at(-1)); // { $time, RSI: 54.2, ... }
131
+ ```
132
+
133
+ `indicator` accepts:
134
+
135
+ - a script ID: `STD;RSI` (built-in Pine), `PUB;xxxx` (community), `USER;xxxx` (private); use `searchIndicators` to find IDs, and `version` to pin a version;
136
+ - a built-in study type: `Volume@tv-basicstudies-241`, `VbPFixed@tv-basicstudies-241!`... (works without an account);
137
+ - a `PineIndicator` or `BuiltInIndicator` instance (it is copied, never modified).
138
+
139
+ `inputs` sets Pine inputs (by input ID `in_0`, number, inline name or internal ID, with type and option validation) or built-in options.
140
+
141
+ The result contains:
142
+
143
+ | Field | Description |
144
+ | --- | --- |
145
+ | `indicator` | The indicator definition used. |
146
+ | `candles` | Chart bars, oldest first. |
147
+ | `values` | One row per bar: `$time` plus one key per plot (unnamed or duplicate plots are `plot_N`). The server may compute a few rows before the first returned candle. |
148
+ | `graphics` | Drawings: `labels`, `lines`, `boxes`, `tables` (with `cells`), `polygons`, `horizLines`, `horizHists`, and `raw`. X positions are bars back from the latest loaded bar. |
149
+ | `strategyReport` | For Pine strategies: `performance`, `trades` (most recent first), `history` (equity, drawdown, buy & hold), `currency`, `settings`. Empty for studies. |
150
+
151
+ Without an account, TradingView refuses Pine studies ("maximum number of studies per chart"): the call rejects with `STUDY_ERROR`. Built-in studies work.
152
+
153
+ `watchIndicator(query, { onData, onError })` streams the same result on every study update; `watcher.latest` holds the last one.
154
+
155
+ ## Search and technical analysis
156
+
157
+ ```ts
158
+ const markets = await searchMarkets('BINANCE:BTC', { type: 'crypto' });
159
+ // [{ id: 'BINANCE:BTCUSDT', exchange, fullExchange, symbol, description, type, currency, country }, ...]
160
+
161
+ const indicators = await searchIndicators('RSI');
162
+ // [{ id: 'STD;RSI', version, name, author, image, source, type, access }, ...]
163
+
164
+ const ratings = await getTechnicalAnalysis('BINANCE:BTCUSDT');
165
+ // { '1': { All, MA, Other }, '5': ..., '1D': ..., '1W': ..., '1M': ... } or null
166
+ ```
167
+
168
+ `searchMarkets` options: `type` (`stock`, `crypto`, `forex`, `futures`, `index`, `cfd`, `economic`...), `exchange`, `offset` (pagination). Ratings range from -2 (strong sell) to 2 (strong buy).
169
+
170
+ ## Connections, timeouts and cancellation
171
+
172
+ Websocket data functions accept:
173
+
174
+ | Option | Description |
175
+ | --- | --- |
176
+ | `timeoutMs` | Default 15 000. One-shot functions: whole call. Watchers: until start-up. Rejects with `TIMEOUT`. Raise it for deep history. |
177
+ | `signal` | An `AbortSignal`. Aborting rejects with `ABORTED` (or stops a running watcher). |
178
+ | `credentials` | `{ session, signature }`: your `sessionid` and `sessionid_sign` cookies, for data your account can access. |
179
+ | `clientOptions` | Options for the connection opened by the call (`server`, `transport`, `debug`, `fetch`...). |
180
+ | `client` | Reuse an open `TradingViewClient`. The call only removes its own sessions and leaves the connection open. Useful for many calls in a row. |
181
+
182
+ ```ts
183
+ import { TradingViewClient, getCandles, getQuote } from '@mathieuc/tradingview';
184
+
185
+ const client = new TradingViewClient({ credentials });
186
+ try {
187
+ const [candles, quote] = await Promise.all([
188
+ getCandles({ symbol: 'BINANCE:BTCUSDT', client }),
189
+ getQuote({ symbol: 'BINANCE:BTCUSDT', client }),
190
+ ]);
191
+ } finally {
192
+ await client.close();
193
+ }
194
+ ```
195
+
196
+ Keep credentials in environment variables or a secret store; never put them in source files, issues or prompts.
197
+
198
+ ## Alternate candle providers
199
+
200
+ The V4 data API remains usable with a source other than TradingView. Implement `MarketDataProvider.watchCandles(query, handlers)`; its worker exposes `latest` and an idempotent `stop()`. Pass the provider to `getCandles(query, provider)` for a one-shot snapshot or `watchCandles(query, handlers, provider)` for streaming. The provider controls its own network access and must resolve only after its first usable snapshot.
201
+
202
+ ```ts
203
+ import { getCandles, type MarketDataProvider } from '@mathieuc/tradingview/data';
204
+
205
+ declare const myProvider: MarketDataProvider;
206
+ const candles = await getCandles({ symbol: 'CUSTOM:ASSET' }, myProvider);
207
+ ```
208
+
209
+ `TradingViewProvider` implements the same contract and can carry default connection options. The regular `getCandles(query)` and `watchCandles(query, handlers)` calls use TradingView directly, so no provider setup is needed.
210
+
211
+ ## Errors
212
+
213
+ Every failure is a `TradingViewError` with a `code`:
214
+
215
+ | Code | Meaning |
216
+ | --- | --- |
217
+ | `INVALID_ARGUMENT` | Invalid query (checked before connecting). |
218
+ | `SYMBOL_ERROR` | Unknown symbol. |
219
+ | `SERIES_ERROR` | Bars refused (for example a chart type or timeframe your account cannot use). |
220
+ | `CRITICAL_ERROR` | Command refused (invalid timeframe, timezone...). |
221
+ | `STUDY_ERROR` | Indicator failed or not allowed. |
222
+ | `QUOTE_ERROR` | Quote refused for a symbol. |
223
+ | `NO_DATA` | No bars in the requested range. |
224
+ | `AUTH_ERROR` | Credentials rejected. |
225
+ | `NOT_FOUND` | Unknown indicator, layout... |
226
+ | `TIMEOUT`, `ABORTED`, `DISCONNECTED`, `CONNECTION_ERROR`, `PROTOCOL_ERROR`, `HTTP_ERROR`, `PARSE_ERROR` | Transport and decoding failures. |
227
+ | `CALLBACK_ERROR` | A watcher callback threw; the stream remains active. |
228
+
229
+ `error.details` keeps the raw server payload when there is one.
@@ -0,0 +1,213 @@
1
+ # Low-level API
2
+
3
+ [README](../README.md) · [Data API](data-api.md) · [Migrating from v3](migration-v4.md) · [Examples](../examples)
4
+
5
+ Use the low-level API when you need full control: several charts and studies on one connection, replay mode, raw protocol access, or long-lived sessions you manage yourself. The [data API](data-api.md) is built on it.
6
+
7
+ ```ts
8
+ import { TradingViewClient, BuiltInIndicator, getIndicator } from '@mathieuc/tradingview';
9
+ ```
10
+
11
+ ## Events
12
+
13
+ Clients, charts, studies, quote sessions and quote subscriptions share one typed event API:
14
+
15
+ ```ts
16
+ const off = emitter.on('update', (changes) => {}); // Returns an unsubscribe function
17
+ emitter.once('symbolLoaded', (info) => {});
18
+ emitter.off('update', listener);
19
+ emitter.onAny((event, ...args) => {}); // Every event
20
+ ```
21
+
22
+ An `error` event without any listener is written to `console.error`. A throwing listener never breaks the protocol state: its exception is re-thrown asynchronously.
23
+
24
+ ## Client
25
+
26
+ ```ts
27
+ const client = new TradingViewClient({
28
+ credentials: { session: process.env.SESSION, signature: process.env.SIGNATURE }, // Optional
29
+ });
30
+ await client.ready; // Optional: packets are queued until the connection is ready
31
+ // ...
32
+ await client.close();
33
+ ```
34
+
35
+ | Option | Description |
36
+ | --- | --- |
37
+ | `credentials` | `sessionid` / `sessionid_sign` cookies. The client loads the account (`getUser`) to get the websocket auth token. Without credentials, the session is anonymous. |
38
+ | `authToken` | Websocket auth token (`User.authToken`), to skip the account lookup. |
39
+ | `location` | Page used for the account lookup, e.g. `https://fr.tradingview.com/`. |
40
+ | `server` | `data` (default), `prodata` (paid accounts) or `widgetdata`. |
41
+ | `headers` | Extra websocket headers. |
42
+ | `debug` | `true` logs traffic with `console.log`; a function receives log arguments. |
43
+ | `transport` | Custom websocket transport (proxy, other library, tests). See [Transport](#transport). |
44
+ | `fetch` | Custom `fetch` for the account lookup. |
45
+ | `connectTimeoutMs` | Time allowed to open and authenticate. Default 20 000. |
46
+
47
+ | Member | Description |
48
+ | --- | --- |
49
+ | `ready` | Promise resolved once the auth token is sent; rejects on credential, connection or timeout failure. |
50
+ | `isOpen`, `isAuthenticated`, `isClosed` | State. |
51
+ | `serverInfo` | Server greeting (`session_id`, `release`, `protocol`...). |
52
+ | `createChart()` | New `ChartSession`. |
53
+ | `createQuoteSession(options)` | New `QuoteSession`. |
54
+ | `send(method, params)` | Sends a raw packet (escape hatch for commands this library does not wrap). |
55
+ | `close()` | Closes the connection; resolves once closed. Sessions are released without errors. |
56
+
57
+ Events: `open`, `hello` (server greeting), `ready`, `heartbeat` (answered automatically), `packet` (packets addressed to no session), `close` (code, reason), `error`.
58
+
59
+ If the connection drops unexpectedly, every chart, study and quote subscription emits a `DISCONNECTED` error. The client does not reconnect automatically: create a new client.
60
+
61
+ ## Charts
62
+
63
+ ```ts
64
+ const chart = client.createChart();
65
+ chart.on('symbolLoaded', (info) => console.log(info.description));
66
+ chart.on('update', (changes) => console.log(chart.lastCandle));
67
+ chart.on('error', (error) => console.error(error.code, error.message));
68
+
69
+ chart.setMarket('BINANCE:BTCEUR', { timeframe: '60', count: 300 });
70
+ ```
71
+
72
+ `setMarket(symbol, options)`:
73
+
74
+ | Option | Description |
75
+ | --- | --- |
76
+ | `timeframe` | Default `D`. |
77
+ | `count` | Bars to load when the series is created (default 100). **Negative** values load bars *after* `to` instead of before it. |
78
+ | `to` | Reference time (Unix seconds) of the last bar. Sent as `['bar_count', to, count]`. |
79
+ | `adjustment`, `backAdjustment`, `session`, `currency` | As in the data API. |
80
+ | `type`, `inputs` | Custom chart type (`HeikinAshi`, `Renko`, `LineBreak`, `Kagi`, `PointAndFigure`, `Range`) and its inputs. |
81
+ | `replay` | Start replay mode at this Unix time. See [Replay](#replay). |
82
+
83
+ The first `setMarket` creates the series. Later calls (another symbol, type or replay) modify it and keep its bar count and studies: TradingView only accepts an empty range when modifying a series. Use `fetchMore` or a new chart to load a different amount.
84
+
85
+ | Member | Description |
86
+ | --- | --- |
87
+ | `candles` | Loaded bars, oldest first: `{ time, open, high, low, close, volume }`. |
88
+ | `lastCandle` | Most recent bar. |
89
+ | `symbolInfo` | Symbol metadata from `symbol_resolved` (with `series_id`). |
90
+ | `timeframe`, `seriesId`, `isReplay`, `isDeleted`, `studies` | State. |
91
+ | `setTimeframe(timeframe)` | Changes the timeframe (keeps studies). Throws `INVALID_STATE` before `setMarket`. |
92
+ | `setTimezone(timezone)` | Changes the chart timezone. |
93
+ | `fetchMore(count)` | Loads older bars (negative: newer bars after a past `to`). |
94
+ | `createStudy(indicator)` | Adds a study. |
95
+ | `delete()` | Deletes the chart and its replay session. Idempotent. |
96
+
97
+ Events: `symbolLoaded` (info), `update` (list of changed keys: `$prices` and/or study IDs), `seriesLoading`, `seriesCompleted` (`{ status, dataCompleted, turnaround }`: `dataCompleted` is `end` or `limit` when no more history can be loaded), `error`, and the replay events.
98
+
99
+ ### Replay
100
+
101
+ ```ts
102
+ chart.setMarket('BINANCE:BTCEUR', { timeframe: 'D', replay: Math.round(Date.now() / 1000) - 86_400 * 7, count: 1 });
103
+ chart.on('replayEnd', () => console.log('Reached the present'));
104
+
105
+ await chart.replayStep(1); // One bar forward; resolves when the server confirms
106
+ await chart.replayStart(1000); // Play automatically, one bar per second
107
+ await chart.replayStop();
108
+ ```
109
+
110
+ Replay events: `replayLoaded` (instance ID), `replayPoint` (cursor time), `replayResolution` (resolutions reported by the server), `replayEnd`. Replay methods reject with `INVALID_STATE` outside replay mode, and pending requests reject when the chart is deleted or the market changes.
111
+
112
+ ## Studies
113
+
114
+ ```ts
115
+ const rsi = await getIndicator('STD;RSI', { credentials });
116
+ rsi.setInput('Length', 21);
117
+ const study = chart.createStudy(rsi);
118
+
119
+ study.on('ready', () => console.log(study.values.at(-1)));
120
+ study.on('update', (changes) => {}); // 'plots', 'graphic', 'report.perf', 'report.trades'...
121
+ study.on('error', (error) => console.error(error.message));
122
+ ```
123
+
124
+ | Member | Description |
125
+ | --- | --- |
126
+ | `values` | Rows `{ $time, <plot>: number }`, oldest first. |
127
+ | `graphics` | Parsed drawings (labels, lines, boxes, tables, polygons, horizontal lines and histograms, `raw`). |
128
+ | `strategyReport` | Strategy `performance`, `trades`, `history`, `currency`, `settings` (decoded from plain or compressed payloads). |
129
+ | `indicator`, `isReady`, `isRemoved` | State. |
130
+ | `setIndicator(indicator)` | Replaces the indicator, e.g. after changing inputs. |
131
+ | `remove()` | Removes the study. Idempotent. |
132
+
133
+ Events: `loading`, `ready` (`study_completed`), `update`, `error`.
134
+
135
+ ### Indicator definitions
136
+
137
+ - `getIndicator(id, { version, credentials })` returns a `PineIndicator` from `STD;...`, `PUB;...` or `USER;...` IDs.
138
+ - `PineIndicator`: `id`, `version`, `description`, `shortDescription`, `inputs`, `plots`, `script`, `type`; `setInput(key, value)`, `setInputs(values)`, `findInput(key)`, `setType('StrategyScript@tv-scripting-101!')`, `clone()`.
139
+ - `new BuiltInIndicator(type, options?)`: built-in studies such as `Volume@tv-basicstudies-241` and the volume profiles (`VbPFixed@...`, `VbPSessions@...`, `VbPVisible@...`). `setOption(key, value, force?)` validates names and types for known types unless `force` is true.
140
+ - `parseIndicatorDefinition(result, id, version)` builds a `PineIndicator` from a cached `pine-facade` response.
141
+
142
+ ## Quotes
143
+
144
+ ```ts
145
+ const quotes = client.createQuoteSession({ fields: ['lp', 'bid', 'ask'] }); // or 'all' / 'price'
146
+ const btc = quotes.subscribe('BINANCE:BTCEUR', { session: 'regular' });
147
+ btc.on('data', (quote, changes) => console.log(quote.lp, changes));
148
+ btc.on('loaded', () => {});
149
+ btc.on('error', (error) => {});
150
+
151
+ btc.close(); // Stops this subscription
152
+ quotes.setFields('all');
153
+ quotes.delete(); // Deletes the session and its subscriptions
154
+ ```
155
+
156
+ Subscriptions to the same symbol and session share one server subscription; the symbol is removed from the server when the last one closes. `btc.data` holds the merged latest values.
157
+
158
+ ## HTTP functions
159
+
160
+ All accept `{ fetch?, signal?, headers? }`; functions that can use an account also accept `credentials`.
161
+
162
+ | Function | Description |
163
+ | --- | --- |
164
+ | `searchMarkets(text, { type, exchange, offset })` | Symbol search (v3 endpoint). |
165
+ | `getTechnicalAnalysis(symbol)` | Technical ratings for 1m to 1M periods, or `null`. |
166
+ | `searchIndicators(text)` | Built-in (cached in memory; `clearIndicatorCache()`) and community indicators. |
167
+ | `getIndicator(id, { version, credentials })` | Pine indicator definition. |
168
+ | `getPrivateIndicators(credentials)` | Your saved scripts. |
169
+ | `loginUser({ username, password, remember, userAgent })` | Signs in and returns the account with its cookies. Accounts with 2FA or captcha challenges are not supported. |
170
+ | `getUser(credentials, { location, maxRedirects })` | Account behind session cookies (follows regional redirects, stops loops). |
171
+ | `getChartToken(layoutId, { credentials, userId })` | Chart-storage token for a layout. |
172
+ | `getDrawings(layoutId, { symbol, chartId, credentials, userId })` | Drawings saved in a layout. |
173
+ | `new PinePermissionManager(pineId, { credentials })` | `getUsers(limit, order)`, `addUser(username, expiration?)`, `modifyExpiration(username, expiration?)`, `removeUser(username)` for invite-only scripts you own. |
174
+
175
+ ## Transport
176
+
177
+ The default transport uses the [`ws`](https://github.com/websockets/ws) package, which works in Node and Bun. To use a proxy or another websocket implementation, pass `transport`:
178
+
179
+ ```ts
180
+ import type { TransportFactory } from '@mathieuc/tradingview';
181
+
182
+ const transport: TransportFactory = ({ url, origin, headers }, handlers) => {
183
+ const socket = createMySocket(url, { origin, headers });
184
+ socket.onopen = () => handlers.onOpen();
185
+ socket.onmessage = (event) => handlers.onMessage(String(event.data));
186
+ socket.onclose = (event) => handlers.onClose(event.code, event.reason);
187
+ socket.onerror = (event) => handlers.onError(new Error(String(event)));
188
+ return {
189
+ get isOpen() { return socket.readyState === 1; },
190
+ send: (data) => socket.send(data),
191
+ close: () => socket.close(),
192
+ };
193
+ };
194
+
195
+ const client = new TradingViewClient({ transport });
196
+ ```
197
+
198
+ The same mechanism lets tests run without network access (see `tests/helpers/fake-server.ts`).
199
+
200
+ ## Protocol helpers
201
+
202
+ `protocol` exposes the wire format for debugging and custom tooling:
203
+
204
+ ```ts
205
+ import { protocol } from '@mathieuc/tradingview';
206
+
207
+ protocol.encodePacket('set_auth_token', ['token']); // '~m~36~m~{"m":"set_auth_token","p":["token"]}'
208
+ protocol.decodeFrames(rawMessage); // [{ type: 'packet' | 'heartbeat' | 'data' | 'invalid', ... }]
209
+ protocol.decodeCompressed(base64); // Strategy report payloads (ZIP, zlib, raw deflate, gzip, JSON)
210
+ protocol.createSessionId('cs'); // 'cs_Ab12Cd34Ef56'
211
+ ```
212
+
213
+ Frames are `~m~<length>~m~<payload>`, where the length counts UTF-16 code units; heartbeats are `~h~<id>` and must be echoed (the client does it).
@@ -0,0 +1,128 @@
1
+ # Migrating from v3 to v4
2
+
3
+ [README](../README.md) · [Data API](data-api.md) · [Low-level API](low-level-api.md) · [Coverage matrix](v4-coverage.md)
4
+
5
+ Version 4 is a complete TypeScript rewrite with **no compatibility layer**. Every v3 capability is still available (see the [coverage matrix](v4-coverage.md)), under a more consistent API. This page shows how to translate v3 code.
6
+
7
+ ## Package
8
+
9
+ | v3 | v4 |
10
+ | --- | --- |
11
+ | CommonJS: `const TradingView = require('@mathieuc/tradingview')` | ESM: `import { ... } from '@mathieuc/tradingview'`. On Node ≥ 20.19 or ≥ 22.12, `require()` of the ESM package also works. |
12
+ | `agent.js` preview (`fetchCandles`, `watchCandles`) | `@mathieuc/tradingview/data` (`getCandles`, `watchCandles`, quotes, indicators...) |
13
+ | Preview `MarketDataProvider` / `TradingViewProvider` | Provider-neutral candle injection remains: `getCandles(query, provider)`, `watchCandles(query, handlers, provider)` |
14
+ | Node ≥ 14, untyped JSDoc | Node ≥ 20 or Bun, bundled TypeScript declarations |
15
+ | `axios`, `jszip`, `ws` dependencies | `ws` only (HTTP uses the built-in `fetch`) |
16
+
17
+ ## Simplest path: the data API
18
+
19
+ ```js
20
+ // v3
21
+ const client = new TradingView.Client();
22
+ const chart = new client.Session.Chart();
23
+ chart.setMarket('BINANCE:BTCEUR', { timeframe: 'D', range: 100 });
24
+ chart.onUpdate(() => { console.log(chart.periods); client.end(); });
25
+
26
+ // v4
27
+ import { getCandles } from '@mathieuc/tradingview/data';
28
+ const candles = await getCandles({ symbol: 'BINANCE:BTCEUR', timeframe: 'D', count: 100 });
29
+ ```
30
+
31
+ ## Client
32
+
33
+ | v3 | v4 |
34
+ | --- | --- |
35
+ | `new Client({ token, signature })` | `new TradingViewClient({ credentials: { session, signature } })` |
36
+ | `server`, `location`, `headers` options | Same names |
37
+ | `DEBUG: true` (sets a global flag) | `debug: true` or `debug: (...args) => {}` (per client) |
38
+ | (not available) | `authToken`, `transport`, `fetch`, `connectTimeoutMs`, `client.ready` |
39
+ | `client.onConnected(cb)` | `client.on('open', cb)` |
40
+ | `client.onDisconnected(cb)` | `client.on('close', cb)` |
41
+ | `client.onLogged(cb)` | `client.on('ready', cb)`; the server greeting is `client.on('hello', cb)` / `client.serverInfo` |
42
+ | `client.onPing(cb)` | `client.on('heartbeat', cb)` |
43
+ | `client.onData(cb)` | `client.on('packet', cb)` |
44
+ | `client.onError(cb)` (`(...messages)`) | `client.on('error', (error) => ...)` (`TradingViewError`) |
45
+ | `client.onEvent(cb)` | `client.onAny((event, ...args) => ...)` |
46
+ | `client.isLogged`, `client.isOpen` | `client.isAuthenticated`, `client.isOpen` (and `client.isClosed`) |
47
+ | `client.send(t, p)` / `client.sendQueue()` | `client.send(method, params)`; the queue is flushed automatically |
48
+ | `client.end()` | `await client.close()` (now resolves once actually closed) |
49
+ | `new client.Session.Chart()` | `client.createChart()` |
50
+ | `new client.Session.Quote(options)` | `client.createQuoteSession(options)` |
51
+
52
+ ## Charts
53
+
54
+ | v3 | v4 |
55
+ | --- | --- |
56
+ | `chart.setMarket(symbol, { range, to, backadjustment, ... })` | `chart.setMarket(symbol, { count, to, backAdjustment, ... })` (`timeframe`, `adjustment`, `session`, `currency`, `type`, `inputs`, `replay` unchanged). The default timeframe is now `D` (v3: `240`). An empty symbol throws instead of loading `BTCEUR`. |
57
+ | `chart.setSeries(timeframe)` | `chart.setTimeframe(timeframe)` (throws `INVALID_STATE` before `setMarket` instead of emitting an error) |
58
+ | `chart.periods` (newest first; `max`/`min`; volume rounded to 2 decimals) | `chart.candles` (**oldest first**; `high`/`low`; raw volume) and `chart.lastCandle` |
59
+ | `chart.infos` | `chart.symbolInfo` |
60
+ | `chart.setTimezone(tz)` | Same (no longer clears loaded bars) |
61
+ | `chart.fetchMore(n)` | Same |
62
+ | `chart.replayStep/Start/Stop()` | Same; they now reject with `INVALID_STATE` outside replay mode instead of never resolving |
63
+ | `chart.onSymbolLoaded(cb)` | `chart.on('symbolLoaded', (info) => ...)` |
64
+ | `chart.onUpdate(cb)` | `chart.on('update', (changes) => ...)` |
65
+ | `chart.onReplayLoaded/Point/Resolution/End(cb)` | `chart.on('replayLoaded' / 'replayPoint' / 'replayResolution' / 'replayEnd', cb)` |
66
+ | `chart.onError(cb)` | `chart.on('error', (error) => ...)` |
67
+ | (not available) | `chart.on('seriesCompleted', ...)`, `chart.on('seriesLoading', ...)` |
68
+ | `new chart.Study(indicator)` | `chart.createStudy(indicator)` |
69
+ | `chart.delete()` | Same (idempotent) |
70
+
71
+ ## Studies
72
+
73
+ | v3 | v4 |
74
+ | --- | --- |
75
+ | `study.periods` (newest first) | `study.values` (**oldest first**, still keyed by `$time` and plot names) |
76
+ | `study.graphic` | `study.graphics`; `tables[i].cells()` is now the `tables[i].cells` array; `raw()` is now the `raw` object |
77
+ | `study.strategyReport` | Same |
78
+ | `study.instance` | `study.indicator` |
79
+ | `study.setIndicator(indicator)`, `study.remove()` | Same |
80
+ | `study.onReady/onUpdate/onError(cb)` | `study.on('ready' / 'update' / 'error', cb)` (also `loading`) |
81
+
82
+ ## Quotes
83
+
84
+ | v3 | v4 |
85
+ | --- | --- |
86
+ | `new client.Session.Quote({ fields: 'all' })` | `client.createQuoteSession({ fields: 'all' })` |
87
+ | `{ customFields: ['lp'] }` | `{ fields: ['lp'] }` |
88
+ | `new quoteSession.Market(symbol, session)` | `quoteSession.subscribe(symbol, { session })` |
89
+ | `market.onLoaded/onData/onError/onEvent(cb)` | `subscription.on('loaded' / 'data' / 'error', cb)` / `onAny(cb)`; `data` also receives the changed fields |
90
+ | `market.close()`, `quoteSession.delete()` | `subscription.close()`, `quoteSession.delete()` |
91
+
92
+ ## Indicators
93
+
94
+ | v3 | v4 |
95
+ | --- | --- |
96
+ | `TradingView.getIndicator(id, version, session, signature)` | `getIndicator(id, { version, credentials })` |
97
+ | `indicator.pineId`, `indicator.pineVersion` | `indicator.id`, `indicator.version` |
98
+ | `indicator.setOption(key, value)` | `indicator.setInput(key, value)` (chainable; also `setInputs({...})`, `clone()`) |
99
+ | `indicator.setType(type)` | Same |
100
+ | `new BuiltInIndicator(type)` + `setOption(key, value, FORCE)` | Same, plus initial options: `new BuiltInIndicator(type, { first_bar_time })` |
101
+
102
+ ## HTTP functions
103
+
104
+ | v3 | v4 |
105
+ | --- | --- |
106
+ | `searchMarketV3(text, filter, offset)` | `searchMarkets(text, { type, offset, exchange })` |
107
+ | `searchMarket(text, filter)` (deprecated v1 endpoint) | `searchMarkets(text, { type })` |
108
+ | `result.getTA()` | `getTechnicalAnalysis(result.id)` |
109
+ | `getTA(symbol)` (returned `false` without data) | `getTechnicalAnalysis(symbol)` (returns `null`) |
110
+ | `searchIndicator(text)` + `result.get()` | `searchIndicators(text)` + `getIndicator(result.id, { version: result.version })` |
111
+ | `getPrivateIndicators(session, signature)` + `result.get()` | `getPrivateIndicators({ session, signature })` + `getIndicator(result.id, { version, credentials })` |
112
+ | `loginUser(username, password, remember, UA)` | `loginUser({ username, password, remember, userAgent })` |
113
+ | `getUser(session, signature, location)` | `getUser({ session, signature }, { location })` (`id` is now a number) |
114
+ | `getChartToken(layout, { id, session, signature })` | `getChartToken(layout, { userId, credentials: { session, signature } })` |
115
+ | `getDrawings(layout, symbol, { id, session, signature }, chartID)` | `getDrawings(layout, { symbol, chartId, userId, credentials })` |
116
+ | `new PinePermManager(session, signature, pineId)` | `new PinePermissionManager(pineId, { credentials: { session, signature } })` (same methods) |
117
+
118
+ ## Errors
119
+
120
+ v3 passed lists of strings to `onError` callbacks, or printed them. v4 uses `TradingViewError` everywhere, with a `code` (`SYMBOL_ERROR`, `SERIES_ERROR`, `CRITICAL_ERROR`, `STUDY_ERROR`, `AUTH_ERROR`, `TIMEOUT`...), a readable `message` and the raw server payload in `details`. Study errors are formatted with their context, e.g. `Study error: Invalid value of the 'factor' argument (-1)...`.
121
+
122
+ ## High-level data preview (formerly agent-api)
123
+
124
+ | Preview (`agent.js`) | v4 |
125
+ | --- | --- |
126
+ | `fetchCandles({ symbol, timeframe, limit, timeoutMs, signal })` | `getCandles({ symbol, timeframe, count, timeoutMs, signal })` |
127
+ | `watchCandles(query, { onData, onError })` → `{ latest, stop() }` | `watchCandles(query, { onData, onError })` → `{ latest, stop(), closed, isActive, symbolInfo }` |
128
+ | `TradingViewProvider` / `MarketDataProvider` injection | Provider injection remains: `getCandles(query, provider)` or `watchCandles(query, handlers, provider)`; `client` and `clientOptions.transport` also work for TradingView |