@mathieuc/tradingview 2.0.9 → 3.0.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.
@@ -0,0 +1,308 @@
1
+ const TRANSLATOR = {
2
+ /** @typedef {'right' | 'left' | 'both' | 'none'} ExtendValue */
3
+ extend: {
4
+ r: 'right',
5
+ l: 'left',
6
+ b: 'both',
7
+ n: 'none',
8
+ },
9
+
10
+ /** @typedef {'price' | 'abovebar' | 'belowbar'} yLocValue */
11
+ yLoc: {
12
+ pr: 'price',
13
+ ab: 'abovebar',
14
+ bl: 'belowbar',
15
+ },
16
+
17
+ /**
18
+ * @typedef {'none' | 'xcross' | 'cross' | 'triangleup'
19
+ * | 'triangledown' | 'flag' | 'circle' | 'arrowup'
20
+ * | 'arrowdown' | 'label_up' | 'label_down' | 'label_left'
21
+ * | 'label_right' | 'label_lower_left' | 'label_lower_right'
22
+ * | 'label_upper_left' | 'label_upper_right' | 'label_center'
23
+ * | 'square' | 'diamond'
24
+ * } LabelStyleValue
25
+ * */
26
+ labelStyle: {
27
+ n: 'none',
28
+ xcr: 'xcross',
29
+ cr: 'cross',
30
+ tup: 'triangleup',
31
+ tdn: 'triangledown',
32
+ flg: 'flag',
33
+ cir: 'circle',
34
+ aup: 'arrowup',
35
+ adn: 'arrowdown',
36
+ lup: 'label_up',
37
+ ldn: 'label_down',
38
+ llf: 'label_left',
39
+ lrg: 'label_right',
40
+ llwlf: 'label_lower_left',
41
+ llwrg: 'label_lower_right',
42
+ luplf: 'label_upper_left',
43
+ luprg: 'label_upper_right',
44
+ lcn: 'label_center',
45
+ sq: 'square',
46
+ dia: 'diamond',
47
+ },
48
+
49
+ /**
50
+ * @typedef {'solid' | 'dotted' | 'dashed'| 'arrow_left'
51
+ * | 'arrow_right' | 'arrow_both'} LineStyleValue
52
+ */
53
+ lineStyle: {
54
+ sol: 'solid',
55
+ dot: 'dotted',
56
+ dsh: 'dashed',
57
+ al: 'arrow_left',
58
+ ar: 'arrow_right',
59
+ ab: 'arrow_both',
60
+ },
61
+
62
+ /** @typedef {'solid' | 'dotted' | 'dashed'} BoxStyleValue */
63
+ boxStyle: {
64
+ sol: 'solid',
65
+ dot: 'dotted',
66
+ dsh: 'dashed',
67
+ },
68
+ };
69
+
70
+ /**
71
+ * @typedef {'auto' | 'huge' | 'large'
72
+ * | 'normal' | 'small' | 'tiny'} SizeValue
73
+ */
74
+ /** @typedef {'top' | 'center' | 'bottom'} VAlignValue */
75
+ /** @typedef {'left' | 'center' | 'right'} HAlignValue */
76
+ /** @typedef {'none' | 'auto'} TextWrapValue */
77
+ /**
78
+ * @typedef {'top_left' | 'top_center' | 'top_right'
79
+ * | 'middle_left' | 'middle_center' | 'middle_right'
80
+ * | 'bottom_left' | 'bottom_center' | 'bottom_right'
81
+ * } TablePositionValue
82
+ */
83
+
84
+ /**
85
+ * @typedef {Object} GraphicLabel
86
+ * @prop {number} id Drawing ID
87
+ * @prop {number} x Label x position
88
+ * @prop {number} y Label y position
89
+ * @prop {yLocValue} yLoc yLoc mode
90
+ * @prop {string} text Label text
91
+ * @prop {LabelStyleValue} style Label style
92
+ * @prop {number} color
93
+ * @prop {number} textColor
94
+ * @prop {SizeValue} size Label size
95
+ * @prop {HAlignValue} textAlign Text horizontal align
96
+ * @prop {string} toolTip Tooltip text
97
+ */
98
+
99
+ /**
100
+ * @typedef {Object} GraphicLine
101
+ * @prop {number} id Drawing ID
102
+ * @prop {number} x1 First x position
103
+ * @prop {number} y1 First y position
104
+ * @prop {number} x2 Second x position
105
+ * @prop {number} y2 Second y position
106
+ * @prop {ExtendValue} extend Horizontal extend
107
+ * @prop {LineStyleValue} style Line style
108
+ * @prop {number} color Line color
109
+ * @prop {number} width Line width
110
+ */
111
+
112
+ /**
113
+ * @typedef {Object} GraphicBox
114
+ * @prop {number} id Drawing ID
115
+ * @prop {number} x1 First x position
116
+ * @prop {number} y1 First y position
117
+ * @prop {number} x2 Second x position
118
+ * @prop {number} y2 Second y position
119
+ * @prop {number} color Box color
120
+ * @prop {number} bgColor Background color
121
+ * @prop {ExtendValue} extend Horizontal extend
122
+ * @prop {BoxStyleValue} style Box style
123
+ * @prop {number} width Box width
124
+ * @prop {string} text Text
125
+ * @prop {SizeValue} textSize Text size
126
+ * @prop {number} textColor Text color
127
+ * @prop {VAlignValue} textVAlign Text vertical align
128
+ * @prop {HAlignValue} textHAlign Text horizontal align
129
+ * @prop {TextWrapValue} textWrap Text wrap
130
+ */
131
+
132
+ /**
133
+ * @typedef {Object} TableCell
134
+ * @prop {number} id Drawing ID
135
+ * @prop {string} text Cell text
136
+ * @prop {number} width Cell width
137
+ * @prop {number} height Cell height
138
+ * @prop {number} textColor Text color
139
+ * @prop {HAlignValue} textHAlign Text horizontal align
140
+ * @prop {VAlignValue} textVAlign Text Vertical align
141
+ * @prop {SizeValue} textSize Text size
142
+ * @prop {number} bgColor Background color
143
+ */
144
+
145
+ /**
146
+ * @typedef {Object} GraphicTable
147
+ * @prop {number} id Drawing ID
148
+ * @prop {TablePositionValue} position Table position
149
+ * @prop {number} rows Number of rows
150
+ * @prop {number} columns Number of columns
151
+ * @prop {number} bgColor Background color
152
+ * @prop {number} frameColor Frame color
153
+ * @prop {number} frameWidth Frame width
154
+ * @prop {number} borderColor Border color
155
+ * @prop {number} borderWidth Border width
156
+ * @prop {() => TableCell[][]} cells Table cells matrix
157
+ */
158
+
159
+ /**
160
+ * @typedef {Object} GraphicHorizline
161
+ * @prop {number} id Drawing ID
162
+ * @prop {number} level Y position of the line
163
+ * @prop {number} startIndex Start index of the line (`chart.periods[line.startIndex]`)
164
+ * @prop {number} endIndex End index of the line (`chart.periods[line.endIndex]`)
165
+ * @prop {boolean} extendRight Is the line extended to the right
166
+ * @prop {boolean} extendLeft Is the line extended to the left
167
+ */
168
+
169
+ /**
170
+ * @typedef {Object} GraphicPoint
171
+ * @prop {number} index X position of the point
172
+ * @prop {number} level Y position of the point
173
+ */
174
+
175
+ /**
176
+ * @typedef {Object} GraphicPolygon
177
+ * @prop {number} id Drawing ID
178
+ * @prop {GraphicPoint[]} points List of polygon points
179
+ */
180
+
181
+ /**
182
+ * @typedef {Object} GraphicHorizHist
183
+ * @prop {number} id Drawing ID
184
+ * @prop {number} priceLow Low Y position
185
+ * @prop {number} priceHigh High Y position
186
+ * @prop {number} firstBarTime First X position
187
+ * @prop {number} lastBarTime Last X position
188
+ * @prop {number[]} rate List of values
189
+ */
190
+
191
+ /**
192
+ * @typedef {Object} GraphicData List of drawings indexed by type
193
+ * @prop {GraphicLabel[]} labels List of labels drawings
194
+ * @prop {GraphicLine[]} lines List of lines drawings
195
+ * @prop {GraphicBox[]} boxes List of boxes drawings
196
+ * @prop {GraphicTable[]} tables List of tables drawings
197
+ * @prop {GraphicPolygon[]} polygons List of polygons drawings
198
+ * @prop {GraphicHorizHist[]} horizHists List of horizontal histograms drawings
199
+ * @prop {GraphicHorizline[]} horizLines List of horizontal lines drawings
200
+ */
201
+
202
+ /**
203
+ * @param {Object} rawGraphic Raw graphic data
204
+ * @param {Object} indexes Drawings xPos indexes
205
+ * @returns {GraphicData}
206
+ */
207
+ module.exports = function graphicParse(rawGraphic = {}, indexes = []) {
208
+ // console.log('indexes', indexes);
209
+ return {
210
+ labels: Object.values(rawGraphic.dwglabels ?? {}).map((l) => ({
211
+ id: l.id,
212
+ x: indexes[l.x],
213
+ y: l.y,
214
+ yLoc: TRANSLATOR.yLoc[l.yl] ?? l.yl,
215
+ text: l.t,
216
+ style: TRANSLATOR.labelStyle[l.st] ?? l.st,
217
+ color: l.ci,
218
+ textColor: l.tci,
219
+ size: l.sz,
220
+ textAlign: l.ta,
221
+ toolTip: l.tt,
222
+ })),
223
+
224
+ lines: Object.values(rawGraphic.dwglines ?? {}).map((l) => ({
225
+ id: l.id,
226
+ x1: indexes[l.x1],
227
+ y1: l.y1,
228
+ x2: indexes[l.x2],
229
+ y2: l.y2,
230
+ extend: TRANSLATOR.extend[l.ex] ?? l.ex,
231
+ style: TRANSLATOR.lineStyle[l.st] ?? l.st,
232
+ color: l.ci,
233
+ width: l.w,
234
+ })),
235
+
236
+ boxes: Object.values(rawGraphic.dwgboxes ?? {}).map((b) => ({
237
+ id: b.id,
238
+ x1: indexes[b.x1],
239
+ y1: b.y1,
240
+ x2: indexes[b.x2],
241
+ y2: b.y2,
242
+ color: b.c,
243
+ bgColor: b.bc,
244
+ extend: TRANSLATOR.extend[b.ex] ?? b.ex,
245
+ style: TRANSLATOR.boxStyle[b.st] ?? b.st,
246
+ width: b.w,
247
+ text: b.t,
248
+ textSize: b.ts,
249
+ textColor: b.tc,
250
+ textVAlign: b.tva,
251
+ textHAlign: b.tha,
252
+ textWrap: b.tw,
253
+ })),
254
+
255
+ tables: Object.values(rawGraphic.dwgtables ?? {}).map((t) => ({
256
+ id: t.id,
257
+ position: t.pos,
258
+ rows: t.rows,
259
+ columns: t.cols,
260
+ bgColor: t.bgc,
261
+ frameColor: t.frmc,
262
+ frameWidth: t.frmw,
263
+ borderColor: t.brdc,
264
+ borderWidth: t.brdw,
265
+ cells: () => {
266
+ const matrix = [];
267
+ Object.values(rawGraphic.dwgtablecells ?? {}).forEach((cell) => {
268
+ if (cell.tid !== t.id) return;
269
+ if (!matrix[cell.row]) matrix[cell.row] = [];
270
+ matrix[cell.row][cell.col] = {
271
+ id: cell.id,
272
+ text: cell.t,
273
+ width: cell.w,
274
+ height: cell.h,
275
+ textColor: cell.tc,
276
+ textHAlign: cell.tha,
277
+ textVAlign: cell.tva,
278
+ textSize: cell.ts,
279
+ bgColor: cell.bgc,
280
+ };
281
+ });
282
+ return matrix;
283
+ },
284
+ })),
285
+
286
+ horizLines: Object.values(rawGraphic.horizlines ?? {}).map((h) => ({
287
+ ...h,
288
+ startIndex: indexes[h.startIndex],
289
+ endIndex: indexes[h.endIndex],
290
+ })),
291
+
292
+ polygons: Object.values(rawGraphic.polygons ?? {}).map((p) => ({
293
+ ...p,
294
+ points: p.points.map((pt) => ({
295
+ ...pt,
296
+ index: indexes[pt.index],
297
+ })),
298
+ })),
299
+
300
+ horizHists: Object.values(rawGraphic.hhists ?? {}).map((h) => ({
301
+ ...h,
302
+ firstBarTime: indexes[h.firstBarTime],
303
+ lastBarTime: indexes[h.lastBarTime],
304
+ })),
305
+
306
+ raw: () => rawGraphic,
307
+ };
308
+ };
@@ -0,0 +1,372 @@
1
+ const { genSessionID } = require('../utils');
2
+
3
+ const studyConstructor = require('./study');
4
+
5
+ /**
6
+ * @typedef {'HeikinAshi' | 'Renko' | 'LineBreak' | 'Kagi' | 'PointAndFigure'
7
+ * | 'Range'} ChartType Custom chart type
8
+ */
9
+
10
+ const ChartTypes = {
11
+ HeikinAshi: 'BarSetHeikenAshi@tv-basicstudies-60!',
12
+ Renko: 'BarSetRenko@tv-prostudies-40!',
13
+ LineBreak: 'BarSetPriceBreak@tv-prostudies-34!',
14
+ Kagi: 'BarSetKagi@tv-prostudies-34!',
15
+ PointAndFigure: 'BarSetPnF@tv-prostudies-34!',
16
+ Range: 'BarSetRange@tv-basicstudies-72!',
17
+ };
18
+
19
+ /**
20
+ * @typedef {Object} ChartInputs Custom chart type
21
+ * @prop {number} [atrLength] Renko/Kagi/PointAndFigure ATR length
22
+ * @prop {'open' | 'high' | 'low' | 'close' | 'hl2'
23
+ * | 'hlc3' | 'ohlc4'} [source] Renko/LineBreak/Kagi source
24
+ * @prop {'ATR' | string} [style] Renko/Kagi/PointAndFigure style
25
+ * @prop {number} [boxSize] Renko/PointAndFigure box size
26
+ * @prop {number} [reversalAmount] Kagi/PointAndFigure reversal amount
27
+ * @prop {'Close'} [sources] Renko/PointAndFigure sources
28
+ * @prop {boolean} [wicks] Renko wicks
29
+ * @prop {number} [lb] LineBreak Line break
30
+ * @prop {boolean} [oneStepBackBuilding] PointAndFigure oneStepBackBuilding
31
+ * @prop {boolean} [phantomBars] Range phantom bars
32
+ * @prop {boolean} [range] Range range
33
+ */
34
+
35
+ /** @typedef {Object<string, Function[]>} StudyListeners */
36
+
37
+ /**
38
+ * @typedef {Object} ChartSessionBridge
39
+ * @prop {string} sessionID
40
+ * @prop {StudyListeners} studyListeners
41
+ * @prop {Object<number, number>} indexes
42
+ * @prop {import('../client').SendPacket} send
43
+ */
44
+
45
+ /**
46
+ * @typedef {'seriesLoaded' | 'symbolLoaded' | 'update' | 'error'} ChartEvent
47
+ */
48
+
49
+ /**
50
+ * @typedef {Object} PricePeriod
51
+ * @prop {number} time Period timestamp
52
+ * @prop {number} open Period open value
53
+ * @prop {number} close Period close value
54
+ * @prop {number} max Period max value
55
+ * @prop {number} min Period min value
56
+ * @prop {number} volume Period volume value
57
+ */
58
+
59
+ /**
60
+ * @typedef {Object} Subsession
61
+ * @prop {string} id Subsession ID (ex: 'regular')
62
+ * @prop {string} description Subsession description (ex: 'Regular')
63
+ * @prop {boolean} private If private
64
+ * @prop {string} session Session (ex: '24x7')
65
+ * @prop {string} session-correction Session correction
66
+ * @prop {string} session-display Session display (ex: '24x7')
67
+ *
68
+ * @typedef {Object} MarketInfos
69
+ * @prop {string} series_id Used series (ex: 'ser_1')
70
+ * @prop {string} base_currency Base currency (ex: 'BTC')
71
+ * @prop {string} base_currency_id Base currency ID (ex: 'XTVCBTC')
72
+ * @prop {string} name Market short name (ex: 'BTCEUR')
73
+ * @prop {string} full_name Market full name (ex: 'COINBASE:BTCEUR')
74
+ * @prop {string} pro_name Market pro name (ex: 'COINBASE:BTCEUR')
75
+ * @prop {string} description Market symbol description (ex: 'BTC/EUR')
76
+ * @prop {string} short_description Market symbol short description (ex: 'BTC/EUR')
77
+ * @prop {string} exchange Market exchange (ex: 'COINBASE')
78
+ * @prop {string} listed_exchange Market exchange (ex: 'COINBASE')
79
+ * @prop {string} provider_id Values provider ID (ex: 'coinbase')
80
+ * @prop {string} currency_id Used currency ID (ex: 'EUR')
81
+ * @prop {string} currency_code Used currency code (ex: 'EUR')
82
+ * @prop {string} variable_tick_size Variable tick size
83
+ * @prop {number} pricescale Price scale
84
+ * @prop {number} pointvalue Point value
85
+ * @prop {string} session Session (ex: '24x7')
86
+ * @prop {string} session_display Session display (ex: '24x7')
87
+ * @prop {string} type Market type (ex: 'crypto')
88
+ * @prop {boolean} has_intraday If intraday values are available
89
+ * @prop {boolean} fractional If market is fractional
90
+ * @prop {boolean} is_tradable If the market is curently tradable
91
+ * @prop {number} minmov Minimum move value
92
+ * @prop {number} minmove2 Minimum move value 2
93
+ * @prop {string} timezone Used timezone
94
+ * @prop {boolean} is_replayable If the replay mode is available
95
+ * @prop {boolean} has_adjustment If the adjustment mode is enabled
96
+ * @prop {boolean} has_extended_hours Has extended hours
97
+ * @prop {string} bar_source Bar source
98
+ * @prop {string} bar_transform Bar transform
99
+ * @prop {boolean} bar_fillgaps Bar fill gaps
100
+ * @prop {string} allowed_adjustment Allowed adjustment (ex: 'none')
101
+ * @prop {string} subsession_id Subsession ID (ex: 'regular')
102
+ * @prop {string} pro_perm Pro permission (ex: '')
103
+ * @prop {[]} base_name Base name (ex: ['COINBASE:BTCEUR'])
104
+ * @prop {[]} legs Legs (ex: ['COINBASE:BTCEUR'])
105
+ * @prop {Subsession[]} subsessions Sub sessions
106
+ * @prop {[]} typespecs Typespecs (ex: [])
107
+ * @prop {[]} resolutions Resolutions (ex: [])
108
+ * @prop {[]} aliases Aliases (ex: [])
109
+ * @prop {[]} alternatives Alternatives (ex: [])
110
+ */
111
+
112
+ /**
113
+ * @param {import('../client').ClientBridge} client
114
+ */
115
+ module.exports = (client) => class ChartSession {
116
+ #sessionID = genSessionID('cs');
117
+
118
+ /** Parent client */
119
+ #client = client;
120
+
121
+ /** @type {StudyListeners} */
122
+ #studyListeners = {};
123
+
124
+ /**
125
+ * Table of periods values indexed by timestamp
126
+ * @type {Object<number, PricePeriod[]>}
127
+ */
128
+ #periods = {};
129
+
130
+ /** @return {PricePeriod[]} List of periods values */
131
+ get periods() {
132
+ return Object.values(this.#periods).sort((a, b) => b.time - a.time);
133
+ }
134
+
135
+ /**
136
+ * Current market infos
137
+ * @type {MarketInfos}
138
+ */
139
+ #infos = {};
140
+
141
+ /** @return {MarketInfos} Current market infos */
142
+ get infos() {
143
+ return this.#infos;
144
+ }
145
+
146
+ #callbacks = {
147
+ seriesLoaded: [],
148
+ symbolLoaded: [],
149
+ update: [],
150
+
151
+ event: [],
152
+ error: [],
153
+ };
154
+
155
+ /**
156
+ * @param {ChartEvent} ev Client event
157
+ * @param {...{}} data Packet data
158
+ */
159
+ #handleEvent(ev, ...data) {
160
+ this.#callbacks[ev].forEach((e) => e(...data));
161
+ this.#callbacks.event.forEach((e) => e(ev, ...data));
162
+ }
163
+
164
+ #handleError(...msgs) {
165
+ if (this.#callbacks.error.length === 0) console.error(...msgs);
166
+ else this.#handleEvent('error', ...msgs);
167
+ }
168
+
169
+ constructor() {
170
+ this.#client.sessions[this.#sessionID] = {
171
+ type: 'chart',
172
+ onData: (packet) => {
173
+ if (global.TW_DEBUG) console.log('§90§30§106 CHART SESSION §0 DATA', packet);
174
+
175
+ if (typeof packet.data[1] === 'string' && this.#studyListeners[packet.data[1]]) {
176
+ this.#studyListeners[packet.data[1]](packet);
177
+ return;
178
+ }
179
+
180
+ if (packet.type === 'symbol_resolved') {
181
+ this.#infos = {
182
+ series_id: packet.data[1],
183
+ ...packet.data[2],
184
+ };
185
+
186
+ this.#handleEvent('symbolLoaded');
187
+ return;
188
+ }
189
+
190
+ if (['timescale_update', 'du'].includes(packet.type)) {
191
+ Object.keys(packet.data[1]).forEach((k) => {
192
+ if (k === '$prices') {
193
+ const periods = packet.data[1].$prices;
194
+ if (!periods || !periods.s) return;
195
+
196
+ periods.s.forEach((p) => {
197
+ [this.#chartSession.indexes[p.i]] = p.v;
198
+ this.#periods[p.v[0]] = {
199
+ time: p.v[0],
200
+ open: p.v[1],
201
+ close: p.v[4],
202
+ max: p.v[2],
203
+ min: p.v[3],
204
+ volume: Math.round(p.v[5] * 100) / 100,
205
+ };
206
+ });
207
+
208
+ return;
209
+ }
210
+
211
+ if (this.#studyListeners[k]) this.#studyListeners[k](packet);
212
+ });
213
+
214
+ this.#handleEvent('update');
215
+ return;
216
+ }
217
+
218
+ if (packet.type === 'symbol_error') {
219
+ this.#handleError(`(${packet.data[1]}) Symbol error:`, packet.data[2]);
220
+ return;
221
+ }
222
+
223
+ if (packet.type === 'series_error') {
224
+ this.#handleError('Series error:', packet.data[3]);
225
+ return;
226
+ }
227
+
228
+ if (packet.type === 'critical_error') {
229
+ const [, name, description] = packet.data;
230
+ this.#handleError('Critical error:', name, description);
231
+ }
232
+ },
233
+ };
234
+
235
+ this.#client.send('chart_create_session', [this.#sessionID]);
236
+ }
237
+
238
+ #seriesCreated = false;
239
+
240
+ #currentSeries = 0;
241
+
242
+ /**
243
+ * @param {import('../types').TimeFrame} timeframe Chart period timeframe
244
+ * @param {number} [range] Number of loaded periods/candles (Default: 100)
245
+ * @param {number} [reference] Reference candle timestamp (Default is now)
246
+ */
247
+ setSeries(timeframe = '240', range = 100, reference = null) {
248
+ if (!this.#currentSeries) {
249
+ this.#handleError('Please set the market before setting series');
250
+ return;
251
+ }
252
+
253
+ const calcRange = !reference ? range : ['bar_count', reference, range];
254
+
255
+ this.#periods = {};
256
+
257
+ this.#client.send(`${this.#seriesCreated ? 'modify' : 'create'}_series`, [
258
+ this.#sessionID,
259
+ '$prices',
260
+ 's1',
261
+ `ser_${this.#currentSeries}`,
262
+ timeframe,
263
+ this.#seriesCreated ? '' : calcRange,
264
+ ]);
265
+
266
+ this.#seriesCreated = true;
267
+ }
268
+
269
+ /**
270
+ * Set the chart market
271
+ * @param {string} symbol Market symbol
272
+ * @param {Object} [options] Chart options
273
+ * @param {import('../types').TimeFrame} [options.timeframe] Chart period timeframe
274
+ * @param {number} [options.range] Number of loaded periods/candles (Default: 100)
275
+ * @param {number} [options.to] Last candle timestamp (Default is now)
276
+ * @param {'splits' | 'dividends'} [options.adjustment] Market adjustment
277
+ * @param {'regular' | 'extended'} [options.session] Chart session
278
+ * @param {'EUR' | 'USD' | string} [options.currency] Chart currency
279
+ * @param {ChartType} [options.type] Chart custom type
280
+ * @param {ChartInputs} [options.inputs] Chart custom inputs
281
+ */
282
+ setMarket(symbol, options = {}) {
283
+ this.#periods = {};
284
+
285
+ const symbolInit = {
286
+ symbol: symbol || 'BTCEUR',
287
+ adjustment: options.adjustment || 'splits',
288
+ };
289
+
290
+ if (options.session) symbolInit.session = options.session;
291
+
292
+ if (options.currency) symbolInit['currency-id'] = options.currency;
293
+
294
+ const chartInit = (options.type && ChartTypes[options.type]) ? {} : symbolInit;
295
+
296
+ if (options.type && ChartTypes[options.type]) {
297
+ chartInit.symbol = symbolInit;
298
+ chartInit.type = ChartTypes[options.type];
299
+ chartInit.inputs = { ...options.inputs };
300
+ }
301
+
302
+ this.#currentSeries += 1;
303
+
304
+ this.#client.send('resolve_symbol', [
305
+ this.#sessionID,
306
+ `ser_${this.#currentSeries}`,
307
+ `=${JSON.stringify(chartInit)}`,
308
+ ]);
309
+
310
+ this.setSeries(options.timeframe, options.range, options.to);
311
+ }
312
+
313
+ /**
314
+ * Set the chart timezone
315
+ * @param {import('../types').Timezone} timezone New timezone
316
+ */
317
+ setTimezone(timezone) {
318
+ this.#periods = {};
319
+ this.#client.send('switch_timezone', [this.#sessionID, timezone]);
320
+ }
321
+
322
+ /**
323
+ * Fetch x additional previous periods/candles values
324
+ * @param {number} number Number of additional periods/candles you want to fetch
325
+ */
326
+ fetchMore(number = 1) {
327
+ this.#client.send('request_more_data', [this.#sessionID, '$prices', number]);
328
+ }
329
+
330
+ /**
331
+ * When a symbol is loaded
332
+ * @param {() => void} cb
333
+ * @event
334
+ */
335
+ onSymbolLoaded(cb) {
336
+ this.#callbacks.symbolLoaded.push(cb);
337
+ }
338
+
339
+ /**
340
+ * When a chart update happens
341
+ * @param {() => void} cb
342
+ * @event
343
+ */
344
+ onUpdate(cb) {
345
+ this.#callbacks.update.push(cb);
346
+ }
347
+
348
+ /**
349
+ * When chart error happens
350
+ * @param {(...any) => void} cb Callback
351
+ * @event
352
+ */
353
+ onError(cb) {
354
+ this.#callbacks.error.push(cb);
355
+ }
356
+
357
+ /** @type {ChartSessionBridge} */
358
+ #chartSession = {
359
+ sessionID: this.#sessionID,
360
+ studyListeners: this.#studyListeners,
361
+ indexes: {},
362
+ send: (t, p) => this.#client.send(t, p),
363
+ };
364
+
365
+ Study = studyConstructor(this.#chartSession);
366
+
367
+ /** Delete the chart session */
368
+ delete() {
369
+ this.#client.send('quote_delete_session', [this.#sessionID]);
370
+ delete this.#client.sessions[this.#sessionID];
371
+ }
372
+ };