@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,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,28 @@
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';
@@ -0,0 +1,15 @@
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';
@@ -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
+ }
@@ -0,0 +1,71 @@
1
+ import { TradingViewClient, type ClientOptions } from '../client/client.js';
2
+ import { TradingViewError } from '../errors.js';
3
+ import type { Credentials } from '../http/request.js';
4
+ /** How high-level functions connect to TradingView. */
5
+ export interface ConnectionOptions {
6
+ /**
7
+ * Reuse an existing connection. It is left open afterwards; only the
8
+ * sessions created by the call are removed. Without it, each call opens and
9
+ * closes its own connection.
10
+ */
11
+ client?: TradingViewClient;
12
+ /** Account cookies, for data your account can access. Ignored with `client`. */
13
+ credentials?: Credentials;
14
+ /** Other options for the connection opened by the call. Ignored with `client`. */
15
+ clientOptions?: Omit<ClientOptions, 'credentials'>;
16
+ }
17
+ export interface OperationOptions extends ConnectionOptions {
18
+ /**
19
+ * Maximum wait in milliseconds. One-shot functions: for the whole call.
20
+ * Watchers: until the first data. Default: 15 000.
21
+ */
22
+ timeoutMs?: number;
23
+ /** Cancels the call (and stops a watcher). */
24
+ signal?: AbortSignal;
25
+ }
26
+ export declare const DEFAULT_TIMEOUT_MS = 15000;
27
+ export declare function validateTimeout(timeoutMs: number | undefined): number;
28
+ export declare function validateSymbol(symbol: unknown): string;
29
+ export declare function throwIfAborted(signal?: AbortSignal): void;
30
+ export interface OperationContext<T> {
31
+ client: TradingViewClient;
32
+ resolve(value: T): void;
33
+ reject(error: unknown): void;
34
+ }
35
+ type Cleanup = (() => void) | void;
36
+ /**
37
+ * Runs a one-shot operation with timeout, abort and guaranteed cleanup:
38
+ * sessions are removed and an owned connection is closed before settling.
39
+ */
40
+ export declare function runOperation<T>(options: OperationOptions, description: string, start: (context: OperationContext<T>) => Cleanup): Promise<T>;
41
+ /** A running watcher. */
42
+ export interface Watcher {
43
+ /** Stops the watcher and releases its sessions/connection. Idempotent. */
44
+ stop(): Promise<void>;
45
+ /** Resolves when the watcher has stopped (after `stop()`, abort or a fatal error). */
46
+ readonly closed: Promise<void>;
47
+ /** True until the watcher stops. */
48
+ readonly isActive: boolean;
49
+ }
50
+ export interface WatchHandlers {
51
+ /** Errors after start-up. Fatal errors also stop the watcher. */
52
+ onError?(error: TradingViewError): void;
53
+ }
54
+ export interface WatchContext {
55
+ client: TradingViewClient;
56
+ /** Marks the watcher as started (resolves the start promise). */
57
+ ready(): void;
58
+ /** Before start-up: rejects. After: reports to `onError` and stops. */
59
+ fail(error: unknown): void;
60
+ /** Reports a non-fatal error to `onError`. */
61
+ warn(error: unknown): void;
62
+ isActive(): boolean;
63
+ }
64
+ /**
65
+ * Starts a watcher. The returned promise resolves once `ready()` is called
66
+ * and rejects on timeout, abort or failure before that.
67
+ */
68
+ export declare function startWatcher<W extends Watcher>(options: OperationOptions, description: string, handlers: WatchHandlers, start: (context: WatchContext) => Cleanup, build: (base: Watcher) => W): Promise<W>;
69
+ /** Converts a Date or Unix time (seconds) into Unix seconds. */
70
+ export declare function toUnixSeconds(value: number | Date | undefined, name: string): number | undefined;
71
+ export {};