@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.
- package/README.md +112 -51
- package/dist/chart/chart-session.d.ts +114 -0
- package/dist/chart/chart-session.js +339 -0
- package/dist/chart/graphics.d.ts +129 -0
- package/dist/chart/graphics.js +153 -0
- package/dist/chart/index.d.ts +11 -0
- package/dist/chart/index.js +6 -0
- package/dist/chart/strategy.d.ts +100 -0
- package/dist/chart/strategy.js +51 -0
- package/dist/chart/study.d.ts +60 -0
- package/dist/chart/study.js +171 -0
- package/dist/chart/timeframes.d.ts +7 -0
- package/dist/chart/timeframes.js +22 -0
- package/dist/chart/types.d.ts +105 -0
- package/dist/chart/types.js +9 -0
- package/dist/client/client.d.ts +89 -0
- package/dist/client/client.js +254 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +2 -0
- package/dist/client/transport.d.ts +27 -0
- package/dist/client/transport.js +38 -0
- package/dist/data/candles.d.ts +40 -0
- package/dist/data/candles.js +101 -0
- package/dist/data/history.d.ts +61 -0
- package/dist/data/history.js +101 -0
- package/dist/data/index.d.ts +28 -0
- package/dist/data/index.js +15 -0
- package/dist/data/indicators.d.ts +54 -0
- package/dist/data/indicators.js +139 -0
- package/dist/data/operation.d.ts +71 -0
- package/dist/data/operation.js +205 -0
- package/dist/data/provider.d.ts +23 -0
- package/dist/data/provider.js +11 -0
- package/dist/data/quotes.d.ts +33 -0
- package/dist/data/quotes.js +117 -0
- package/dist/data/symbols.d.ts +9 -0
- package/dist/data/symbols.js +19 -0
- package/dist/errors.d.ts +53 -0
- package/dist/errors.js +24 -0
- package/dist/events.d.ts +36 -0
- package/dist/events.js +70 -0
- package/dist/http/account.d.ts +47 -0
- package/dist/http/account.js +127 -0
- package/dist/http/index.d.ts +11 -0
- package/dist/http/index.js +5 -0
- package/dist/http/indicators.d.ts +37 -0
- package/dist/http/indicators.js +144 -0
- package/dist/http/layouts.d.ts +36 -0
- package/dist/http/layouts.js +24 -0
- package/dist/http/market.d.ts +45 -0
- package/dist/http/market.js +59 -0
- package/dist/http/pine-permissions.d.ts +29 -0
- package/dist/http/pine-permissions.js +56 -0
- package/dist/http/request.d.ts +44 -0
- package/dist/http/request.js +83 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +19 -0
- package/dist/indicators/builtin-indicator.d.ts +17 -0
- package/dist/indicators/builtin-indicator.js +75 -0
- package/dist/indicators/index.d.ts +8 -0
- package/dist/indicators/index.js +2 -0
- package/dist/indicators/pine-indicator.d.ts +63 -0
- package/dist/indicators/pine-indicator.js +101 -0
- package/dist/protocol/compression.d.ts +12 -0
- package/dist/protocol/compression.js +83 -0
- package/dist/protocol/framing.d.ts +59 -0
- package/dist/protocol/framing.js +68 -0
- package/dist/protocol/ids.d.ts +2 -0
- package/dist/protocol/ids.js +10 -0
- package/dist/protocol/index.d.ts +4 -0
- package/dist/protocol/index.js +3 -0
- package/dist/quote/fields.d.ts +35 -0
- package/dist/quote/fields.js +21 -0
- package/dist/quote/index.d.ts +4 -0
- package/dist/quote/index.js +2 -0
- package/dist/quote/quote-session.d.ts +73 -0
- package/dist/quote/quote-session.js +205 -0
- package/docs/README.es.md +58 -0
- package/docs/README.fr.md +58 -0
- package/docs/README.pt.md +58 -0
- package/docs/data-api.md +229 -0
- package/docs/low-level-api.md +213 -0
- package/docs/migration-v4.md +128 -0
- package/docs/v4-coverage.md +192 -0
- package/docs/v4-reliability.md +33 -0
- package/package.json +55 -20
- package/.env.sample +0 -2
- package/.eslintrc.js +0 -28
- package/.gitattributes +0 -2
- package/docs/DOCS.md +0 -3
- package/examples/AllPrivateIndicators.js +0 -44
- package/examples/BuiltInIndicator.js +0 -51
- package/examples/CustomChartType.js +0 -123
- package/examples/CustomTimeframe.js +0 -31
- package/examples/Errors.js +0 -155
- package/examples/FakeReplayMode.js +0 -40
- package/examples/FromToData.js +0 -35
- package/examples/GetDrawings.js +0 -27
- package/examples/GraphicIndicator.js +0 -42
- package/examples/MultipleSyncFetch.js +0 -44
- package/examples/PinePermManage.js +0 -67
- package/examples/ReplayMode.js +0 -103
- package/examples/Search.js +0 -14
- package/examples/SimpleChart.js +0 -63
- package/examples/UserLogin.js +0 -16
- package/main.js +0 -11
- package/src/chart/graphicParser.js +0 -308
- package/src/chart/session.js +0 -553
- package/src/chart/study.js +0 -435
- package/src/classes/BuiltInIndicator.js +0 -137
- package/src/classes/PineIndicator.js +0 -132
- package/src/classes/PinePermManager.js +0 -159
- package/src/client.js +0 -294
- package/src/miscRequests.js +0 -607
- package/src/protocol.js +0 -60
- package/src/quote/market.js +0 -132
- package/src/quote/session.js +0 -125
- package/src/types.js +0 -36
- package/src/utils.js +0 -20
- package/vite.config.js +0 -7
package/src/chart/study.js
DELETED
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
const { genSessionID } = require('../utils');
|
|
2
|
-
const { parseCompressed } = require('../protocol');
|
|
3
|
-
const graphicParser = require('./graphicParser');
|
|
4
|
-
|
|
5
|
-
const PineIndicator = require('../classes/PineIndicator');
|
|
6
|
-
const BuiltInIndicator = require('../classes/BuiltInIndicator');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get pine inputs
|
|
10
|
-
* @param {PineIndicator | BuiltInIndicator} options
|
|
11
|
-
*/
|
|
12
|
-
function getInputs(options) {
|
|
13
|
-
if (options instanceof PineIndicator) {
|
|
14
|
-
const pineInputs = { text: options.script };
|
|
15
|
-
|
|
16
|
-
if (options.pineId) pineInputs.pineId = options.pineId;
|
|
17
|
-
if (options.pineVersion) pineInputs.pineVersion = options.pineVersion;
|
|
18
|
-
|
|
19
|
-
Object.keys(options.inputs).forEach((inputID, n) => {
|
|
20
|
-
const input = options.inputs[inputID];
|
|
21
|
-
|
|
22
|
-
pineInputs[inputID] = {
|
|
23
|
-
v: (input.type !== 'color') ? input.value : n,
|
|
24
|
-
f: input.isFake,
|
|
25
|
-
t: input.type,
|
|
26
|
-
};
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
return pineInputs;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return options.options;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const parseTrades = (trades) => trades.reverse().map((t) => ({
|
|
36
|
-
entry: {
|
|
37
|
-
name: t.e.c,
|
|
38
|
-
type: (t.e.tp[0] === 's' ? 'short' : 'long'),
|
|
39
|
-
value: t.e.p,
|
|
40
|
-
time: t.e.tm,
|
|
41
|
-
},
|
|
42
|
-
exit: {
|
|
43
|
-
name: t.x.c,
|
|
44
|
-
value: t.x.p,
|
|
45
|
-
time: t.x.tm,
|
|
46
|
-
},
|
|
47
|
-
quantity: t.q,
|
|
48
|
-
profit: t.tp,
|
|
49
|
-
cumulative: t.cp,
|
|
50
|
-
runup: t.rn,
|
|
51
|
-
drawdown: t.dd,
|
|
52
|
-
}));
|
|
53
|
-
|
|
54
|
-
// const historyParser = (history) => history.reverse().map((h) => ({
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @typedef {Object} TradeReport Trade report
|
|
58
|
-
|
|
59
|
-
* @prop {Object} entry Trade entry
|
|
60
|
-
* @prop {string} entry.name Trade name
|
|
61
|
-
* @prop {'long' | 'short'} entry.type Entry type (long/short)
|
|
62
|
-
* @prop {number} entry.value Entry price value
|
|
63
|
-
* @prop {number} entry.time Entry timestamp
|
|
64
|
-
|
|
65
|
-
* @prop {Object} exit Trade exit
|
|
66
|
-
* @prop {'' | string} exit.name Trade name ('' if false exit)
|
|
67
|
-
* @prop {number} exit.value Exit price value
|
|
68
|
-
* @prop {number} exit.time Exit timestamp
|
|
69
|
-
|
|
70
|
-
* @prop {number} quantity Trade quantity
|
|
71
|
-
* @prop {RelAbsValue} profit Trade profit
|
|
72
|
-
* @prop {RelAbsValue} cumulative Trade cummulative profit
|
|
73
|
-
* @prop {RelAbsValue} runup Trade run-up
|
|
74
|
-
* @prop {RelAbsValue} drawdown Trade drawdown
|
|
75
|
-
*/
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @typedef {Object} PerfReport
|
|
79
|
-
* @prop {number} avgBarsInTrade Average bars in trade
|
|
80
|
-
* @prop {number} avgBarsInWinTrade Average bars in winning trade
|
|
81
|
-
* @prop {number} avgBarsInLossTrade Average bars in losing trade
|
|
82
|
-
* @prop {number} avgTrade Average trade gain
|
|
83
|
-
* @prop {number} avgTradePercent Average trade performace
|
|
84
|
-
* @prop {number} avgLosTrade Average losing trade gain
|
|
85
|
-
* @prop {number} avgLosTradePercent Average losing trade performace
|
|
86
|
-
* @prop {number} avgWinTrade Average winning trade gain
|
|
87
|
-
* @prop {number} avgWinTradePercent Average winning trade performace
|
|
88
|
-
* @prop {number} commissionPaid Commission paid
|
|
89
|
-
* @prop {number} grossLoss Gross loss value
|
|
90
|
-
* @prop {number} grossLossPercent Gross loss percent
|
|
91
|
-
* @prop {number} grossProfit Gross profit
|
|
92
|
-
* @prop {number} grossProfitPercent Gross profit percent
|
|
93
|
-
* @prop {number} largestLosTrade Largest losing trade gain
|
|
94
|
-
* @prop {number} largestLosTradePercent Largent losing trade performance (percentage)
|
|
95
|
-
* @prop {number} largestWinTrade Largest winning trade gain
|
|
96
|
-
* @prop {number} largestWinTradePercent Largest winning trade performance (percentage)
|
|
97
|
-
* @prop {number} marginCalls Margin calls
|
|
98
|
-
* @prop {number} maxContractsHeld Max Contracts Held
|
|
99
|
-
* @prop {number} netProfit Net profit
|
|
100
|
-
* @prop {number} netProfitPercent Net performance (percentage)
|
|
101
|
-
* @prop {number} numberOfLosingTrades Number of losing trades
|
|
102
|
-
* @prop {number} numberOfWiningTrades Number of winning trades
|
|
103
|
-
* @prop {number} percentProfitable Strategy winrate
|
|
104
|
-
* @prop {number} profitFactor Profit factor
|
|
105
|
-
* @prop {number} ratioAvgWinAvgLoss Ratio Average Win / Average Loss
|
|
106
|
-
* @prop {number} totalOpenTrades Total open trades
|
|
107
|
-
* @prop {number} totalTrades Total trades
|
|
108
|
-
*/
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* @typedef {Object} FromTo
|
|
112
|
-
* @prop {number} from From timestamp
|
|
113
|
-
* @prop {number} to To timestamp
|
|
114
|
-
*/
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* @typedef {Object} StrategyReport
|
|
118
|
-
* @prop {'EUR' | 'USD' | 'JPY' | '' | 'CHF'} [currency] Selected currency
|
|
119
|
-
* @prop {Object} [settings] Backtester settings
|
|
120
|
-
* @prop {Object} [settings.dateRange] Backtester date range
|
|
121
|
-
* @prop {FromTo} [settings.dateRange.backtest] Date range for backtest
|
|
122
|
-
* @prop {FromTo} [settings.dateRange.trade] Date range for trade
|
|
123
|
-
* @prop {TradeReport[]} trades Trade list starting by the last
|
|
124
|
-
* @prop {Object} history History Chart value
|
|
125
|
-
* @prop {number[]} [history.buyHold] Buy hold values
|
|
126
|
-
* @prop {number[]} [history.buyHoldPercent] Buy hold percent values
|
|
127
|
-
* @prop {number[]} [history.drawDown] Drawdown values
|
|
128
|
-
* @prop {number[]} [history.drawDownPercent] Drawdown percent values
|
|
129
|
-
* @prop {number[]} [history.equity] Equity values
|
|
130
|
-
* @prop {number[]} [history.equityPercent] Equity percent values
|
|
131
|
-
* @prop {Object} performance Strategy performance
|
|
132
|
-
* @prop {PerfReport} [performance.all] Strategy long/short performances
|
|
133
|
-
* @prop {PerfReport} [performance.long] Strategy long performances
|
|
134
|
-
* @prop {PerfReport} [performance.short] Strategy short performances
|
|
135
|
-
* @prop {number} [performance.buyHoldReturn] Strategy Buy & Hold Return
|
|
136
|
-
* @prop {number} [performance.buyHoldReturnPercent] Strategy Buy & Hold Return percent
|
|
137
|
-
* @prop {number} [performance.maxDrawDown] Strategy max drawdown
|
|
138
|
-
* @prop {number} [performance.maxDrawDownPercent] Strategy max drawdown percent
|
|
139
|
-
* @prop {number} [performance.openPL] Strategy Open P&L (Profit And Loss)
|
|
140
|
-
* @prop {number} [performance.openPLPercent] Strategy Open P&L (Profit And Loss) percent
|
|
141
|
-
* @prop {number} [performance.sharpeRatio] Strategy Sharpe Ratio
|
|
142
|
-
* @prop {number} [performance.sortinoRatio] Strategy Sortino Ratio
|
|
143
|
-
*/
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* @param {import('./session').ChartSessionBridge} chartSession
|
|
147
|
-
*/
|
|
148
|
-
module.exports = (chartSession) => class ChartStudy {
|
|
149
|
-
#studID = genSessionID('st');
|
|
150
|
-
|
|
151
|
-
#studyListeners = chartSession.studyListeners;
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Table of periods values indexed by timestamp
|
|
155
|
-
* @type {Object<number, {}[]>}
|
|
156
|
-
*/
|
|
157
|
-
#periods = {};
|
|
158
|
-
|
|
159
|
-
/** @return {{}[]} List of periods values */
|
|
160
|
-
get periods() {
|
|
161
|
-
return Object.values(this.#periods).sort((a, b) => b.$time - a.$time);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* List of graphic xPos indexes
|
|
166
|
-
* @type {number[]}
|
|
167
|
-
*/
|
|
168
|
-
#indexes = [];
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Table of graphic drawings indexed by type and ID
|
|
172
|
-
* @type {Object<string, Object<number, {}>>}
|
|
173
|
-
*/
|
|
174
|
-
#graphic = {};
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Table of graphic drawings indexed by type
|
|
178
|
-
* @return {import('./graphicParser').GraphicData}
|
|
179
|
-
*/
|
|
180
|
-
get graphic() {
|
|
181
|
-
const translator = {};
|
|
182
|
-
|
|
183
|
-
Object.keys(chartSession.indexes)
|
|
184
|
-
.sort((a, b) => chartSession.indexes[b] - chartSession.indexes[a])
|
|
185
|
-
.forEach((r, n) => { translator[r] = n; });
|
|
186
|
-
|
|
187
|
-
const indexes = this.#indexes.map((i) => translator[i]);
|
|
188
|
-
return graphicParser(this.#graphic, indexes);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** @type {StrategyReport} */
|
|
192
|
-
#strategyReport = {
|
|
193
|
-
trades: [],
|
|
194
|
-
history: {},
|
|
195
|
-
performance: {},
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/** @return {StrategyReport} Get the strategy report if available */
|
|
199
|
-
get strategyReport() {
|
|
200
|
-
return this.#strategyReport;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
#callbacks = {
|
|
204
|
-
studyCompleted: [],
|
|
205
|
-
update: [],
|
|
206
|
-
|
|
207
|
-
event: [],
|
|
208
|
-
error: [],
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* @param {ChartEvent} ev Client event
|
|
213
|
-
* @param {...{}} data Packet data
|
|
214
|
-
*/
|
|
215
|
-
#handleEvent(ev, ...data) {
|
|
216
|
-
this.#callbacks[ev].forEach((e) => e(...data));
|
|
217
|
-
this.#callbacks.event.forEach((e) => e(ev, ...data));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
#handleError(...msgs) {
|
|
221
|
-
if (this.#callbacks.error.length === 0) console.error(...msgs);
|
|
222
|
-
else this.#handleEvent('error', ...msgs);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @param {PineIndicator | BuiltInIndicator} indicator Indicator object instance
|
|
227
|
-
*/
|
|
228
|
-
constructor(indicator) {
|
|
229
|
-
if (!(indicator instanceof PineIndicator) && !(indicator instanceof BuiltInIndicator)) {
|
|
230
|
-
throw new Error(`Indicator argument must be an instance of PineIndicator or BuiltInIndicator.
|
|
231
|
-
Please use 'TradingView.getIndicator(...)' function.`);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** @type {PineIndicator | BuiltInIndicator} Indicator instance */
|
|
235
|
-
this.instance = indicator;
|
|
236
|
-
|
|
237
|
-
this.#studyListeners[this.#studID] = async (packet) => {
|
|
238
|
-
if (global.TW_DEBUG) console.log('§90§30§105 STUDY §0 DATA', packet);
|
|
239
|
-
|
|
240
|
-
if (packet.type === 'study_completed') {
|
|
241
|
-
this.#handleEvent('studyCompleted');
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (['timescale_update', 'du'].includes(packet.type)) {
|
|
246
|
-
const changes = [];
|
|
247
|
-
const data = packet.data[1][this.#studID];
|
|
248
|
-
|
|
249
|
-
if (data && data.st && data.st[0]) {
|
|
250
|
-
data.st.forEach((p) => {
|
|
251
|
-
const period = {};
|
|
252
|
-
|
|
253
|
-
p.v.forEach((plot, i) => {
|
|
254
|
-
if (!this.instance.plots) {
|
|
255
|
-
period[i === 0 ? '$time' : `plot_${i - 1}`] = plot;
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
const plotName = (i === 0 ? '$time' : this.instance.plots[`plot_${i - 1}`]);
|
|
259
|
-
if (plotName && !period[plotName]) period[plotName] = plot;
|
|
260
|
-
else period[`plot_${i - 1}`] = plot;
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
this.#periods[p.v[0]] = period;
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
changes.push('plots');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (data.ns && data.ns.d) {
|
|
270
|
-
const parsed = JSON.parse(data.ns.d);
|
|
271
|
-
|
|
272
|
-
if (parsed.graphicsCmds) {
|
|
273
|
-
if (parsed.graphicsCmds.erase) {
|
|
274
|
-
parsed.graphicsCmds.erase.forEach((instruction) => {
|
|
275
|
-
// console.log('Erase', instruction);
|
|
276
|
-
if (instruction.action === 'all') {
|
|
277
|
-
if (!instruction.type) {
|
|
278
|
-
Object.keys(this.#graphic).forEach((drawType) => {
|
|
279
|
-
this.#graphic[drawType] = {};
|
|
280
|
-
});
|
|
281
|
-
} else delete this.#graphic[instruction.type];
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (instruction.action === 'one') {
|
|
286
|
-
delete this.#graphic[instruction.type][instruction.id];
|
|
287
|
-
}
|
|
288
|
-
// Can an 'instruction' contains other things ?
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (parsed.graphicsCmds.create) {
|
|
293
|
-
Object.keys(parsed.graphicsCmds.create).forEach((drawType) => {
|
|
294
|
-
if (!this.#graphic[drawType]) this.#graphic[drawType] = {};
|
|
295
|
-
parsed.graphicsCmds.create[drawType].forEach((group) => {
|
|
296
|
-
group.data.forEach((item) => {
|
|
297
|
-
this.#graphic[drawType][item.id] = item;
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// console.log('graphicsCmds', Object.keys(parsed.graphicsCmds));
|
|
304
|
-
// Can 'graphicsCmds' contains other things ?
|
|
305
|
-
|
|
306
|
-
changes.push('graphic');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const updateStrategyReport = (report) => {
|
|
310
|
-
if (report.currency) {
|
|
311
|
-
this.#strategyReport.currency = report.currency;
|
|
312
|
-
changes.push('report.currency');
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (report.settings) {
|
|
316
|
-
this.#strategyReport.settings = report.settings;
|
|
317
|
-
changes.push('report.settings');
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (report.performance) {
|
|
321
|
-
this.#strategyReport.performance = report.performance;
|
|
322
|
-
changes.push('report.perf');
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (report.trades) {
|
|
326
|
-
this.#strategyReport.trades = parseTrades(report.trades);
|
|
327
|
-
changes.push('report.trades');
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (report.equity) {
|
|
331
|
-
this.#strategyReport.history = {
|
|
332
|
-
buyHold: report.buyHold,
|
|
333
|
-
buyHoldPercent: report.buyHoldPercent,
|
|
334
|
-
drawDown: report.drawDown,
|
|
335
|
-
drawDownPercent: report.drawDownPercent,
|
|
336
|
-
equity: report.equity,
|
|
337
|
-
equityPercent: report.equityPercent,
|
|
338
|
-
};
|
|
339
|
-
changes.push('report.history');
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
if (parsed.dataCompressed) {
|
|
344
|
-
updateStrategyReport((await parseCompressed(parsed.dataCompressed)).report);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (parsed.data && parsed.data.report) updateStrategyReport(parsed.data.report);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (data.ns.indexes && typeof data.ns.indexes === 'object') {
|
|
351
|
-
this.#indexes = data.ns.indexes;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
this.#handleEvent('update', changes);
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (packet.type === 'study_error') {
|
|
359
|
-
this.#handleError(packet.data[3], packet.data[4]);
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
chartSession.send('create_study', [
|
|
364
|
-
chartSession.sessionID,
|
|
365
|
-
`${this.#studID}`,
|
|
366
|
-
'st1',
|
|
367
|
-
'$prices',
|
|
368
|
-
this.instance.type,
|
|
369
|
-
getInputs(this.instance),
|
|
370
|
-
]);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* @param {PineIndicator | BuiltInIndicator} indicator Indicator instance
|
|
375
|
-
*/
|
|
376
|
-
setIndicator(indicator) {
|
|
377
|
-
if (!(indicator instanceof PineIndicator) && !(indicator instanceof BuiltInIndicator)) {
|
|
378
|
-
throw new Error(`Indicator argument must be an instance of PineIndicator or BuiltInIndicator.
|
|
379
|
-
Please use 'TradingView.getIndicator(...)' function.`);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
this.instance = indicator;
|
|
383
|
-
|
|
384
|
-
chartSession.send('modify_study', [
|
|
385
|
-
chartSession.sessionID,
|
|
386
|
-
`${this.#studID}`,
|
|
387
|
-
'st1',
|
|
388
|
-
getInputs(this.instance),
|
|
389
|
-
]);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* When the indicator is ready
|
|
394
|
-
* @param {() => void} cb
|
|
395
|
-
* @event
|
|
396
|
-
*/
|
|
397
|
-
onReady(cb) {
|
|
398
|
-
this.#callbacks.studyCompleted.push(cb);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* @typedef {'plots' | 'report.currency'
|
|
403
|
-
* | 'report.settings' | 'report.perf'
|
|
404
|
-
* | 'report.trades' | 'report.history'
|
|
405
|
-
* | 'graphic'
|
|
406
|
-
* } UpdateChangeType
|
|
407
|
-
*/
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* When an indicator update happens
|
|
411
|
-
* @param {(changes: UpdateChangeType[]) => void} cb
|
|
412
|
-
* @event
|
|
413
|
-
*/
|
|
414
|
-
onUpdate(cb) {
|
|
415
|
-
this.#callbacks.update.push(cb);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* When indicator error happens
|
|
420
|
-
* @param {(...any) => void} cb Callback
|
|
421
|
-
* @event
|
|
422
|
-
*/
|
|
423
|
-
onError(cb) {
|
|
424
|
-
this.#callbacks.error.push(cb);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/** Remove the study */
|
|
428
|
-
remove() {
|
|
429
|
-
chartSession.send('remove_study', [
|
|
430
|
-
chartSession.sessionID,
|
|
431
|
-
this.#studID,
|
|
432
|
-
]);
|
|
433
|
-
delete this.#studyListeners[this.#studID];
|
|
434
|
-
}
|
|
435
|
-
};
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {'Volume@tv-basicstudies-241'
|
|
3
|
-
* | 'VbPFixed@tv-basicstudies-241'
|
|
4
|
-
* | 'VbPFixed@tv-basicstudies-241!'
|
|
5
|
-
* | 'VbPFixed@tv-volumebyprice-53!'
|
|
6
|
-
* | 'VbPSessions@tv-volumebyprice-53'
|
|
7
|
-
* | 'VbPSessionsRough@tv-volumebyprice-53!'
|
|
8
|
-
* | 'VbPSessionsDetailed@tv-volumebyprice-53!'
|
|
9
|
-
* | 'VbPVisible@tv-volumebyprice-53'} BuiltInIndicatorType Built-in indicator type
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {'rowsLayout' | 'rows' | 'volume'
|
|
14
|
-
* | 'vaVolume' | 'subscribeRealtime'
|
|
15
|
-
* | 'first_bar_time' | 'first_visible_bar_time'
|
|
16
|
-
* | 'last_bar_time' | 'last_visible_bar_time'
|
|
17
|
-
* | 'extendPocRight'} BuiltInIndicatorOption Built-in indicator Option
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const defaultValues = {
|
|
21
|
-
'Volume@tv-basicstudies-241': {
|
|
22
|
-
length: 20,
|
|
23
|
-
col_prev_close: false,
|
|
24
|
-
},
|
|
25
|
-
'VbPFixed@tv-basicstudies-241': {
|
|
26
|
-
rowsLayout: 'Number Of Rows',
|
|
27
|
-
rows: 24,
|
|
28
|
-
volume: 'Up/Down',
|
|
29
|
-
vaVolume: 70,
|
|
30
|
-
subscribeRealtime: false,
|
|
31
|
-
first_bar_time: NaN,
|
|
32
|
-
last_bar_time: Date.now(),
|
|
33
|
-
extendToRight: false,
|
|
34
|
-
mapRightBoundaryToBarStartTime: true,
|
|
35
|
-
},
|
|
36
|
-
'VbPFixed@tv-basicstudies-241!': {
|
|
37
|
-
rowsLayout: 'Number Of Rows',
|
|
38
|
-
rows: 24,
|
|
39
|
-
volume: 'Up/Down',
|
|
40
|
-
vaVolume: 70,
|
|
41
|
-
subscribeRealtime: false,
|
|
42
|
-
first_bar_time: NaN,
|
|
43
|
-
last_bar_time: Date.now(),
|
|
44
|
-
},
|
|
45
|
-
'VbPFixed@tv-volumebyprice-53!': {
|
|
46
|
-
rowsLayout: 'Number Of Rows',
|
|
47
|
-
rows: 24,
|
|
48
|
-
volume: 'Up/Down',
|
|
49
|
-
vaVolume: 70,
|
|
50
|
-
subscribeRealtime: false,
|
|
51
|
-
first_bar_time: NaN,
|
|
52
|
-
last_bar_time: Date.now(),
|
|
53
|
-
},
|
|
54
|
-
'VbPSessions@tv-volumebyprice-53': {
|
|
55
|
-
rowsLayout: 'Number Of Rows',
|
|
56
|
-
rows: 24,
|
|
57
|
-
volume: 'Up/Down',
|
|
58
|
-
vaVolume: 70,
|
|
59
|
-
extendPocRight: false,
|
|
60
|
-
},
|
|
61
|
-
'VbPSessionsRough@tv-volumebyprice-53!': {
|
|
62
|
-
volume: 'Up/Down',
|
|
63
|
-
vaVolume: 70,
|
|
64
|
-
},
|
|
65
|
-
'VbPSessionsDetailed@tv-volumebyprice-53!': {
|
|
66
|
-
volume: 'Up/Down',
|
|
67
|
-
vaVolume: 70,
|
|
68
|
-
subscribeRealtime: false,
|
|
69
|
-
first_visible_bar_time: NaN,
|
|
70
|
-
last_visible_bar_time: Date.now(),
|
|
71
|
-
},
|
|
72
|
-
'VbPVisible@tv-volumebyprice-53': {
|
|
73
|
-
rowsLayout: 'Number Of Rows',
|
|
74
|
-
rows: 24,
|
|
75
|
-
volume: 'Up/Down',
|
|
76
|
-
vaVolume: 70,
|
|
77
|
-
subscribeRealtime: false,
|
|
78
|
-
first_visible_bar_time: NaN,
|
|
79
|
-
last_visible_bar_time: Date.now(),
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/** @class */
|
|
84
|
-
module.exports = class BuiltInIndicator {
|
|
85
|
-
/** @type {BuiltInIndicatorType} */
|
|
86
|
-
#type;
|
|
87
|
-
|
|
88
|
-
/** @return {BuiltInIndicatorType} Indicator script */
|
|
89
|
-
get type() {
|
|
90
|
-
return this.#type;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** @type {Object<string, any>} */
|
|
94
|
-
#options = {};
|
|
95
|
-
|
|
96
|
-
/** @return {Object<string, any>} Indicator script */
|
|
97
|
-
get options() {
|
|
98
|
-
return this.#options;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* @param {BuiltInIndicatorType} type Buit-in indocator raw type
|
|
103
|
-
*/
|
|
104
|
-
constructor(type = '') {
|
|
105
|
-
if (!type) throw new Error(`Wrong buit-in indicator type "${type}".`);
|
|
106
|
-
|
|
107
|
-
this.#type = type;
|
|
108
|
-
if (defaultValues[type]) this.#options = { ...defaultValues[type] };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Set an option
|
|
113
|
-
* @param {BuiltInIndicatorOption} key The option you want to change
|
|
114
|
-
* @param {*} value The new value of the property
|
|
115
|
-
* @param {boolean} FORCE Ignore type and key verifications
|
|
116
|
-
*/
|
|
117
|
-
setOption(key, value, FORCE = false) {
|
|
118
|
-
if (FORCE) {
|
|
119
|
-
this.#options[key] = value;
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (defaultValues[this.#type] && defaultValues[this.#type][key] !== undefined) {
|
|
124
|
-
const requiredType = typeof defaultValues[this.#type][key];
|
|
125
|
-
const valType = typeof value;
|
|
126
|
-
if (requiredType !== valType) {
|
|
127
|
-
throw new Error(`Wrong '${key}' value type '${valType}' (must be '${requiredType}')`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (defaultValues[this.#type] && defaultValues[this.#type][key] === undefined) {
|
|
132
|
-
throw new Error(`Option '${key}' is denied with '${this.#type}' indicator`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.#options[key] = value;
|
|
136
|
-
}
|
|
137
|
-
};
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} IndicatorInput
|
|
3
|
-
* @property {string} name Input name
|
|
4
|
-
* @property {string} inline Input inline name
|
|
5
|
-
* @property {string} [internalID] Input internal ID
|
|
6
|
-
* @property {string} [tooltip] Input tooltip
|
|
7
|
-
* @property {'text' | 'source' | 'integer'
|
|
8
|
-
* | 'float' | 'resolution' | 'bool' | 'color'
|
|
9
|
-
* } type Input type
|
|
10
|
-
* @property {string | number | boolean} value Input default value
|
|
11
|
-
* @property {boolean} isHidden If the input is hidden
|
|
12
|
-
* @property {boolean} isFake If the input is fake
|
|
13
|
-
* @property {string[]} [options] Input options if the input is a select
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {Object} Indicator
|
|
18
|
-
* @property {string} pineId Indicator ID
|
|
19
|
-
* @property {string} pineVersion Indicator version
|
|
20
|
-
* @property {string} description Indicator description
|
|
21
|
-
* @property {string} shortDescription Indicator short description
|
|
22
|
-
* @property {Object<string, IndicatorInput>} inputs Indicator inputs
|
|
23
|
-
* @property {Object<string, string>} plots Indicator plots
|
|
24
|
-
* @property {string} script Indicator script
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {'Script@tv-scripting-101!'
|
|
29
|
-
* | 'StrategyScript@tv-scripting-101!'} IndicatorType Indicator type
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/** @class */
|
|
33
|
-
module.exports = class PineIndicator {
|
|
34
|
-
#options;
|
|
35
|
-
|
|
36
|
-
/** @type {IndicatorType} */
|
|
37
|
-
#type = 'Script@tv-scripting-101!';
|
|
38
|
-
|
|
39
|
-
/** @param {Indicator} options Indicator */
|
|
40
|
-
constructor(options) {
|
|
41
|
-
this.#options = options;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** @return {string} Indicator ID */
|
|
45
|
-
get pineId() {
|
|
46
|
-
return this.#options.pineId;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** @return {string} Indicator version */
|
|
50
|
-
get pineVersion() {
|
|
51
|
-
return this.#options.pineVersion;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** @return {string} Indicator description */
|
|
55
|
-
get description() {
|
|
56
|
-
return this.#options.description;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** @return {string} Indicator short description */
|
|
60
|
-
get shortDescription() {
|
|
61
|
-
return this.#options.shortDescription;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** @return {Object<string, IndicatorInput>} Indicator inputs */
|
|
65
|
-
get inputs() {
|
|
66
|
-
return this.#options.inputs;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** @return {Object<string, string>} Indicator plots */
|
|
70
|
-
get plots() {
|
|
71
|
-
return this.#options.plots;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** @return {IndicatorType} Indicator script */
|
|
75
|
-
get type() {
|
|
76
|
-
return this.#type;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Set the indicator type
|
|
81
|
-
* @param {IndicatorType} type Indicator type
|
|
82
|
-
*/
|
|
83
|
-
setType(type = 'Script@tv-scripting-101!') {
|
|
84
|
-
this.#type = type;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** @return {string} Indicator script */
|
|
88
|
-
get script() {
|
|
89
|
-
return this.#options.script;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Set an option
|
|
94
|
-
* @param {number | string} key The key can be ID of the property (`in_{ID}`),
|
|
95
|
-
* the inline name or the internalID.
|
|
96
|
-
* @param {*} value The new value of the property
|
|
97
|
-
*/
|
|
98
|
-
setOption(key, value) {
|
|
99
|
-
let propI = '';
|
|
100
|
-
|
|
101
|
-
if (this.#options.inputs[`in_${key}`]) propI = `in_${key}`;
|
|
102
|
-
else if (this.#options.inputs[key]) propI = key;
|
|
103
|
-
else {
|
|
104
|
-
propI = Object.keys(this.#options.inputs).find((I) => (
|
|
105
|
-
this.#options.inputs[I].inline === key
|
|
106
|
-
|| this.#options.inputs[I].internalID === key
|
|
107
|
-
));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (propI && this.#options.inputs[propI]) {
|
|
111
|
-
const input = this.#options.inputs[propI];
|
|
112
|
-
|
|
113
|
-
const types = {
|
|
114
|
-
bool: 'Boolean',
|
|
115
|
-
integer: 'Number',
|
|
116
|
-
float: 'Number',
|
|
117
|
-
text: 'String',
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// eslint-disable-next-line valid-typeof
|
|
121
|
-
if (types[input.type] && typeof value !== types[input.type].toLowerCase()) {
|
|
122
|
-
throw new Error(`Input '${input.name}' (${propI}) must be a ${types[input.type]} !`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (input.options && !input.options.includes(value)) {
|
|
126
|
-
throw new Error(`Input '${input.name}' (${propI}) must be one of these values:`, input.options);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
input.value = value;
|
|
130
|
-
} else throw new Error(`Input '${key}' not found (${propI}).`);
|
|
131
|
-
}
|
|
132
|
-
};
|