oakscriptjs 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -253
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +387 -47
- package/dist/index.mjs +387 -47
- package/dist/input.d.ts +1 -1
- package/dist/input.d.ts.map +1 -1
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/zigzag/index.d.ts +2 -0
- package/dist/lib/zigzag/index.d.ts.map +1 -0
- package/dist/lib/zigzag/zigzag.d.ts +47 -0
- package/dist/lib/zigzag/zigzag.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/series.d.ts +1 -0
- package/dist/runtime/series.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +26 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/str/index.d.ts +1 -0
- package/dist/str/index.d.ts.map +1 -1
- package/dist/ta/index.d.ts +1 -1
- package/dist/ta/index.d.ts.map +1 -1
- package/dist/ta-series.d.ts +8 -2
- package/dist/ta-series.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +24 -24
package/dist/index.js
CHANGED
|
@@ -25,12 +25,15 @@ __export(src_exports, {
|
|
|
25
25
|
Series: () => Series,
|
|
26
26
|
SimpleInputAdapter: () => SimpleInputAdapter,
|
|
27
27
|
VERSION: () => VERSION,
|
|
28
|
+
ZigZag: () => ZigZag,
|
|
28
29
|
abs: () => abs,
|
|
30
|
+
alertcondition: () => alertcondition,
|
|
29
31
|
array: () => array_exports,
|
|
30
32
|
atr: () => atr2,
|
|
31
33
|
avg: () => avg,
|
|
32
34
|
bb: () => bb2,
|
|
33
35
|
box: () => box_exports,
|
|
36
|
+
calculateZigZag: () => calculateZigZag,
|
|
34
37
|
ceil: () => ceil,
|
|
35
38
|
change: () => change2,
|
|
36
39
|
chartPoint: () => chartpoint_exports,
|
|
@@ -51,6 +54,7 @@ __export(src_exports, {
|
|
|
51
54
|
getHigh: () => getHigh,
|
|
52
55
|
getLow: () => getLow,
|
|
53
56
|
getOpen: () => getOpen,
|
|
57
|
+
getSourceSeries: () => getSourceSeries,
|
|
54
58
|
hline: () => hline,
|
|
55
59
|
indicator: () => indicator,
|
|
56
60
|
info: () => info,
|
|
@@ -69,8 +73,9 @@ __export(src_exports, {
|
|
|
69
73
|
matrix: () => matrix_exports,
|
|
70
74
|
max: () => max2,
|
|
71
75
|
min: () => min2,
|
|
72
|
-
na: () =>
|
|
76
|
+
na: () => isNA,
|
|
73
77
|
new_color: () => new_color2,
|
|
78
|
+
nz: () => nz,
|
|
74
79
|
ohlcFromBars: () => ohlcFromBars,
|
|
75
80
|
plot: () => plot2,
|
|
76
81
|
plotHelper: () => plot,
|
|
@@ -94,12 +99,12 @@ __export(src_exports, {
|
|
|
94
99
|
time: () => time_exports,
|
|
95
100
|
tr: () => tr2,
|
|
96
101
|
vwap: () => vwap2,
|
|
97
|
-
xloc: () => xloc
|
|
102
|
+
xloc: () => xloc,
|
|
103
|
+
zigZagDefaults: () => defaultSettings
|
|
98
104
|
});
|
|
99
105
|
module.exports = __toCommonJS(src_exports);
|
|
100
106
|
|
|
101
107
|
// src/types/index.ts
|
|
102
|
-
var na = null;
|
|
103
108
|
var xloc = {
|
|
104
109
|
/** X-coordinates are bar indices */
|
|
105
110
|
bar_index: "bar_index",
|
|
@@ -174,34 +179,51 @@ __export(ta_exports, {
|
|
|
174
179
|
});
|
|
175
180
|
function sma(source, length2) {
|
|
176
181
|
const result = [];
|
|
182
|
+
const len = Math.floor(length2);
|
|
177
183
|
for (let i = 0; i < source.length; i++) {
|
|
178
|
-
if (i <
|
|
184
|
+
if (i < len - 1) {
|
|
179
185
|
result.push(NaN);
|
|
180
186
|
} else {
|
|
181
187
|
let sum4 = 0;
|
|
182
|
-
for (let j = 0; j <
|
|
188
|
+
for (let j = 0; j < len; j++) {
|
|
183
189
|
sum4 += source[i - j];
|
|
184
190
|
}
|
|
185
|
-
result.push(sum4 /
|
|
191
|
+
result.push(sum4 / len);
|
|
186
192
|
}
|
|
187
193
|
}
|
|
188
194
|
return result;
|
|
189
195
|
}
|
|
190
196
|
function ema(source, length2) {
|
|
191
197
|
const result = [];
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
const len = Math.floor(length2);
|
|
199
|
+
const multiplier = 2 / (len + 1);
|
|
200
|
+
let firstValidIndex = -1;
|
|
201
|
+
let validCount = 0;
|
|
202
|
+
let initSum = 0;
|
|
203
|
+
for (let i = 0; i < source.length; i++) {
|
|
204
|
+
const val = source[i];
|
|
205
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
206
|
+
initSum += val;
|
|
207
|
+
validCount++;
|
|
208
|
+
if (validCount === len) {
|
|
209
|
+
firstValidIndex = i;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
196
213
|
}
|
|
197
|
-
|
|
214
|
+
let emaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
215
|
+
let emaInitialized = firstValidIndex >= 0;
|
|
198
216
|
for (let i = 0; i < source.length; i++) {
|
|
199
|
-
if (i
|
|
200
|
-
result.push(
|
|
201
|
-
|
|
217
|
+
if (!emaInitialized || i < firstValidIndex) {
|
|
218
|
+
result.push(NaN);
|
|
219
|
+
} else if (i === firstValidIndex) {
|
|
220
|
+
result.push(emaValue);
|
|
202
221
|
} else {
|
|
203
|
-
|
|
204
|
-
|
|
222
|
+
const val = source[i];
|
|
223
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
224
|
+
emaValue = (val - emaValue) * multiplier + emaValue;
|
|
225
|
+
}
|
|
226
|
+
result.push(emaValue);
|
|
205
227
|
}
|
|
206
228
|
}
|
|
207
229
|
return result;
|
|
@@ -254,17 +276,18 @@ function bb(series, length2, mult2) {
|
|
|
254
276
|
}
|
|
255
277
|
function stdev(source, length2) {
|
|
256
278
|
const result = [];
|
|
257
|
-
const
|
|
279
|
+
const len = Math.floor(length2);
|
|
280
|
+
const avg4 = sma(source, len);
|
|
258
281
|
for (let i = 0; i < source.length; i++) {
|
|
259
|
-
if (i <
|
|
282
|
+
if (i < len - 1) {
|
|
260
283
|
result.push(NaN);
|
|
261
284
|
} else {
|
|
262
285
|
let sumSquares = 0;
|
|
263
|
-
for (let j = 0; j <
|
|
286
|
+
for (let j = 0; j < len; j++) {
|
|
264
287
|
const diff2 = source[i - j] - avg4[i];
|
|
265
288
|
sumSquares += diff2 * diff2;
|
|
266
289
|
}
|
|
267
|
-
result.push(Math.sqrt(sumSquares /
|
|
290
|
+
result.push(Math.sqrt(sumSquares / len));
|
|
268
291
|
}
|
|
269
292
|
}
|
|
270
293
|
return result;
|
|
@@ -393,17 +416,34 @@ function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
|
|
|
393
416
|
}
|
|
394
417
|
function rma(source, length2) {
|
|
395
418
|
const result = [];
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
419
|
+
const len = Math.floor(length2);
|
|
420
|
+
const alpha = 1 / len;
|
|
421
|
+
let firstValidIndex = -1;
|
|
422
|
+
let validCount = 0;
|
|
423
|
+
let initSum = 0;
|
|
424
|
+
for (let i = 0; i < source.length; i++) {
|
|
425
|
+
const val = source[i];
|
|
426
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
427
|
+
initSum += val;
|
|
428
|
+
validCount++;
|
|
429
|
+
if (validCount === len) {
|
|
430
|
+
firstValidIndex = i;
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
400
434
|
}
|
|
401
|
-
rmaValue =
|
|
435
|
+
let rmaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
436
|
+
let rmaInitialized = firstValidIndex >= 0;
|
|
402
437
|
for (let i = 0; i < source.length; i++) {
|
|
403
|
-
if (i
|
|
438
|
+
if (!rmaInitialized || i < firstValidIndex) {
|
|
439
|
+
result.push(NaN);
|
|
440
|
+
} else if (i === firstValidIndex) {
|
|
404
441
|
result.push(rmaValue);
|
|
405
442
|
} else {
|
|
406
|
-
|
|
443
|
+
const val = source[i];
|
|
444
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
445
|
+
rmaValue = alpha * val + (1 - alpha) * rmaValue;
|
|
446
|
+
}
|
|
407
447
|
result.push(rmaValue);
|
|
408
448
|
}
|
|
409
449
|
}
|
|
@@ -411,14 +451,15 @@ function rma(source, length2) {
|
|
|
411
451
|
}
|
|
412
452
|
function wma(source, length2) {
|
|
413
453
|
const result = [];
|
|
454
|
+
const len = Math.floor(length2);
|
|
414
455
|
for (let i = 0; i < source.length; i++) {
|
|
415
|
-
if (i <
|
|
456
|
+
if (i < len - 1) {
|
|
416
457
|
result.push(NaN);
|
|
417
458
|
} else {
|
|
418
459
|
let sum4 = 0;
|
|
419
460
|
let weightSum = 0;
|
|
420
|
-
for (let j = 0; j <
|
|
421
|
-
const weight =
|
|
461
|
+
for (let j = 0; j < len; j++) {
|
|
462
|
+
const weight = len - j;
|
|
422
463
|
sum4 += source[i - j] * weight;
|
|
423
464
|
weightSum += weight;
|
|
424
465
|
}
|
|
@@ -658,12 +699,13 @@ function vwma(source, length2, volume) {
|
|
|
658
699
|
}
|
|
659
700
|
function linreg(source, length2, offset = 0) {
|
|
660
701
|
const result = [];
|
|
702
|
+
const len = Math.floor(length2);
|
|
661
703
|
for (let i = 0; i < source.length; i++) {
|
|
662
|
-
if (i <
|
|
704
|
+
if (i < len - 1) {
|
|
663
705
|
result.push(NaN);
|
|
664
706
|
} else {
|
|
665
707
|
let hasNaN = false;
|
|
666
|
-
for (let j = 0; j <
|
|
708
|
+
for (let j = 0; j < len; j++) {
|
|
667
709
|
if (isNaN(source[i - j])) {
|
|
668
710
|
hasNaN = true;
|
|
669
711
|
break;
|
|
@@ -676,17 +718,17 @@ function linreg(source, length2, offset = 0) {
|
|
|
676
718
|
let sumY = 0;
|
|
677
719
|
let sumXY = 0;
|
|
678
720
|
let sumX2 = 0;
|
|
679
|
-
for (let j = 0; j <
|
|
721
|
+
for (let j = 0; j < len; j++) {
|
|
680
722
|
const x2 = j;
|
|
681
|
-
const y = source[i - (
|
|
723
|
+
const y = source[i - (len - 1 - j)];
|
|
682
724
|
sumX += x2;
|
|
683
725
|
sumY += y;
|
|
684
726
|
sumXY += x2 * y;
|
|
685
727
|
sumX2 += x2 * x2;
|
|
686
728
|
}
|
|
687
|
-
const slope = (
|
|
688
|
-
const intercept = (sumY - slope * sumX) /
|
|
689
|
-
const x =
|
|
729
|
+
const slope = (len * sumXY - sumX * sumY) / (len * sumX2 - sumX * sumX);
|
|
730
|
+
const intercept = (sumY - slope * sumX) / len;
|
|
731
|
+
const x = len - 1 - offset;
|
|
690
732
|
result.push(intercept + slope * x);
|
|
691
733
|
}
|
|
692
734
|
}
|
|
@@ -775,8 +817,8 @@ function cci(source, length2) {
|
|
|
775
817
|
if (isNaN(smaValues[i]) || isNaN(devValues[i]) || devValues[i] === 0) {
|
|
776
818
|
result.push(NaN);
|
|
777
819
|
} else {
|
|
778
|
-
const
|
|
779
|
-
result.push(
|
|
820
|
+
const cci3 = (source[i] - smaValues[i]) / (0.015 * devValues[i]);
|
|
821
|
+
result.push(cci3);
|
|
780
822
|
}
|
|
781
823
|
}
|
|
782
824
|
return result;
|
|
@@ -1237,9 +1279,9 @@ function vwap(source, volume) {
|
|
|
1237
1279
|
}
|
|
1238
1280
|
return result;
|
|
1239
1281
|
}
|
|
1240
|
-
function alma(source, length2 = 9, offset = 0.85, sigma = 6) {
|
|
1282
|
+
function alma(source, length2 = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
1241
1283
|
const result = [];
|
|
1242
|
-
const m = Math.floor(offset * (length2 - 1));
|
|
1284
|
+
const m = floor2 ? Math.floor(offset * (length2 - 1)) : offset * (length2 - 1);
|
|
1243
1285
|
const s = length2 / sigma;
|
|
1244
1286
|
const weights = [];
|
|
1245
1287
|
let weightSum = 0;
|
|
@@ -2241,6 +2283,38 @@ var Series = class _Series {
|
|
|
2241
2283
|
});
|
|
2242
2284
|
}
|
|
2243
2285
|
// ============================================
|
|
2286
|
+
// Conditional Selection
|
|
2287
|
+
// ============================================
|
|
2288
|
+
/**
|
|
2289
|
+
* Bar-by-bar conditional selection (ternary operator for Series)
|
|
2290
|
+
* Uses this Series as the condition (truthy = nonzero, falsy = zero/NaN)
|
|
2291
|
+
*
|
|
2292
|
+
* @param trueValue - Value or Series to use when condition is truthy
|
|
2293
|
+
* @param falseValue - Value or Series to use when condition is falsy
|
|
2294
|
+
* @returns New Series with bar-by-bar selected values
|
|
2295
|
+
*
|
|
2296
|
+
* @example
|
|
2297
|
+
* ```typescript
|
|
2298
|
+
* // PineScript: result = condition ? trueVal : falseVal
|
|
2299
|
+
* const result = condition.iff(trueVal, falseVal);
|
|
2300
|
+
*
|
|
2301
|
+
* // Example: RSI formula uses ternary for edge cases
|
|
2302
|
+
* // rsi = down == 0 ? 100 : up == 0 ? 0 : formula
|
|
2303
|
+
* const rsi = down.eq(0).iff(100, up.eq(0).iff(0, formula));
|
|
2304
|
+
* ```
|
|
2305
|
+
*/
|
|
2306
|
+
iff(trueValue, falseValue) {
|
|
2307
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
2308
|
+
const condition = this.extractor(bar, i, data);
|
|
2309
|
+
const isTruthy = condition !== 0 && !Number.isNaN(condition);
|
|
2310
|
+
if (isTruthy) {
|
|
2311
|
+
return typeof trueValue === "number" ? trueValue : trueValue.extractor(bar, i, data);
|
|
2312
|
+
} else {
|
|
2313
|
+
return typeof falseValue === "number" ? falseValue : falseValue.extractor(bar, i, data);
|
|
2314
|
+
}
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
// ============================================
|
|
2244
2318
|
// Offset/History
|
|
2245
2319
|
// ============================================
|
|
2246
2320
|
/**
|
|
@@ -3138,6 +3212,7 @@ __export(str_exports, {
|
|
|
3138
3212
|
lower: () => lower,
|
|
3139
3213
|
match: () => match,
|
|
3140
3214
|
pos: () => pos,
|
|
3215
|
+
repeat: () => repeat,
|
|
3141
3216
|
replace: () => replace,
|
|
3142
3217
|
replace_all: () => replace_all,
|
|
3143
3218
|
split: () => split,
|
|
@@ -3223,6 +3298,13 @@ function trimRight(str) {
|
|
|
3223
3298
|
function match(source, regex) {
|
|
3224
3299
|
return new RegExp(regex).test(source);
|
|
3225
3300
|
}
|
|
3301
|
+
function repeat(source, count, separator = "") {
|
|
3302
|
+
if (source == null)
|
|
3303
|
+
return null;
|
|
3304
|
+
if (count <= 0)
|
|
3305
|
+
return "";
|
|
3306
|
+
return Array(count).fill(source).join(separator);
|
|
3307
|
+
}
|
|
3226
3308
|
function format_time(time, format2) {
|
|
3227
3309
|
const date = new Date(time);
|
|
3228
3310
|
const monthNames = [
|
|
@@ -5166,6 +5248,7 @@ __export(ta_series_exports, {
|
|
|
5166
5248
|
alma: () => alma2,
|
|
5167
5249
|
atr: () => atr2,
|
|
5168
5250
|
bb: () => bb2,
|
|
5251
|
+
cci: () => cci2,
|
|
5169
5252
|
change: () => change2,
|
|
5170
5253
|
cross: () => cross2,
|
|
5171
5254
|
crossover: () => crossover2,
|
|
@@ -5183,8 +5266,10 @@ __export(ta_series_exports, {
|
|
|
5183
5266
|
rma: () => rma2,
|
|
5184
5267
|
roc: () => roc2,
|
|
5185
5268
|
rsi: () => rsi2,
|
|
5269
|
+
sar: () => sar2,
|
|
5186
5270
|
sma: () => sma2,
|
|
5187
5271
|
stdev: () => stdev3,
|
|
5272
|
+
stoch: () => stoch2,
|
|
5188
5273
|
supertrend: () => supertrend2,
|
|
5189
5274
|
ta: () => ta,
|
|
5190
5275
|
tr: () => tr2,
|
|
@@ -5254,11 +5339,19 @@ function atr2(bars, length2) {
|
|
|
5254
5339
|
const result = atr(length2, high, low, close);
|
|
5255
5340
|
return Series.fromArray(bars, result);
|
|
5256
5341
|
}
|
|
5257
|
-
function tr2(bars) {
|
|
5342
|
+
function tr2(bars, handle_na = false) {
|
|
5258
5343
|
const high = bars.map((b2) => b2.high);
|
|
5259
5344
|
const low = bars.map((b2) => b2.low);
|
|
5260
5345
|
const close = bars.map((b2) => b2.close);
|
|
5261
|
-
const result = tr(
|
|
5346
|
+
const result = tr(handle_na, high, low, close);
|
|
5347
|
+
return Series.fromArray(bars, result);
|
|
5348
|
+
}
|
|
5349
|
+
function stoch2(source, high, low, length2) {
|
|
5350
|
+
const bars = source.bars;
|
|
5351
|
+
const sourceValues = source.toArray();
|
|
5352
|
+
const highValues = high.toArray();
|
|
5353
|
+
const lowValues = low.toArray();
|
|
5354
|
+
const result = stoch(sourceValues, highValues, lowValues, length2);
|
|
5262
5355
|
return Series.fromArray(bars, result);
|
|
5263
5356
|
}
|
|
5264
5357
|
function crossover2(source1, source2) {
|
|
@@ -5385,10 +5478,10 @@ function linreg2(source, length2, offset = 0) {
|
|
|
5385
5478
|
const result = linreg(sourceValues, length2, offset);
|
|
5386
5479
|
return Series.fromArray(bars, result);
|
|
5387
5480
|
}
|
|
5388
|
-
function alma2(source, length2 = 9, offset = 0.85, sigma = 6) {
|
|
5481
|
+
function alma2(source, length2 = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
5389
5482
|
const bars = source.bars;
|
|
5390
5483
|
const sourceValues = source.toArray();
|
|
5391
|
-
const result = alma(sourceValues, length2, offset, sigma);
|
|
5484
|
+
const result = alma(sourceValues, length2, offset, sigma, floor2);
|
|
5392
5485
|
return Series.fromArray(bars, result);
|
|
5393
5486
|
}
|
|
5394
5487
|
function zigzag2(bars, deviation = 5, depth = 10, backstep = 3) {
|
|
@@ -5408,6 +5501,19 @@ function zigzag2(bars, deviation = 5, depth = 10, backstep = 3) {
|
|
|
5408
5501
|
Series.fromArray(bars, pivotVals.map((b2) => b2 ? 1 : 0))
|
|
5409
5502
|
];
|
|
5410
5503
|
}
|
|
5504
|
+
function cci2(source, length2) {
|
|
5505
|
+
const bars = source.bars;
|
|
5506
|
+
const sourceValues = source.toArray();
|
|
5507
|
+
const result = cci(sourceValues, length2);
|
|
5508
|
+
return Series.fromArray(bars, result);
|
|
5509
|
+
}
|
|
5510
|
+
function sar2(bars, start = 0.02, inc = 0.02, max5 = 0.2) {
|
|
5511
|
+
const high = bars.map((b2) => b2.high);
|
|
5512
|
+
const low = bars.map((b2) => b2.low);
|
|
5513
|
+
const close = bars.map((b2) => b2.close);
|
|
5514
|
+
const result = sar(start, inc, max5, high, low, close);
|
|
5515
|
+
return Series.fromArray(bars, result);
|
|
5516
|
+
}
|
|
5411
5517
|
var ta = {
|
|
5412
5518
|
sma: sma2,
|
|
5413
5519
|
ema: ema2,
|
|
@@ -5420,6 +5526,7 @@ var ta = {
|
|
|
5420
5526
|
stdev: stdev3,
|
|
5421
5527
|
atr: atr2,
|
|
5422
5528
|
tr: tr2,
|
|
5529
|
+
stoch: stoch2,
|
|
5423
5530
|
crossover: crossover2,
|
|
5424
5531
|
crossunder: crossunder2,
|
|
5425
5532
|
cross: cross2,
|
|
@@ -5436,10 +5543,18 @@ var ta = {
|
|
|
5436
5543
|
ichimoku: ichimoku2,
|
|
5437
5544
|
linreg: linreg2,
|
|
5438
5545
|
alma: alma2,
|
|
5439
|
-
zigzag: zigzag2
|
|
5546
|
+
zigzag: zigzag2,
|
|
5547
|
+
cci: cci2,
|
|
5548
|
+
sar: sar2
|
|
5440
5549
|
};
|
|
5441
5550
|
|
|
5442
5551
|
// src/utils/index.ts
|
|
5552
|
+
function isNA(value) {
|
|
5553
|
+
return value === null || value === void 0 || Number.isNaN(value);
|
|
5554
|
+
}
|
|
5555
|
+
function nz(value, defaultValue = 0) {
|
|
5556
|
+
return isNA(value) ? defaultValue : value;
|
|
5557
|
+
}
|
|
5443
5558
|
function ohlcFromBars(bars) {
|
|
5444
5559
|
return {
|
|
5445
5560
|
open: bars.map((b2) => b2.open),
|
|
@@ -5460,6 +5575,229 @@ function getLow(ohlc) {
|
|
|
5460
5575
|
function getOpen(ohlc) {
|
|
5461
5576
|
return ohlc.open;
|
|
5462
5577
|
}
|
|
5578
|
+
function getSourceSeries(bars, source = "close") {
|
|
5579
|
+
const open = new Series(bars, (bar) => bar.open);
|
|
5580
|
+
const high = new Series(bars, (bar) => bar.high);
|
|
5581
|
+
const low = new Series(bars, (bar) => bar.low);
|
|
5582
|
+
const close = new Series(bars, (bar) => bar.close);
|
|
5583
|
+
switch (source) {
|
|
5584
|
+
case "open":
|
|
5585
|
+
return open;
|
|
5586
|
+
case "high":
|
|
5587
|
+
return high;
|
|
5588
|
+
case "low":
|
|
5589
|
+
return low;
|
|
5590
|
+
case "close":
|
|
5591
|
+
return close;
|
|
5592
|
+
case "hl2":
|
|
5593
|
+
return high.add(low).div(2);
|
|
5594
|
+
case "hlc3":
|
|
5595
|
+
return high.add(low).add(close).div(3);
|
|
5596
|
+
case "ohlc4":
|
|
5597
|
+
return open.add(high).add(low).add(close).div(4);
|
|
5598
|
+
case "hlcc4":
|
|
5599
|
+
return high.add(low).add(close).add(close).div(4);
|
|
5600
|
+
default:
|
|
5601
|
+
return close;
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5605
|
+
// src/lib/zigzag/zigzag.ts
|
|
5606
|
+
var defaultSettings = {
|
|
5607
|
+
devThreshold: 5,
|
|
5608
|
+
depth: 10,
|
|
5609
|
+
lineColor: "#2962FF",
|
|
5610
|
+
extendLast: true,
|
|
5611
|
+
displayReversalPrice: true,
|
|
5612
|
+
displayCumulativeVolume: true,
|
|
5613
|
+
displayReversalPriceChange: true,
|
|
5614
|
+
differencePriceMode: "Absolute",
|
|
5615
|
+
allowZigZagOnOneBar: true
|
|
5616
|
+
};
|
|
5617
|
+
var ZigZag = class {
|
|
5618
|
+
settings;
|
|
5619
|
+
pivots = [];
|
|
5620
|
+
sumVol = 0;
|
|
5621
|
+
highBuffer = [];
|
|
5622
|
+
lowBuffer = [];
|
|
5623
|
+
timeBuffer = [];
|
|
5624
|
+
barCount = 0;
|
|
5625
|
+
constructor(settings) {
|
|
5626
|
+
this.settings = { ...defaultSettings, ...settings };
|
|
5627
|
+
}
|
|
5628
|
+
/**
|
|
5629
|
+
* Process a single bar, detecting pivots.
|
|
5630
|
+
* Must be called sequentially for each bar.
|
|
5631
|
+
* @returns true if a new pivot was detected or updated
|
|
5632
|
+
*/
|
|
5633
|
+
update(bar, barIndex) {
|
|
5634
|
+
this.highBuffer.push(bar.high);
|
|
5635
|
+
this.lowBuffer.push(bar.low);
|
|
5636
|
+
this.timeBuffer.push(bar.time);
|
|
5637
|
+
this.barCount++;
|
|
5638
|
+
const depth = Math.max(2, Math.floor(this.settings.depth / 2));
|
|
5639
|
+
this.sumVol += bar.volume ?? 0;
|
|
5640
|
+
if (this.barCount < depth * 2 + 1)
|
|
5641
|
+
return false;
|
|
5642
|
+
let somethingChanged = this.tryFindPivot(true, depth, barIndex);
|
|
5643
|
+
somethingChanged = this.tryFindPivot(
|
|
5644
|
+
false,
|
|
5645
|
+
depth,
|
|
5646
|
+
barIndex,
|
|
5647
|
+
this.settings.allowZigZagOnOneBar || !somethingChanged
|
|
5648
|
+
) || somethingChanged;
|
|
5649
|
+
return somethingChanged;
|
|
5650
|
+
}
|
|
5651
|
+
/**
|
|
5652
|
+
* Get the last confirmed pivot
|
|
5653
|
+
*/
|
|
5654
|
+
lastPivot() {
|
|
5655
|
+
if (this.pivots.length === 0)
|
|
5656
|
+
return null;
|
|
5657
|
+
const last2 = this.pivots[this.pivots.length - 1];
|
|
5658
|
+
return last2 !== void 0 ? last2 : null;
|
|
5659
|
+
}
|
|
5660
|
+
/**
|
|
5661
|
+
* Get extension line to current bar (if extendLast enabled)
|
|
5662
|
+
*/
|
|
5663
|
+
getExtension(currentBar, barIndex) {
|
|
5664
|
+
if (!this.settings.extendLast)
|
|
5665
|
+
return null;
|
|
5666
|
+
const last2 = this.lastPivot();
|
|
5667
|
+
if (!last2)
|
|
5668
|
+
return null;
|
|
5669
|
+
const isHigh = !last2.isHigh;
|
|
5670
|
+
const price = isHigh ? currentBar.high : currentBar.low;
|
|
5671
|
+
return {
|
|
5672
|
+
isHigh,
|
|
5673
|
+
volume: this.sumVol,
|
|
5674
|
+
start: last2.end,
|
|
5675
|
+
end: { time: currentBar.time, barIndex, price }
|
|
5676
|
+
};
|
|
5677
|
+
}
|
|
5678
|
+
// ============ Private Methods ============
|
|
5679
|
+
tryFindPivot(isHigh, depth, currentBarIndex, registerPivot = true) {
|
|
5680
|
+
const point = this.findPivotPoint(isHigh, depth, currentBarIndex);
|
|
5681
|
+
if (!point || !registerPivot)
|
|
5682
|
+
return false;
|
|
5683
|
+
return this.newPivotPointFound(isHigh, point);
|
|
5684
|
+
}
|
|
5685
|
+
/**
|
|
5686
|
+
* Check if a bar from `depth` bars back is a pivot point.
|
|
5687
|
+
* A pivot high has prices below it on both sides.
|
|
5688
|
+
* A pivot low has prices above it on both sides.
|
|
5689
|
+
*/
|
|
5690
|
+
findPivotPoint(isHigh, depth, currentBarIndex) {
|
|
5691
|
+
const buffer = isHigh ? this.highBuffer : this.lowBuffer;
|
|
5692
|
+
const pivotBufferIdx = buffer.length - 1 - depth;
|
|
5693
|
+
if (pivotBufferIdx < depth)
|
|
5694
|
+
return null;
|
|
5695
|
+
const pivotPrice = buffer[pivotBufferIdx];
|
|
5696
|
+
if (pivotPrice === void 0)
|
|
5697
|
+
return null;
|
|
5698
|
+
for (let i = pivotBufferIdx + 1; i < buffer.length; i++) {
|
|
5699
|
+
const price = buffer[i];
|
|
5700
|
+
if (price === void 0)
|
|
5701
|
+
return null;
|
|
5702
|
+
if (isHigh) {
|
|
5703
|
+
if (price > pivotPrice)
|
|
5704
|
+
return null;
|
|
5705
|
+
} else {
|
|
5706
|
+
if (price < pivotPrice)
|
|
5707
|
+
return null;
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
for (let i = pivotBufferIdx - depth; i < pivotBufferIdx; i++) {
|
|
5711
|
+
const price = buffer[i];
|
|
5712
|
+
if (price === void 0)
|
|
5713
|
+
return null;
|
|
5714
|
+
if (isHigh) {
|
|
5715
|
+
if (price >= pivotPrice)
|
|
5716
|
+
return null;
|
|
5717
|
+
} else {
|
|
5718
|
+
if (price <= pivotPrice)
|
|
5719
|
+
return null;
|
|
5720
|
+
}
|
|
5721
|
+
}
|
|
5722
|
+
const pivotBarIndex = currentBarIndex - depth;
|
|
5723
|
+
const pivotTime = this.timeBuffer[pivotBufferIdx];
|
|
5724
|
+
if (pivotTime === void 0)
|
|
5725
|
+
return null;
|
|
5726
|
+
return {
|
|
5727
|
+
time: pivotTime,
|
|
5728
|
+
barIndex: pivotBarIndex,
|
|
5729
|
+
price: pivotPrice
|
|
5730
|
+
};
|
|
5731
|
+
}
|
|
5732
|
+
/**
|
|
5733
|
+
* Calculate percentage deviation from base price to new price
|
|
5734
|
+
*/
|
|
5735
|
+
calcDev(basePrice, price) {
|
|
5736
|
+
return 100 * (price - basePrice) / Math.abs(basePrice);
|
|
5737
|
+
}
|
|
5738
|
+
/**
|
|
5739
|
+
* Handle a newly detected pivot point.
|
|
5740
|
+
* Either updates existing pivot (if same direction) or adds new pivot (if direction changed).
|
|
5741
|
+
*/
|
|
5742
|
+
newPivotPointFound(isHigh, point) {
|
|
5743
|
+
const last2 = this.lastPivot();
|
|
5744
|
+
if (!last2) {
|
|
5745
|
+
this.pivots.push({
|
|
5746
|
+
isHigh,
|
|
5747
|
+
volume: this.sumVol,
|
|
5748
|
+
start: point,
|
|
5749
|
+
end: point
|
|
5750
|
+
});
|
|
5751
|
+
this.sumVol = 0;
|
|
5752
|
+
return true;
|
|
5753
|
+
}
|
|
5754
|
+
if (last2.isHigh === isHigh) {
|
|
5755
|
+
const isMoreExtreme = isHigh ? point.price > last2.end.price : point.price < last2.end.price;
|
|
5756
|
+
if (isMoreExtreme) {
|
|
5757
|
+
last2.end = point;
|
|
5758
|
+
last2.volume += this.sumVol;
|
|
5759
|
+
this.sumVol = 0;
|
|
5760
|
+
return true;
|
|
5761
|
+
}
|
|
5762
|
+
} else {
|
|
5763
|
+
const dev2 = this.calcDev(last2.end.price, point.price);
|
|
5764
|
+
const threshold = this.settings.devThreshold;
|
|
5765
|
+
const meetsThreshold = last2.isHigh ? dev2 <= -threshold : dev2 >= threshold;
|
|
5766
|
+
if (meetsThreshold) {
|
|
5767
|
+
this.pivots.push({
|
|
5768
|
+
isHigh,
|
|
5769
|
+
volume: this.sumVol,
|
|
5770
|
+
start: last2.end,
|
|
5771
|
+
end: point
|
|
5772
|
+
});
|
|
5773
|
+
this.sumVol = 0;
|
|
5774
|
+
return true;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
return false;
|
|
5778
|
+
}
|
|
5779
|
+
};
|
|
5780
|
+
function calculateZigZag(bars, settings) {
|
|
5781
|
+
if (bars.length === 0) {
|
|
5782
|
+
return { pivots: [], extension: null };
|
|
5783
|
+
}
|
|
5784
|
+
const zz = new ZigZag(settings);
|
|
5785
|
+
for (let i = 0; i < bars.length; i++) {
|
|
5786
|
+
const bar = bars[i];
|
|
5787
|
+
if (bar !== void 0) {
|
|
5788
|
+
zz.update(bar, i);
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
const lastBar = bars[bars.length - 1];
|
|
5792
|
+
if (lastBar === void 0) {
|
|
5793
|
+
return { pivots: zz.pivots, extension: null };
|
|
5794
|
+
}
|
|
5795
|
+
const extension = zz.getExtension(lastBar, bars.length - 1);
|
|
5796
|
+
return {
|
|
5797
|
+
pivots: zz.pivots,
|
|
5798
|
+
extension
|
|
5799
|
+
};
|
|
5800
|
+
}
|
|
5463
5801
|
|
|
5464
5802
|
// src/indicator.ts
|
|
5465
5803
|
function indicator(metadata, setup) {
|
|
@@ -5579,7 +5917,7 @@ var input = {
|
|
|
5579
5917
|
defaultValue,
|
|
5580
5918
|
value: defaultValue,
|
|
5581
5919
|
title: options.title,
|
|
5582
|
-
options: ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4"]
|
|
5920
|
+
options: ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4", "hlcc4"]
|
|
5583
5921
|
};
|
|
5584
5922
|
},
|
|
5585
5923
|
/**
|
|
@@ -6175,3 +6513,5 @@ var info = {
|
|
|
6175
6513
|
indicator: ["indicator", "input", "plotHelper", "createPlot"]
|
|
6176
6514
|
}
|
|
6177
6515
|
};
|
|
6516
|
+
function alertcondition(_condition, _title, _message) {
|
|
6517
|
+
}
|