@mathieuc/tradingview 3.5.2 → 4.0.0-beta.1

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 +113 -0
  9. package/dist/chart/strategy.js +78 -0
  10. package/dist/chart/study.d.ts +60 -0
  11. package/dist/chart/study.js +179 -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 +30 -0
  27. package/dist/data/index.js +16 -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 +285 -0
  82. package/docs/low-level-api.md +213 -0
  83. package/docs/migration-v4.md +128 -0
  84. package/docs/release-4.0.0-beta.1.md +39 -0
  85. package/docs/v4-coverage.md +213 -0
  86. package/package.json +53 -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,27 @@
1
+ /** Callbacks a transport must call. */
2
+ export interface TransportHandlers {
3
+ onOpen(): void;
4
+ onMessage(data: string): void;
5
+ onClose(code?: number, reason?: string): void;
6
+ onError(error: Error): void;
7
+ }
8
+ /** Minimal websocket abstraction used by `TradingViewClient`. */
9
+ export interface Transport {
10
+ /** True when the connection is open and can send. */
11
+ readonly isOpen: boolean;
12
+ send(data: string): void;
13
+ /** Starts a graceful close. `onClose` must be called once closed. */
14
+ close(): void;
15
+ }
16
+ export interface TransportRequest {
17
+ url: string;
18
+ origin: string;
19
+ headers: Record<string, string>;
20
+ }
21
+ /**
22
+ * Creates a transport. The default uses the `ws` package (Node and Bun).
23
+ * Provide your own to use a proxy, another websocket library or a test double.
24
+ */
25
+ export type TransportFactory = (request: TransportRequest, handlers: TransportHandlers) => Transport;
26
+ /** Default transport based on the `ws` package. */
27
+ export declare const wsTransport: TransportFactory;
@@ -0,0 +1,38 @@
1
+ import WebSocket from 'ws';
2
+ function toText(data) {
3
+ if (typeof data === 'string')
4
+ return data;
5
+ if (Array.isArray(data))
6
+ return Buffer.concat(data).toString('utf8');
7
+ if (data instanceof ArrayBuffer)
8
+ return Buffer.from(data).toString('utf8');
9
+ return data.toString('utf8');
10
+ }
11
+ /** Default transport based on the `ws` package. */
12
+ export const wsTransport = (request, handlers) => {
13
+ // Bun's `ws` implementation ignores the `origin` option: also send it as a header.
14
+ const socket = new WebSocket(request.url, {
15
+ origin: request.origin,
16
+ headers: { Origin: request.origin, ...request.headers },
17
+ });
18
+ socket.on('open', () => handlers.onOpen());
19
+ socket.on('message', (data) => handlers.onMessage(toText(data)));
20
+ socket.on('close', (code, reason) => handlers.onClose(code, reason.toString()));
21
+ socket.on('error', (error) => handlers.onError(error));
22
+ return {
23
+ get isOpen() {
24
+ return socket.readyState === WebSocket.OPEN;
25
+ },
26
+ send(data) {
27
+ socket.send(data);
28
+ },
29
+ close() {
30
+ if (socket.readyState === WebSocket.CLOSED)
31
+ return;
32
+ if (socket.readyState === WebSocket.CONNECTING)
33
+ socket.terminate();
34
+ else
35
+ socket.close();
36
+ },
37
+ };
38
+ };
@@ -0,0 +1,40 @@
1
+ import type { Candle, SymbolInfo } from '../chart/types.js';
2
+ import type { CandleSourceWorker, MarketDataProvider } from './provider.js';
3
+ import { type ChartQuery, type RangeQuery } from './history.js';
4
+ import { type OperationOptions, type WatchHandlers, type Watcher } from './operation.js';
5
+ export interface CandleQuery extends ChartQuery, RangeQuery, OperationOptions {
6
+ }
7
+ /**
8
+ * Fetches candles once, oldest first, then releases every resource.
9
+ *
10
+ * - `{ symbol, timeframe, count }`: the `count` most recent bars (deep history
11
+ * is loaded automatically when `count` exceeds one server batch).
12
+ * - `{ symbol, timeframe, from, to }`: every bar in the range.
13
+ *
14
+ * @example
15
+ * const candles = await getCandles({ symbol: 'BINANCE:BTCUSDT', timeframe: '60', count: 500 });
16
+ */
17
+ export declare function getCandles(query: CandleQuery | string, provider?: MarketDataProvider): Promise<Candle[]>;
18
+ export type WatchCandlesQuery = ChartQuery & OperationOptions;
19
+ export interface CandleHandlers extends WatchHandlers {
20
+ /** Receives a fresh immutable snapshot (oldest first) on every update. */
21
+ onData(candles: readonly Candle[]): void;
22
+ }
23
+ export interface CandleWatcher extends Watcher {
24
+ /** Latest snapshot, oldest first (at most `count` bars). */
25
+ readonly latest: readonly Candle[];
26
+ /** Symbol metadata. */
27
+ readonly symbolInfo: SymbolInfo | undefined;
28
+ }
29
+ /**
30
+ * Streams candles in real time. Resolves once the initial history is loaded
31
+ * (and already passed to `onData`); call `stop()` when finished.
32
+ *
33
+ * @example
34
+ * const watcher = await watchCandles({ symbol: 'BINANCE:BTCUSDT', timeframe: '1' }, {
35
+ * onData: (candles) => console.log(candles.at(-1)?.close),
36
+ * });
37
+ * await watcher.stop();
38
+ */
39
+ export declare function watchCandles(query: WatchCandlesQuery, handlers: CandleHandlers): Promise<CandleWatcher>;
40
+ export declare function watchCandles(query: WatchCandlesQuery, handlers: CandleHandlers, provider: MarketDataProvider): Promise<CandleSourceWorker>;
@@ -0,0 +1,101 @@
1
+ import { TradingViewError } from '../errors.js';
2
+ import { loadHistory, noDataError, openMarket, planHistory, selectCandles, } from './history.js';
3
+ import { runOperation, startWatcher, } from './operation.js';
4
+ /**
5
+ * Fetches candles once, oldest first, then releases every resource.
6
+ *
7
+ * - `{ symbol, timeframe, count }`: the `count` most recent bars (deep history
8
+ * is loaded automatically when `count` exceeds one server batch).
9
+ * - `{ symbol, timeframe, from, to }`: every bar in the range.
10
+ *
11
+ * @example
12
+ * const candles = await getCandles({ symbol: 'BINANCE:BTCUSDT', timeframe: '60', count: 500 });
13
+ */
14
+ export function getCandles(query, provider) {
15
+ const options = typeof query === 'string' ? { symbol: query } : query;
16
+ let plan;
17
+ try {
18
+ plan = planHistory(options);
19
+ }
20
+ catch (error) {
21
+ return Promise.reject(error);
22
+ }
23
+ if (provider) {
24
+ return (async () => {
25
+ let snapshot = [];
26
+ const worker = await provider.watchCandles(options, {
27
+ onData: (candles) => { snapshot = candles; },
28
+ });
29
+ try {
30
+ return [...(snapshot.length ? snapshot : worker.latest)];
31
+ }
32
+ finally {
33
+ await worker.stop();
34
+ }
35
+ })();
36
+ }
37
+ return runOperation(options, `getCandles(${plan.symbol})`, ({ client, resolve, reject }) => {
38
+ const chart = client.createChart();
39
+ chart.on('error', reject);
40
+ loadHistory(chart, plan, true, (info) => {
41
+ const candles = selectCandles(chart.candles, plan);
42
+ if (candles.length === 0)
43
+ reject(noDataError(plan.symbol, info));
44
+ else
45
+ resolve(candles);
46
+ });
47
+ openMarket(chart, plan);
48
+ return () => chart.delete();
49
+ });
50
+ }
51
+ export function watchCandles(query, handlers, provider) {
52
+ let plan;
53
+ try {
54
+ if (typeof handlers?.onData !== 'function')
55
+ throw new TradingViewError('INVALID_ARGUMENT', 'onData must be a function');
56
+ plan = planHistory({ ...query, from: undefined, to: undefined });
57
+ }
58
+ catch (error) {
59
+ return Promise.reject(error);
60
+ }
61
+ if (provider)
62
+ return provider.watchCandles(query, handlers);
63
+ let latest = Object.freeze([]);
64
+ let symbolInfo;
65
+ return startWatcher(query, `watchCandles(${plan.symbol})`, handlers, (ctx) => {
66
+ const chart = ctx.client.createChart();
67
+ let started = false;
68
+ const publish = () => {
69
+ latest = Object.freeze(selectCandles(chart.candles, plan).map((candle) => Object.freeze({ ...candle })));
70
+ try {
71
+ handlers.onData(latest);
72
+ }
73
+ catch (error) {
74
+ ctx.warn(new TradingViewError('CALLBACK_ERROR', 'onData callback threw', { cause: error }));
75
+ }
76
+ };
77
+ chart.on('error', (error) => ctx.fail(error));
78
+ chart.on('symbolLoaded', (info) => { symbolInfo = info; });
79
+ chart.on('update', (changes) => {
80
+ if (started && ctx.isActive() && changes.includes('$prices'))
81
+ publish();
82
+ });
83
+ loadHistory(chart, plan, false, (info) => {
84
+ if (chart.candles.length === 0) {
85
+ ctx.fail(noDataError(plan.symbol, info));
86
+ return;
87
+ }
88
+ started = true;
89
+ publish();
90
+ ctx.ready();
91
+ });
92
+ openMarket(chart, plan);
93
+ return () => chart.delete();
94
+ }, (base) => ({
95
+ stop: base.stop,
96
+ closed: base.closed,
97
+ get isActive() { return base.isActive; },
98
+ get latest() { return latest; },
99
+ get symbolInfo() { return symbolInfo; },
100
+ }));
101
+ }
@@ -0,0 +1,61 @@
1
+ import type { ChartSession, MarketOptions, SeriesCompletedInfo } from '../chart/chart-session.js';
2
+ import type { Adjustment, Candle, ChartType, ChartTypeInputs, Timeframe, Timezone, TradingSession } from '../chart/types.js';
3
+ import { TradingViewError } from '../errors.js';
4
+ /** Chart settings shared by candle and indicator queries. */
5
+ export interface ChartQuery {
6
+ /** Exchange-qualified symbol, e.g. `BINANCE:BTCUSDT`. */
7
+ symbol: string;
8
+ /** Bar resolution (`1`, `15`, `60`, `240`, `D`, `W`, `M`, `1S`...). Default: `D`. */
9
+ timeframe?: Timeframe;
10
+ /** Number of most recent bars. Default: 100. */
11
+ count?: number;
12
+ /** Custom bar type: `HeikinAshi`, `Renko`, `LineBreak`, `Kagi`, `PointAndFigure` or `Range`. */
13
+ chartType?: ChartType;
14
+ /** Inputs for `chartType`. */
15
+ chartInputs?: ChartTypeInputs;
16
+ /** Convert prices to this currency, e.g. `EUR`. */
17
+ currency?: string;
18
+ /** `regular` or `extended` trading session. */
19
+ session?: TradingSession;
20
+ /** Price adjustment. Default: `splits`. */
21
+ adjustment?: Adjustment;
22
+ /** Back-adjust continuous futures contracts. */
23
+ backAdjustment?: boolean;
24
+ /** Chart timezone (affects how daily and longer bars are aligned). */
25
+ timezone?: Timezone;
26
+ }
27
+ /** Historical range options. */
28
+ export interface RangeQuery {
29
+ /**
30
+ * Oldest bar time (Date or Unix seconds). When set, history is loaded back
31
+ * to this time (deep history) and `count` is ignored.
32
+ */
33
+ from?: number | Date;
34
+ /** Newest bar time (Date or Unix seconds). Default: now. */
35
+ to?: number | Date;
36
+ /** Safety cap on bars loaded for a `from` range. Default: 20 000. */
37
+ maxCount?: number;
38
+ }
39
+ export interface HistoryPlan {
40
+ symbol: string;
41
+ market: MarketOptions;
42
+ timezone?: Timezone;
43
+ count: number;
44
+ from?: number;
45
+ to?: number;
46
+ maxCount: number;
47
+ }
48
+ /** Validates a query and computes how many bars to request first. */
49
+ export declare function planHistory(query: ChartQuery & RangeQuery): HistoryPlan;
50
+ /** Selects the requested bars from everything loaded on a chart. */
51
+ export declare function selectCandles(candles: Candle[], plan: Pick<HistoryPlan, 'from' | 'to' | 'count'>): Candle[];
52
+ /** Opens the market on a chart according to a plan. */
53
+ export declare function openMarket(chart: ChartSession, plan: HistoryPlan): void;
54
+ /**
55
+ * Loads history: after each `series_completed`, requests more bars until the
56
+ * plan is satisfied or the server has no more data. `onDone` receives the
57
+ * last completion info.
58
+ */
59
+ export declare function loadHistory(chart: ChartSession, plan: HistoryPlan, deep: boolean, onDone: (info: SeriesCompletedInfo) => void): () => void;
60
+ /** Error for an empty result, with the server's reason when known. */
61
+ export declare function noDataError(symbol: string, info?: SeriesCompletedInfo): TradingViewError;
@@ -0,0 +1,101 @@
1
+ import { timeframeSeconds } from '../chart/timeframes.js';
2
+ import { TradingViewError } from '../errors.js';
3
+ import { toUnixSeconds, validateSymbol } from './operation.js';
4
+ const MAX_CHUNK = 5_000;
5
+ function positiveInteger(value, name, fallback) {
6
+ const result = value ?? fallback;
7
+ if (!Number.isInteger(result) || result < 1) {
8
+ throw new TradingViewError('INVALID_ARGUMENT', `${name} must be a positive integer`);
9
+ }
10
+ return result;
11
+ }
12
+ /** Validates a query and computes how many bars to request first. */
13
+ export function planHistory(query) {
14
+ const symbol = validateSymbol(query.symbol);
15
+ const count = positiveInteger(query.count, 'count', 100);
16
+ const maxCount = positiveInteger(query.maxCount, 'maxCount', 20_000);
17
+ const from = toUnixSeconds(query.from, 'from');
18
+ const to = toUnixSeconds(query.to, 'to');
19
+ if (from !== undefined && to !== undefined && from > to) {
20
+ throw new TradingViewError('INVALID_ARGUMENT', 'from must be before to');
21
+ }
22
+ const timeframe = query.timeframe ?? 'D';
23
+ let initial = Math.min(count, MAX_CHUNK);
24
+ if (from !== undefined) {
25
+ const step = timeframeSeconds(timeframe);
26
+ const end = to ?? Math.floor(Date.now() / 1000);
27
+ initial = step ? Math.ceil((end - from) / step) + 2 : 1_000;
28
+ initial = Math.max(1, Math.min(initial, maxCount, MAX_CHUNK));
29
+ }
30
+ return {
31
+ symbol,
32
+ count,
33
+ from,
34
+ to,
35
+ maxCount,
36
+ timezone: query.timezone,
37
+ market: {
38
+ timeframe,
39
+ count: initial,
40
+ to,
41
+ type: query.chartType,
42
+ inputs: query.chartInputs,
43
+ currency: query.currency,
44
+ session: query.session,
45
+ adjustment: query.adjustment,
46
+ backAdjustment: query.backAdjustment,
47
+ },
48
+ };
49
+ }
50
+ /** Selects the requested bars from everything loaded on a chart. */
51
+ export function selectCandles(candles, plan) {
52
+ let result = candles;
53
+ if (plan.to !== undefined)
54
+ result = result.filter((candle) => candle.time <= plan.to);
55
+ if (plan.from !== undefined)
56
+ return result.filter((candle) => candle.time >= plan.from);
57
+ return result.slice(-plan.count);
58
+ }
59
+ /** Opens the market on a chart according to a plan. */
60
+ export function openMarket(chart, plan) {
61
+ if (plan.timezone)
62
+ chart.setTimezone(plan.timezone);
63
+ chart.setMarket(plan.symbol, plan.market);
64
+ }
65
+ /**
66
+ * Loads history: after each `series_completed`, requests more bars until the
67
+ * plan is satisfied or the server has no more data. `onDone` receives the
68
+ * last completion info.
69
+ */
70
+ export function loadHistory(chart, plan, deep, onDone) {
71
+ let previousSize = -1;
72
+ let done = false;
73
+ return chart.on('seriesCompleted', (info) => {
74
+ if (done)
75
+ return;
76
+ const candles = chart.candles;
77
+ const inRange = plan.to === undefined ? candles : candles.filter((c) => c.time <= plan.to);
78
+ const target = plan.from !== undefined ? plan.maxCount : plan.count;
79
+ const satisfied = plan.from !== undefined
80
+ ? (inRange[0] !== undefined && inRange[0].time <= plan.from)
81
+ : inRange.length >= plan.count;
82
+ const exhausted = info.dataCompleted !== undefined || candles.length <= previousSize;
83
+ if (!deep || satisfied || exhausted || candles.length >= target) {
84
+ done = true;
85
+ onDone(info);
86
+ return;
87
+ }
88
+ previousSize = candles.length;
89
+ const missing = plan.from !== undefined ? MAX_CHUNK : plan.count - inRange.length;
90
+ chart.fetchMore(Math.max(1, Math.min(missing, MAX_CHUNK, target - candles.length)));
91
+ });
92
+ }
93
+ /** Error for an empty result, with the server's reason when known. */
94
+ export function noDataError(symbol, info) {
95
+ const reason = info?.dataCompleted === 'limit'
96
+ ? ' (the server reported an access limit: this range or timeframe may need an account with access)'
97
+ : '';
98
+ return new TradingViewError('NO_DATA', `No candles available for ${symbol} in the requested range${reason}`, {
99
+ details: info?.raw,
100
+ });
101
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * High-level data API: one-shot functions and watchers that manage
3
+ * connections, sessions, timeouts and cleanup for you.
4
+ *
5
+ * @module @mathieuc/tradingview/data
6
+ */
7
+ export { getCandles, watchCandles } from './candles.js';
8
+ export type { CandleHandlers, CandleQuery, CandleWatcher, WatchCandlesQuery, } from './candles.js';
9
+ export { TradingViewProvider } from './provider.js';
10
+ export type { CandleSourceWorker, MarketDataProvider } from './provider.js';
11
+ export { getQuote, getQuotes, watchQuotes } from './quotes.js';
12
+ export type { QuoteHandlers, QuoteOptions, QuoteQuery, QuotesQuery, QuoteWatcher, } from './quotes.js';
13
+ export { getSymbolInfo } from './symbols.js';
14
+ export type { SymbolInfoQuery } from './symbols.js';
15
+ export { getIndicatorData, watchIndicator } from './indicators.js';
16
+ export type { IndicatorData, IndicatorHandlers, IndicatorQuery, IndicatorWatcher, } from './indicators.js';
17
+ export type { ChartQuery, RangeQuery } from './history.js';
18
+ export { DEFAULT_TIMEOUT_MS } from './operation.js';
19
+ export type { ConnectionOptions, OperationOptions, WatchHandlers, Watcher, } from './operation.js';
20
+ export { getTechnicalAnalysis, searchIndicators, searchMarkets, } from '../http/index.js';
21
+ export type { Credentials, IndicatorSearchResult, MarketSearchResult, SearchMarketsOptions, TechnicalAnalysis, TechnicalRating, } from '../http/index.js';
22
+ export { TradingViewError } from '../errors.js';
23
+ export type { TradingViewErrorCode } from '../errors.js';
24
+ export type { Candle, ChartType, ChartTypeInputs, SymbolInfo, Timeframe, Timezone, } from '../chart/types.js';
25
+ export type { QuoteData, QuoteField } from '../quote/fields.js';
26
+ export type { StudyValue } from '../chart/study.js';
27
+ export type { GraphicsData } from '../chart/graphics.js';
28
+ export type { StrategyReport } from '../chart/strategy.js';
29
+ export { summarizeStrategyReport } from '../chart/strategy.js';
30
+ export type { StrategySummary } from '../chart/strategy.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * High-level data API: one-shot functions and watchers that manage
3
+ * connections, sessions, timeouts and cleanup for you.
4
+ *
5
+ * @module @mathieuc/tradingview/data
6
+ */
7
+ export { getCandles, watchCandles } from './candles.js';
8
+ export { TradingViewProvider } from './provider.js';
9
+ export { getQuote, getQuotes, watchQuotes } from './quotes.js';
10
+ export { getSymbolInfo } from './symbols.js';
11
+ export { getIndicatorData, watchIndicator } from './indicators.js';
12
+ export { DEFAULT_TIMEOUT_MS } from './operation.js';
13
+ // Plain HTTP lookups are already one-shot; they are part of the data API too.
14
+ export { getTechnicalAnalysis, searchIndicators, searchMarkets, } from '../http/index.js';
15
+ export { TradingViewError } from '../errors.js';
16
+ export { summarizeStrategyReport } from '../chart/strategy.js';
@@ -0,0 +1,54 @@
1
+ import type { GraphicsData } from '../chart/graphics.js';
2
+ import type { StrategyReport } from '../chart/strategy.js';
3
+ import type { StudyValue } from '../chart/study.js';
4
+ import type { Candle } from '../chart/types.js';
5
+ import type { Indicator } from '../indicators/index.js';
6
+ import { type ChartQuery } from './history.js';
7
+ import { type OperationOptions, type WatchHandlers, type Watcher } from './operation.js';
8
+ export interface IndicatorQuery extends ChartQuery, OperationOptions {
9
+ /**
10
+ * Indicator to run: a script ID (`STD;RSI`, `PUB;xxxx`, `USER;xxxx`), a
11
+ * built-in study type (`Volume@tv-basicstudies-241`), or an instance.
12
+ */
13
+ indicator: string | Indicator;
14
+ /** Script version when `indicator` is a script ID. Default: `last`. */
15
+ version?: string;
16
+ /** Input values (Pine) or options (built-in) to apply. */
17
+ inputs?: Record<string, unknown>;
18
+ /** Newest bar time (Date or Unix seconds). Default: now. */
19
+ to?: number | Date;
20
+ }
21
+ export interface IndicatorData {
22
+ indicator: Indicator;
23
+ /** Chart bars, oldest first. */
24
+ candles: Candle[];
25
+ /**
26
+ * Plot values, oldest first. The server may compute a few rows before the
27
+ * first returned candle.
28
+ */
29
+ values: StudyValue[];
30
+ /** Drawings (labels, lines, boxes, tables...). */
31
+ graphics: GraphicsData;
32
+ /** Strategy report (strategies only; otherwise empty). */
33
+ strategyReport: StrategyReport;
34
+ }
35
+ /** Resolves an indicator ID or instance and applies inputs. */
36
+ export declare function resolveIndicator(query: IndicatorQuery): Promise<Indicator>;
37
+ /**
38
+ * Runs an indicator or strategy once and returns its values, drawings and
39
+ * strategy report.
40
+ *
41
+ * @example
42
+ * const { values } = await getIndicatorData({ symbol: 'BINANCE:BTCUSDT', indicator: 'STD;RSI' });
43
+ */
44
+ export declare function getIndicatorData(query: IndicatorQuery): Promise<IndicatorData>;
45
+ export interface IndicatorHandlers extends WatchHandlers {
46
+ /** Receives a fresh result on every study update. */
47
+ onData(data: IndicatorData): void;
48
+ }
49
+ export interface IndicatorWatcher extends Watcher {
50
+ /** Latest result. */
51
+ readonly latest: IndicatorData | undefined;
52
+ }
53
+ /** Streams an indicator in real time. Call `stop()` when finished. */
54
+ export declare function watchIndicator(query: IndicatorQuery, handlers: IndicatorHandlers): Promise<IndicatorWatcher>;
@@ -0,0 +1,139 @@
1
+ import { TradingViewError, toTradingViewError } from '../errors.js';
2
+ import { getIndicator } from '../http/indicators.js';
3
+ import { BuiltInIndicator } from '../indicators/builtin-indicator.js';
4
+ import { PineIndicator } from '../indicators/pine-indicator.js';
5
+ import { loadHistory, noDataError, openMarket, planHistory, selectCandles, } from './history.js';
6
+ import { runOperation, startWatcher, throwIfAborted, } from './operation.js';
7
+ /** Resolves an indicator ID or instance and applies inputs. */
8
+ export async function resolveIndicator(query) {
9
+ const { indicator, inputs } = query;
10
+ let resolved;
11
+ if (indicator instanceof PineIndicator)
12
+ resolved = indicator.clone();
13
+ else if (indicator instanceof BuiltInIndicator)
14
+ resolved = new BuiltInIndicator(indicator.type, indicator.options);
15
+ else if (typeof indicator === 'string' && indicator.trim()) {
16
+ resolved = indicator.includes('@')
17
+ ? new BuiltInIndicator(indicator)
18
+ : await getIndicator(indicator, {
19
+ version: query.version,
20
+ credentials: query.credentials,
21
+ signal: query.signal,
22
+ fetch: query.clientOptions?.fetch,
23
+ });
24
+ }
25
+ else {
26
+ throw new TradingViewError('INVALID_ARGUMENT', 'indicator must be a script ID, a built-in type or an indicator instance');
27
+ }
28
+ for (const [key, value] of Object.entries(inputs ?? {})) {
29
+ if (resolved instanceof PineIndicator)
30
+ resolved.setInput(key, value);
31
+ else
32
+ resolved.setOption(key, value);
33
+ }
34
+ return resolved;
35
+ }
36
+ function snapshot(chart, study, plan) {
37
+ return {
38
+ indicator: study.indicator,
39
+ candles: selectCandles(chart.candles, plan),
40
+ values: study.values,
41
+ graphics: study.graphics,
42
+ strategyReport: structuredClone(study.strategyReport),
43
+ };
44
+ }
45
+ /**
46
+ * Runs history loading and the study together; calls `onReady` once both
47
+ * the bars and the study computation are complete.
48
+ */
49
+ function trackReadiness(chart, study, plan, deep, onReady, onEmpty) {
50
+ let historyDone = false;
51
+ let studyLoading = true;
52
+ let fired = false;
53
+ const check = () => {
54
+ if (fired || !historyDone || studyLoading)
55
+ return;
56
+ fired = true;
57
+ onReady();
58
+ };
59
+ study.on('loading', () => { studyLoading = true; });
60
+ study.on('ready', () => { studyLoading = false; check(); });
61
+ loadHistory(chart, plan, deep, (info) => {
62
+ if (chart.candles.length === 0) {
63
+ fired = true;
64
+ onEmpty(noDataError(plan.symbol, info));
65
+ return;
66
+ }
67
+ historyDone = true;
68
+ check();
69
+ });
70
+ }
71
+ function prepare(query) {
72
+ return planHistory({ ...query, from: undefined });
73
+ }
74
+ /**
75
+ * Runs an indicator or strategy once and returns its values, drawings and
76
+ * strategy report.
77
+ *
78
+ * @example
79
+ * const { values } = await getIndicatorData({ symbol: 'BINANCE:BTCUSDT', indicator: 'STD;RSI' });
80
+ */
81
+ export async function getIndicatorData(query) {
82
+ const plan = prepare(query);
83
+ throwIfAborted(query.signal);
84
+ const indicator = await resolveIndicator(query).catch((error) => {
85
+ throw toTradingViewError(error, 'NOT_FOUND');
86
+ });
87
+ return runOperation(query, `getIndicatorData(${plan.symbol})`, ({ client, resolve, reject }) => {
88
+ const chart = client.createChart();
89
+ chart.on('error', reject);
90
+ openMarket(chart, plan);
91
+ const study = chart.createStudy(indicator);
92
+ study.on('error', reject);
93
+ trackReadiness(chart, study, plan, true, () => resolve(snapshot(chart, study, plan)), reject);
94
+ return () => chart.delete();
95
+ });
96
+ }
97
+ /** Streams an indicator in real time. Call `stop()` when finished. */
98
+ export async function watchIndicator(query, handlers) {
99
+ if (typeof handlers?.onData !== 'function')
100
+ throw new TradingViewError('INVALID_ARGUMENT', 'onData must be a function');
101
+ const plan = prepare({ ...query, to: undefined });
102
+ throwIfAborted(query.signal);
103
+ const indicator = await resolveIndicator(query).catch((error) => {
104
+ throw toTradingViewError(error, 'NOT_FOUND');
105
+ });
106
+ let latest;
107
+ return startWatcher(query, `watchIndicator(${plan.symbol})`, handlers, (ctx) => {
108
+ const chart = ctx.client.createChart();
109
+ let started = false;
110
+ const publish = () => {
111
+ latest = snapshot(chart, study, plan);
112
+ try {
113
+ handlers.onData(latest);
114
+ }
115
+ catch (error) {
116
+ ctx.warn(new TradingViewError('CALLBACK_ERROR', 'onData callback threw', { cause: error }));
117
+ }
118
+ };
119
+ chart.on('error', (error) => ctx.fail(error));
120
+ openMarket(chart, plan);
121
+ const study = chart.createStudy(indicator);
122
+ study.on('error', (error) => ctx.fail(error));
123
+ study.on('update', () => {
124
+ if (started && ctx.isActive())
125
+ publish();
126
+ });
127
+ trackReadiness(chart, study, plan, false, () => {
128
+ started = true;
129
+ publish();
130
+ ctx.ready();
131
+ }, (error) => ctx.fail(error));
132
+ return () => chart.delete();
133
+ }, (base) => ({
134
+ stop: base.stop,
135
+ closed: base.closed,
136
+ get isActive() { return base.isActive; },
137
+ get latest() { return latest; },
138
+ }));
139
+ }