oakscriptjs 0.3.0 → 0.5.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/dist/index.cjs +13 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +13 -1
- package/dist/runtime/series.d.ts +1 -0
- package/dist/runtime/series.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +19 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/script/index.cjs +3630 -0
- package/dist/script/index.d.ts +290 -0
- package/dist/script/index.d.ts.map +1 -0
- package/dist/script/index.mjs +3613 -0
- package/dist/types/metadata.d.ts +23 -0
- package/dist/types/metadata.d.ts.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,3630 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/script/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BarData: () => BarData,
|
|
24
|
+
Series: () => Series,
|
|
25
|
+
alertcondition: () => alertcondition,
|
|
26
|
+
barcolor: () => barcolor,
|
|
27
|
+
bgcolor: () => bgcolor,
|
|
28
|
+
close: () => close,
|
|
29
|
+
color: () => color,
|
|
30
|
+
eachBar: () => eachBar,
|
|
31
|
+
executeScript: () => executeScript,
|
|
32
|
+
fill: () => fill,
|
|
33
|
+
high: () => high,
|
|
34
|
+
hl2: () => hl2,
|
|
35
|
+
hlc3: () => hlc3,
|
|
36
|
+
hlcc4: () => hlcc4,
|
|
37
|
+
hline: () => hline,
|
|
38
|
+
indicator: () => indicator,
|
|
39
|
+
input: () => input,
|
|
40
|
+
low: () => low,
|
|
41
|
+
math: () => math_exports,
|
|
42
|
+
na: () => isNA,
|
|
43
|
+
nz: () => nz,
|
|
44
|
+
ohlc4: () => ohlc4,
|
|
45
|
+
open: () => open,
|
|
46
|
+
plot: () => plot,
|
|
47
|
+
plotchar: () => plotchar,
|
|
48
|
+
plotshape: () => plotshape,
|
|
49
|
+
seriesOf: () => seriesOf,
|
|
50
|
+
ta: () => ta2,
|
|
51
|
+
volume: () => volume
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
54
|
+
|
|
55
|
+
// src/runtime/series.ts
|
|
56
|
+
var BarData = class _BarData {
|
|
57
|
+
_bars;
|
|
58
|
+
_version = 0;
|
|
59
|
+
/**
|
|
60
|
+
* Create a new BarData wrapper
|
|
61
|
+
* @param bars - Initial bar data (defaults to empty array)
|
|
62
|
+
*/
|
|
63
|
+
constructor(bars2 = []) {
|
|
64
|
+
this._bars = bars2;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the current version number
|
|
68
|
+
* Version increments whenever data is mutated
|
|
69
|
+
*/
|
|
70
|
+
get version() {
|
|
71
|
+
return this._version;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the underlying bar array
|
|
75
|
+
* Note: Direct mutations to this array will NOT increment version
|
|
76
|
+
* Use the provided mutation methods instead
|
|
77
|
+
*/
|
|
78
|
+
get bars() {
|
|
79
|
+
return this._bars;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get the number of bars
|
|
83
|
+
*/
|
|
84
|
+
get length() {
|
|
85
|
+
return this._bars.length;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Add a new bar to the end
|
|
89
|
+
* @param bar - Bar to add
|
|
90
|
+
*/
|
|
91
|
+
push(bar) {
|
|
92
|
+
this._bars.push(bar);
|
|
93
|
+
this._version++;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Remove and return the last bar
|
|
97
|
+
* @returns The removed bar, or undefined if empty
|
|
98
|
+
*/
|
|
99
|
+
pop() {
|
|
100
|
+
const bar = this._bars.pop();
|
|
101
|
+
if (bar !== void 0) {
|
|
102
|
+
this._version++;
|
|
103
|
+
}
|
|
104
|
+
return bar;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Replace a bar at a specific index
|
|
108
|
+
* @param index - Index to update
|
|
109
|
+
* @param bar - New bar data
|
|
110
|
+
*/
|
|
111
|
+
set(index, bar) {
|
|
112
|
+
if (index >= 0 && index < this._bars.length) {
|
|
113
|
+
this._bars[index] = bar;
|
|
114
|
+
this._version++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Update the last bar (useful for real-time/streaming updates)
|
|
119
|
+
* @param bar - New bar data
|
|
120
|
+
*/
|
|
121
|
+
updateLast(bar) {
|
|
122
|
+
if (this._bars.length > 0) {
|
|
123
|
+
this._bars[this._bars.length - 1] = bar;
|
|
124
|
+
this._version++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Replace all bars with new data
|
|
129
|
+
* @param bars - New bar array
|
|
130
|
+
*/
|
|
131
|
+
setAll(bars2) {
|
|
132
|
+
this._bars = bars2;
|
|
133
|
+
this._version++;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Manually increment version (for advanced use cases)
|
|
137
|
+
* Call this after direct mutations to the bars array
|
|
138
|
+
*/
|
|
139
|
+
invalidate() {
|
|
140
|
+
this._version++;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get a bar at a specific index
|
|
144
|
+
* @param index - Bar index
|
|
145
|
+
* @returns Bar at that index, or undefined
|
|
146
|
+
*/
|
|
147
|
+
at(index) {
|
|
148
|
+
return this._bars[index];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create a BarData from an existing Bar array
|
|
152
|
+
* @param bars - Bar array
|
|
153
|
+
* @returns New BarData instance
|
|
154
|
+
*/
|
|
155
|
+
static from(bars2) {
|
|
156
|
+
return new _BarData(bars2);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var Series = class _Series {
|
|
160
|
+
extractor;
|
|
161
|
+
dataSource;
|
|
162
|
+
cached = null;
|
|
163
|
+
cachedVersion = -1;
|
|
164
|
+
/**
|
|
165
|
+
* Create a new Series
|
|
166
|
+
*
|
|
167
|
+
* @param data - Bar data (Bar[] or BarData)
|
|
168
|
+
* @param extractor - Function to extract/compute value for each bar
|
|
169
|
+
*/
|
|
170
|
+
constructor(data, extractor) {
|
|
171
|
+
this.dataSource = data instanceof BarData ? data : new BarData(data);
|
|
172
|
+
this.extractor = extractor;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get the underlying bar data
|
|
176
|
+
* @returns Bar array
|
|
177
|
+
*/
|
|
178
|
+
get bars() {
|
|
179
|
+
return this.dataSource.bars;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get the underlying BarData source
|
|
183
|
+
* @returns BarData instance
|
|
184
|
+
*/
|
|
185
|
+
get barData() {
|
|
186
|
+
return this.dataSource;
|
|
187
|
+
}
|
|
188
|
+
// ============================================
|
|
189
|
+
// Static Factory Methods
|
|
190
|
+
// ============================================
|
|
191
|
+
/**
|
|
192
|
+
* Create Series from bar data by extracting a specific field
|
|
193
|
+
* @param bars - Bar data (Bar[] or BarData)
|
|
194
|
+
* @param field - Field to extract ('open', 'high', 'low', 'close', 'volume')
|
|
195
|
+
* @returns Series with extracted field values
|
|
196
|
+
*/
|
|
197
|
+
static fromBars(bars2, field) {
|
|
198
|
+
return new _Series(bars2, (bar) => bar[field] ?? NaN);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create a constant series (same value for all bars)
|
|
202
|
+
* @param bars - Bar data (Bar[] or BarData)
|
|
203
|
+
* @param value - Constant value
|
|
204
|
+
* @returns Series with constant value
|
|
205
|
+
*/
|
|
206
|
+
static constant(bars2, value) {
|
|
207
|
+
return new _Series(bars2, () => value);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Create series from array of values
|
|
211
|
+
* @param bars - Bar data (Bar[] or BarData) for time alignment
|
|
212
|
+
* @param values - Array of values
|
|
213
|
+
* @returns Series
|
|
214
|
+
*/
|
|
215
|
+
static fromArray(bars2, values) {
|
|
216
|
+
return new _Series(bars2, (_bar, i) => values[i] ?? NaN);
|
|
217
|
+
}
|
|
218
|
+
// ============================================
|
|
219
|
+
// Arithmetic Operations
|
|
220
|
+
// ============================================
|
|
221
|
+
/**
|
|
222
|
+
* Add two series or a series and a number
|
|
223
|
+
* @param other - Series or number to add
|
|
224
|
+
* @returns New Series representing the sum
|
|
225
|
+
*/
|
|
226
|
+
add(other) {
|
|
227
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
228
|
+
const a = this.extractor(bar, i, data);
|
|
229
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
230
|
+
return a + b2;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Subtract two series or subtract a number from a series
|
|
235
|
+
* @param other - Series or number to subtract
|
|
236
|
+
* @returns New Series representing the difference
|
|
237
|
+
*/
|
|
238
|
+
sub(other) {
|
|
239
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
240
|
+
const a = this.extractor(bar, i, data);
|
|
241
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
242
|
+
return a - b2;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Multiply two series or a series by a number
|
|
247
|
+
* @param other - Series or number to multiply
|
|
248
|
+
* @returns New Series representing the product
|
|
249
|
+
*/
|
|
250
|
+
mul(other) {
|
|
251
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
252
|
+
const a = this.extractor(bar, i, data);
|
|
253
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
254
|
+
return a * b2;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Raise the series to a power (PineScript math.pow on a series)
|
|
259
|
+
* @param exponent - Series or number exponent
|
|
260
|
+
* @returns New Series representing base ** exponent
|
|
261
|
+
*/
|
|
262
|
+
pow(exponent) {
|
|
263
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
264
|
+
const a = this.extractor(bar, i, data);
|
|
265
|
+
const b2 = typeof exponent === "number" ? exponent : exponent.extractor(bar, i, data);
|
|
266
|
+
return Math.pow(a, b2);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Divide two series or a series by a number
|
|
271
|
+
* @param other - Series or number to divide by
|
|
272
|
+
* @returns New Series representing the quotient
|
|
273
|
+
*/
|
|
274
|
+
div(other) {
|
|
275
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
276
|
+
const a = this.extractor(bar, i, data);
|
|
277
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
278
|
+
return b2 !== 0 ? a / b2 : NaN;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Modulo operation
|
|
283
|
+
* @param other - Series or number for modulo
|
|
284
|
+
* @returns New Series representing the remainder
|
|
285
|
+
*/
|
|
286
|
+
mod(other) {
|
|
287
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
288
|
+
const a = this.extractor(bar, i, data);
|
|
289
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
290
|
+
return b2 !== 0 ? a % b2 : NaN;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Negation
|
|
295
|
+
* @returns New Series with negated values
|
|
296
|
+
*/
|
|
297
|
+
neg() {
|
|
298
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
299
|
+
return -this.extractor(bar, i, data);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
// ============================================
|
|
303
|
+
// Comparison Operations
|
|
304
|
+
// ============================================
|
|
305
|
+
/**
|
|
306
|
+
* Greater than comparison
|
|
307
|
+
* @param other - Series or number to compare
|
|
308
|
+
* @returns New Series with 1 where true, 0 where false
|
|
309
|
+
*/
|
|
310
|
+
gt(other) {
|
|
311
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
312
|
+
const a = this.extractor(bar, i, data);
|
|
313
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
314
|
+
return a > b2 ? 1 : 0;
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Greater than or equal comparison
|
|
319
|
+
* @param other - Series or number to compare
|
|
320
|
+
* @returns New Series with 1 where true, 0 where false
|
|
321
|
+
*/
|
|
322
|
+
gte(other) {
|
|
323
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
324
|
+
const a = this.extractor(bar, i, data);
|
|
325
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
326
|
+
return a >= b2 ? 1 : 0;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Less than comparison
|
|
331
|
+
* @param other - Series or number to compare
|
|
332
|
+
* @returns New Series with 1 where true, 0 where false
|
|
333
|
+
*/
|
|
334
|
+
lt(other) {
|
|
335
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
336
|
+
const a = this.extractor(bar, i, data);
|
|
337
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
338
|
+
return a < b2 ? 1 : 0;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Less than or equal comparison
|
|
343
|
+
* @param other - Series or number to compare
|
|
344
|
+
* @returns New Series with 1 where true, 0 where false
|
|
345
|
+
*/
|
|
346
|
+
lte(other) {
|
|
347
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
348
|
+
const a = this.extractor(bar, i, data);
|
|
349
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
350
|
+
return a <= b2 ? 1 : 0;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Equality comparison
|
|
355
|
+
* @param other - Series or number to compare
|
|
356
|
+
* @returns New Series with 1 where true, 0 where false
|
|
357
|
+
*/
|
|
358
|
+
eq(other) {
|
|
359
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
360
|
+
const a = this.extractor(bar, i, data);
|
|
361
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
362
|
+
return a === b2 ? 1 : 0;
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Not equal comparison
|
|
367
|
+
* @param other - Series or number to compare
|
|
368
|
+
* @returns New Series with 1 where true, 0 where false
|
|
369
|
+
*/
|
|
370
|
+
neq(other) {
|
|
371
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
372
|
+
const a = this.extractor(bar, i, data);
|
|
373
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
374
|
+
return a !== b2 ? 1 : 0;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
// ============================================
|
|
378
|
+
// Logical Operations (for boolean series)
|
|
379
|
+
// ============================================
|
|
380
|
+
/**
|
|
381
|
+
* Logical AND
|
|
382
|
+
* @param other - Series or number (truthy/falsy)
|
|
383
|
+
* @returns New Series with 1 where both true, 0 otherwise
|
|
384
|
+
*/
|
|
385
|
+
and(other) {
|
|
386
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
387
|
+
const a = this.extractor(bar, i, data);
|
|
388
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
389
|
+
return a && b2 ? 1 : 0;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Logical OR
|
|
394
|
+
* @param other - Series or number (truthy/falsy)
|
|
395
|
+
* @returns New Series with 1 where either true, 0 otherwise
|
|
396
|
+
*/
|
|
397
|
+
or(other) {
|
|
398
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
399
|
+
const a = this.extractor(bar, i, data);
|
|
400
|
+
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
401
|
+
return a || b2 ? 1 : 0;
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Logical NOT
|
|
406
|
+
* @returns New Series with inverted boolean values
|
|
407
|
+
*/
|
|
408
|
+
not() {
|
|
409
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
410
|
+
const a = this.extractor(bar, i, data);
|
|
411
|
+
return !a ? 1 : 0;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
// ============================================
|
|
415
|
+
// Conditional Selection
|
|
416
|
+
// ============================================
|
|
417
|
+
/**
|
|
418
|
+
* Bar-by-bar conditional selection (ternary operator for Series)
|
|
419
|
+
* Uses this Series as the condition (truthy = nonzero, falsy = zero/NaN)
|
|
420
|
+
*
|
|
421
|
+
* @param trueValue - Value or Series to use when condition is truthy
|
|
422
|
+
* @param falseValue - Value or Series to use when condition is falsy
|
|
423
|
+
* @returns New Series with bar-by-bar selected values
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```typescript
|
|
427
|
+
* // PineScript: result = condition ? trueVal : falseVal
|
|
428
|
+
* const result = condition.iff(trueVal, falseVal);
|
|
429
|
+
*
|
|
430
|
+
* // Example: RSI formula uses ternary for edge cases
|
|
431
|
+
* // rsi = down == 0 ? 100 : up == 0 ? 0 : formula
|
|
432
|
+
* const rsi = down.eq(0).iff(100, up.eq(0).iff(0, formula));
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
iff(trueValue, falseValue) {
|
|
436
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
437
|
+
const condition = this.extractor(bar, i, data);
|
|
438
|
+
const isTruthy = condition !== 0 && !Number.isNaN(condition);
|
|
439
|
+
if (isTruthy) {
|
|
440
|
+
return typeof trueValue === "number" ? trueValue : trueValue.extractor(bar, i, data);
|
|
441
|
+
} else {
|
|
442
|
+
return typeof falseValue === "number" ? falseValue : falseValue.extractor(bar, i, data);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
// ============================================
|
|
447
|
+
// Offset/History
|
|
448
|
+
// ============================================
|
|
449
|
+
/**
|
|
450
|
+
* Access previous bars (like close[1] in PineScript)
|
|
451
|
+
* @param offset - Number of bars back (positive = past)
|
|
452
|
+
* @returns New Series with offset values
|
|
453
|
+
*/
|
|
454
|
+
offset(offset) {
|
|
455
|
+
return new _Series(this.dataSource, (_bar, i, data) => {
|
|
456
|
+
const targetIndex = i - offset;
|
|
457
|
+
if (targetIndex < 0 || targetIndex >= data.length) {
|
|
458
|
+
return NaN;
|
|
459
|
+
}
|
|
460
|
+
return this.extractor(data[targetIndex], targetIndex, data);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
// ============================================
|
|
464
|
+
// Computation & Access
|
|
465
|
+
// ============================================
|
|
466
|
+
/**
|
|
467
|
+
* Compute all values for the series
|
|
468
|
+
*
|
|
469
|
+
* Now includes automatic cache invalidation:
|
|
470
|
+
* - Checks if dataSource version has changed since cache was computed
|
|
471
|
+
* - Recomputes if version mismatch detected
|
|
472
|
+
* - Stores version when cache is created
|
|
473
|
+
*
|
|
474
|
+
* @returns Array of computed values
|
|
475
|
+
*/
|
|
476
|
+
toArray() {
|
|
477
|
+
if (this.cached !== null && this.cachedVersion === this.dataSource.version) {
|
|
478
|
+
return this.cached;
|
|
479
|
+
}
|
|
480
|
+
const bars2 = this.dataSource.bars;
|
|
481
|
+
this.cached = bars2.map((bar, i) => this.extractor(bar, i, bars2));
|
|
482
|
+
this.cachedVersion = this.dataSource.version;
|
|
483
|
+
return this.cached;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Materialize this Series by eagerly computing values and breaking closure chain
|
|
487
|
+
*
|
|
488
|
+
* This method addresses the closure chain memory leak issue:
|
|
489
|
+
* - Computes all values immediately
|
|
490
|
+
* - Creates a new Series that stores values directly
|
|
491
|
+
* - Breaks references to parent Series objects
|
|
492
|
+
* - Useful for complex expressions to free intermediate Series memory
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```typescript
|
|
496
|
+
* // Without materialize: keeps a, b, c, d in memory via closures
|
|
497
|
+
* const result = a.add(b).mul(c).div(d).sub(e);
|
|
498
|
+
*
|
|
499
|
+
* // With materialize: breaks chain, frees a, b, c memory
|
|
500
|
+
* const result = a.add(b).mul(c).materialize().div(d).sub(e);
|
|
501
|
+
* ```
|
|
502
|
+
*
|
|
503
|
+
* @returns New Series with computed values and no closure dependencies
|
|
504
|
+
*/
|
|
505
|
+
materialize() {
|
|
506
|
+
const values = this.toArray();
|
|
507
|
+
const valuesCopy = [...values];
|
|
508
|
+
return _Series.fromArray(this.dataSource, valuesCopy);
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Invalidate cache (when data changes)
|
|
512
|
+
*
|
|
513
|
+
* Note: With BarData versioning, manual invalidation is rarely needed.
|
|
514
|
+
* The cache is automatically invalidated when the underlying BarData changes.
|
|
515
|
+
* This method is kept for backward compatibility and advanced use cases.
|
|
516
|
+
*
|
|
517
|
+
* @internal
|
|
518
|
+
*/
|
|
519
|
+
_invalidate() {
|
|
520
|
+
this.cached = null;
|
|
521
|
+
this.cachedVersion = -1;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Compute all values (alias for toArray)
|
|
525
|
+
* @returns Array of computed values
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
528
|
+
_compute() {
|
|
529
|
+
return this.toArray();
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get value at specific index
|
|
533
|
+
* @param index - Bar index
|
|
534
|
+
* @returns Value at that index
|
|
535
|
+
*/
|
|
536
|
+
get(index) {
|
|
537
|
+
const values = this.toArray();
|
|
538
|
+
return values[index] ?? NaN;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Get the last value (most recent bar)
|
|
542
|
+
* @returns Most recent value
|
|
543
|
+
*/
|
|
544
|
+
last() {
|
|
545
|
+
const values = this.toArray();
|
|
546
|
+
return values[values.length - 1] ?? NaN;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Get number of values
|
|
550
|
+
* @returns Length of series
|
|
551
|
+
*/
|
|
552
|
+
length() {
|
|
553
|
+
return this.dataSource.length;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Convert series to time-value pairs for charting
|
|
557
|
+
* @returns Array of { time, value } objects
|
|
558
|
+
*/
|
|
559
|
+
toTimeValuePairs() {
|
|
560
|
+
const values = this.toArray();
|
|
561
|
+
const bars2 = this.dataSource.bars;
|
|
562
|
+
return bars2.map((bar, i) => ({
|
|
563
|
+
time: bar.time,
|
|
564
|
+
value: values[i]
|
|
565
|
+
})).filter((point) => !Number.isNaN(point.value));
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// src/ta-series.ts
|
|
570
|
+
var ta_series_exports = {};
|
|
571
|
+
__export(ta_series_exports, {
|
|
572
|
+
alma: () => alma2,
|
|
573
|
+
atr: () => atr2,
|
|
574
|
+
barssince: () => barssince2,
|
|
575
|
+
bb: () => bb2,
|
|
576
|
+
bbw: () => bbw2,
|
|
577
|
+
cci: () => cci2,
|
|
578
|
+
change: () => change2,
|
|
579
|
+
cmo: () => cmo2,
|
|
580
|
+
cog: () => cog2,
|
|
581
|
+
correlation: () => correlation2,
|
|
582
|
+
cross: () => cross2,
|
|
583
|
+
crossover: () => crossover2,
|
|
584
|
+
crossunder: () => crossunder2,
|
|
585
|
+
cum: () => cum2,
|
|
586
|
+
dev: () => dev2,
|
|
587
|
+
dmi: () => dmi2,
|
|
588
|
+
ema: () => ema2,
|
|
589
|
+
falling: () => falling2,
|
|
590
|
+
highest: () => highest2,
|
|
591
|
+
highestbars: () => highestbars2,
|
|
592
|
+
hma: () => hma2,
|
|
593
|
+
ichimoku: () => ichimoku2,
|
|
594
|
+
kc: () => kc2,
|
|
595
|
+
kcw: () => kcw2,
|
|
596
|
+
linreg: () => linreg2,
|
|
597
|
+
lowest: () => lowest2,
|
|
598
|
+
lowestbars: () => lowestbars2,
|
|
599
|
+
macd: () => macd2,
|
|
600
|
+
max: () => max2,
|
|
601
|
+
median: () => median2,
|
|
602
|
+
mfi: () => mfi2,
|
|
603
|
+
min: () => min2,
|
|
604
|
+
mode: () => mode2,
|
|
605
|
+
mom: () => mom2,
|
|
606
|
+
percentile_linear_interpolation: () => percentile_linear_interpolation2,
|
|
607
|
+
percentile_nearest_rank: () => percentile_nearest_rank2,
|
|
608
|
+
percentrank: () => percentrank2,
|
|
609
|
+
pivothigh: () => pivothigh2,
|
|
610
|
+
pivotlow: () => pivotlow2,
|
|
611
|
+
range: () => range2,
|
|
612
|
+
rci: () => rci2,
|
|
613
|
+
rising: () => rising2,
|
|
614
|
+
rma: () => rma2,
|
|
615
|
+
roc: () => roc2,
|
|
616
|
+
rsi: () => rsi2,
|
|
617
|
+
sar: () => sar2,
|
|
618
|
+
sma: () => sma2,
|
|
619
|
+
stdev: () => stdev2,
|
|
620
|
+
stoch: () => stoch2,
|
|
621
|
+
supertrend: () => supertrend2,
|
|
622
|
+
swma: () => swma2,
|
|
623
|
+
ta: () => ta,
|
|
624
|
+
tr: () => tr2,
|
|
625
|
+
tsi: () => tsi2,
|
|
626
|
+
valuewhen: () => valuewhen2,
|
|
627
|
+
variance: () => variance2,
|
|
628
|
+
vwap: () => vwap2,
|
|
629
|
+
vwma: () => vwma2,
|
|
630
|
+
wma: () => wma2,
|
|
631
|
+
wpr: () => wpr2,
|
|
632
|
+
zigzag: () => zigzag2
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// src/ta/index.ts
|
|
636
|
+
function sma(source, length) {
|
|
637
|
+
const result = [];
|
|
638
|
+
const len = Math.floor(length);
|
|
639
|
+
for (let i = 0; i < source.length; i++) {
|
|
640
|
+
if (i < len - 1) {
|
|
641
|
+
result.push(NaN);
|
|
642
|
+
} else {
|
|
643
|
+
let sum2 = 0;
|
|
644
|
+
for (let j = 0; j < len; j++) {
|
|
645
|
+
sum2 += source[i - j];
|
|
646
|
+
}
|
|
647
|
+
result.push(sum2 / len);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return result;
|
|
651
|
+
}
|
|
652
|
+
function ema(source, length) {
|
|
653
|
+
const result = [];
|
|
654
|
+
const len = Math.floor(length);
|
|
655
|
+
const multiplier = 2 / (len + 1);
|
|
656
|
+
let firstValidIndex = -1;
|
|
657
|
+
let validCount = 0;
|
|
658
|
+
let initSum = 0;
|
|
659
|
+
for (let i = 0; i < source.length; i++) {
|
|
660
|
+
const val = source[i];
|
|
661
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
662
|
+
initSum += val;
|
|
663
|
+
validCount++;
|
|
664
|
+
if (validCount === len) {
|
|
665
|
+
firstValidIndex = i;
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
let emaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
671
|
+
const emaInitialized = firstValidIndex >= 0;
|
|
672
|
+
for (let i = 0; i < source.length; i++) {
|
|
673
|
+
if (!emaInitialized || i < firstValidIndex) {
|
|
674
|
+
result.push(NaN);
|
|
675
|
+
} else if (i === firstValidIndex) {
|
|
676
|
+
result.push(emaValue);
|
|
677
|
+
} else {
|
|
678
|
+
const val = source[i];
|
|
679
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
680
|
+
emaValue = (val - emaValue) * multiplier + emaValue;
|
|
681
|
+
}
|
|
682
|
+
result.push(emaValue);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return result;
|
|
686
|
+
}
|
|
687
|
+
function rsi(source, length) {
|
|
688
|
+
const result = [];
|
|
689
|
+
const changes = [];
|
|
690
|
+
for (let i = 1; i < source.length; i++) {
|
|
691
|
+
changes.push(source[i] - source[i - 1]);
|
|
692
|
+
}
|
|
693
|
+
const gains = changes.map((c) => c > 0 ? c : 0);
|
|
694
|
+
const losses = changes.map((c) => c < 0 ? -c : 0);
|
|
695
|
+
const avgGains = rma(gains, length);
|
|
696
|
+
const avgLosses = rma(losses, length);
|
|
697
|
+
result.push(NaN);
|
|
698
|
+
for (let i = 0; i < avgGains.length; i++) {
|
|
699
|
+
if (avgLosses[i] === 0) {
|
|
700
|
+
result.push(100);
|
|
701
|
+
} else {
|
|
702
|
+
const rs = avgGains[i] / avgLosses[i];
|
|
703
|
+
result.push(100 - 100 / (1 + rs));
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return result;
|
|
707
|
+
}
|
|
708
|
+
function macd(source, fastLength, slowLength, signalLength) {
|
|
709
|
+
const fastEma = ema(source, fastLength);
|
|
710
|
+
const slowEma = ema(source, slowLength);
|
|
711
|
+
const macdLine = [];
|
|
712
|
+
for (let i = 0; i < source.length; i++) {
|
|
713
|
+
macdLine.push(fastEma[i] - slowEma[i]);
|
|
714
|
+
}
|
|
715
|
+
const signalLine = ema(macdLine, signalLength);
|
|
716
|
+
const histogram = [];
|
|
717
|
+
for (let i = 0; i < source.length; i++) {
|
|
718
|
+
histogram.push(macdLine[i] - signalLine[i]);
|
|
719
|
+
}
|
|
720
|
+
return [macdLine, signalLine, histogram];
|
|
721
|
+
}
|
|
722
|
+
function bb(series, length, mult) {
|
|
723
|
+
const basis = sma(series, length);
|
|
724
|
+
const dev3 = stdev(series, length);
|
|
725
|
+
const upper = [];
|
|
726
|
+
const lower = [];
|
|
727
|
+
for (let i = 0; i < series.length; i++) {
|
|
728
|
+
upper.push(basis[i] + mult * dev3[i]);
|
|
729
|
+
lower.push(basis[i] - mult * dev3[i]);
|
|
730
|
+
}
|
|
731
|
+
return [basis, upper, lower];
|
|
732
|
+
}
|
|
733
|
+
function stdev(source, length) {
|
|
734
|
+
const result = [];
|
|
735
|
+
const len = Math.floor(length);
|
|
736
|
+
const avg2 = sma(source, len);
|
|
737
|
+
for (let i = 0; i < source.length; i++) {
|
|
738
|
+
if (i < len - 1) {
|
|
739
|
+
result.push(NaN);
|
|
740
|
+
} else {
|
|
741
|
+
let sumSquares = 0;
|
|
742
|
+
for (let j = 0; j < len; j++) {
|
|
743
|
+
const diff = source[i - j] - avg2[i];
|
|
744
|
+
sumSquares += diff * diff;
|
|
745
|
+
}
|
|
746
|
+
result.push(Math.sqrt(sumSquares / len));
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
function crossover(series1, series2) {
|
|
752
|
+
const result = [];
|
|
753
|
+
for (let i = 0; i < series1.length; i++) {
|
|
754
|
+
if (i === 0) {
|
|
755
|
+
result.push(false);
|
|
756
|
+
} else {
|
|
757
|
+
result.push(series1[i] > series2[i] && series1[i - 1] <= series2[i - 1]);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
function crossunder(series1, series2) {
|
|
763
|
+
const result = [];
|
|
764
|
+
for (let i = 0; i < series1.length; i++) {
|
|
765
|
+
if (i === 0) {
|
|
766
|
+
result.push(false);
|
|
767
|
+
} else {
|
|
768
|
+
result.push(series1[i] < series2[i] && series1[i - 1] >= series2[i - 1]);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return result;
|
|
772
|
+
}
|
|
773
|
+
function change(source, length = 1) {
|
|
774
|
+
const result = [];
|
|
775
|
+
for (let i = 0; i < source.length; i++) {
|
|
776
|
+
if (i < length) {
|
|
777
|
+
result.push(NaN);
|
|
778
|
+
} else {
|
|
779
|
+
result.push(source[i] - source[i - length]);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return result;
|
|
783
|
+
}
|
|
784
|
+
function tr(handle_na = false, high2, low2, close2) {
|
|
785
|
+
if (!high2 || !low2 || !close2) {
|
|
786
|
+
throw new Error(
|
|
787
|
+
"ta.tr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
const result = [];
|
|
791
|
+
for (let i = 0; i < high2.length; i++) {
|
|
792
|
+
if (i === 0) {
|
|
793
|
+
result.push(high2[i] - low2[i]);
|
|
794
|
+
} else {
|
|
795
|
+
const prevClose = close2[i - 1];
|
|
796
|
+
if (isNaN(prevClose)) {
|
|
797
|
+
if (handle_na) {
|
|
798
|
+
result.push(high2[i] - low2[i]);
|
|
799
|
+
} else {
|
|
800
|
+
result.push(NaN);
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
const tr3 = Math.max(
|
|
804
|
+
high2[i] - low2[i],
|
|
805
|
+
Math.abs(high2[i] - prevClose),
|
|
806
|
+
Math.abs(low2[i] - prevClose)
|
|
807
|
+
);
|
|
808
|
+
result.push(tr3);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return result;
|
|
813
|
+
}
|
|
814
|
+
function atr(length, high2, low2, close2) {
|
|
815
|
+
if (!high2 || !low2 || !close2) {
|
|
816
|
+
throw new Error(
|
|
817
|
+
"ta.atr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
const trueRange = tr(false, high2, low2, close2);
|
|
821
|
+
return rma(trueRange, length);
|
|
822
|
+
}
|
|
823
|
+
function supertrend(factor, atrPeriod, high2, low2, close2, wicks = false) {
|
|
824
|
+
if (!high2 || !low2 || !close2) {
|
|
825
|
+
throw new Error(
|
|
826
|
+
"ta.supertrend() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
const supertrendValues = [];
|
|
830
|
+
const directions = [];
|
|
831
|
+
const source = [];
|
|
832
|
+
for (let i = 0; i < high2.length; i++) {
|
|
833
|
+
source.push((high2[i] + low2[i]) / 2);
|
|
834
|
+
}
|
|
835
|
+
const atrValues = atr(atrPeriod, high2, low2, close2);
|
|
836
|
+
let prevLowerBand = NaN;
|
|
837
|
+
let prevUpperBand = NaN;
|
|
838
|
+
let prevSuperTrend = NaN;
|
|
839
|
+
for (let i = 0; i < source.length; i++) {
|
|
840
|
+
const atrValue = atrValues[i] * factor;
|
|
841
|
+
if (isNaN(atrValue)) {
|
|
842
|
+
supertrendValues.push(NaN);
|
|
843
|
+
directions.push(1);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
let upperBand = source[i] + atrValue;
|
|
847
|
+
let lowerBand = source[i] - atrValue;
|
|
848
|
+
const highPrice = wicks ? high2[i] : close2[i];
|
|
849
|
+
const lowPrice = wicks ? low2[i] : close2[i];
|
|
850
|
+
const prevLowPrice = i > 0 ? wicks ? low2[i - 1] : close2[i - 1] : 0;
|
|
851
|
+
const prevHighPrice = i > 0 ? wicks ? high2[i - 1] : close2[i - 1] : 0;
|
|
852
|
+
if (i > 0 && !isNaN(prevLowerBand) && !isNaN(prevUpperBand)) {
|
|
853
|
+
lowerBand = lowerBand > prevLowerBand || prevLowPrice < prevLowerBand ? lowerBand : prevLowerBand;
|
|
854
|
+
upperBand = upperBand < prevUpperBand || prevHighPrice > prevUpperBand ? upperBand : prevUpperBand;
|
|
855
|
+
}
|
|
856
|
+
let currentDirection;
|
|
857
|
+
if (isNaN(prevSuperTrend)) {
|
|
858
|
+
currentDirection = 1;
|
|
859
|
+
} else if (prevSuperTrend === prevUpperBand) {
|
|
860
|
+
currentDirection = highPrice > upperBand ? -1 : 1;
|
|
861
|
+
} else {
|
|
862
|
+
currentDirection = lowPrice < lowerBand ? 1 : -1;
|
|
863
|
+
}
|
|
864
|
+
const superTrendValue = currentDirection === -1 ? lowerBand : upperBand;
|
|
865
|
+
supertrendValues.push(superTrendValue);
|
|
866
|
+
directions.push(currentDirection);
|
|
867
|
+
prevLowerBand = lowerBand;
|
|
868
|
+
prevUpperBand = upperBand;
|
|
869
|
+
prevSuperTrend = superTrendValue;
|
|
870
|
+
}
|
|
871
|
+
return [supertrendValues, directions];
|
|
872
|
+
}
|
|
873
|
+
function rma(source, length) {
|
|
874
|
+
const result = [];
|
|
875
|
+
const len = Math.floor(length);
|
|
876
|
+
const alpha = 1 / len;
|
|
877
|
+
let firstValidIndex = -1;
|
|
878
|
+
let validCount = 0;
|
|
879
|
+
let initSum = 0;
|
|
880
|
+
for (let i = 0; i < source.length; i++) {
|
|
881
|
+
const val = source[i];
|
|
882
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
883
|
+
initSum += val;
|
|
884
|
+
validCount++;
|
|
885
|
+
if (validCount === len) {
|
|
886
|
+
firstValidIndex = i;
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
let rmaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
892
|
+
const rmaInitialized = firstValidIndex >= 0;
|
|
893
|
+
for (let i = 0; i < source.length; i++) {
|
|
894
|
+
if (!rmaInitialized || i < firstValidIndex) {
|
|
895
|
+
result.push(NaN);
|
|
896
|
+
} else if (i === firstValidIndex) {
|
|
897
|
+
result.push(rmaValue);
|
|
898
|
+
} else {
|
|
899
|
+
const val = source[i];
|
|
900
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
901
|
+
rmaValue = alpha * val + (1 - alpha) * rmaValue;
|
|
902
|
+
}
|
|
903
|
+
result.push(rmaValue);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return result;
|
|
907
|
+
}
|
|
908
|
+
function wma(source, length) {
|
|
909
|
+
const result = [];
|
|
910
|
+
const len = Math.floor(length);
|
|
911
|
+
for (let i = 0; i < source.length; i++) {
|
|
912
|
+
if (i < len - 1) {
|
|
913
|
+
result.push(NaN);
|
|
914
|
+
} else {
|
|
915
|
+
let sum2 = 0;
|
|
916
|
+
let weightSum = 0;
|
|
917
|
+
for (let j = 0; j < len; j++) {
|
|
918
|
+
const weight = len - j;
|
|
919
|
+
sum2 += source[i - j] * weight;
|
|
920
|
+
weightSum += weight;
|
|
921
|
+
}
|
|
922
|
+
result.push(sum2 / weightSum);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return result;
|
|
926
|
+
}
|
|
927
|
+
function highest(source, length) {
|
|
928
|
+
const result = [];
|
|
929
|
+
for (let i = 0; i < source.length; i++) {
|
|
930
|
+
if (i < length - 1) {
|
|
931
|
+
result.push(NaN);
|
|
932
|
+
} else {
|
|
933
|
+
let max4 = -Infinity;
|
|
934
|
+
for (let j = 0; j < length; j++) {
|
|
935
|
+
if (!isNaN(source[i - j])) {
|
|
936
|
+
max4 = Math.max(max4, source[i - j]);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
result.push(max4 === -Infinity ? NaN : max4);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return result;
|
|
943
|
+
}
|
|
944
|
+
function lowest(source, length) {
|
|
945
|
+
const result = [];
|
|
946
|
+
for (let i = 0; i < source.length; i++) {
|
|
947
|
+
if (i < length - 1) {
|
|
948
|
+
result.push(NaN);
|
|
949
|
+
} else {
|
|
950
|
+
let min4 = Infinity;
|
|
951
|
+
for (let j = 0; j < length; j++) {
|
|
952
|
+
if (!isNaN(source[i - j])) {
|
|
953
|
+
min4 = Math.min(min4, source[i - j]);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
result.push(min4 === Infinity ? NaN : min4);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return result;
|
|
960
|
+
}
|
|
961
|
+
function cum(source) {
|
|
962
|
+
const result = [];
|
|
963
|
+
let sum2 = 0;
|
|
964
|
+
for (let i = 0; i < source.length; i++) {
|
|
965
|
+
sum2 += source[i];
|
|
966
|
+
result.push(sum2);
|
|
967
|
+
}
|
|
968
|
+
return result;
|
|
969
|
+
}
|
|
970
|
+
function cross(source1, source2) {
|
|
971
|
+
const result = [];
|
|
972
|
+
for (let i = 0; i < source1.length; i++) {
|
|
973
|
+
if (i === 0) {
|
|
974
|
+
result.push(false);
|
|
975
|
+
} else {
|
|
976
|
+
const crossedUp = source1[i] > source2[i] && source1[i - 1] <= source2[i - 1];
|
|
977
|
+
const crossedDown = source1[i] < source2[i] && source1[i - 1] >= source2[i - 1];
|
|
978
|
+
result.push(crossedUp || crossedDown);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return result;
|
|
982
|
+
}
|
|
983
|
+
function rising(source, length) {
|
|
984
|
+
const result = [];
|
|
985
|
+
for (let i = 0; i < source.length; i++) {
|
|
986
|
+
if (i < length) {
|
|
987
|
+
result.push(false);
|
|
988
|
+
} else {
|
|
989
|
+
let isRising = true;
|
|
990
|
+
for (let j = 1; j <= length; j++) {
|
|
991
|
+
if (source[i - j + 1] <= source[i - j]) {
|
|
992
|
+
isRising = false;
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
result.push(isRising);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
return result;
|
|
1000
|
+
}
|
|
1001
|
+
function falling(source, length) {
|
|
1002
|
+
const result = [];
|
|
1003
|
+
for (let i = 0; i < source.length; i++) {
|
|
1004
|
+
if (i < length) {
|
|
1005
|
+
result.push(false);
|
|
1006
|
+
} else {
|
|
1007
|
+
let isFalling = true;
|
|
1008
|
+
for (let j = 1; j <= length; j++) {
|
|
1009
|
+
if (source[i - j + 1] >= source[i - j]) {
|
|
1010
|
+
isFalling = false;
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
result.push(isFalling);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return result;
|
|
1018
|
+
}
|
|
1019
|
+
function roc(source, length) {
|
|
1020
|
+
const result = [];
|
|
1021
|
+
for (let i = 0; i < source.length; i++) {
|
|
1022
|
+
if (i < length) {
|
|
1023
|
+
result.push(NaN);
|
|
1024
|
+
} else {
|
|
1025
|
+
const oldValue = source[i - length];
|
|
1026
|
+
if (oldValue === 0 || isNaN(oldValue) || isNaN(source[i])) {
|
|
1027
|
+
result.push(NaN);
|
|
1028
|
+
} else {
|
|
1029
|
+
const changeValue = source[i] - oldValue;
|
|
1030
|
+
result.push(100 * changeValue / oldValue);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1035
|
+
}
|
|
1036
|
+
function mom(source, length) {
|
|
1037
|
+
const result = [];
|
|
1038
|
+
for (let i = 0; i < source.length; i++) {
|
|
1039
|
+
if (i < length) {
|
|
1040
|
+
result.push(NaN);
|
|
1041
|
+
} else {
|
|
1042
|
+
if (isNaN(source[i]) || isNaN(source[i - length])) {
|
|
1043
|
+
result.push(NaN);
|
|
1044
|
+
} else {
|
|
1045
|
+
result.push(source[i] - source[i - length]);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return result;
|
|
1050
|
+
}
|
|
1051
|
+
function dev(source, length) {
|
|
1052
|
+
const result = [];
|
|
1053
|
+
const meanValues = sma(source, length);
|
|
1054
|
+
for (let i = 0; i < source.length; i++) {
|
|
1055
|
+
if (i < length - 1 || isNaN(meanValues[i])) {
|
|
1056
|
+
result.push(NaN);
|
|
1057
|
+
} else {
|
|
1058
|
+
let sum2 = 0;
|
|
1059
|
+
for (let j = 0; j < length; j++) {
|
|
1060
|
+
if (!isNaN(source[i - j])) {
|
|
1061
|
+
sum2 += Math.abs(source[i - j] - meanValues[i]);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
result.push(sum2 / length);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return result;
|
|
1068
|
+
}
|
|
1069
|
+
function variance(source, length, biased = true) {
|
|
1070
|
+
const result = [];
|
|
1071
|
+
const meanValues = sma(source, length);
|
|
1072
|
+
for (let i = 0; i < source.length; i++) {
|
|
1073
|
+
if (i < length - 1 || isNaN(meanValues[i])) {
|
|
1074
|
+
result.push(NaN);
|
|
1075
|
+
} else {
|
|
1076
|
+
let sumSquares = 0;
|
|
1077
|
+
let count = 0;
|
|
1078
|
+
for (let j = 0; j < length; j++) {
|
|
1079
|
+
if (!isNaN(source[i - j])) {
|
|
1080
|
+
const diff = source[i - j] - meanValues[i];
|
|
1081
|
+
sumSquares += diff * diff;
|
|
1082
|
+
count++;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const divisor = biased ? count : count - 1;
|
|
1086
|
+
result.push(divisor > 0 ? sumSquares / divisor : NaN);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return result;
|
|
1090
|
+
}
|
|
1091
|
+
function median(source, length) {
|
|
1092
|
+
const result = [];
|
|
1093
|
+
for (let i = 0; i < source.length; i++) {
|
|
1094
|
+
if (i < length - 1) {
|
|
1095
|
+
result.push(NaN);
|
|
1096
|
+
} else {
|
|
1097
|
+
const values = [];
|
|
1098
|
+
for (let j = 0; j < length; j++) {
|
|
1099
|
+
if (!isNaN(source[i - j])) {
|
|
1100
|
+
values.push(source[i - j]);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
if (values.length === 0) {
|
|
1104
|
+
result.push(NaN);
|
|
1105
|
+
} else {
|
|
1106
|
+
values.sort((a, b2) => a - b2);
|
|
1107
|
+
const mid = Math.floor(values.length / 2);
|
|
1108
|
+
if (values.length % 2 === 0) {
|
|
1109
|
+
result.push((values[mid - 1] + values[mid]) / 2);
|
|
1110
|
+
} else {
|
|
1111
|
+
result.push(values[mid]);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
return result;
|
|
1117
|
+
}
|
|
1118
|
+
function swma(source) {
|
|
1119
|
+
const result = [];
|
|
1120
|
+
for (let i = 0; i < source.length; i++) {
|
|
1121
|
+
if (i < 3) {
|
|
1122
|
+
result.push(NaN);
|
|
1123
|
+
} else {
|
|
1124
|
+
if (isNaN(source[i]) || isNaN(source[i - 1]) || isNaN(source[i - 2]) || isNaN(source[i - 3])) {
|
|
1125
|
+
result.push(NaN);
|
|
1126
|
+
} else {
|
|
1127
|
+
const value = source[i - 3] * (1 / 6) + source[i - 2] * (2 / 6) + source[i - 1] * (2 / 6) + source[i] * (1 / 6);
|
|
1128
|
+
result.push(value);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return result;
|
|
1133
|
+
}
|
|
1134
|
+
function vwma(source, length, volume2) {
|
|
1135
|
+
if (!volume2) {
|
|
1136
|
+
throw new Error(
|
|
1137
|
+
"ta.vwma() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
const sourceTimesVolume = [];
|
|
1141
|
+
for (let i = 0; i < source.length; i++) {
|
|
1142
|
+
sourceTimesVolume.push(source[i] * volume2[i]);
|
|
1143
|
+
}
|
|
1144
|
+
const numerator = sma(sourceTimesVolume, length);
|
|
1145
|
+
const denominator = sma(volume2, length);
|
|
1146
|
+
const result = [];
|
|
1147
|
+
for (let i = 0; i < source.length; i++) {
|
|
1148
|
+
if (denominator[i] === 0 || isNaN(denominator[i])) {
|
|
1149
|
+
result.push(NaN);
|
|
1150
|
+
} else {
|
|
1151
|
+
result.push(numerator[i] / denominator[i]);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return result;
|
|
1155
|
+
}
|
|
1156
|
+
function linreg(source, length, offset = 0) {
|
|
1157
|
+
const result = [];
|
|
1158
|
+
const len = Math.floor(length);
|
|
1159
|
+
for (let i = 0; i < source.length; i++) {
|
|
1160
|
+
if (i < len - 1) {
|
|
1161
|
+
result.push(NaN);
|
|
1162
|
+
} else {
|
|
1163
|
+
let hasNaN = false;
|
|
1164
|
+
for (let j = 0; j < len; j++) {
|
|
1165
|
+
if (isNaN(source[i - j])) {
|
|
1166
|
+
hasNaN = true;
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
if (hasNaN) {
|
|
1171
|
+
result.push(NaN);
|
|
1172
|
+
} else {
|
|
1173
|
+
let sumX = 0;
|
|
1174
|
+
let sumY = 0;
|
|
1175
|
+
let sumXY = 0;
|
|
1176
|
+
let sumX2 = 0;
|
|
1177
|
+
for (let j = 0; j < len; j++) {
|
|
1178
|
+
const x2 = j;
|
|
1179
|
+
const y = source[i - (len - 1 - j)];
|
|
1180
|
+
sumX += x2;
|
|
1181
|
+
sumY += y;
|
|
1182
|
+
sumXY += x2 * y;
|
|
1183
|
+
sumX2 += x2 * x2;
|
|
1184
|
+
}
|
|
1185
|
+
const slope = (len * sumXY - sumX * sumY) / (len * sumX2 - sumX * sumX);
|
|
1186
|
+
const intercept = (sumY - slope * sumX) / len;
|
|
1187
|
+
const x = len - 1 - offset;
|
|
1188
|
+
result.push(intercept + slope * x);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
return result;
|
|
1193
|
+
}
|
|
1194
|
+
function correlation(source1, source2, length) {
|
|
1195
|
+
const result = [];
|
|
1196
|
+
for (let i = 0; i < source1.length; i++) {
|
|
1197
|
+
if (i < length - 1) {
|
|
1198
|
+
result.push(NaN);
|
|
1199
|
+
} else {
|
|
1200
|
+
const pairs = [];
|
|
1201
|
+
for (let j = 0; j < length; j++) {
|
|
1202
|
+
if (!isNaN(source1[i - j]) && !isNaN(source2[i - j])) {
|
|
1203
|
+
pairs.push([source1[i - j], source2[i - j]]);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
if (pairs.length === 0) {
|
|
1207
|
+
result.push(NaN);
|
|
1208
|
+
} else {
|
|
1209
|
+
let sum1 = 0;
|
|
1210
|
+
let sum2 = 0;
|
|
1211
|
+
for (const [v1, v2] of pairs) {
|
|
1212
|
+
sum1 += v1;
|
|
1213
|
+
sum2 += v2;
|
|
1214
|
+
}
|
|
1215
|
+
const mean1 = sum1 / pairs.length;
|
|
1216
|
+
const mean2 = sum2 / pairs.length;
|
|
1217
|
+
let numerator = 0;
|
|
1218
|
+
let sum1Sq = 0;
|
|
1219
|
+
let sum2Sq = 0;
|
|
1220
|
+
for (const [v1, v2] of pairs) {
|
|
1221
|
+
const dev1 = v1 - mean1;
|
|
1222
|
+
const dev22 = v2 - mean2;
|
|
1223
|
+
numerator += dev1 * dev22;
|
|
1224
|
+
sum1Sq += dev1 * dev1;
|
|
1225
|
+
sum2Sq += dev22 * dev22;
|
|
1226
|
+
}
|
|
1227
|
+
const denominator = Math.sqrt(sum1Sq * sum2Sq);
|
|
1228
|
+
if (denominator === 0) {
|
|
1229
|
+
result.push(NaN);
|
|
1230
|
+
} else {
|
|
1231
|
+
result.push(numerator / denominator);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return result;
|
|
1237
|
+
}
|
|
1238
|
+
function percentrank(source, length) {
|
|
1239
|
+
const result = [];
|
|
1240
|
+
for (let i = 0; i < source.length; i++) {
|
|
1241
|
+
if (i < length) {
|
|
1242
|
+
result.push(NaN);
|
|
1243
|
+
} else {
|
|
1244
|
+
if (isNaN(source[i])) {
|
|
1245
|
+
result.push(NaN);
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
let hasNaN = false;
|
|
1249
|
+
for (let j = 1; j <= length; j++) {
|
|
1250
|
+
if (isNaN(source[i - j])) {
|
|
1251
|
+
hasNaN = true;
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
if (hasNaN) {
|
|
1256
|
+
result.push(NaN);
|
|
1257
|
+
} else {
|
|
1258
|
+
const currentValue = source[i];
|
|
1259
|
+
let countLessOrEqual = 0;
|
|
1260
|
+
for (let j = 1; j <= length; j++) {
|
|
1261
|
+
if (source[i - j] <= currentValue) {
|
|
1262
|
+
countLessOrEqual++;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
result.push(countLessOrEqual / length * 100);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return result;
|
|
1270
|
+
}
|
|
1271
|
+
function cci(source, length) {
|
|
1272
|
+
const result = [];
|
|
1273
|
+
const smaValues = sma(source, length);
|
|
1274
|
+
const devValues = dev(source, length);
|
|
1275
|
+
for (let i = 0; i < source.length; i++) {
|
|
1276
|
+
if (isNaN(smaValues[i]) || isNaN(devValues[i]) || devValues[i] === 0) {
|
|
1277
|
+
result.push(NaN);
|
|
1278
|
+
} else {
|
|
1279
|
+
const cci3 = (source[i] - smaValues[i]) / (0.015 * devValues[i]);
|
|
1280
|
+
result.push(cci3);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return result;
|
|
1284
|
+
}
|
|
1285
|
+
function stoch(source, high2, low2, length) {
|
|
1286
|
+
const result = [];
|
|
1287
|
+
const lowestValues = lowest(low2, length);
|
|
1288
|
+
const highestValues = highest(high2, length);
|
|
1289
|
+
for (let i = 0; i < source.length; i++) {
|
|
1290
|
+
if (isNaN(lowestValues[i]) || isNaN(highestValues[i])) {
|
|
1291
|
+
result.push(NaN);
|
|
1292
|
+
} else {
|
|
1293
|
+
const range3 = highestValues[i] - lowestValues[i];
|
|
1294
|
+
if (range3 === 0) {
|
|
1295
|
+
result.push(NaN);
|
|
1296
|
+
} else {
|
|
1297
|
+
const stochValue = 100 * (source[i] - lowestValues[i]) / range3;
|
|
1298
|
+
result.push(stochValue);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return result;
|
|
1303
|
+
}
|
|
1304
|
+
function mfi(source, length, volume2) {
|
|
1305
|
+
if (!volume2) {
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
"ta.mfi() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
const result = [];
|
|
1311
|
+
const changes = [NaN];
|
|
1312
|
+
for (let i = 1; i < source.length; i++) {
|
|
1313
|
+
changes.push(source[i] - source[i - 1]);
|
|
1314
|
+
}
|
|
1315
|
+
const positiveFlow = [];
|
|
1316
|
+
const negativeFlow = [];
|
|
1317
|
+
for (let i = 0; i < source.length; i++) {
|
|
1318
|
+
if (i === 0 || isNaN(changes[i])) {
|
|
1319
|
+
positiveFlow.push(0);
|
|
1320
|
+
negativeFlow.push(0);
|
|
1321
|
+
} else if (changes[i] > 0) {
|
|
1322
|
+
positiveFlow.push(volume2[i] * source[i]);
|
|
1323
|
+
negativeFlow.push(0);
|
|
1324
|
+
} else if (changes[i] < 0) {
|
|
1325
|
+
positiveFlow.push(0);
|
|
1326
|
+
negativeFlow.push(volume2[i] * source[i]);
|
|
1327
|
+
} else {
|
|
1328
|
+
positiveFlow.push(0);
|
|
1329
|
+
negativeFlow.push(0);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
for (let i = 0; i < source.length; i++) {
|
|
1333
|
+
if (i < length) {
|
|
1334
|
+
result.push(NaN);
|
|
1335
|
+
} else {
|
|
1336
|
+
let posSum = 0;
|
|
1337
|
+
let negSum = 0;
|
|
1338
|
+
for (let j = 0; j < length; j++) {
|
|
1339
|
+
posSum += positiveFlow[i - j];
|
|
1340
|
+
negSum += negativeFlow[i - j];
|
|
1341
|
+
}
|
|
1342
|
+
if (negSum === 0) {
|
|
1343
|
+
result.push(100);
|
|
1344
|
+
} else {
|
|
1345
|
+
const moneyRatio = posSum / negSum;
|
|
1346
|
+
const mfiValue = 100 - 100 / (1 + moneyRatio);
|
|
1347
|
+
result.push(mfiValue);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
return result;
|
|
1352
|
+
}
|
|
1353
|
+
function hma(source, length) {
|
|
1354
|
+
const halfLength = Math.floor(length / 2);
|
|
1355
|
+
const sqrtLength = Math.floor(Math.sqrt(length));
|
|
1356
|
+
const wmaHalf = wma(source, halfLength);
|
|
1357
|
+
const wmaFull = wma(source, length);
|
|
1358
|
+
const diff = [];
|
|
1359
|
+
for (let i = 0; i < source.length; i++) {
|
|
1360
|
+
diff.push(2 * wmaHalf[i] - wmaFull[i]);
|
|
1361
|
+
}
|
|
1362
|
+
return wma(diff, sqrtLength);
|
|
1363
|
+
}
|
|
1364
|
+
function sar(start, inc, max4, high2, low2, close2) {
|
|
1365
|
+
if (!high2 || !low2 || !close2) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
"ta.sar() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
const result = [];
|
|
1371
|
+
let sarValue = NaN;
|
|
1372
|
+
let extremePoint = NaN;
|
|
1373
|
+
let acceleration = start;
|
|
1374
|
+
let isUpTrend = false;
|
|
1375
|
+
let isFirstTrendBar = false;
|
|
1376
|
+
for (let i = 0; i < close2.length; i++) {
|
|
1377
|
+
if (i === 0) {
|
|
1378
|
+
result.push(NaN);
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
if (i === 1) {
|
|
1382
|
+
if (close2[i] > close2[i - 1]) {
|
|
1383
|
+
isUpTrend = true;
|
|
1384
|
+
extremePoint = high2[i];
|
|
1385
|
+
sarValue = low2[i - 1];
|
|
1386
|
+
} else {
|
|
1387
|
+
isUpTrend = false;
|
|
1388
|
+
extremePoint = low2[i];
|
|
1389
|
+
sarValue = high2[i - 1];
|
|
1390
|
+
}
|
|
1391
|
+
isFirstTrendBar = true;
|
|
1392
|
+
acceleration = start;
|
|
1393
|
+
}
|
|
1394
|
+
sarValue = sarValue + acceleration * (extremePoint - sarValue);
|
|
1395
|
+
if (isUpTrend) {
|
|
1396
|
+
if (sarValue > low2[i]) {
|
|
1397
|
+
isFirstTrendBar = true;
|
|
1398
|
+
isUpTrend = false;
|
|
1399
|
+
sarValue = Math.max(high2[i], extremePoint);
|
|
1400
|
+
extremePoint = low2[i];
|
|
1401
|
+
acceleration = start;
|
|
1402
|
+
}
|
|
1403
|
+
} else {
|
|
1404
|
+
if (sarValue < high2[i]) {
|
|
1405
|
+
isFirstTrendBar = true;
|
|
1406
|
+
isUpTrend = true;
|
|
1407
|
+
sarValue = Math.min(low2[i], extremePoint);
|
|
1408
|
+
extremePoint = high2[i];
|
|
1409
|
+
acceleration = start;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
if (!isFirstTrendBar) {
|
|
1413
|
+
if (isUpTrend) {
|
|
1414
|
+
if (high2[i] > extremePoint) {
|
|
1415
|
+
extremePoint = high2[i];
|
|
1416
|
+
acceleration = Math.min(acceleration + inc, max4);
|
|
1417
|
+
}
|
|
1418
|
+
} else {
|
|
1419
|
+
if (low2[i] < extremePoint) {
|
|
1420
|
+
extremePoint = low2[i];
|
|
1421
|
+
acceleration = Math.min(acceleration + inc, max4);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
if (isUpTrend) {
|
|
1426
|
+
sarValue = Math.min(sarValue, low2[i - 1]);
|
|
1427
|
+
if (i > 1) {
|
|
1428
|
+
sarValue = Math.min(sarValue, low2[i - 2]);
|
|
1429
|
+
}
|
|
1430
|
+
} else {
|
|
1431
|
+
sarValue = Math.max(sarValue, high2[i - 1]);
|
|
1432
|
+
if (i > 1) {
|
|
1433
|
+
sarValue = Math.max(sarValue, high2[i - 2]);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
result.push(sarValue);
|
|
1437
|
+
isFirstTrendBar = false;
|
|
1438
|
+
}
|
|
1439
|
+
return result;
|
|
1440
|
+
}
|
|
1441
|
+
function pivothigh(sourceOrLeftbars, leftbarsOrRightbars, rightbars, high2) {
|
|
1442
|
+
let source;
|
|
1443
|
+
let leftbars;
|
|
1444
|
+
let right;
|
|
1445
|
+
if (rightbars === void 0) {
|
|
1446
|
+
if (!high2) {
|
|
1447
|
+
throw new Error("ta.pivothigh() requires high series when using two-parameter version.");
|
|
1448
|
+
}
|
|
1449
|
+
source = high2;
|
|
1450
|
+
leftbars = sourceOrLeftbars;
|
|
1451
|
+
right = leftbarsOrRightbars;
|
|
1452
|
+
} else {
|
|
1453
|
+
source = sourceOrLeftbars;
|
|
1454
|
+
leftbars = leftbarsOrRightbars;
|
|
1455
|
+
right = rightbars;
|
|
1456
|
+
}
|
|
1457
|
+
const result = [];
|
|
1458
|
+
for (let i = 0; i < source.length; i++) {
|
|
1459
|
+
if (i < leftbars || i + right >= source.length) {
|
|
1460
|
+
result.push(NaN);
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
const centerValue = source[i];
|
|
1464
|
+
let isPivot = true;
|
|
1465
|
+
for (let j = 1; j <= leftbars; j++) {
|
|
1466
|
+
if (source[i - j] >= centerValue) {
|
|
1467
|
+
isPivot = false;
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
if (isPivot) {
|
|
1472
|
+
for (let j = 1; j <= right; j++) {
|
|
1473
|
+
if (source[i + j] >= centerValue) {
|
|
1474
|
+
isPivot = false;
|
|
1475
|
+
break;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
result.push(isPivot ? centerValue : NaN);
|
|
1480
|
+
}
|
|
1481
|
+
return result;
|
|
1482
|
+
}
|
|
1483
|
+
function pivotlow(sourceOrLeftbars, leftbarsOrRightbars, rightbars, low2) {
|
|
1484
|
+
let source;
|
|
1485
|
+
let leftbars;
|
|
1486
|
+
let right;
|
|
1487
|
+
if (rightbars === void 0) {
|
|
1488
|
+
if (!low2) {
|
|
1489
|
+
throw new Error("ta.pivotlow() requires low series when using two-parameter version.");
|
|
1490
|
+
}
|
|
1491
|
+
source = low2;
|
|
1492
|
+
leftbars = sourceOrLeftbars;
|
|
1493
|
+
right = leftbarsOrRightbars;
|
|
1494
|
+
} else {
|
|
1495
|
+
source = sourceOrLeftbars;
|
|
1496
|
+
leftbars = leftbarsOrRightbars;
|
|
1497
|
+
right = rightbars;
|
|
1498
|
+
}
|
|
1499
|
+
const result = [];
|
|
1500
|
+
for (let i = 0; i < source.length; i++) {
|
|
1501
|
+
if (i < leftbars || i + right >= source.length) {
|
|
1502
|
+
result.push(NaN);
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
const centerValue = source[i];
|
|
1506
|
+
let isPivot = true;
|
|
1507
|
+
for (let j = 1; j <= leftbars; j++) {
|
|
1508
|
+
if (source[i - j] <= centerValue) {
|
|
1509
|
+
isPivot = false;
|
|
1510
|
+
break;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
if (isPivot) {
|
|
1514
|
+
for (let j = 1; j <= right; j++) {
|
|
1515
|
+
if (source[i + j] <= centerValue) {
|
|
1516
|
+
isPivot = false;
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
result.push(isPivot ? centerValue : NaN);
|
|
1522
|
+
}
|
|
1523
|
+
return result;
|
|
1524
|
+
}
|
|
1525
|
+
function barssince(condition) {
|
|
1526
|
+
const result = [];
|
|
1527
|
+
let barsSinceTrue = NaN;
|
|
1528
|
+
for (let i = 0; i < condition.length; i++) {
|
|
1529
|
+
if (condition[i]) {
|
|
1530
|
+
barsSinceTrue = 0;
|
|
1531
|
+
} else if (!isNaN(barsSinceTrue)) {
|
|
1532
|
+
barsSinceTrue++;
|
|
1533
|
+
}
|
|
1534
|
+
result.push(barsSinceTrue);
|
|
1535
|
+
}
|
|
1536
|
+
return result;
|
|
1537
|
+
}
|
|
1538
|
+
function valuewhen(condition, source, occurrence) {
|
|
1539
|
+
const result = [];
|
|
1540
|
+
for (let i = 0; i < condition.length; i++) {
|
|
1541
|
+
let occurrenceCount = 0;
|
|
1542
|
+
let foundValue = NaN;
|
|
1543
|
+
for (let j = i; j >= 0; j--) {
|
|
1544
|
+
if (condition[j]) {
|
|
1545
|
+
if (occurrenceCount === occurrence) {
|
|
1546
|
+
foundValue = source[j];
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
occurrenceCount++;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
result.push(foundValue);
|
|
1553
|
+
}
|
|
1554
|
+
return result;
|
|
1555
|
+
}
|
|
1556
|
+
function dmi(diLength, adxSmoothing, high2, low2, close2) {
|
|
1557
|
+
const len = Math.max(high2.length, low2.length, close2.length);
|
|
1558
|
+
const plusDM = [];
|
|
1559
|
+
const minusDM = [];
|
|
1560
|
+
const trueRangeValues = tr(false, high2, low2, close2);
|
|
1561
|
+
for (let i = 0; i < len; i++) {
|
|
1562
|
+
if (i === 0) {
|
|
1563
|
+
plusDM.push(0);
|
|
1564
|
+
minusDM.push(0);
|
|
1565
|
+
} else {
|
|
1566
|
+
const upMove = high2[i] - high2[i - 1];
|
|
1567
|
+
const downMove = low2[i - 1] - low2[i];
|
|
1568
|
+
let plusDMVal = 0;
|
|
1569
|
+
let minusDMVal = 0;
|
|
1570
|
+
if (upMove > downMove && upMove > 0) {
|
|
1571
|
+
plusDMVal = upMove;
|
|
1572
|
+
}
|
|
1573
|
+
if (downMove > upMove && downMove > 0) {
|
|
1574
|
+
minusDMVal = downMove;
|
|
1575
|
+
}
|
|
1576
|
+
plusDM.push(plusDMVal);
|
|
1577
|
+
minusDM.push(minusDMVal);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
const smoothedPlusDM = rma(plusDM, diLength);
|
|
1581
|
+
const smoothedMinusDM = rma(minusDM, diLength);
|
|
1582
|
+
const smoothedTR = rma(trueRangeValues, diLength);
|
|
1583
|
+
const plusDI = [];
|
|
1584
|
+
const minusDI = [];
|
|
1585
|
+
for (let i = 0; i < len; i++) {
|
|
1586
|
+
if (smoothedTR[i] === 0) {
|
|
1587
|
+
plusDI.push(0);
|
|
1588
|
+
minusDI.push(0);
|
|
1589
|
+
} else {
|
|
1590
|
+
plusDI.push(smoothedPlusDM[i] / smoothedTR[i] * 100);
|
|
1591
|
+
minusDI.push(smoothedMinusDM[i] / smoothedTR[i] * 100);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
const dx = [];
|
|
1595
|
+
for (let i = 0; i < len; i++) {
|
|
1596
|
+
const sum2 = plusDI[i] + minusDI[i];
|
|
1597
|
+
if (sum2 === 0) {
|
|
1598
|
+
dx.push(0);
|
|
1599
|
+
} else {
|
|
1600
|
+
dx.push(Math.abs(plusDI[i] - minusDI[i]) / sum2 * 100);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
const adx = rma(dx, adxSmoothing);
|
|
1604
|
+
return [plusDI, minusDI, adx];
|
|
1605
|
+
}
|
|
1606
|
+
function tsi(source, shortLength, longLength) {
|
|
1607
|
+
const momentum = [];
|
|
1608
|
+
for (let i = 0; i < source.length; i++) {
|
|
1609
|
+
if (i === 0) {
|
|
1610
|
+
momentum.push(NaN);
|
|
1611
|
+
} else {
|
|
1612
|
+
momentum.push(source[i] - source[i - 1]);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
const smoothedMomentum = ema(ema(momentum, longLength), shortLength);
|
|
1616
|
+
const absMomentum = momentum.map(Math.abs);
|
|
1617
|
+
const smoothedAbsMomentum = ema(ema(absMomentum, longLength), shortLength);
|
|
1618
|
+
const result = [];
|
|
1619
|
+
for (let i = 0; i < source.length; i++) {
|
|
1620
|
+
if (smoothedAbsMomentum[i] === 0) {
|
|
1621
|
+
result.push(0);
|
|
1622
|
+
} else {
|
|
1623
|
+
result.push(smoothedMomentum[i] / smoothedAbsMomentum[i] * 100);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return result;
|
|
1627
|
+
}
|
|
1628
|
+
function cmo(source, length) {
|
|
1629
|
+
const result = [];
|
|
1630
|
+
for (let i = 0; i < source.length; i++) {
|
|
1631
|
+
if (i < length) {
|
|
1632
|
+
result.push(NaN);
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
let sumGains = 0;
|
|
1636
|
+
let sumLosses = 0;
|
|
1637
|
+
for (let j = 0; j < length; j++) {
|
|
1638
|
+
const change3 = source[i - j] - source[i - j - 1];
|
|
1639
|
+
if (change3 > 0) {
|
|
1640
|
+
sumGains += change3;
|
|
1641
|
+
} else {
|
|
1642
|
+
sumLosses += Math.abs(change3);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
const totalMovement = sumGains + sumLosses;
|
|
1646
|
+
if (totalMovement === 0) {
|
|
1647
|
+
result.push(0);
|
|
1648
|
+
} else {
|
|
1649
|
+
const cmoValue = (sumGains - sumLosses) / totalMovement * 100;
|
|
1650
|
+
result.push(cmoValue);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return result;
|
|
1654
|
+
}
|
|
1655
|
+
function kc(source, length, mult, useTrueRange = true, high2, low2, close2) {
|
|
1656
|
+
const middle = ema(source, length);
|
|
1657
|
+
let range3;
|
|
1658
|
+
if (useTrueRange) {
|
|
1659
|
+
if (!high2 || !low2 || !close2) {
|
|
1660
|
+
throw new Error("ta.kc() with useTrueRange=true requires high, low, and close data");
|
|
1661
|
+
}
|
|
1662
|
+
range3 = tr(false, high2, low2, close2);
|
|
1663
|
+
} else {
|
|
1664
|
+
if (!high2 || !low2) {
|
|
1665
|
+
throw new Error("ta.kc() requires high and low data");
|
|
1666
|
+
}
|
|
1667
|
+
range3 = [];
|
|
1668
|
+
for (let i = 0; i < high2.length; i++) {
|
|
1669
|
+
range3.push(high2[i] - low2[i]);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
const rangeEma = ema(range3, length);
|
|
1673
|
+
const upper = [];
|
|
1674
|
+
const lower = [];
|
|
1675
|
+
for (let i = 0; i < middle.length; i++) {
|
|
1676
|
+
upper.push(middle[i] + rangeEma[i] * mult);
|
|
1677
|
+
lower.push(middle[i] - rangeEma[i] * mult);
|
|
1678
|
+
}
|
|
1679
|
+
return [middle, upper, lower];
|
|
1680
|
+
}
|
|
1681
|
+
function bbw(source, length, mult) {
|
|
1682
|
+
const [basis, upper, lower] = bb(source, length, mult);
|
|
1683
|
+
const result = [];
|
|
1684
|
+
for (let i = 0; i < source.length; i++) {
|
|
1685
|
+
if (basis[i] === 0) {
|
|
1686
|
+
result.push(NaN);
|
|
1687
|
+
} else {
|
|
1688
|
+
const width = (upper[i] - lower[i]) / basis[i] * 100;
|
|
1689
|
+
result.push(width);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return result;
|
|
1693
|
+
}
|
|
1694
|
+
function wpr(high2, low2, close2, length = 14) {
|
|
1695
|
+
const result = [];
|
|
1696
|
+
for (let i = 0; i < close2.length; i++) {
|
|
1697
|
+
if (i < length - 1) {
|
|
1698
|
+
result.push(NaN);
|
|
1699
|
+
continue;
|
|
1700
|
+
}
|
|
1701
|
+
let highestHigh = high2[i - length + 1];
|
|
1702
|
+
let lowestLow = low2[i - length + 1];
|
|
1703
|
+
for (let j = i - length + 2; j <= i; j++) {
|
|
1704
|
+
if (high2[j] > highestHigh) highestHigh = high2[j];
|
|
1705
|
+
if (low2[j] < lowestLow) lowestLow = low2[j];
|
|
1706
|
+
}
|
|
1707
|
+
const range3 = highestHigh - lowestLow;
|
|
1708
|
+
if (range3 === 0) {
|
|
1709
|
+
result.push(NaN);
|
|
1710
|
+
} else {
|
|
1711
|
+
const wprValue = (highestHigh - close2[i]) / range3 * -100;
|
|
1712
|
+
result.push(wprValue);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
return result;
|
|
1716
|
+
}
|
|
1717
|
+
function vwap(source, volume2) {
|
|
1718
|
+
if (source.length !== volume2.length) {
|
|
1719
|
+
throw new Error("ta.vwap: source and volume must have the same length");
|
|
1720
|
+
}
|
|
1721
|
+
const result = [];
|
|
1722
|
+
let cumulativePV = 0;
|
|
1723
|
+
let cumulativeVolume = 0;
|
|
1724
|
+
for (let i = 0; i < source.length; i++) {
|
|
1725
|
+
if (isNaN(source[i]) || isNaN(volume2[i])) {
|
|
1726
|
+
result.push(NaN);
|
|
1727
|
+
continue;
|
|
1728
|
+
}
|
|
1729
|
+
cumulativePV += source[i] * volume2[i];
|
|
1730
|
+
cumulativeVolume += volume2[i];
|
|
1731
|
+
if (cumulativeVolume === 0) {
|
|
1732
|
+
result.push(NaN);
|
|
1733
|
+
} else {
|
|
1734
|
+
result.push(cumulativePV / cumulativeVolume);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return result;
|
|
1738
|
+
}
|
|
1739
|
+
function alma(source, length = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
1740
|
+
const result = [];
|
|
1741
|
+
const m = floor2 ? Math.floor(offset * (length - 1)) : offset * (length - 1);
|
|
1742
|
+
const s = length / sigma;
|
|
1743
|
+
const weights = [];
|
|
1744
|
+
let weightSum = 0;
|
|
1745
|
+
for (let i = 0; i < length; i++) {
|
|
1746
|
+
const weight = Math.exp(-1 * Math.pow(i - m, 2) / (2 * Math.pow(s, 2)));
|
|
1747
|
+
weights.push(weight);
|
|
1748
|
+
weightSum += weight;
|
|
1749
|
+
}
|
|
1750
|
+
for (let i = 0; i < length; i++) {
|
|
1751
|
+
weights[i] /= weightSum;
|
|
1752
|
+
}
|
|
1753
|
+
for (let i = 0; i < source.length; i++) {
|
|
1754
|
+
if (i < length - 1) {
|
|
1755
|
+
result.push(NaN);
|
|
1756
|
+
continue;
|
|
1757
|
+
}
|
|
1758
|
+
let almaValue = 0;
|
|
1759
|
+
for (let j = 0; j < length; j++) {
|
|
1760
|
+
almaValue += source[i - length + 1 + j] * weights[j];
|
|
1761
|
+
}
|
|
1762
|
+
result.push(almaValue);
|
|
1763
|
+
}
|
|
1764
|
+
return result;
|
|
1765
|
+
}
|
|
1766
|
+
function kcw(source, length = 20, mult = 2, useTrueRange = true, high2, low2, close2) {
|
|
1767
|
+
const [basis, upper, lower] = kc(source, length, mult, useTrueRange, high2, low2, close2);
|
|
1768
|
+
const result = [];
|
|
1769
|
+
for (let i = 0; i < source.length; i++) {
|
|
1770
|
+
if (isNaN(basis[i]) || basis[i] === 0) {
|
|
1771
|
+
result.push(NaN);
|
|
1772
|
+
} else {
|
|
1773
|
+
const width = (upper[i] - lower[i]) / basis[i] * 100;
|
|
1774
|
+
result.push(width);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
return result;
|
|
1778
|
+
}
|
|
1779
|
+
function range(high2, low2) {
|
|
1780
|
+
if (high2.length !== low2.length) {
|
|
1781
|
+
throw new Error("ta.range: high and low must have the same length");
|
|
1782
|
+
}
|
|
1783
|
+
const result = [];
|
|
1784
|
+
for (let i = 0; i < high2.length; i++) {
|
|
1785
|
+
if (isNaN(high2[i]) || isNaN(low2[i])) {
|
|
1786
|
+
result.push(NaN);
|
|
1787
|
+
} else {
|
|
1788
|
+
result.push(high2[i] - low2[i]);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
return result;
|
|
1792
|
+
}
|
|
1793
|
+
function highestbars(source, length) {
|
|
1794
|
+
const result = [];
|
|
1795
|
+
for (let i = 0; i < source.length; i++) {
|
|
1796
|
+
if (i < length - 1) {
|
|
1797
|
+
result.push(NaN);
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
let highestValue = source[i - length + 1];
|
|
1801
|
+
let highestOffset = length - 1;
|
|
1802
|
+
for (let j = i - length + 2; j <= i; j++) {
|
|
1803
|
+
if (source[j] > highestValue) {
|
|
1804
|
+
highestValue = source[j];
|
|
1805
|
+
highestOffset = i - j;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
result.push(highestOffset);
|
|
1809
|
+
}
|
|
1810
|
+
return result;
|
|
1811
|
+
}
|
|
1812
|
+
function lowestbars(source, length) {
|
|
1813
|
+
const result = [];
|
|
1814
|
+
for (let i = 0; i < source.length; i++) {
|
|
1815
|
+
if (i < length - 1) {
|
|
1816
|
+
result.push(NaN);
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
let lowestValue = source[i - length + 1];
|
|
1820
|
+
let lowestOffset = length - 1;
|
|
1821
|
+
for (let j = i - length + 2; j <= i; j++) {
|
|
1822
|
+
if (source[j] < lowestValue) {
|
|
1823
|
+
lowestValue = source[j];
|
|
1824
|
+
lowestOffset = i - j;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
result.push(lowestOffset);
|
|
1828
|
+
}
|
|
1829
|
+
return result;
|
|
1830
|
+
}
|
|
1831
|
+
function max(source1, source2) {
|
|
1832
|
+
if (source1.length !== source2.length) {
|
|
1833
|
+
throw new Error("ta.max: source1 and source2 must have the same length");
|
|
1834
|
+
}
|
|
1835
|
+
const result = [];
|
|
1836
|
+
for (let i = 0; i < source1.length; i++) {
|
|
1837
|
+
if (isNaN(source1[i]) || isNaN(source2[i])) {
|
|
1838
|
+
result.push(NaN);
|
|
1839
|
+
} else {
|
|
1840
|
+
result.push(Math.max(source1[i], source2[i]));
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
return result;
|
|
1844
|
+
}
|
|
1845
|
+
function min(source1, source2) {
|
|
1846
|
+
if (source1.length !== source2.length) {
|
|
1847
|
+
throw new Error("ta.min: source1 and source2 must have the same length");
|
|
1848
|
+
}
|
|
1849
|
+
const result = [];
|
|
1850
|
+
for (let i = 0; i < source1.length; i++) {
|
|
1851
|
+
if (isNaN(source1[i]) || isNaN(source2[i])) {
|
|
1852
|
+
result.push(NaN);
|
|
1853
|
+
} else {
|
|
1854
|
+
result.push(Math.min(source1[i], source2[i]));
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
return result;
|
|
1858
|
+
}
|
|
1859
|
+
function cog(source, length = 10) {
|
|
1860
|
+
const result = [];
|
|
1861
|
+
for (let i = 0; i < source.length; i++) {
|
|
1862
|
+
if (i < length - 1) {
|
|
1863
|
+
result.push(NaN);
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
let numerator = 0;
|
|
1867
|
+
let denominator = 0;
|
|
1868
|
+
for (let j = 0; j < length; j++) {
|
|
1869
|
+
const weight = j + 1;
|
|
1870
|
+
const price = source[i - length + 1 + j];
|
|
1871
|
+
numerator += weight * price;
|
|
1872
|
+
denominator += price;
|
|
1873
|
+
}
|
|
1874
|
+
if (denominator === 0) {
|
|
1875
|
+
result.push(NaN);
|
|
1876
|
+
} else {
|
|
1877
|
+
const cog3 = -1 * (numerator / denominator) + (length + 1) / 2;
|
|
1878
|
+
result.push(cog3);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
return result;
|
|
1882
|
+
}
|
|
1883
|
+
function mode(source, length) {
|
|
1884
|
+
const result = [];
|
|
1885
|
+
for (let i = 0; i < source.length; i++) {
|
|
1886
|
+
if (i < length - 1) {
|
|
1887
|
+
result.push(NaN);
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
const values = [];
|
|
1891
|
+
for (let j = 0; j < length; j++) {
|
|
1892
|
+
const value = source[i - j];
|
|
1893
|
+
if (!isNaN(value)) {
|
|
1894
|
+
values.push(value);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
if (values.length === 0) {
|
|
1898
|
+
result.push(NaN);
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
const frequencyMap = /* @__PURE__ */ new Map();
|
|
1902
|
+
for (const value of values) {
|
|
1903
|
+
frequencyMap.set(value, (frequencyMap.get(value) || 0) + 1);
|
|
1904
|
+
}
|
|
1905
|
+
let maxFrequency = 0;
|
|
1906
|
+
frequencyMap.forEach((freq) => {
|
|
1907
|
+
if (freq > maxFrequency) {
|
|
1908
|
+
maxFrequency = freq;
|
|
1909
|
+
}
|
|
1910
|
+
});
|
|
1911
|
+
const modesWithMaxFreq = [];
|
|
1912
|
+
frequencyMap.forEach((freq, value) => {
|
|
1913
|
+
if (freq === maxFrequency) {
|
|
1914
|
+
modesWithMaxFreq.push(value);
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
result.push(Math.min(...modesWithMaxFreq));
|
|
1918
|
+
}
|
|
1919
|
+
return result;
|
|
1920
|
+
}
|
|
1921
|
+
function percentile_linear_interpolation(source, length, percentage) {
|
|
1922
|
+
const result = [];
|
|
1923
|
+
for (let i = 0; i < source.length; i++) {
|
|
1924
|
+
if (i < length - 1) {
|
|
1925
|
+
result.push(NaN);
|
|
1926
|
+
continue;
|
|
1927
|
+
}
|
|
1928
|
+
const values = [];
|
|
1929
|
+
for (let j = 0; j < length; j++) {
|
|
1930
|
+
const value = source[i - j];
|
|
1931
|
+
values.push(value);
|
|
1932
|
+
}
|
|
1933
|
+
if (values.some((v) => isNaN(v))) {
|
|
1934
|
+
result.push(NaN);
|
|
1935
|
+
continue;
|
|
1936
|
+
}
|
|
1937
|
+
const sorted = [...values].sort((a, b2) => a - b2);
|
|
1938
|
+
const position = percentage / 100 * (sorted.length - 1);
|
|
1939
|
+
const lowerIndex = Math.floor(position);
|
|
1940
|
+
const upperIndex = Math.ceil(position);
|
|
1941
|
+
if (lowerIndex === upperIndex) {
|
|
1942
|
+
result.push(sorted[lowerIndex]);
|
|
1943
|
+
} else {
|
|
1944
|
+
const fraction = position - lowerIndex;
|
|
1945
|
+
const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
|
|
1946
|
+
result.push(interpolated);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
return result;
|
|
1950
|
+
}
|
|
1951
|
+
function percentile_nearest_rank(source, length, percentage) {
|
|
1952
|
+
const result = [];
|
|
1953
|
+
for (let i = 0; i < source.length; i++) {
|
|
1954
|
+
if (i < length - 1) {
|
|
1955
|
+
result.push(NaN);
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
const values = [];
|
|
1959
|
+
for (let j = 0; j < length; j++) {
|
|
1960
|
+
const value = source[i - j];
|
|
1961
|
+
if (!isNaN(value)) {
|
|
1962
|
+
values.push(value);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
if (values.length === 0) {
|
|
1966
|
+
result.push(NaN);
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
const sorted = [...values].sort((a, b2) => a - b2);
|
|
1970
|
+
if (percentage >= 100) {
|
|
1971
|
+
result.push(sorted[sorted.length - 1]);
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
const rank = Math.ceil(percentage / 100 * sorted.length);
|
|
1975
|
+
const index = Math.max(0, rank - 1);
|
|
1976
|
+
result.push(sorted[index]);
|
|
1977
|
+
}
|
|
1978
|
+
return result;
|
|
1979
|
+
}
|
|
1980
|
+
function rci(source, length) {
|
|
1981
|
+
const result = [];
|
|
1982
|
+
for (let i = 0; i < source.length; i++) {
|
|
1983
|
+
if (i < length - 1) {
|
|
1984
|
+
result.push(NaN);
|
|
1985
|
+
continue;
|
|
1986
|
+
}
|
|
1987
|
+
const values = [];
|
|
1988
|
+
for (let j = 0; j < length; j++) {
|
|
1989
|
+
values.push(source[i - length + 1 + j]);
|
|
1990
|
+
}
|
|
1991
|
+
if (values.some((v) => isNaN(v))) {
|
|
1992
|
+
result.push(NaN);
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
const indexed = values.map((value, index) => ({ value, index }));
|
|
1996
|
+
const sorted = [...indexed].sort((a, b2) => a.value - b2.value);
|
|
1997
|
+
const ranks = new Array(length).fill(0);
|
|
1998
|
+
let currentRank = 1;
|
|
1999
|
+
for (let j = 0; j < sorted.length; j++) {
|
|
2000
|
+
let tieCount = 1;
|
|
2001
|
+
while (j + tieCount < sorted.length && sorted[j].value === sorted[j + tieCount].value) {
|
|
2002
|
+
tieCount++;
|
|
2003
|
+
}
|
|
2004
|
+
const avgRank = (currentRank + (currentRank + tieCount - 1)) / 2;
|
|
2005
|
+
for (let k = 0; k < tieCount; k++) {
|
|
2006
|
+
ranks[sorted[j + k].index] = avgRank;
|
|
2007
|
+
}
|
|
2008
|
+
j += tieCount - 1;
|
|
2009
|
+
currentRank += tieCount;
|
|
2010
|
+
}
|
|
2011
|
+
let sumSquaredDiff = 0;
|
|
2012
|
+
for (let j = 0; j < length; j++) {
|
|
2013
|
+
const timeRank = j + 1;
|
|
2014
|
+
const diff = ranks[j] - timeRank;
|
|
2015
|
+
sumSquaredDiff += diff * diff;
|
|
2016
|
+
}
|
|
2017
|
+
const n = length;
|
|
2018
|
+
const base = (n * n * n - n) / 12;
|
|
2019
|
+
let tieCorrection = 0;
|
|
2020
|
+
for (let j = 0; j < sorted.length; ) {
|
|
2021
|
+
let tieCount = 1;
|
|
2022
|
+
while (j + tieCount < sorted.length && sorted[j].value === sorted[j + tieCount].value) {
|
|
2023
|
+
tieCount++;
|
|
2024
|
+
}
|
|
2025
|
+
if (tieCount > 1) {
|
|
2026
|
+
tieCorrection += (tieCount * tieCount * tieCount - tieCount) / 12;
|
|
2027
|
+
}
|
|
2028
|
+
j += tieCount;
|
|
2029
|
+
}
|
|
2030
|
+
const A = base - tieCorrection;
|
|
2031
|
+
const B = base;
|
|
2032
|
+
let rho;
|
|
2033
|
+
if (A === 0 || B === 0) {
|
|
2034
|
+
rho = 0;
|
|
2035
|
+
} else {
|
|
2036
|
+
rho = (A + B - sumSquaredDiff) / (2 * Math.sqrt(A * B));
|
|
2037
|
+
}
|
|
2038
|
+
result.push(rho * 100);
|
|
2039
|
+
}
|
|
2040
|
+
return result;
|
|
2041
|
+
}
|
|
2042
|
+
function ichimoku(conversionPeriods, basePeriods, laggingSpan2Periods, displacement, high2, low2, close2) {
|
|
2043
|
+
const length = high2.length;
|
|
2044
|
+
const highestConversion = highest(high2, conversionPeriods);
|
|
2045
|
+
const lowestConversion = lowest(low2, conversionPeriods);
|
|
2046
|
+
const tenkanSen = [];
|
|
2047
|
+
for (let i = 0; i < length; i++) {
|
|
2048
|
+
if (isNaN(highestConversion[i]) || isNaN(lowestConversion[i])) {
|
|
2049
|
+
tenkanSen.push(NaN);
|
|
2050
|
+
} else {
|
|
2051
|
+
tenkanSen.push((highestConversion[i] + lowestConversion[i]) / 2);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
const highestBase = highest(high2, basePeriods);
|
|
2055
|
+
const lowestBase = lowest(low2, basePeriods);
|
|
2056
|
+
const kijunSen = [];
|
|
2057
|
+
for (let i = 0; i < length; i++) {
|
|
2058
|
+
if (isNaN(highestBase[i]) || isNaN(lowestBase[i])) {
|
|
2059
|
+
kijunSen.push(NaN);
|
|
2060
|
+
} else {
|
|
2061
|
+
kijunSen.push((highestBase[i] + lowestBase[i]) / 2);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
const senkouSpanA = [];
|
|
2065
|
+
for (let i = 0; i < length; i++) {
|
|
2066
|
+
const sourceIndex = i - displacement;
|
|
2067
|
+
if (sourceIndex < 0 || isNaN(tenkanSen[sourceIndex]) || isNaN(kijunSen[sourceIndex])) {
|
|
2068
|
+
senkouSpanA.push(NaN);
|
|
2069
|
+
} else {
|
|
2070
|
+
senkouSpanA.push((tenkanSen[sourceIndex] + kijunSen[sourceIndex]) / 2);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
const highestLagging = highest(high2, laggingSpan2Periods);
|
|
2074
|
+
const lowestLagging = lowest(low2, laggingSpan2Periods);
|
|
2075
|
+
const senkouSpanB = [];
|
|
2076
|
+
for (let i = 0; i < length; i++) {
|
|
2077
|
+
const sourceIndex = i - displacement;
|
|
2078
|
+
if (sourceIndex < 0 || isNaN(highestLagging[sourceIndex]) || isNaN(lowestLagging[sourceIndex])) {
|
|
2079
|
+
senkouSpanB.push(NaN);
|
|
2080
|
+
} else {
|
|
2081
|
+
senkouSpanB.push((highestLagging[sourceIndex] + lowestLagging[sourceIndex]) / 2);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
const chikouSpan = [];
|
|
2085
|
+
for (let i = 0; i < length; i++) {
|
|
2086
|
+
const sourceIndex = i + displacement;
|
|
2087
|
+
if (sourceIndex >= length || isNaN(close2[sourceIndex])) {
|
|
2088
|
+
chikouSpan.push(NaN);
|
|
2089
|
+
} else {
|
|
2090
|
+
chikouSpan.push(close2[sourceIndex]);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
return [tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan];
|
|
2094
|
+
}
|
|
2095
|
+
function zigzag(deviation = 5, depth = 10, backstep = 3, source, high2, low2) {
|
|
2096
|
+
let highSource;
|
|
2097
|
+
let lowSource;
|
|
2098
|
+
if (high2 && low2) {
|
|
2099
|
+
highSource = high2;
|
|
2100
|
+
lowSource = low2;
|
|
2101
|
+
} else if (source) {
|
|
2102
|
+
highSource = source;
|
|
2103
|
+
lowSource = source;
|
|
2104
|
+
} else {
|
|
2105
|
+
throw new Error(
|
|
2106
|
+
"ta.zigzag() requires either source series or high/low series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
const length = highSource.length;
|
|
2110
|
+
if (lowSource.length !== length) {
|
|
2111
|
+
throw new Error("ta.zigzag: high and low must have the same length");
|
|
2112
|
+
}
|
|
2113
|
+
const zigzagValues = new Array(length).fill(NaN);
|
|
2114
|
+
const directions = new Array(length).fill(0);
|
|
2115
|
+
const isPivot = new Array(length).fill(false);
|
|
2116
|
+
const getDeviation = (price1, price2) => {
|
|
2117
|
+
if (Math.abs(price1) < 1e-10 || isNaN(price1) || isNaN(price2)) return 0;
|
|
2118
|
+
return Math.abs((price2 - price1) / price1) * 100;
|
|
2119
|
+
};
|
|
2120
|
+
let lastConfirmedPivot = null;
|
|
2121
|
+
let potentialPivot = null;
|
|
2122
|
+
let currentDirection = 0;
|
|
2123
|
+
const confirmedPivots = [];
|
|
2124
|
+
let startIndex;
|
|
2125
|
+
let initialHighest = -Infinity;
|
|
2126
|
+
let initialLowest = Infinity;
|
|
2127
|
+
let initialHighIndex = -1;
|
|
2128
|
+
let initialLowIndex = -1;
|
|
2129
|
+
for (let i = 0; i < Math.min(depth, length); i++) {
|
|
2130
|
+
if (!isNaN(highSource[i]) && highSource[i] > initialHighest) {
|
|
2131
|
+
initialHighest = highSource[i];
|
|
2132
|
+
initialHighIndex = i;
|
|
2133
|
+
}
|
|
2134
|
+
if (!isNaN(lowSource[i]) && lowSource[i] < initialLowest) {
|
|
2135
|
+
initialLowest = lowSource[i];
|
|
2136
|
+
initialLowIndex = i;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
if (initialHighIndex >= 0 && initialLowIndex >= 0) {
|
|
2140
|
+
if (initialLowIndex <= initialHighIndex) {
|
|
2141
|
+
lastConfirmedPivot = { index: initialLowIndex, price: initialLowest, type: "low" };
|
|
2142
|
+
currentDirection = 1;
|
|
2143
|
+
} else {
|
|
2144
|
+
lastConfirmedPivot = { index: initialHighIndex, price: initialHighest, type: "high" };
|
|
2145
|
+
currentDirection = -1;
|
|
2146
|
+
}
|
|
2147
|
+
confirmedPivots.push(lastConfirmedPivot);
|
|
2148
|
+
startIndex = lastConfirmedPivot.index + 1;
|
|
2149
|
+
} else {
|
|
2150
|
+
startIndex = depth;
|
|
2151
|
+
}
|
|
2152
|
+
for (let i = Math.max(startIndex, depth); i < length; i++) {
|
|
2153
|
+
const currentHigh = highSource[i];
|
|
2154
|
+
const currentLow = lowSource[i];
|
|
2155
|
+
if (isNaN(currentHigh) || isNaN(currentLow)) {
|
|
2156
|
+
directions[i] = currentDirection;
|
|
2157
|
+
continue;
|
|
2158
|
+
}
|
|
2159
|
+
let isPotentialHigh = true;
|
|
2160
|
+
for (let j = 1; j <= backstep && i - j >= 0; j++) {
|
|
2161
|
+
if (!isNaN(highSource[i - j]) && highSource[i - j] >= currentHigh) {
|
|
2162
|
+
isPotentialHigh = false;
|
|
2163
|
+
break;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
let isPotentialLow = true;
|
|
2167
|
+
for (let j = 1; j <= backstep && i - j >= 0; j++) {
|
|
2168
|
+
if (!isNaN(lowSource[i - j]) && lowSource[i - j] <= currentLow) {
|
|
2169
|
+
isPotentialLow = false;
|
|
2170
|
+
break;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
if (currentDirection === 1) {
|
|
2174
|
+
if (isPotentialHigh) {
|
|
2175
|
+
if (potentialPivot === null || potentialPivot.type !== "high") {
|
|
2176
|
+
if (lastConfirmedPivot && getDeviation(lastConfirmedPivot.price, currentHigh) >= deviation) {
|
|
2177
|
+
potentialPivot = { index: i, price: currentHigh, type: "high" };
|
|
2178
|
+
}
|
|
2179
|
+
} else {
|
|
2180
|
+
if (currentHigh > potentialPivot.price) {
|
|
2181
|
+
potentialPivot = { index: i, price: currentHigh, type: "high" };
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
if (potentialPivot && potentialPivot.type === "high") {
|
|
2186
|
+
const deviationFromPotential = getDeviation(potentialPivot.price, currentLow);
|
|
2187
|
+
if (deviationFromPotential >= deviation && i - potentialPivot.index >= backstep) {
|
|
2188
|
+
confirmedPivots.push(potentialPivot);
|
|
2189
|
+
lastConfirmedPivot = potentialPivot;
|
|
2190
|
+
currentDirection = -1;
|
|
2191
|
+
potentialPivot = null;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
} else if (currentDirection === -1) {
|
|
2195
|
+
if (isPotentialLow) {
|
|
2196
|
+
if (potentialPivot === null || potentialPivot.type !== "low") {
|
|
2197
|
+
if (lastConfirmedPivot && getDeviation(lastConfirmedPivot.price, currentLow) >= deviation) {
|
|
2198
|
+
potentialPivot = { index: i, price: currentLow, type: "low" };
|
|
2199
|
+
}
|
|
2200
|
+
} else {
|
|
2201
|
+
if (currentLow < potentialPivot.price) {
|
|
2202
|
+
potentialPivot = { index: i, price: currentLow, type: "low" };
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
if (potentialPivot && potentialPivot.type === "low") {
|
|
2207
|
+
const deviationFromPotential = getDeviation(potentialPivot.price, currentHigh);
|
|
2208
|
+
if (deviationFromPotential >= deviation && i - potentialPivot.index >= backstep) {
|
|
2209
|
+
confirmedPivots.push(potentialPivot);
|
|
2210
|
+
lastConfirmedPivot = potentialPivot;
|
|
2211
|
+
currentDirection = 1;
|
|
2212
|
+
potentialPivot = null;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
} else {
|
|
2216
|
+
if (lastConfirmedPivot) {
|
|
2217
|
+
if (lastConfirmedPivot.type === "low" && getDeviation(lastConfirmedPivot.price, currentHigh) >= deviation) {
|
|
2218
|
+
currentDirection = 1;
|
|
2219
|
+
} else if (lastConfirmedPivot.type === "high" && getDeviation(lastConfirmedPivot.price, currentLow) >= deviation) {
|
|
2220
|
+
currentDirection = -1;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
directions[i] = currentDirection;
|
|
2225
|
+
}
|
|
2226
|
+
const lastPotentialWasAdded = potentialPivot !== null;
|
|
2227
|
+
if (potentialPivot) {
|
|
2228
|
+
confirmedPivots.push(potentialPivot);
|
|
2229
|
+
}
|
|
2230
|
+
for (const pivot of confirmedPivots) {
|
|
2231
|
+
zigzagValues[pivot.index] = pivot.price;
|
|
2232
|
+
isPivot[pivot.index] = true;
|
|
2233
|
+
}
|
|
2234
|
+
let currentDir = 0;
|
|
2235
|
+
let pivotIdx = 0;
|
|
2236
|
+
const numConfirmed = lastPotentialWasAdded ? confirmedPivots.length - 1 : confirmedPivots.length;
|
|
2237
|
+
for (let i = 0; i < length; i++) {
|
|
2238
|
+
while (pivotIdx < numConfirmed && confirmedPivots[pivotIdx].index <= i) {
|
|
2239
|
+
const pivot = confirmedPivots[pivotIdx];
|
|
2240
|
+
currentDir = pivot.type === "low" ? 1 : -1;
|
|
2241
|
+
pivotIdx++;
|
|
2242
|
+
}
|
|
2243
|
+
directions[i] = currentDir;
|
|
2244
|
+
}
|
|
2245
|
+
return [zigzagValues, directions, isPivot];
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
// src/ta-series.ts
|
|
2249
|
+
function sma2(source, length) {
|
|
2250
|
+
const bars2 = source.bars;
|
|
2251
|
+
const sourceValues = source.toArray();
|
|
2252
|
+
const result = sma(sourceValues, length);
|
|
2253
|
+
return Series.fromArray(bars2, result);
|
|
2254
|
+
}
|
|
2255
|
+
function ema2(source, length) {
|
|
2256
|
+
const bars2 = source.bars;
|
|
2257
|
+
const sourceValues = source.toArray();
|
|
2258
|
+
const result = ema(sourceValues, length);
|
|
2259
|
+
return Series.fromArray(bars2, result);
|
|
2260
|
+
}
|
|
2261
|
+
function wma2(source, length) {
|
|
2262
|
+
const bars2 = source.bars;
|
|
2263
|
+
const sourceValues = source.toArray();
|
|
2264
|
+
const result = wma(sourceValues, length);
|
|
2265
|
+
return Series.fromArray(bars2, result);
|
|
2266
|
+
}
|
|
2267
|
+
function rma2(source, length) {
|
|
2268
|
+
const bars2 = source.bars;
|
|
2269
|
+
const sourceValues = source.toArray();
|
|
2270
|
+
const result = rma(sourceValues, length);
|
|
2271
|
+
return Series.fromArray(bars2, result);
|
|
2272
|
+
}
|
|
2273
|
+
function rsi2(source, length) {
|
|
2274
|
+
const bars2 = source.bars;
|
|
2275
|
+
const sourceValues = source.toArray();
|
|
2276
|
+
const result = rsi(sourceValues, length);
|
|
2277
|
+
return Series.fromArray(bars2, result);
|
|
2278
|
+
}
|
|
2279
|
+
function macd2(source, fastLength, slowLength, signalLength) {
|
|
2280
|
+
const bars2 = source.bars;
|
|
2281
|
+
const sourceValues = source.toArray();
|
|
2282
|
+
const [macdVals, signalVals, histVals] = macd(sourceValues, fastLength, slowLength, signalLength);
|
|
2283
|
+
const macdSeries = Series.fromArray(bars2, macdVals);
|
|
2284
|
+
const signalSeries = Series.fromArray(bars2, signalVals);
|
|
2285
|
+
const histSeries = Series.fromArray(bars2, histVals);
|
|
2286
|
+
return [macdSeries, signalSeries, histSeries];
|
|
2287
|
+
}
|
|
2288
|
+
function bb2(source, length, mult) {
|
|
2289
|
+
const bars2 = source.bars;
|
|
2290
|
+
const sourceValues = source.toArray();
|
|
2291
|
+
const [upperVals, basisVals, lowerVals] = bb(sourceValues, length, mult);
|
|
2292
|
+
const upperSeries = Series.fromArray(bars2, upperVals);
|
|
2293
|
+
const basisSeries = Series.fromArray(bars2, basisVals);
|
|
2294
|
+
const lowerSeries = Series.fromArray(bars2, lowerVals);
|
|
2295
|
+
return [upperSeries, basisSeries, lowerSeries];
|
|
2296
|
+
}
|
|
2297
|
+
function stdev2(source, length) {
|
|
2298
|
+
const bars2 = source.bars;
|
|
2299
|
+
const sourceValues = source.toArray();
|
|
2300
|
+
const result = stdev(sourceValues, length);
|
|
2301
|
+
return Series.fromArray(bars2, result);
|
|
2302
|
+
}
|
|
2303
|
+
function atr2(bars2, length) {
|
|
2304
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2305
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2306
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2307
|
+
const result = atr(length, high2, low2, close2);
|
|
2308
|
+
return Series.fromArray(bars2, result);
|
|
2309
|
+
}
|
|
2310
|
+
function tr2(bars2, handle_na = false) {
|
|
2311
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2312
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2313
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2314
|
+
const result = tr(handle_na, high2, low2, close2);
|
|
2315
|
+
return Series.fromArray(bars2, result);
|
|
2316
|
+
}
|
|
2317
|
+
function stoch2(source, high2, low2, length) {
|
|
2318
|
+
const bars2 = source.bars;
|
|
2319
|
+
const sourceValues = source.toArray();
|
|
2320
|
+
const highValues = high2.toArray();
|
|
2321
|
+
const lowValues = low2.toArray();
|
|
2322
|
+
const result = stoch(sourceValues, highValues, lowValues, length);
|
|
2323
|
+
return Series.fromArray(bars2, result);
|
|
2324
|
+
}
|
|
2325
|
+
function crossover2(source1, source2) {
|
|
2326
|
+
const bars2 = source1.bars;
|
|
2327
|
+
const vals1 = source1.toArray();
|
|
2328
|
+
const vals2 = typeof source2 === "number" ? Array(bars2.length).fill(source2) : source2.toArray();
|
|
2329
|
+
const result = crossover(vals1, vals2);
|
|
2330
|
+
const numResult = result.map((b2) => b2 ? 1 : 0);
|
|
2331
|
+
return Series.fromArray(bars2, numResult);
|
|
2332
|
+
}
|
|
2333
|
+
function crossunder2(source1, source2) {
|
|
2334
|
+
const bars2 = source1.bars;
|
|
2335
|
+
const vals1 = source1.toArray();
|
|
2336
|
+
const vals2 = typeof source2 === "number" ? Array(bars2.length).fill(source2) : source2.toArray();
|
|
2337
|
+
const result = crossunder(vals1, vals2);
|
|
2338
|
+
const numResult = result.map((b2) => b2 ? 1 : 0);
|
|
2339
|
+
return Series.fromArray(bars2, numResult);
|
|
2340
|
+
}
|
|
2341
|
+
function cross2(source1, source2) {
|
|
2342
|
+
const bars2 = source1.bars;
|
|
2343
|
+
const vals1 = source1.toArray();
|
|
2344
|
+
const vals2 = typeof source2 === "number" ? Array(bars2.length).fill(source2) : source2.toArray();
|
|
2345
|
+
const result = cross(vals1, vals2);
|
|
2346
|
+
const numResult = result.map((b2) => b2 ? 1 : 0);
|
|
2347
|
+
return Series.fromArray(bars2, numResult);
|
|
2348
|
+
}
|
|
2349
|
+
function change2(source, length = 1) {
|
|
2350
|
+
const bars2 = source.bars;
|
|
2351
|
+
const sourceValues = source.toArray();
|
|
2352
|
+
const result = change(sourceValues, length);
|
|
2353
|
+
return Series.fromArray(bars2, result);
|
|
2354
|
+
}
|
|
2355
|
+
function mom2(source, length) {
|
|
2356
|
+
const bars2 = source.bars;
|
|
2357
|
+
const sourceValues = source.toArray();
|
|
2358
|
+
const result = mom(sourceValues, length);
|
|
2359
|
+
return Series.fromArray(bars2, result);
|
|
2360
|
+
}
|
|
2361
|
+
function roc2(source, length) {
|
|
2362
|
+
const bars2 = source.bars;
|
|
2363
|
+
const sourceValues = source.toArray();
|
|
2364
|
+
const result = roc(sourceValues, length);
|
|
2365
|
+
return Series.fromArray(bars2, result);
|
|
2366
|
+
}
|
|
2367
|
+
function highest2(source, length) {
|
|
2368
|
+
const bars2 = source.bars;
|
|
2369
|
+
const sourceValues = source.toArray();
|
|
2370
|
+
const result = highest(sourceValues, length);
|
|
2371
|
+
return Series.fromArray(bars2, result);
|
|
2372
|
+
}
|
|
2373
|
+
function lowest2(source, length) {
|
|
2374
|
+
const bars2 = source.bars;
|
|
2375
|
+
const sourceValues = source.toArray();
|
|
2376
|
+
const result = lowest(sourceValues, length);
|
|
2377
|
+
return Series.fromArray(bars2, result);
|
|
2378
|
+
}
|
|
2379
|
+
function rising2(source, length) {
|
|
2380
|
+
const bars2 = source.bars;
|
|
2381
|
+
const sourceValues = source.toArray();
|
|
2382
|
+
const result = rising(sourceValues, length);
|
|
2383
|
+
const numResult = result.map((b2) => b2 ? 1 : 0);
|
|
2384
|
+
return Series.fromArray(bars2, numResult);
|
|
2385
|
+
}
|
|
2386
|
+
function falling2(source, length) {
|
|
2387
|
+
const bars2 = source.bars;
|
|
2388
|
+
const sourceValues = source.toArray();
|
|
2389
|
+
const result = falling(sourceValues, length);
|
|
2390
|
+
const numResult = result.map((b2) => b2 ? 1 : 0);
|
|
2391
|
+
return Series.fromArray(bars2, numResult);
|
|
2392
|
+
}
|
|
2393
|
+
function cum2(source) {
|
|
2394
|
+
const bars2 = source.bars;
|
|
2395
|
+
const sourceValues = source.toArray();
|
|
2396
|
+
const result = cum(sourceValues);
|
|
2397
|
+
return Series.fromArray(bars2, result);
|
|
2398
|
+
}
|
|
2399
|
+
function supertrend2(bars2, factor, atrLength) {
|
|
2400
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2401
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2402
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2403
|
+
const [trendVals, dirVals] = supertrend(factor, atrLength, high2, low2, close2);
|
|
2404
|
+
const trendSeries = Series.fromArray(bars2, trendVals);
|
|
2405
|
+
const dirSeries = Series.fromArray(bars2, dirVals);
|
|
2406
|
+
return [trendSeries, dirSeries];
|
|
2407
|
+
}
|
|
2408
|
+
function vwap2(source, volume2) {
|
|
2409
|
+
const bars2 = source.bars;
|
|
2410
|
+
const sourceValues = source.toArray();
|
|
2411
|
+
const volumeValues = volume2.toArray();
|
|
2412
|
+
const result = vwap(sourceValues, volumeValues);
|
|
2413
|
+
return Series.fromArray(bars2, result);
|
|
2414
|
+
}
|
|
2415
|
+
function ichimoku2(bars2, conversionPeriods, basePeriods, laggingSpan2Periods, displacement) {
|
|
2416
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2417
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2418
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2419
|
+
const [tenkan, kijun, senkouA, senkouB, chikou] = ichimoku(
|
|
2420
|
+
conversionPeriods,
|
|
2421
|
+
basePeriods,
|
|
2422
|
+
laggingSpan2Periods,
|
|
2423
|
+
displacement,
|
|
2424
|
+
high2,
|
|
2425
|
+
low2,
|
|
2426
|
+
close2
|
|
2427
|
+
);
|
|
2428
|
+
return [
|
|
2429
|
+
Series.fromArray(bars2, tenkan),
|
|
2430
|
+
Series.fromArray(bars2, kijun),
|
|
2431
|
+
Series.fromArray(bars2, senkouA),
|
|
2432
|
+
Series.fromArray(bars2, senkouB),
|
|
2433
|
+
Series.fromArray(bars2, chikou)
|
|
2434
|
+
];
|
|
2435
|
+
}
|
|
2436
|
+
function vwma2(source, length, volume2) {
|
|
2437
|
+
const bars2 = source.bars;
|
|
2438
|
+
const sourceValues = source.toArray();
|
|
2439
|
+
const volumeValues = volume2.toArray();
|
|
2440
|
+
const result = vwma(sourceValues, length, volumeValues);
|
|
2441
|
+
return Series.fromArray(bars2, result);
|
|
2442
|
+
}
|
|
2443
|
+
function linreg2(source, length, offset = 0) {
|
|
2444
|
+
const bars2 = source.bars;
|
|
2445
|
+
const sourceValues = source.toArray();
|
|
2446
|
+
const result = linreg(sourceValues, length, offset);
|
|
2447
|
+
return Series.fromArray(bars2, result);
|
|
2448
|
+
}
|
|
2449
|
+
function alma2(source, length = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
2450
|
+
const bars2 = source.bars;
|
|
2451
|
+
const sourceValues = source.toArray();
|
|
2452
|
+
const result = alma(sourceValues, length, offset, sigma, floor2);
|
|
2453
|
+
return Series.fromArray(bars2, result);
|
|
2454
|
+
}
|
|
2455
|
+
function zigzag2(bars2, deviation = 5, depth = 10, backstep = 3) {
|
|
2456
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2457
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2458
|
+
const [zigzagVals, dirVals, pivotVals] = zigzag(
|
|
2459
|
+
deviation,
|
|
2460
|
+
depth,
|
|
2461
|
+
backstep,
|
|
2462
|
+
void 0,
|
|
2463
|
+
high2,
|
|
2464
|
+
low2
|
|
2465
|
+
);
|
|
2466
|
+
return [
|
|
2467
|
+
Series.fromArray(bars2, zigzagVals),
|
|
2468
|
+
Series.fromArray(bars2, dirVals),
|
|
2469
|
+
Series.fromArray(bars2, pivotVals.map((b2) => b2 ? 1 : 0))
|
|
2470
|
+
];
|
|
2471
|
+
}
|
|
2472
|
+
function cci2(source, length) {
|
|
2473
|
+
const bars2 = source.bars;
|
|
2474
|
+
const sourceValues = source.toArray();
|
|
2475
|
+
const result = cci(sourceValues, length);
|
|
2476
|
+
return Series.fromArray(bars2, result);
|
|
2477
|
+
}
|
|
2478
|
+
function sar2(bars2, start = 0.02, inc = 0.02, max4 = 0.2) {
|
|
2479
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2480
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2481
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2482
|
+
const result = sar(start, inc, max4, high2, low2, close2);
|
|
2483
|
+
return Series.fromArray(bars2, result);
|
|
2484
|
+
}
|
|
2485
|
+
function dev2(source, length) {
|
|
2486
|
+
const bars2 = source.bars;
|
|
2487
|
+
const result = dev(source.toArray(), length);
|
|
2488
|
+
return Series.fromArray(bars2, result);
|
|
2489
|
+
}
|
|
2490
|
+
function variance2(source, length, biased = true) {
|
|
2491
|
+
const bars2 = source.bars;
|
|
2492
|
+
const result = variance(source.toArray(), length, biased);
|
|
2493
|
+
return Series.fromArray(bars2, result);
|
|
2494
|
+
}
|
|
2495
|
+
function median2(source, length) {
|
|
2496
|
+
const bars2 = source.bars;
|
|
2497
|
+
const result = median(source.toArray(), length);
|
|
2498
|
+
return Series.fromArray(bars2, result);
|
|
2499
|
+
}
|
|
2500
|
+
function swma2(source) {
|
|
2501
|
+
const bars2 = source.bars;
|
|
2502
|
+
const result = swma(source.toArray());
|
|
2503
|
+
return Series.fromArray(bars2, result);
|
|
2504
|
+
}
|
|
2505
|
+
function correlation2(source1, source2, length) {
|
|
2506
|
+
const bars2 = source1.bars;
|
|
2507
|
+
const result = correlation(source1.toArray(), source2.toArray(), length);
|
|
2508
|
+
return Series.fromArray(bars2, result);
|
|
2509
|
+
}
|
|
2510
|
+
function percentrank2(source, length) {
|
|
2511
|
+
const bars2 = source.bars;
|
|
2512
|
+
const result = percentrank(source.toArray(), length);
|
|
2513
|
+
return Series.fromArray(bars2, result);
|
|
2514
|
+
}
|
|
2515
|
+
function mfi2(source, length, volume2) {
|
|
2516
|
+
const bars2 = source.bars;
|
|
2517
|
+
const result = mfi(source.toArray(), length, volume2.toArray());
|
|
2518
|
+
return Series.fromArray(bars2, result);
|
|
2519
|
+
}
|
|
2520
|
+
function hma2(source, length) {
|
|
2521
|
+
const bars2 = source.bars;
|
|
2522
|
+
const result = hma(source.toArray(), length);
|
|
2523
|
+
return Series.fromArray(bars2, result);
|
|
2524
|
+
}
|
|
2525
|
+
function pivothigh2(source, leftbars, rightbars) {
|
|
2526
|
+
const bars2 = source.bars;
|
|
2527
|
+
const result = pivothigh(source.toArray(), leftbars, rightbars);
|
|
2528
|
+
return Series.fromArray(bars2, result);
|
|
2529
|
+
}
|
|
2530
|
+
function pivotlow2(source, leftbars, rightbars) {
|
|
2531
|
+
const bars2 = source.bars;
|
|
2532
|
+
const result = pivotlow(source.toArray(), leftbars, rightbars);
|
|
2533
|
+
return Series.fromArray(bars2, result);
|
|
2534
|
+
}
|
|
2535
|
+
function barssince2(condition) {
|
|
2536
|
+
const bars2 = condition.bars;
|
|
2537
|
+
const boolValues = condition.toArray().map((v) => v === 1);
|
|
2538
|
+
const result = barssince(boolValues);
|
|
2539
|
+
return Series.fromArray(bars2, result);
|
|
2540
|
+
}
|
|
2541
|
+
function valuewhen2(condition, source, occurrence) {
|
|
2542
|
+
const bars2 = condition.bars;
|
|
2543
|
+
const boolValues = condition.toArray().map((v) => v === 1);
|
|
2544
|
+
const result = valuewhen(boolValues, source.toArray(), occurrence);
|
|
2545
|
+
return Series.fromArray(bars2, result);
|
|
2546
|
+
}
|
|
2547
|
+
function dmi2(bars2, diLength, adxSmoothing) {
|
|
2548
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2549
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2550
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2551
|
+
const [plusDI, minusDI, adx] = dmi(diLength, adxSmoothing, high2, low2, close2);
|
|
2552
|
+
return [
|
|
2553
|
+
Series.fromArray(bars2, plusDI),
|
|
2554
|
+
Series.fromArray(bars2, minusDI),
|
|
2555
|
+
Series.fromArray(bars2, adx)
|
|
2556
|
+
];
|
|
2557
|
+
}
|
|
2558
|
+
function tsi2(source, shortLength, longLength) {
|
|
2559
|
+
const bars2 = source.bars;
|
|
2560
|
+
const result = tsi(source.toArray(), shortLength, longLength);
|
|
2561
|
+
return Series.fromArray(bars2, result);
|
|
2562
|
+
}
|
|
2563
|
+
function cmo2(source, length) {
|
|
2564
|
+
const bars2 = source.bars;
|
|
2565
|
+
const result = cmo(source.toArray(), length);
|
|
2566
|
+
return Series.fromArray(bars2, result);
|
|
2567
|
+
}
|
|
2568
|
+
function kc2(bars2, source, length, mult, useTrueRange = true) {
|
|
2569
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2570
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2571
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2572
|
+
const [middle, upper, lower] = kc(source.toArray(), length, mult, useTrueRange, high2, low2, close2);
|
|
2573
|
+
return [
|
|
2574
|
+
Series.fromArray(bars2, middle),
|
|
2575
|
+
Series.fromArray(bars2, upper),
|
|
2576
|
+
Series.fromArray(bars2, lower)
|
|
2577
|
+
];
|
|
2578
|
+
}
|
|
2579
|
+
function bbw2(source, length, mult) {
|
|
2580
|
+
const bars2 = source.bars;
|
|
2581
|
+
const result = bbw(source.toArray(), length, mult);
|
|
2582
|
+
return Series.fromArray(bars2, result);
|
|
2583
|
+
}
|
|
2584
|
+
function wpr2(bars2, length = 14) {
|
|
2585
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2586
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2587
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2588
|
+
const result = wpr(high2, low2, close2, length);
|
|
2589
|
+
return Series.fromArray(bars2, result);
|
|
2590
|
+
}
|
|
2591
|
+
function kcw2(bars2, source, length = 20, mult = 2, useTrueRange = true) {
|
|
2592
|
+
const high2 = bars2.map((b2) => b2.high);
|
|
2593
|
+
const low2 = bars2.map((b2) => b2.low);
|
|
2594
|
+
const close2 = bars2.map((b2) => b2.close);
|
|
2595
|
+
const result = kcw(source.toArray(), length, mult, useTrueRange, high2, low2, close2);
|
|
2596
|
+
return Series.fromArray(bars2, result);
|
|
2597
|
+
}
|
|
2598
|
+
function range2(high2, low2) {
|
|
2599
|
+
const bars2 = high2.bars;
|
|
2600
|
+
const result = range(high2.toArray(), low2.toArray());
|
|
2601
|
+
return Series.fromArray(bars2, result);
|
|
2602
|
+
}
|
|
2603
|
+
function highestbars2(source, length) {
|
|
2604
|
+
const bars2 = source.bars;
|
|
2605
|
+
const result = highestbars(source.toArray(), length);
|
|
2606
|
+
return Series.fromArray(bars2, result);
|
|
2607
|
+
}
|
|
2608
|
+
function lowestbars2(source, length) {
|
|
2609
|
+
const bars2 = source.bars;
|
|
2610
|
+
const result = lowestbars(source.toArray(), length);
|
|
2611
|
+
return Series.fromArray(bars2, result);
|
|
2612
|
+
}
|
|
2613
|
+
function max2(source1, source2) {
|
|
2614
|
+
const bars2 = source1.bars;
|
|
2615
|
+
const result = max(source1.toArray(), source2.toArray());
|
|
2616
|
+
return Series.fromArray(bars2, result);
|
|
2617
|
+
}
|
|
2618
|
+
function min2(source1, source2) {
|
|
2619
|
+
const bars2 = source1.bars;
|
|
2620
|
+
const result = min(source1.toArray(), source2.toArray());
|
|
2621
|
+
return Series.fromArray(bars2, result);
|
|
2622
|
+
}
|
|
2623
|
+
function cog2(source, length = 10) {
|
|
2624
|
+
const bars2 = source.bars;
|
|
2625
|
+
const result = cog(source.toArray(), length);
|
|
2626
|
+
return Series.fromArray(bars2, result);
|
|
2627
|
+
}
|
|
2628
|
+
function mode2(source, length) {
|
|
2629
|
+
const bars2 = source.bars;
|
|
2630
|
+
const result = mode(source.toArray(), length);
|
|
2631
|
+
return Series.fromArray(bars2, result);
|
|
2632
|
+
}
|
|
2633
|
+
function percentile_linear_interpolation2(source, length, percentage) {
|
|
2634
|
+
const bars2 = source.bars;
|
|
2635
|
+
const result = percentile_linear_interpolation(source.toArray(), length, percentage);
|
|
2636
|
+
return Series.fromArray(bars2, result);
|
|
2637
|
+
}
|
|
2638
|
+
function percentile_nearest_rank2(source, length, percentage) {
|
|
2639
|
+
const bars2 = source.bars;
|
|
2640
|
+
const result = percentile_nearest_rank(source.toArray(), length, percentage);
|
|
2641
|
+
return Series.fromArray(bars2, result);
|
|
2642
|
+
}
|
|
2643
|
+
function rci2(source, length) {
|
|
2644
|
+
const bars2 = source.bars;
|
|
2645
|
+
const result = rci(source.toArray(), length);
|
|
2646
|
+
return Series.fromArray(bars2, result);
|
|
2647
|
+
}
|
|
2648
|
+
var ta = {
|
|
2649
|
+
sma: sma2,
|
|
2650
|
+
ema: ema2,
|
|
2651
|
+
wma: wma2,
|
|
2652
|
+
vwma: vwma2,
|
|
2653
|
+
rma: rma2,
|
|
2654
|
+
rsi: rsi2,
|
|
2655
|
+
macd: macd2,
|
|
2656
|
+
bb: bb2,
|
|
2657
|
+
stdev: stdev2,
|
|
2658
|
+
atr: atr2,
|
|
2659
|
+
tr: tr2,
|
|
2660
|
+
stoch: stoch2,
|
|
2661
|
+
crossover: crossover2,
|
|
2662
|
+
crossunder: crossunder2,
|
|
2663
|
+
cross: cross2,
|
|
2664
|
+
change: change2,
|
|
2665
|
+
mom: mom2,
|
|
2666
|
+
roc: roc2,
|
|
2667
|
+
highest: highest2,
|
|
2668
|
+
lowest: lowest2,
|
|
2669
|
+
rising: rising2,
|
|
2670
|
+
falling: falling2,
|
|
2671
|
+
cum: cum2,
|
|
2672
|
+
supertrend: supertrend2,
|
|
2673
|
+
vwap: vwap2,
|
|
2674
|
+
ichimoku: ichimoku2,
|
|
2675
|
+
linreg: linreg2,
|
|
2676
|
+
alma: alma2,
|
|
2677
|
+
zigzag: zigzag2,
|
|
2678
|
+
cci: cci2,
|
|
2679
|
+
sar: sar2,
|
|
2680
|
+
dev: dev2,
|
|
2681
|
+
variance: variance2,
|
|
2682
|
+
median: median2,
|
|
2683
|
+
swma: swma2,
|
|
2684
|
+
correlation: correlation2,
|
|
2685
|
+
percentrank: percentrank2,
|
|
2686
|
+
mfi: mfi2,
|
|
2687
|
+
hma: hma2,
|
|
2688
|
+
pivothigh: pivothigh2,
|
|
2689
|
+
pivotlow: pivotlow2,
|
|
2690
|
+
barssince: barssince2,
|
|
2691
|
+
valuewhen: valuewhen2,
|
|
2692
|
+
dmi: dmi2,
|
|
2693
|
+
tsi: tsi2,
|
|
2694
|
+
cmo: cmo2,
|
|
2695
|
+
kc: kc2,
|
|
2696
|
+
bbw: bbw2,
|
|
2697
|
+
wpr: wpr2,
|
|
2698
|
+
kcw: kcw2,
|
|
2699
|
+
range: range2,
|
|
2700
|
+
highestbars: highestbars2,
|
|
2701
|
+
lowestbars: lowestbars2,
|
|
2702
|
+
max: max2,
|
|
2703
|
+
min: min2,
|
|
2704
|
+
cog: cog2,
|
|
2705
|
+
mode: mode2,
|
|
2706
|
+
percentile_linear_interpolation: percentile_linear_interpolation2,
|
|
2707
|
+
percentile_nearest_rank: percentile_nearest_rank2,
|
|
2708
|
+
rci: rci2
|
|
2709
|
+
};
|
|
2710
|
+
|
|
2711
|
+
// src/color/index.ts
|
|
2712
|
+
var color_exports = {};
|
|
2713
|
+
__export(color_exports, {
|
|
2714
|
+
aqua: () => aqua,
|
|
2715
|
+
b: () => b,
|
|
2716
|
+
black: () => black,
|
|
2717
|
+
blue: () => blue,
|
|
2718
|
+
from_gradient: () => from_gradient,
|
|
2719
|
+
from_hex: () => from_hex,
|
|
2720
|
+
fuchsia: () => fuchsia,
|
|
2721
|
+
g: () => g,
|
|
2722
|
+
gray: () => gray,
|
|
2723
|
+
green: () => green,
|
|
2724
|
+
lime: () => lime,
|
|
2725
|
+
maroon: () => maroon,
|
|
2726
|
+
navy: () => navy,
|
|
2727
|
+
new: () => new_color,
|
|
2728
|
+
new_color: () => new_color,
|
|
2729
|
+
olive: () => olive,
|
|
2730
|
+
orange: () => orange,
|
|
2731
|
+
purple: () => purple,
|
|
2732
|
+
r: () => r,
|
|
2733
|
+
red: () => red,
|
|
2734
|
+
rgb: () => rgb,
|
|
2735
|
+
silver: () => silver,
|
|
2736
|
+
t: () => t,
|
|
2737
|
+
teal: () => teal,
|
|
2738
|
+
white: () => white,
|
|
2739
|
+
yellow: () => yellow
|
|
2740
|
+
});
|
|
2741
|
+
function rgb(red2, green2, blue2, transp) {
|
|
2742
|
+
const r2 = Math.max(0, Math.min(255, red2));
|
|
2743
|
+
const g2 = Math.max(0, Math.min(255, green2));
|
|
2744
|
+
const b2 = Math.max(0, Math.min(255, blue2));
|
|
2745
|
+
const a = transp !== void 0 ? 1 - transp / 100 : 1;
|
|
2746
|
+
if (a === 1) {
|
|
2747
|
+
return `rgb(${r2}, ${g2}, ${b2})`;
|
|
2748
|
+
}
|
|
2749
|
+
return `rgba(${r2}, ${g2}, ${b2}, ${a})`;
|
|
2750
|
+
}
|
|
2751
|
+
function from_hex(hex, transp) {
|
|
2752
|
+
hex = hex.replace("#", "");
|
|
2753
|
+
const r2 = parseInt(hex.substring(0, 2), 16);
|
|
2754
|
+
const g2 = parseInt(hex.substring(2, 4), 16);
|
|
2755
|
+
const b2 = parseInt(hex.substring(4, 6), 16);
|
|
2756
|
+
return rgb(r2, g2, b2, transp);
|
|
2757
|
+
}
|
|
2758
|
+
function new_color(baseColor, transp) {
|
|
2759
|
+
const rgba = parseColor(baseColor);
|
|
2760
|
+
return rgb(rgba.r, rgba.g, rgba.b, transp);
|
|
2761
|
+
}
|
|
2762
|
+
function r(clr) {
|
|
2763
|
+
return parseColor(clr).r;
|
|
2764
|
+
}
|
|
2765
|
+
function g(clr) {
|
|
2766
|
+
return parseColor(clr).g;
|
|
2767
|
+
}
|
|
2768
|
+
function b(clr) {
|
|
2769
|
+
return parseColor(clr).b;
|
|
2770
|
+
}
|
|
2771
|
+
function t(clr) {
|
|
2772
|
+
return parseColor(clr).t;
|
|
2773
|
+
}
|
|
2774
|
+
function parseColor(clr) {
|
|
2775
|
+
if (typeof clr === "string") {
|
|
2776
|
+
const match = clr.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
2777
|
+
if (match) {
|
|
2778
|
+
return {
|
|
2779
|
+
r: parseInt(match[1]),
|
|
2780
|
+
g: parseInt(match[2]),
|
|
2781
|
+
b: parseInt(match[3]),
|
|
2782
|
+
t: match[4] ? (1 - parseFloat(match[4])) * 100 : 0
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
return { r: 0, g: 0, b: 0, t: 0 };
|
|
2787
|
+
}
|
|
2788
|
+
var aqua = "#00FFFF";
|
|
2789
|
+
var black = "#000000";
|
|
2790
|
+
var blue = "#0000FF";
|
|
2791
|
+
var fuchsia = "#FF00FF";
|
|
2792
|
+
var gray = "#808080";
|
|
2793
|
+
var green = "#00FF00";
|
|
2794
|
+
var lime = "#00FF00";
|
|
2795
|
+
var maroon = "#800000";
|
|
2796
|
+
var navy = "#000080";
|
|
2797
|
+
var olive = "#808000";
|
|
2798
|
+
var orange = "#FFA500";
|
|
2799
|
+
var purple = "#800080";
|
|
2800
|
+
var red = "#FF0000";
|
|
2801
|
+
var silver = "#C0C0C0";
|
|
2802
|
+
var teal = "#008080";
|
|
2803
|
+
var white = "#FFFFFF";
|
|
2804
|
+
var yellow = "#FFFF00";
|
|
2805
|
+
function from_gradient(value, bottom_value, top_value, bottom_color, top_color) {
|
|
2806
|
+
const parseColor2 = (col) => {
|
|
2807
|
+
if (typeof col === "string") {
|
|
2808
|
+
if (col.startsWith("#")) {
|
|
2809
|
+
const hex = col.slice(1);
|
|
2810
|
+
const r4 = parseInt(hex.slice(0, 2), 16);
|
|
2811
|
+
const g4 = parseInt(hex.slice(2, 4), 16);
|
|
2812
|
+
const b4 = parseInt(hex.slice(4, 6), 16);
|
|
2813
|
+
return [r4, g4, b4, 1];
|
|
2814
|
+
}
|
|
2815
|
+
const match = col.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
2816
|
+
if (match) {
|
|
2817
|
+
return [
|
|
2818
|
+
parseInt(match[1]),
|
|
2819
|
+
parseInt(match[2]),
|
|
2820
|
+
parseInt(match[3]),
|
|
2821
|
+
match[4] ? parseFloat(match[4]) : 1
|
|
2822
|
+
];
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
return [0, 0, 0, 1];
|
|
2826
|
+
};
|
|
2827
|
+
const [r1, g1, b1, a1] = parseColor2(bottom_color);
|
|
2828
|
+
const [r2, g2, b2, a2] = parseColor2(top_color);
|
|
2829
|
+
let t2;
|
|
2830
|
+
if (value <= bottom_value) {
|
|
2831
|
+
t2 = 0;
|
|
2832
|
+
} else if (value >= top_value) {
|
|
2833
|
+
t2 = 1;
|
|
2834
|
+
} else {
|
|
2835
|
+
t2 = (value - bottom_value) / (top_value - bottom_value);
|
|
2836
|
+
}
|
|
2837
|
+
const r3 = Math.round(r1 + (r2 - r1) * t2);
|
|
2838
|
+
const g3 = Math.round(g1 + (g2 - g1) * t2);
|
|
2839
|
+
const b3 = Math.round(b1 + (b2 - b1) * t2);
|
|
2840
|
+
const a = a1 + (a2 - a1) * t2;
|
|
2841
|
+
if (a === 1) {
|
|
2842
|
+
return `rgb(${r3}, ${g3}, ${b3})`;
|
|
2843
|
+
}
|
|
2844
|
+
return `rgba(${r3}, ${g3}, ${b3}, ${a})`;
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
// src/utils/index.ts
|
|
2848
|
+
function isNA(value) {
|
|
2849
|
+
return value === null || value === void 0 || Number.isNaN(value);
|
|
2850
|
+
}
|
|
2851
|
+
function nz(value, defaultValue = 0) {
|
|
2852
|
+
return isNA(value) ? defaultValue : value;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
// src/math/index.ts
|
|
2856
|
+
var math_exports = {};
|
|
2857
|
+
__export(math_exports, {
|
|
2858
|
+
abs: () => abs,
|
|
2859
|
+
acos: () => acos,
|
|
2860
|
+
asin: () => asin,
|
|
2861
|
+
atan: () => atan,
|
|
2862
|
+
avg: () => avg,
|
|
2863
|
+
ceil: () => ceil,
|
|
2864
|
+
cos: () => cos,
|
|
2865
|
+
e: () => e,
|
|
2866
|
+
exp: () => exp,
|
|
2867
|
+
floor: () => floor,
|
|
2868
|
+
log: () => log,
|
|
2869
|
+
log10: () => log10,
|
|
2870
|
+
max: () => max3,
|
|
2871
|
+
min: () => min3,
|
|
2872
|
+
phi: () => phi,
|
|
2873
|
+
pi: () => pi,
|
|
2874
|
+
pow: () => pow,
|
|
2875
|
+
random: () => random,
|
|
2876
|
+
round: () => round,
|
|
2877
|
+
round_to_mintick: () => round_to_mintick,
|
|
2878
|
+
rphi: () => rphi,
|
|
2879
|
+
sign: () => sign,
|
|
2880
|
+
sin: () => sin,
|
|
2881
|
+
sqrt: () => sqrt,
|
|
2882
|
+
sum: () => sum,
|
|
2883
|
+
tan: () => tan,
|
|
2884
|
+
todegrees: () => todegrees,
|
|
2885
|
+
toradians: () => toradians
|
|
2886
|
+
});
|
|
2887
|
+
function abs(value) {
|
|
2888
|
+
if (value instanceof Series) {
|
|
2889
|
+
const bars2 = value.bars;
|
|
2890
|
+
const valueArray = value.toArray();
|
|
2891
|
+
const length = bars2.length;
|
|
2892
|
+
const result = [];
|
|
2893
|
+
for (let i = 0; i < length; i++) {
|
|
2894
|
+
const v = valueArray[i] ?? NaN;
|
|
2895
|
+
result.push(Math.abs(v));
|
|
2896
|
+
}
|
|
2897
|
+
return Series.fromArray(bars2, result);
|
|
2898
|
+
}
|
|
2899
|
+
return Math.abs(value);
|
|
2900
|
+
}
|
|
2901
|
+
function ceil(value) {
|
|
2902
|
+
if (value instanceof Series) {
|
|
2903
|
+
const bars2 = value.bars;
|
|
2904
|
+
const valueArray = value.toArray();
|
|
2905
|
+
const length = bars2.length;
|
|
2906
|
+
const result = [];
|
|
2907
|
+
for (let i = 0; i < length; i++) {
|
|
2908
|
+
const v = valueArray[i] ?? NaN;
|
|
2909
|
+
result.push(Math.ceil(v));
|
|
2910
|
+
}
|
|
2911
|
+
return Series.fromArray(bars2, result);
|
|
2912
|
+
}
|
|
2913
|
+
return Math.ceil(value);
|
|
2914
|
+
}
|
|
2915
|
+
function floor(value) {
|
|
2916
|
+
if (value instanceof Series) {
|
|
2917
|
+
const bars2 = value.bars;
|
|
2918
|
+
const valueArray = value.toArray();
|
|
2919
|
+
const length = bars2.length;
|
|
2920
|
+
const result = [];
|
|
2921
|
+
for (let i = 0; i < length; i++) {
|
|
2922
|
+
const v = valueArray[i] ?? NaN;
|
|
2923
|
+
result.push(Math.floor(v));
|
|
2924
|
+
}
|
|
2925
|
+
return Series.fromArray(bars2, result);
|
|
2926
|
+
}
|
|
2927
|
+
return Math.floor(value);
|
|
2928
|
+
}
|
|
2929
|
+
function round(value, precision) {
|
|
2930
|
+
if (value instanceof Series) {
|
|
2931
|
+
const bars2 = value.bars;
|
|
2932
|
+
const valueArray = value.toArray();
|
|
2933
|
+
const length = bars2.length;
|
|
2934
|
+
const result = [];
|
|
2935
|
+
if (precision === void 0) {
|
|
2936
|
+
for (let i = 0; i < length; i++) {
|
|
2937
|
+
const v = valueArray[i] ?? NaN;
|
|
2938
|
+
result.push(Math.round(v));
|
|
2939
|
+
}
|
|
2940
|
+
} else {
|
|
2941
|
+
const multiplier2 = Math.pow(10, precision);
|
|
2942
|
+
for (let i = 0; i < length; i++) {
|
|
2943
|
+
const v = valueArray[i] ?? NaN;
|
|
2944
|
+
result.push(Math.round(v * multiplier2) / multiplier2);
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
return Series.fromArray(bars2, result);
|
|
2948
|
+
}
|
|
2949
|
+
if (precision === void 0) {
|
|
2950
|
+
return Math.round(value);
|
|
2951
|
+
}
|
|
2952
|
+
const multiplier = Math.pow(10, precision);
|
|
2953
|
+
return Math.round(value * multiplier) / multiplier;
|
|
2954
|
+
}
|
|
2955
|
+
function max3(...values) {
|
|
2956
|
+
const hasSeries = values.some((v) => v instanceof Series);
|
|
2957
|
+
if (!hasSeries) {
|
|
2958
|
+
return Math.max(...values);
|
|
2959
|
+
}
|
|
2960
|
+
const firstSeries = values.find((v) => v instanceof Series);
|
|
2961
|
+
const bars2 = firstSeries.bars;
|
|
2962
|
+
const length = bars2.length;
|
|
2963
|
+
const valueArrays = values.map(
|
|
2964
|
+
(v) => v instanceof Series ? v.toArray() : null
|
|
2965
|
+
);
|
|
2966
|
+
const result = [];
|
|
2967
|
+
for (let i = 0; i < length; i++) {
|
|
2968
|
+
const nums = values.map(
|
|
2969
|
+
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
2970
|
+
);
|
|
2971
|
+
result.push(Math.max(...nums));
|
|
2972
|
+
}
|
|
2973
|
+
return Series.fromArray(bars2, result);
|
|
2974
|
+
}
|
|
2975
|
+
function min3(...values) {
|
|
2976
|
+
const hasSeries = values.some((v) => v instanceof Series);
|
|
2977
|
+
if (!hasSeries) {
|
|
2978
|
+
return Math.min(...values);
|
|
2979
|
+
}
|
|
2980
|
+
const firstSeries = values.find((v) => v instanceof Series);
|
|
2981
|
+
const bars2 = firstSeries.bars;
|
|
2982
|
+
const length = bars2.length;
|
|
2983
|
+
const valueArrays = values.map(
|
|
2984
|
+
(v) => v instanceof Series ? v.toArray() : null
|
|
2985
|
+
);
|
|
2986
|
+
const result = [];
|
|
2987
|
+
for (let i = 0; i < length; i++) {
|
|
2988
|
+
const nums = values.map(
|
|
2989
|
+
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
2990
|
+
);
|
|
2991
|
+
result.push(Math.min(...nums));
|
|
2992
|
+
}
|
|
2993
|
+
return Series.fromArray(bars2, result);
|
|
2994
|
+
}
|
|
2995
|
+
function avg(...values) {
|
|
2996
|
+
const hasSeries = values.some((v) => v instanceof Series);
|
|
2997
|
+
if (!hasSeries) {
|
|
2998
|
+
return values.reduce((a, b2) => a + b2, 0) / values.length;
|
|
2999
|
+
}
|
|
3000
|
+
const firstSeries = values.find((v) => v instanceof Series);
|
|
3001
|
+
const bars2 = firstSeries.bars;
|
|
3002
|
+
const length = bars2.length;
|
|
3003
|
+
const valueArrays = values.map(
|
|
3004
|
+
(v) => v instanceof Series ? v.toArray() : null
|
|
3005
|
+
);
|
|
3006
|
+
const result = [];
|
|
3007
|
+
for (let i = 0; i < length; i++) {
|
|
3008
|
+
const nums = values.map(
|
|
3009
|
+
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
3010
|
+
);
|
|
3011
|
+
const sum2 = nums.reduce((a, b2) => a + b2, 0);
|
|
3012
|
+
result.push(sum2 / nums.length);
|
|
3013
|
+
}
|
|
3014
|
+
return Series.fromArray(bars2, result);
|
|
3015
|
+
}
|
|
3016
|
+
function sum(source, length) {
|
|
3017
|
+
const sourceArray = source instanceof Series ? source.toArray() : source;
|
|
3018
|
+
const result = [];
|
|
3019
|
+
for (let i = 0; i < sourceArray.length; i++) {
|
|
3020
|
+
if (i < length - 1) {
|
|
3021
|
+
result.push(NaN);
|
|
3022
|
+
} else {
|
|
3023
|
+
let total = 0;
|
|
3024
|
+
for (let j = 0; j < length; j++) {
|
|
3025
|
+
total += sourceArray[i - j];
|
|
3026
|
+
}
|
|
3027
|
+
result.push(total);
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
if (source instanceof Series) {
|
|
3031
|
+
return Series.fromArray(source.bars, result);
|
|
3032
|
+
}
|
|
3033
|
+
return result;
|
|
3034
|
+
}
|
|
3035
|
+
function sqrt(value) {
|
|
3036
|
+
if (value instanceof Series) {
|
|
3037
|
+
const bars2 = value.bars;
|
|
3038
|
+
const valueArray = value.toArray();
|
|
3039
|
+
const length = bars2.length;
|
|
3040
|
+
const result = [];
|
|
3041
|
+
for (let i = 0; i < length; i++) {
|
|
3042
|
+
const v = valueArray[i] ?? NaN;
|
|
3043
|
+
result.push(Math.sqrt(v));
|
|
3044
|
+
}
|
|
3045
|
+
return Series.fromArray(bars2, result);
|
|
3046
|
+
}
|
|
3047
|
+
return Math.sqrt(value);
|
|
3048
|
+
}
|
|
3049
|
+
function pow(base, exponent) {
|
|
3050
|
+
const baseIsSeries = base instanceof Series;
|
|
3051
|
+
const expIsSeries = exponent instanceof Series;
|
|
3052
|
+
if (!baseIsSeries && !expIsSeries) {
|
|
3053
|
+
return Math.pow(base, exponent);
|
|
3054
|
+
}
|
|
3055
|
+
const bars2 = baseIsSeries ? base.bars : exponent.bars;
|
|
3056
|
+
const baseArray = baseIsSeries ? base.toArray() : null;
|
|
3057
|
+
const expArray = expIsSeries ? exponent.toArray() : null;
|
|
3058
|
+
const length = bars2.length;
|
|
3059
|
+
const result = [];
|
|
3060
|
+
for (let i = 0; i < length; i++) {
|
|
3061
|
+
const b2 = baseArray ? baseArray[i] ?? NaN : base;
|
|
3062
|
+
const e2 = expArray ? expArray[i] ?? NaN : exponent;
|
|
3063
|
+
result.push(Math.pow(b2, e2));
|
|
3064
|
+
}
|
|
3065
|
+
return Series.fromArray(bars2, result);
|
|
3066
|
+
}
|
|
3067
|
+
function exp(value) {
|
|
3068
|
+
if (value instanceof Series) {
|
|
3069
|
+
const bars2 = value.bars;
|
|
3070
|
+
const valueArray = value.toArray();
|
|
3071
|
+
const length = bars2.length;
|
|
3072
|
+
const result = [];
|
|
3073
|
+
for (let i = 0; i < length; i++) {
|
|
3074
|
+
const v = valueArray[i] ?? NaN;
|
|
3075
|
+
result.push(Math.exp(v));
|
|
3076
|
+
}
|
|
3077
|
+
return Series.fromArray(bars2, result);
|
|
3078
|
+
}
|
|
3079
|
+
return Math.exp(value);
|
|
3080
|
+
}
|
|
3081
|
+
function log(value) {
|
|
3082
|
+
if (value instanceof Series) {
|
|
3083
|
+
const bars2 = value.bars;
|
|
3084
|
+
const valueArray = value.toArray();
|
|
3085
|
+
const length = bars2.length;
|
|
3086
|
+
const result = [];
|
|
3087
|
+
for (let i = 0; i < length; i++) {
|
|
3088
|
+
const v = valueArray[i] ?? NaN;
|
|
3089
|
+
result.push(Math.log(v));
|
|
3090
|
+
}
|
|
3091
|
+
return Series.fromArray(bars2, result);
|
|
3092
|
+
}
|
|
3093
|
+
return Math.log(value);
|
|
3094
|
+
}
|
|
3095
|
+
function log10(value) {
|
|
3096
|
+
if (value instanceof Series) {
|
|
3097
|
+
const bars2 = value.bars;
|
|
3098
|
+
const valueArray = value.toArray();
|
|
3099
|
+
const length = bars2.length;
|
|
3100
|
+
const result = [];
|
|
3101
|
+
for (let i = 0; i < length; i++) {
|
|
3102
|
+
const v = valueArray[i] ?? NaN;
|
|
3103
|
+
result.push(Math.log10(v));
|
|
3104
|
+
}
|
|
3105
|
+
return Series.fromArray(bars2, result);
|
|
3106
|
+
}
|
|
3107
|
+
return Math.log10(value);
|
|
3108
|
+
}
|
|
3109
|
+
function sin(value) {
|
|
3110
|
+
if (value instanceof Series) {
|
|
3111
|
+
const bars2 = value.bars;
|
|
3112
|
+
const valueArray = value.toArray();
|
|
3113
|
+
const length = bars2.length;
|
|
3114
|
+
const result = [];
|
|
3115
|
+
for (let i = 0; i < length; i++) {
|
|
3116
|
+
const v = valueArray[i] ?? NaN;
|
|
3117
|
+
result.push(Math.sin(v));
|
|
3118
|
+
}
|
|
3119
|
+
return Series.fromArray(bars2, result);
|
|
3120
|
+
}
|
|
3121
|
+
return Math.sin(value);
|
|
3122
|
+
}
|
|
3123
|
+
function cos(value) {
|
|
3124
|
+
if (value instanceof Series) {
|
|
3125
|
+
const bars2 = value.bars;
|
|
3126
|
+
const valueArray = value.toArray();
|
|
3127
|
+
const length = bars2.length;
|
|
3128
|
+
const result = [];
|
|
3129
|
+
for (let i = 0; i < length; i++) {
|
|
3130
|
+
const v = valueArray[i] ?? NaN;
|
|
3131
|
+
result.push(Math.cos(v));
|
|
3132
|
+
}
|
|
3133
|
+
return Series.fromArray(bars2, result);
|
|
3134
|
+
}
|
|
3135
|
+
return Math.cos(value);
|
|
3136
|
+
}
|
|
3137
|
+
function tan(value) {
|
|
3138
|
+
if (value instanceof Series) {
|
|
3139
|
+
const bars2 = value.bars;
|
|
3140
|
+
const valueArray = value.toArray();
|
|
3141
|
+
const length = bars2.length;
|
|
3142
|
+
const result = [];
|
|
3143
|
+
for (let i = 0; i < length; i++) {
|
|
3144
|
+
const v = valueArray[i] ?? NaN;
|
|
3145
|
+
result.push(Math.tan(v));
|
|
3146
|
+
}
|
|
3147
|
+
return Series.fromArray(bars2, result);
|
|
3148
|
+
}
|
|
3149
|
+
return Math.tan(value);
|
|
3150
|
+
}
|
|
3151
|
+
function asin(value) {
|
|
3152
|
+
if (value instanceof Series) {
|
|
3153
|
+
const bars2 = value.bars;
|
|
3154
|
+
const valueArray = value.toArray();
|
|
3155
|
+
const length = bars2.length;
|
|
3156
|
+
const result = [];
|
|
3157
|
+
for (let i = 0; i < length; i++) {
|
|
3158
|
+
const v = valueArray[i] ?? NaN;
|
|
3159
|
+
result.push(Math.asin(v));
|
|
3160
|
+
}
|
|
3161
|
+
return Series.fromArray(bars2, result);
|
|
3162
|
+
}
|
|
3163
|
+
return Math.asin(value);
|
|
3164
|
+
}
|
|
3165
|
+
function acos(value) {
|
|
3166
|
+
if (value instanceof Series) {
|
|
3167
|
+
const bars2 = value.bars;
|
|
3168
|
+
const valueArray = value.toArray();
|
|
3169
|
+
const length = bars2.length;
|
|
3170
|
+
const result = [];
|
|
3171
|
+
for (let i = 0; i < length; i++) {
|
|
3172
|
+
const v = valueArray[i] ?? NaN;
|
|
3173
|
+
result.push(Math.acos(v));
|
|
3174
|
+
}
|
|
3175
|
+
return Series.fromArray(bars2, result);
|
|
3176
|
+
}
|
|
3177
|
+
return Math.acos(value);
|
|
3178
|
+
}
|
|
3179
|
+
function atan(value) {
|
|
3180
|
+
if (value instanceof Series) {
|
|
3181
|
+
const bars2 = value.bars;
|
|
3182
|
+
const valueArray = value.toArray();
|
|
3183
|
+
const length = bars2.length;
|
|
3184
|
+
const result = [];
|
|
3185
|
+
for (let i = 0; i < length; i++) {
|
|
3186
|
+
const v = valueArray[i] ?? NaN;
|
|
3187
|
+
result.push(Math.atan(v));
|
|
3188
|
+
}
|
|
3189
|
+
return Series.fromArray(bars2, result);
|
|
3190
|
+
}
|
|
3191
|
+
return Math.atan(value);
|
|
3192
|
+
}
|
|
3193
|
+
function toradians(degrees) {
|
|
3194
|
+
if (degrees instanceof Series) {
|
|
3195
|
+
const bars2 = degrees.bars;
|
|
3196
|
+
const valueArray = degrees.toArray();
|
|
3197
|
+
const length = bars2.length;
|
|
3198
|
+
const result = [];
|
|
3199
|
+
for (let i = 0; i < length; i++) {
|
|
3200
|
+
const v = valueArray[i] ?? NaN;
|
|
3201
|
+
result.push(v * (Math.PI / 180));
|
|
3202
|
+
}
|
|
3203
|
+
return Series.fromArray(bars2, result);
|
|
3204
|
+
}
|
|
3205
|
+
return degrees * (Math.PI / 180);
|
|
3206
|
+
}
|
|
3207
|
+
function todegrees(radians) {
|
|
3208
|
+
if (radians instanceof Series) {
|
|
3209
|
+
const bars2 = radians.bars;
|
|
3210
|
+
const valueArray = radians.toArray();
|
|
3211
|
+
const length = bars2.length;
|
|
3212
|
+
const result = [];
|
|
3213
|
+
for (let i = 0; i < length; i++) {
|
|
3214
|
+
const v = valueArray[i] ?? NaN;
|
|
3215
|
+
result.push(v * (180 / Math.PI));
|
|
3216
|
+
}
|
|
3217
|
+
return Series.fromArray(bars2, result);
|
|
3218
|
+
}
|
|
3219
|
+
return radians * (180 / Math.PI);
|
|
3220
|
+
}
|
|
3221
|
+
function random(min4, max4, _seed) {
|
|
3222
|
+
const rand = Math.random();
|
|
3223
|
+
if (min4 !== void 0 && max4 !== void 0) {
|
|
3224
|
+
return min4 + rand * (max4 - min4);
|
|
3225
|
+
}
|
|
3226
|
+
return rand;
|
|
3227
|
+
}
|
|
3228
|
+
function sign(value) {
|
|
3229
|
+
if (value instanceof Series) {
|
|
3230
|
+
const bars2 = value.bars;
|
|
3231
|
+
const valueArray = value.toArray();
|
|
3232
|
+
const length = bars2.length;
|
|
3233
|
+
const result = [];
|
|
3234
|
+
for (let i = 0; i < length; i++) {
|
|
3235
|
+
const v = valueArray[i] ?? NaN;
|
|
3236
|
+
result.push(v > 0 ? 1 : v < 0 ? -1 : 0);
|
|
3237
|
+
}
|
|
3238
|
+
return Series.fromArray(bars2, result);
|
|
3239
|
+
}
|
|
3240
|
+
return value > 0 ? 1 : value < 0 ? -1 : 0;
|
|
3241
|
+
}
|
|
3242
|
+
function round_to_mintick(number, mintick) {
|
|
3243
|
+
if (number instanceof Series) {
|
|
3244
|
+
const bars2 = number.bars;
|
|
3245
|
+
const valueArray = number.toArray();
|
|
3246
|
+
const length = bars2.length;
|
|
3247
|
+
if (mintick === void 0) {
|
|
3248
|
+
throw new Error(
|
|
3249
|
+
"math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
|
|
3250
|
+
);
|
|
3251
|
+
}
|
|
3252
|
+
const result = [];
|
|
3253
|
+
if (mintick === 0) {
|
|
3254
|
+
for (let i = 0; i < length; i++) {
|
|
3255
|
+
result.push(valueArray[i] ?? NaN);
|
|
3256
|
+
}
|
|
3257
|
+
} else {
|
|
3258
|
+
for (let i = 0; i < length; i++) {
|
|
3259
|
+
const v = valueArray[i];
|
|
3260
|
+
if (v === void 0 || isNaN(v)) {
|
|
3261
|
+
result.push(NaN);
|
|
3262
|
+
} else {
|
|
3263
|
+
result.push(Math.round(v / mintick) * mintick);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
return Series.fromArray(bars2, result);
|
|
3268
|
+
}
|
|
3269
|
+
if (isNaN(number)) {
|
|
3270
|
+
return NaN;
|
|
3271
|
+
}
|
|
3272
|
+
if (mintick === void 0) {
|
|
3273
|
+
throw new Error(
|
|
3274
|
+
"math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
|
|
3275
|
+
);
|
|
3276
|
+
}
|
|
3277
|
+
if (mintick === 0) {
|
|
3278
|
+
return number;
|
|
3279
|
+
}
|
|
3280
|
+
return Math.round(number / mintick) * mintick;
|
|
3281
|
+
}
|
|
3282
|
+
var pi = Math.PI;
|
|
3283
|
+
var e = Math.E;
|
|
3284
|
+
var phi = 1.618033988749895;
|
|
3285
|
+
var rphi = 0.618033988749895;
|
|
3286
|
+
|
|
3287
|
+
// src/script/index.ts
|
|
3288
|
+
var ctxBars = new BarData([]);
|
|
3289
|
+
var ctx = null;
|
|
3290
|
+
function collector() {
|
|
3291
|
+
if (!ctx) {
|
|
3292
|
+
throw new Error(
|
|
3293
|
+
"oakscriptjs/script functions can only run inside executeScript() \u2014 the host (chart/worker) drives script execution."
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
3296
|
+
return ctx;
|
|
3297
|
+
}
|
|
3298
|
+
function freshCollector(inputValues) {
|
|
3299
|
+
return {
|
|
3300
|
+
title: "Untitled",
|
|
3301
|
+
overlay: true,
|
|
3302
|
+
inputValues,
|
|
3303
|
+
inputConfig: [],
|
|
3304
|
+
inputIds: /* @__PURE__ */ new Set(),
|
|
3305
|
+
defaultInputs: {},
|
|
3306
|
+
plotConfig: [],
|
|
3307
|
+
plots: {},
|
|
3308
|
+
hlineConfig: [],
|
|
3309
|
+
fills: [],
|
|
3310
|
+
fillConfig: [],
|
|
3311
|
+
shapeConfig: [],
|
|
3312
|
+
markers: [],
|
|
3313
|
+
barColorConfig: [],
|
|
3314
|
+
bgcolors: [],
|
|
3315
|
+
barcolors: [],
|
|
3316
|
+
alertConfig: [],
|
|
3317
|
+
alerts: []
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
function slugId(title, fallback, taken) {
|
|
3321
|
+
const base = (title ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || fallback;
|
|
3322
|
+
let id = base;
|
|
3323
|
+
for (let n = 2; taken.has(id); n++) id = `${base}_${n}`;
|
|
3324
|
+
taken.add(id);
|
|
3325
|
+
return id;
|
|
3326
|
+
}
|
|
3327
|
+
function executeScript(body, barsInput, inputs = {}) {
|
|
3328
|
+
ctxBars.setAll(barsInput);
|
|
3329
|
+
ctx = freshCollector(inputs);
|
|
3330
|
+
try {
|
|
3331
|
+
body();
|
|
3332
|
+
const c = ctx;
|
|
3333
|
+
return {
|
|
3334
|
+
metadata: {
|
|
3335
|
+
title: c.title,
|
|
3336
|
+
shortTitle: c.shortTitle,
|
|
3337
|
+
overlay: c.overlay,
|
|
3338
|
+
precision: c.precision,
|
|
3339
|
+
format: c.format
|
|
3340
|
+
},
|
|
3341
|
+
inputConfig: c.inputConfig,
|
|
3342
|
+
plotConfig: c.plotConfig,
|
|
3343
|
+
hlineConfig: c.hlineConfig,
|
|
3344
|
+
fillConfig: c.fillConfig,
|
|
3345
|
+
shapeConfig: c.shapeConfig,
|
|
3346
|
+
barColorConfig: c.barColorConfig,
|
|
3347
|
+
defaultInputs: c.defaultInputs,
|
|
3348
|
+
alertConfig: c.alertConfig,
|
|
3349
|
+
result: {
|
|
3350
|
+
metadata: { title: c.title, shorttitle: c.shortTitle, overlay: c.overlay, precision: c.precision },
|
|
3351
|
+
plots: c.plots,
|
|
3352
|
+
fills: c.fills.length ? c.fills : void 0,
|
|
3353
|
+
markers: c.markers.length ? c.markers : void 0,
|
|
3354
|
+
bgcolors: c.bgcolors.length ? c.bgcolors : void 0,
|
|
3355
|
+
barcolors: c.barcolors.length ? c.barcolors : void 0,
|
|
3356
|
+
alerts: c.alerts.length ? c.alerts : void 0
|
|
3357
|
+
}
|
|
3358
|
+
};
|
|
3359
|
+
} finally {
|
|
3360
|
+
ctx = null;
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
function indicator(title, options = {}) {
|
|
3364
|
+
const c = collector();
|
|
3365
|
+
c.title = title;
|
|
3366
|
+
c.shortTitle = options.shorttitle;
|
|
3367
|
+
c.overlay = options.overlay ?? true;
|
|
3368
|
+
c.precision = options.precision;
|
|
3369
|
+
c.format = options.format;
|
|
3370
|
+
}
|
|
3371
|
+
function registerInput(config, fallbackId) {
|
|
3372
|
+
const c = collector();
|
|
3373
|
+
const id = slugId(config.title, fallbackId, c.inputIds);
|
|
3374
|
+
c.inputConfig.push({ id, ...config });
|
|
3375
|
+
c.defaultInputs[id] = config.defval;
|
|
3376
|
+
const value = c.inputValues[id];
|
|
3377
|
+
return value === void 0 ? config.defval : value;
|
|
3378
|
+
}
|
|
3379
|
+
function registerNumeric(type, defval, title, opts = {}) {
|
|
3380
|
+
const v = registerInput(
|
|
3381
|
+
{ type, defval, title, min: opts.minval, max: opts.maxval, step: opts.step },
|
|
3382
|
+
`input_${type}`
|
|
3383
|
+
);
|
|
3384
|
+
return typeof v === "number" ? v : defval;
|
|
3385
|
+
}
|
|
3386
|
+
var input = {
|
|
3387
|
+
int(defval, title, opts = {}) {
|
|
3388
|
+
return Math.round(registerNumeric("int", defval, title, opts));
|
|
3389
|
+
},
|
|
3390
|
+
float(defval, title, opts = {}) {
|
|
3391
|
+
return registerNumeric("float", defval, title, opts);
|
|
3392
|
+
},
|
|
3393
|
+
bool(defval, title) {
|
|
3394
|
+
return registerInput({ type: "bool", defval, title }, "input_bool");
|
|
3395
|
+
},
|
|
3396
|
+
string(defval, title, opts = {}) {
|
|
3397
|
+
return registerInput({ type: "string", defval, title, options: opts.options }, "input_string");
|
|
3398
|
+
},
|
|
3399
|
+
color(defval, title) {
|
|
3400
|
+
return registerInput({ type: "color", defval, title }, "input_color");
|
|
3401
|
+
},
|
|
3402
|
+
/** Declares a source input and returns it as a Series. */
|
|
3403
|
+
source(defval = "close", title) {
|
|
3404
|
+
const name = registerInput({ type: "source", defval, title }, "input_source");
|
|
3405
|
+
return sourceSeries(name);
|
|
3406
|
+
}
|
|
3407
|
+
};
|
|
3408
|
+
var open = Series.fromBars(ctxBars, "open");
|
|
3409
|
+
var high = Series.fromBars(ctxBars, "high");
|
|
3410
|
+
var low = Series.fromBars(ctxBars, "low");
|
|
3411
|
+
var close = Series.fromBars(ctxBars, "close");
|
|
3412
|
+
var volume = Series.fromBars(ctxBars, "volume");
|
|
3413
|
+
var hl2 = new Series(ctxBars, (b2) => (b2.high + b2.low) / 2);
|
|
3414
|
+
var hlc3 = new Series(ctxBars, (b2) => (b2.high + b2.low + b2.close) / 3);
|
|
3415
|
+
var ohlc4 = new Series(ctxBars, (b2) => (b2.open + b2.high + b2.low + b2.close) / 4);
|
|
3416
|
+
var hlcc4 = new Series(ctxBars, (b2) => (b2.high + b2.low + b2.close + b2.close) / 4);
|
|
3417
|
+
var SOURCES = { open, high, low, close, volume, hl2, hlc3, ohlc4, hlcc4 };
|
|
3418
|
+
function sourceSeries(name) {
|
|
3419
|
+
return SOURCES[name] ?? close;
|
|
3420
|
+
}
|
|
3421
|
+
var bars = () => ctxBars.bars;
|
|
3422
|
+
var ta2 = {
|
|
3423
|
+
...ta_series_exports,
|
|
3424
|
+
tr: (handleNa = false) => tr2(bars(), handleNa),
|
|
3425
|
+
atr: (length) => atr2(bars(), length),
|
|
3426
|
+
sar: (start, inc, max4) => sar2(bars(), start, inc, max4),
|
|
3427
|
+
wpr: (length) => wpr2(bars(), length),
|
|
3428
|
+
supertrend: (factor, atrLength) => supertrend2(bars(), factor, atrLength),
|
|
3429
|
+
dmi: (diLength, adxSmoothing) => dmi2(bars(), diLength, adxSmoothing),
|
|
3430
|
+
kc: (source, length, mult, useTrueRange) => kc2(bars(), source, length, mult, useTrueRange),
|
|
3431
|
+
kcw: (source, length, mult, useTrueRange) => kcw2(bars(), source, length, mult, useTrueRange),
|
|
3432
|
+
ichimoku: (conversionPeriods, basePeriods, laggingSpan2Periods, displacement) => ichimoku2(bars(), conversionPeriods, basePeriods, laggingSpan2Periods, displacement)
|
|
3433
|
+
};
|
|
3434
|
+
function colorWhen(cond, colorTrue, colorFalse) {
|
|
3435
|
+
return cond.toArray().map((v) => !isNA(v) && v !== 0 ? colorTrue : colorFalse);
|
|
3436
|
+
}
|
|
3437
|
+
var color = {
|
|
3438
|
+
...color_exports,
|
|
3439
|
+
new: new_color,
|
|
3440
|
+
when: colorWhen
|
|
3441
|
+
};
|
|
3442
|
+
function plot(series, title, options = {}) {
|
|
3443
|
+
const c = collector();
|
|
3444
|
+
const id = `plot${c.plotConfig.length}`;
|
|
3445
|
+
const perBar = Array.isArray(options.color) ? options.color : void 0;
|
|
3446
|
+
const staticColor = typeof options.color === "string" ? options.color : perBar?.[perBar.length - 1] ?? "#2962FF";
|
|
3447
|
+
c.plotConfig.push({
|
|
3448
|
+
id,
|
|
3449
|
+
title: title ?? id,
|
|
3450
|
+
color: staticColor,
|
|
3451
|
+
lineWidth: options.linewidth,
|
|
3452
|
+
style: options.style,
|
|
3453
|
+
display: options.display,
|
|
3454
|
+
histbase: options.histbase
|
|
3455
|
+
});
|
|
3456
|
+
const values = series.toArray();
|
|
3457
|
+
const b2 = bars();
|
|
3458
|
+
const data = [];
|
|
3459
|
+
for (let i = 0; i < b2.length; i++) {
|
|
3460
|
+
const value = values[i];
|
|
3461
|
+
if (value === void 0 || Number.isNaN(value)) continue;
|
|
3462
|
+
data.push(perBar ? { time: b2[i].time, value, color: perBar[i] } : { time: b2[i].time, value });
|
|
3463
|
+
}
|
|
3464
|
+
c.plots[id] = data;
|
|
3465
|
+
return { id, kind: "plot" };
|
|
3466
|
+
}
|
|
3467
|
+
function hline(price, title, options = {}) {
|
|
3468
|
+
const c = collector();
|
|
3469
|
+
const id = `hline${c.hlineConfig.length}`;
|
|
3470
|
+
c.hlineConfig.push({
|
|
3471
|
+
id,
|
|
3472
|
+
price,
|
|
3473
|
+
title,
|
|
3474
|
+
color: options.color,
|
|
3475
|
+
linestyle: options.linestyle,
|
|
3476
|
+
linewidth: options.linewidth
|
|
3477
|
+
});
|
|
3478
|
+
return { id, kind: "hline" };
|
|
3479
|
+
}
|
|
3480
|
+
function fill(a, b2, options = {}) {
|
|
3481
|
+
const c = collector();
|
|
3482
|
+
const perBar = Array.isArray(options.color) ? options.color : void 0;
|
|
3483
|
+
const staticColor = typeof options.color === "string" ? options.color : void 0;
|
|
3484
|
+
if (a.kind === "hline" && b2.kind === "hline") {
|
|
3485
|
+
c.fillConfig.push({
|
|
3486
|
+
id: `fill${c.fillConfig.length}`,
|
|
3487
|
+
plot1: a.id,
|
|
3488
|
+
plot2: b2.id,
|
|
3489
|
+
color: staticColor,
|
|
3490
|
+
colors: perBar,
|
|
3491
|
+
title: options.title
|
|
3492
|
+
});
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3495
|
+
c.fills.push({
|
|
3496
|
+
plot1: a.id,
|
|
3497
|
+
plot2: b2.id,
|
|
3498
|
+
options: { color: staticColor, title: options.title },
|
|
3499
|
+
colors: perBar
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
function emitMarkers(kind, condition, title, options) {
|
|
3503
|
+
const c = collector();
|
|
3504
|
+
const id = `${kind}${c.shapeConfig.filter((s) => s.kind === kind).length}`;
|
|
3505
|
+
const style = kind === "shape" ? options.style ?? "xcross" : "char";
|
|
3506
|
+
const char = kind === "char" ? options.char ?? "*" : void 0;
|
|
3507
|
+
const location = options.location ?? "abovebar";
|
|
3508
|
+
c.shapeConfig.push({
|
|
3509
|
+
id,
|
|
3510
|
+
kind,
|
|
3511
|
+
title,
|
|
3512
|
+
style: kind === "shape" ? style : void 0,
|
|
3513
|
+
char,
|
|
3514
|
+
location,
|
|
3515
|
+
color: options.color,
|
|
3516
|
+
text: options.text,
|
|
3517
|
+
textcolor: options.textcolor,
|
|
3518
|
+
size: options.size,
|
|
3519
|
+
offset: options.offset
|
|
3520
|
+
});
|
|
3521
|
+
const values = condition.toArray();
|
|
3522
|
+
const b2 = bars();
|
|
3523
|
+
const off = options.offset ?? 0;
|
|
3524
|
+
for (let i = 0; i < b2.length; i++) {
|
|
3525
|
+
const v = values[i];
|
|
3526
|
+
if (v === void 0 || Number.isNaN(v) || v === 0) continue;
|
|
3527
|
+
const j = i + off;
|
|
3528
|
+
if (j < 0 || j >= b2.length) continue;
|
|
3529
|
+
c.markers.push({
|
|
3530
|
+
time: b2[j].time,
|
|
3531
|
+
id,
|
|
3532
|
+
location,
|
|
3533
|
+
style,
|
|
3534
|
+
char,
|
|
3535
|
+
color: options.color,
|
|
3536
|
+
text: options.text,
|
|
3537
|
+
textcolor: options.textcolor,
|
|
3538
|
+
size: options.size,
|
|
3539
|
+
tooltip: options.tooltip,
|
|
3540
|
+
price: location === "absolute" ? v : void 0
|
|
3541
|
+
});
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
function plotshape(condition, title, options = {}) {
|
|
3545
|
+
emitMarkers("shape", condition, title, options);
|
|
3546
|
+
}
|
|
3547
|
+
function plotchar(condition, title, options = {}) {
|
|
3548
|
+
emitMarkers("char", condition, title, options);
|
|
3549
|
+
}
|
|
3550
|
+
function emitBarColors(kind, colors, options) {
|
|
3551
|
+
const c = collector();
|
|
3552
|
+
const id = `${kind}${c.barColorConfig.filter((x) => x.kind === kind).length}`;
|
|
3553
|
+
c.barColorConfig.push({ id, kind, title: options.title, offset: options.offset });
|
|
3554
|
+
const b2 = bars();
|
|
3555
|
+
const off = options.offset ?? 0;
|
|
3556
|
+
const target = kind === "bgcolor" ? c.bgcolors : c.barcolors;
|
|
3557
|
+
for (let i = 0; i < b2.length; i++) {
|
|
3558
|
+
const col = typeof colors === "string" ? colors : colors[i];
|
|
3559
|
+
if (!col) continue;
|
|
3560
|
+
const j = i + off;
|
|
3561
|
+
if (j < 0 || j >= b2.length) continue;
|
|
3562
|
+
target.push({ time: b2[j].time, color: col });
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
function bgcolor(colors, options = {}) {
|
|
3566
|
+
emitBarColors("bgcolor", colors, options);
|
|
3567
|
+
}
|
|
3568
|
+
function barcolor(colors, options = {}) {
|
|
3569
|
+
emitBarColors("barcolor", colors, options);
|
|
3570
|
+
}
|
|
3571
|
+
function eachBar(fn) {
|
|
3572
|
+
collector();
|
|
3573
|
+
const b2 = bars();
|
|
3574
|
+
const out = new Array(b2.length).fill(NaN);
|
|
3575
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3576
|
+
let idx = 0;
|
|
3577
|
+
const ctx2 = {
|
|
3578
|
+
get i() {
|
|
3579
|
+
return idx;
|
|
3580
|
+
},
|
|
3581
|
+
n: b2.length,
|
|
3582
|
+
get time() {
|
|
3583
|
+
return b2[idx].time;
|
|
3584
|
+
},
|
|
3585
|
+
get open() {
|
|
3586
|
+
return b2[idx].open;
|
|
3587
|
+
},
|
|
3588
|
+
get high() {
|
|
3589
|
+
return b2[idx].high;
|
|
3590
|
+
},
|
|
3591
|
+
get low() {
|
|
3592
|
+
return b2[idx].low;
|
|
3593
|
+
},
|
|
3594
|
+
get close() {
|
|
3595
|
+
return b2[idx].close;
|
|
3596
|
+
},
|
|
3597
|
+
get volume() {
|
|
3598
|
+
return b2[idx].volume ?? NaN;
|
|
3599
|
+
},
|
|
3600
|
+
get(src, offset = 0) {
|
|
3601
|
+
let vals = cache.get(src);
|
|
3602
|
+
if (!vals) {
|
|
3603
|
+
vals = src.toArray();
|
|
3604
|
+
cache.set(src, vals);
|
|
3605
|
+
}
|
|
3606
|
+
const j = idx - offset;
|
|
3607
|
+
return j >= 0 && j < vals.length ? vals[j] ?? NaN : NaN;
|
|
3608
|
+
},
|
|
3609
|
+
prev(offset = 1) {
|
|
3610
|
+
const j = idx - Math.max(1, offset);
|
|
3611
|
+
return j >= 0 ? out[j] : NaN;
|
|
3612
|
+
}
|
|
3613
|
+
};
|
|
3614
|
+
for (idx = 0; idx < b2.length; idx++) {
|
|
3615
|
+
const r2 = fn(ctx2);
|
|
3616
|
+
out[idx] = typeof r2 === "number" ? r2 : typeof r2 === "boolean" ? r2 ? 1 : 0 : NaN;
|
|
3617
|
+
}
|
|
3618
|
+
return Series.fromArray(ctxBars, out);
|
|
3619
|
+
}
|
|
3620
|
+
function seriesOf(values) {
|
|
3621
|
+
collector();
|
|
3622
|
+
return Series.fromArray(ctxBars, values);
|
|
3623
|
+
}
|
|
3624
|
+
function alertcondition(condition, title, message) {
|
|
3625
|
+
const c = collector();
|
|
3626
|
+
const id = `alert${c.alertConfig.length}`;
|
|
3627
|
+
c.alertConfig.push({ id, title, message });
|
|
3628
|
+
const last = condition.length() ? condition.last() : NaN;
|
|
3629
|
+
c.alerts.push({ id, title, message, triggered: !isNA(last) && last !== 0 });
|
|
3630
|
+
}
|