oakscriptjs 0.1.6 → 0.1.7
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 +30 -240
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +357 -40
- package/dist/index.mjs +357 -40
- 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/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,27 +179,29 @@ __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
|
|
198
|
+
const len = Math.floor(length2);
|
|
199
|
+
const multiplier = 2 / (len + 1);
|
|
193
200
|
let ema3 = 0;
|
|
194
|
-
for (let i = 0; i < Math.min(
|
|
201
|
+
for (let i = 0; i < Math.min(len, source.length); i++) {
|
|
195
202
|
ema3 += source[i];
|
|
196
203
|
}
|
|
197
|
-
ema3 = ema3 / Math.min(
|
|
204
|
+
ema3 = ema3 / Math.min(len, source.length);
|
|
198
205
|
for (let i = 0; i < source.length; i++) {
|
|
199
206
|
if (i === 0) {
|
|
200
207
|
result.push(source[i]);
|
|
@@ -254,17 +261,18 @@ function bb(series, length2, mult2) {
|
|
|
254
261
|
}
|
|
255
262
|
function stdev(source, length2) {
|
|
256
263
|
const result = [];
|
|
257
|
-
const
|
|
264
|
+
const len = Math.floor(length2);
|
|
265
|
+
const avg4 = sma(source, len);
|
|
258
266
|
for (let i = 0; i < source.length; i++) {
|
|
259
|
-
if (i <
|
|
267
|
+
if (i < len - 1) {
|
|
260
268
|
result.push(NaN);
|
|
261
269
|
} else {
|
|
262
270
|
let sumSquares = 0;
|
|
263
|
-
for (let j = 0; j <
|
|
271
|
+
for (let j = 0; j < len; j++) {
|
|
264
272
|
const diff2 = source[i - j] - avg4[i];
|
|
265
273
|
sumSquares += diff2 * diff2;
|
|
266
274
|
}
|
|
267
|
-
result.push(Math.sqrt(sumSquares /
|
|
275
|
+
result.push(Math.sqrt(sumSquares / len));
|
|
268
276
|
}
|
|
269
277
|
}
|
|
270
278
|
return result;
|
|
@@ -393,17 +401,34 @@ function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
|
|
|
393
401
|
}
|
|
394
402
|
function rma(source, length2) {
|
|
395
403
|
const result = [];
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
404
|
+
const len = Math.floor(length2);
|
|
405
|
+
const alpha = 1 / len;
|
|
406
|
+
let firstValidIndex = -1;
|
|
407
|
+
let validCount = 0;
|
|
408
|
+
let initSum = 0;
|
|
409
|
+
for (let i = 0; i < source.length; i++) {
|
|
410
|
+
const val = source[i];
|
|
411
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
412
|
+
initSum += val;
|
|
413
|
+
validCount++;
|
|
414
|
+
if (validCount === len) {
|
|
415
|
+
firstValidIndex = i;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
400
419
|
}
|
|
401
|
-
rmaValue =
|
|
420
|
+
let rmaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
421
|
+
let rmaInitialized = firstValidIndex >= 0;
|
|
402
422
|
for (let i = 0; i < source.length; i++) {
|
|
403
|
-
if (i
|
|
423
|
+
if (!rmaInitialized || i < firstValidIndex) {
|
|
424
|
+
result.push(NaN);
|
|
425
|
+
} else if (i === firstValidIndex) {
|
|
404
426
|
result.push(rmaValue);
|
|
405
427
|
} else {
|
|
406
|
-
|
|
428
|
+
const val = source[i];
|
|
429
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
430
|
+
rmaValue = alpha * val + (1 - alpha) * rmaValue;
|
|
431
|
+
}
|
|
407
432
|
result.push(rmaValue);
|
|
408
433
|
}
|
|
409
434
|
}
|
|
@@ -411,14 +436,15 @@ function rma(source, length2) {
|
|
|
411
436
|
}
|
|
412
437
|
function wma(source, length2) {
|
|
413
438
|
const result = [];
|
|
439
|
+
const len = Math.floor(length2);
|
|
414
440
|
for (let i = 0; i < source.length; i++) {
|
|
415
|
-
if (i <
|
|
441
|
+
if (i < len - 1) {
|
|
416
442
|
result.push(NaN);
|
|
417
443
|
} else {
|
|
418
444
|
let sum4 = 0;
|
|
419
445
|
let weightSum = 0;
|
|
420
|
-
for (let j = 0; j <
|
|
421
|
-
const weight =
|
|
446
|
+
for (let j = 0; j < len; j++) {
|
|
447
|
+
const weight = len - j;
|
|
422
448
|
sum4 += source[i - j] * weight;
|
|
423
449
|
weightSum += weight;
|
|
424
450
|
}
|
|
@@ -658,12 +684,13 @@ function vwma(source, length2, volume) {
|
|
|
658
684
|
}
|
|
659
685
|
function linreg(source, length2, offset = 0) {
|
|
660
686
|
const result = [];
|
|
687
|
+
const len = Math.floor(length2);
|
|
661
688
|
for (let i = 0; i < source.length; i++) {
|
|
662
|
-
if (i <
|
|
689
|
+
if (i < len - 1) {
|
|
663
690
|
result.push(NaN);
|
|
664
691
|
} else {
|
|
665
692
|
let hasNaN = false;
|
|
666
|
-
for (let j = 0; j <
|
|
693
|
+
for (let j = 0; j < len; j++) {
|
|
667
694
|
if (isNaN(source[i - j])) {
|
|
668
695
|
hasNaN = true;
|
|
669
696
|
break;
|
|
@@ -676,17 +703,17 @@ function linreg(source, length2, offset = 0) {
|
|
|
676
703
|
let sumY = 0;
|
|
677
704
|
let sumXY = 0;
|
|
678
705
|
let sumX2 = 0;
|
|
679
|
-
for (let j = 0; j <
|
|
706
|
+
for (let j = 0; j < len; j++) {
|
|
680
707
|
const x2 = j;
|
|
681
|
-
const y = source[i - (
|
|
708
|
+
const y = source[i - (len - 1 - j)];
|
|
682
709
|
sumX += x2;
|
|
683
710
|
sumY += y;
|
|
684
711
|
sumXY += x2 * y;
|
|
685
712
|
sumX2 += x2 * x2;
|
|
686
713
|
}
|
|
687
|
-
const slope = (
|
|
688
|
-
const intercept = (sumY - slope * sumX) /
|
|
689
|
-
const x =
|
|
714
|
+
const slope = (len * sumXY - sumX * sumY) / (len * sumX2 - sumX * sumX);
|
|
715
|
+
const intercept = (sumY - slope * sumX) / len;
|
|
716
|
+
const x = len - 1 - offset;
|
|
690
717
|
result.push(intercept + slope * x);
|
|
691
718
|
}
|
|
692
719
|
}
|
|
@@ -775,8 +802,8 @@ function cci(source, length2) {
|
|
|
775
802
|
if (isNaN(smaValues[i]) || isNaN(devValues[i]) || devValues[i] === 0) {
|
|
776
803
|
result.push(NaN);
|
|
777
804
|
} else {
|
|
778
|
-
const
|
|
779
|
-
result.push(
|
|
805
|
+
const cci3 = (source[i] - smaValues[i]) / (0.015 * devValues[i]);
|
|
806
|
+
result.push(cci3);
|
|
780
807
|
}
|
|
781
808
|
}
|
|
782
809
|
return result;
|
|
@@ -1237,9 +1264,9 @@ function vwap(source, volume) {
|
|
|
1237
1264
|
}
|
|
1238
1265
|
return result;
|
|
1239
1266
|
}
|
|
1240
|
-
function alma(source, length2 = 9, offset = 0.85, sigma = 6) {
|
|
1267
|
+
function alma(source, length2 = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
1241
1268
|
const result = [];
|
|
1242
|
-
const m = Math.floor(offset * (length2 - 1));
|
|
1269
|
+
const m = floor2 ? Math.floor(offset * (length2 - 1)) : offset * (length2 - 1);
|
|
1243
1270
|
const s = length2 / sigma;
|
|
1244
1271
|
const weights = [];
|
|
1245
1272
|
let weightSum = 0;
|
|
@@ -2241,6 +2268,38 @@ var Series = class _Series {
|
|
|
2241
2268
|
});
|
|
2242
2269
|
}
|
|
2243
2270
|
// ============================================
|
|
2271
|
+
// Conditional Selection
|
|
2272
|
+
// ============================================
|
|
2273
|
+
/**
|
|
2274
|
+
* Bar-by-bar conditional selection (ternary operator for Series)
|
|
2275
|
+
* Uses this Series as the condition (truthy = nonzero, falsy = zero/NaN)
|
|
2276
|
+
*
|
|
2277
|
+
* @param trueValue - Value or Series to use when condition is truthy
|
|
2278
|
+
* @param falseValue - Value or Series to use when condition is falsy
|
|
2279
|
+
* @returns New Series with bar-by-bar selected values
|
|
2280
|
+
*
|
|
2281
|
+
* @example
|
|
2282
|
+
* ```typescript
|
|
2283
|
+
* // PineScript: result = condition ? trueVal : falseVal
|
|
2284
|
+
* const result = condition.iff(trueVal, falseVal);
|
|
2285
|
+
*
|
|
2286
|
+
* // Example: RSI formula uses ternary for edge cases
|
|
2287
|
+
* // rsi = down == 0 ? 100 : up == 0 ? 0 : formula
|
|
2288
|
+
* const rsi = down.eq(0).iff(100, up.eq(0).iff(0, formula));
|
|
2289
|
+
* ```
|
|
2290
|
+
*/
|
|
2291
|
+
iff(trueValue, falseValue) {
|
|
2292
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
2293
|
+
const condition = this.extractor(bar, i, data);
|
|
2294
|
+
const isTruthy = condition !== 0 && !Number.isNaN(condition);
|
|
2295
|
+
if (isTruthy) {
|
|
2296
|
+
return typeof trueValue === "number" ? trueValue : trueValue.extractor(bar, i, data);
|
|
2297
|
+
} else {
|
|
2298
|
+
return typeof falseValue === "number" ? falseValue : falseValue.extractor(bar, i, data);
|
|
2299
|
+
}
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
// ============================================
|
|
2244
2303
|
// Offset/History
|
|
2245
2304
|
// ============================================
|
|
2246
2305
|
/**
|
|
@@ -5166,6 +5225,7 @@ __export(ta_series_exports, {
|
|
|
5166
5225
|
alma: () => alma2,
|
|
5167
5226
|
atr: () => atr2,
|
|
5168
5227
|
bb: () => bb2,
|
|
5228
|
+
cci: () => cci2,
|
|
5169
5229
|
change: () => change2,
|
|
5170
5230
|
cross: () => cross2,
|
|
5171
5231
|
crossover: () => crossover2,
|
|
@@ -5183,8 +5243,10 @@ __export(ta_series_exports, {
|
|
|
5183
5243
|
rma: () => rma2,
|
|
5184
5244
|
roc: () => roc2,
|
|
5185
5245
|
rsi: () => rsi2,
|
|
5246
|
+
sar: () => sar2,
|
|
5186
5247
|
sma: () => sma2,
|
|
5187
5248
|
stdev: () => stdev3,
|
|
5249
|
+
stoch: () => stoch2,
|
|
5188
5250
|
supertrend: () => supertrend2,
|
|
5189
5251
|
ta: () => ta,
|
|
5190
5252
|
tr: () => tr2,
|
|
@@ -5254,11 +5316,19 @@ function atr2(bars, length2) {
|
|
|
5254
5316
|
const result = atr(length2, high, low, close);
|
|
5255
5317
|
return Series.fromArray(bars, result);
|
|
5256
5318
|
}
|
|
5257
|
-
function tr2(bars) {
|
|
5319
|
+
function tr2(bars, handle_na = false) {
|
|
5258
5320
|
const high = bars.map((b2) => b2.high);
|
|
5259
5321
|
const low = bars.map((b2) => b2.low);
|
|
5260
5322
|
const close = bars.map((b2) => b2.close);
|
|
5261
|
-
const result = tr(
|
|
5323
|
+
const result = tr(handle_na, high, low, close);
|
|
5324
|
+
return Series.fromArray(bars, result);
|
|
5325
|
+
}
|
|
5326
|
+
function stoch2(source, high, low, length2) {
|
|
5327
|
+
const bars = source.bars;
|
|
5328
|
+
const sourceValues = source.toArray();
|
|
5329
|
+
const highValues = high.toArray();
|
|
5330
|
+
const lowValues = low.toArray();
|
|
5331
|
+
const result = stoch(sourceValues, highValues, lowValues, length2);
|
|
5262
5332
|
return Series.fromArray(bars, result);
|
|
5263
5333
|
}
|
|
5264
5334
|
function crossover2(source1, source2) {
|
|
@@ -5385,10 +5455,10 @@ function linreg2(source, length2, offset = 0) {
|
|
|
5385
5455
|
const result = linreg(sourceValues, length2, offset);
|
|
5386
5456
|
return Series.fromArray(bars, result);
|
|
5387
5457
|
}
|
|
5388
|
-
function alma2(source, length2 = 9, offset = 0.85, sigma = 6) {
|
|
5458
|
+
function alma2(source, length2 = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
5389
5459
|
const bars = source.bars;
|
|
5390
5460
|
const sourceValues = source.toArray();
|
|
5391
|
-
const result = alma(sourceValues, length2, offset, sigma);
|
|
5461
|
+
const result = alma(sourceValues, length2, offset, sigma, floor2);
|
|
5392
5462
|
return Series.fromArray(bars, result);
|
|
5393
5463
|
}
|
|
5394
5464
|
function zigzag2(bars, deviation = 5, depth = 10, backstep = 3) {
|
|
@@ -5408,6 +5478,19 @@ function zigzag2(bars, deviation = 5, depth = 10, backstep = 3) {
|
|
|
5408
5478
|
Series.fromArray(bars, pivotVals.map((b2) => b2 ? 1 : 0))
|
|
5409
5479
|
];
|
|
5410
5480
|
}
|
|
5481
|
+
function cci2(source, length2) {
|
|
5482
|
+
const bars = source.bars;
|
|
5483
|
+
const sourceValues = source.toArray();
|
|
5484
|
+
const result = cci(sourceValues, length2);
|
|
5485
|
+
return Series.fromArray(bars, result);
|
|
5486
|
+
}
|
|
5487
|
+
function sar2(bars, start = 0.02, inc = 0.02, max5 = 0.2) {
|
|
5488
|
+
const high = bars.map((b2) => b2.high);
|
|
5489
|
+
const low = bars.map((b2) => b2.low);
|
|
5490
|
+
const close = bars.map((b2) => b2.close);
|
|
5491
|
+
const result = sar(start, inc, max5, high, low, close);
|
|
5492
|
+
return Series.fromArray(bars, result);
|
|
5493
|
+
}
|
|
5411
5494
|
var ta = {
|
|
5412
5495
|
sma: sma2,
|
|
5413
5496
|
ema: ema2,
|
|
@@ -5420,6 +5503,7 @@ var ta = {
|
|
|
5420
5503
|
stdev: stdev3,
|
|
5421
5504
|
atr: atr2,
|
|
5422
5505
|
tr: tr2,
|
|
5506
|
+
stoch: stoch2,
|
|
5423
5507
|
crossover: crossover2,
|
|
5424
5508
|
crossunder: crossunder2,
|
|
5425
5509
|
cross: cross2,
|
|
@@ -5436,10 +5520,18 @@ var ta = {
|
|
|
5436
5520
|
ichimoku: ichimoku2,
|
|
5437
5521
|
linreg: linreg2,
|
|
5438
5522
|
alma: alma2,
|
|
5439
|
-
zigzag: zigzag2
|
|
5523
|
+
zigzag: zigzag2,
|
|
5524
|
+
cci: cci2,
|
|
5525
|
+
sar: sar2
|
|
5440
5526
|
};
|
|
5441
5527
|
|
|
5442
5528
|
// src/utils/index.ts
|
|
5529
|
+
function isNA(value) {
|
|
5530
|
+
return value === null || value === void 0 || Number.isNaN(value);
|
|
5531
|
+
}
|
|
5532
|
+
function nz(value, defaultValue = 0) {
|
|
5533
|
+
return isNA(value) ? defaultValue : value;
|
|
5534
|
+
}
|
|
5443
5535
|
function ohlcFromBars(bars) {
|
|
5444
5536
|
return {
|
|
5445
5537
|
open: bars.map((b2) => b2.open),
|
|
@@ -5460,6 +5552,229 @@ function getLow(ohlc) {
|
|
|
5460
5552
|
function getOpen(ohlc) {
|
|
5461
5553
|
return ohlc.open;
|
|
5462
5554
|
}
|
|
5555
|
+
function getSourceSeries(bars, source = "close") {
|
|
5556
|
+
const open = new Series(bars, (bar) => bar.open);
|
|
5557
|
+
const high = new Series(bars, (bar) => bar.high);
|
|
5558
|
+
const low = new Series(bars, (bar) => bar.low);
|
|
5559
|
+
const close = new Series(bars, (bar) => bar.close);
|
|
5560
|
+
switch (source) {
|
|
5561
|
+
case "open":
|
|
5562
|
+
return open;
|
|
5563
|
+
case "high":
|
|
5564
|
+
return high;
|
|
5565
|
+
case "low":
|
|
5566
|
+
return low;
|
|
5567
|
+
case "close":
|
|
5568
|
+
return close;
|
|
5569
|
+
case "hl2":
|
|
5570
|
+
return high.add(low).div(2);
|
|
5571
|
+
case "hlc3":
|
|
5572
|
+
return high.add(low).add(close).div(3);
|
|
5573
|
+
case "ohlc4":
|
|
5574
|
+
return open.add(high).add(low).add(close).div(4);
|
|
5575
|
+
case "hlcc4":
|
|
5576
|
+
return high.add(low).add(close).add(close).div(4);
|
|
5577
|
+
default:
|
|
5578
|
+
return close;
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
|
|
5582
|
+
// src/lib/zigzag/zigzag.ts
|
|
5583
|
+
var defaultSettings = {
|
|
5584
|
+
devThreshold: 5,
|
|
5585
|
+
depth: 10,
|
|
5586
|
+
lineColor: "#2962FF",
|
|
5587
|
+
extendLast: true,
|
|
5588
|
+
displayReversalPrice: true,
|
|
5589
|
+
displayCumulativeVolume: true,
|
|
5590
|
+
displayReversalPriceChange: true,
|
|
5591
|
+
differencePriceMode: "Absolute",
|
|
5592
|
+
allowZigZagOnOneBar: true
|
|
5593
|
+
};
|
|
5594
|
+
var ZigZag = class {
|
|
5595
|
+
settings;
|
|
5596
|
+
pivots = [];
|
|
5597
|
+
sumVol = 0;
|
|
5598
|
+
highBuffer = [];
|
|
5599
|
+
lowBuffer = [];
|
|
5600
|
+
timeBuffer = [];
|
|
5601
|
+
barCount = 0;
|
|
5602
|
+
constructor(settings) {
|
|
5603
|
+
this.settings = { ...defaultSettings, ...settings };
|
|
5604
|
+
}
|
|
5605
|
+
/**
|
|
5606
|
+
* Process a single bar, detecting pivots.
|
|
5607
|
+
* Must be called sequentially for each bar.
|
|
5608
|
+
* @returns true if a new pivot was detected or updated
|
|
5609
|
+
*/
|
|
5610
|
+
update(bar, barIndex) {
|
|
5611
|
+
this.highBuffer.push(bar.high);
|
|
5612
|
+
this.lowBuffer.push(bar.low);
|
|
5613
|
+
this.timeBuffer.push(bar.time);
|
|
5614
|
+
this.barCount++;
|
|
5615
|
+
const depth = Math.max(2, Math.floor(this.settings.depth / 2));
|
|
5616
|
+
this.sumVol += bar.volume ?? 0;
|
|
5617
|
+
if (this.barCount < depth * 2 + 1)
|
|
5618
|
+
return false;
|
|
5619
|
+
let somethingChanged = this.tryFindPivot(true, depth, barIndex);
|
|
5620
|
+
somethingChanged = this.tryFindPivot(
|
|
5621
|
+
false,
|
|
5622
|
+
depth,
|
|
5623
|
+
barIndex,
|
|
5624
|
+
this.settings.allowZigZagOnOneBar || !somethingChanged
|
|
5625
|
+
) || somethingChanged;
|
|
5626
|
+
return somethingChanged;
|
|
5627
|
+
}
|
|
5628
|
+
/**
|
|
5629
|
+
* Get the last confirmed pivot
|
|
5630
|
+
*/
|
|
5631
|
+
lastPivot() {
|
|
5632
|
+
if (this.pivots.length === 0)
|
|
5633
|
+
return null;
|
|
5634
|
+
const last2 = this.pivots[this.pivots.length - 1];
|
|
5635
|
+
return last2 !== void 0 ? last2 : null;
|
|
5636
|
+
}
|
|
5637
|
+
/**
|
|
5638
|
+
* Get extension line to current bar (if extendLast enabled)
|
|
5639
|
+
*/
|
|
5640
|
+
getExtension(currentBar, barIndex) {
|
|
5641
|
+
if (!this.settings.extendLast)
|
|
5642
|
+
return null;
|
|
5643
|
+
const last2 = this.lastPivot();
|
|
5644
|
+
if (!last2)
|
|
5645
|
+
return null;
|
|
5646
|
+
const isHigh = !last2.isHigh;
|
|
5647
|
+
const price = isHigh ? currentBar.high : currentBar.low;
|
|
5648
|
+
return {
|
|
5649
|
+
isHigh,
|
|
5650
|
+
volume: this.sumVol,
|
|
5651
|
+
start: last2.end,
|
|
5652
|
+
end: { time: currentBar.time, barIndex, price }
|
|
5653
|
+
};
|
|
5654
|
+
}
|
|
5655
|
+
// ============ Private Methods ============
|
|
5656
|
+
tryFindPivot(isHigh, depth, currentBarIndex, registerPivot = true) {
|
|
5657
|
+
const point = this.findPivotPoint(isHigh, depth, currentBarIndex);
|
|
5658
|
+
if (!point || !registerPivot)
|
|
5659
|
+
return false;
|
|
5660
|
+
return this.newPivotPointFound(isHigh, point);
|
|
5661
|
+
}
|
|
5662
|
+
/**
|
|
5663
|
+
* Check if a bar from `depth` bars back is a pivot point.
|
|
5664
|
+
* A pivot high has prices below it on both sides.
|
|
5665
|
+
* A pivot low has prices above it on both sides.
|
|
5666
|
+
*/
|
|
5667
|
+
findPivotPoint(isHigh, depth, currentBarIndex) {
|
|
5668
|
+
const buffer = isHigh ? this.highBuffer : this.lowBuffer;
|
|
5669
|
+
const pivotBufferIdx = buffer.length - 1 - depth;
|
|
5670
|
+
if (pivotBufferIdx < depth)
|
|
5671
|
+
return null;
|
|
5672
|
+
const pivotPrice = buffer[pivotBufferIdx];
|
|
5673
|
+
if (pivotPrice === void 0)
|
|
5674
|
+
return null;
|
|
5675
|
+
for (let i = pivotBufferIdx + 1; i < buffer.length; i++) {
|
|
5676
|
+
const price = buffer[i];
|
|
5677
|
+
if (price === void 0)
|
|
5678
|
+
return null;
|
|
5679
|
+
if (isHigh) {
|
|
5680
|
+
if (price > pivotPrice)
|
|
5681
|
+
return null;
|
|
5682
|
+
} else {
|
|
5683
|
+
if (price < pivotPrice)
|
|
5684
|
+
return null;
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
for (let i = pivotBufferIdx - depth; i < pivotBufferIdx; i++) {
|
|
5688
|
+
const price = buffer[i];
|
|
5689
|
+
if (price === void 0)
|
|
5690
|
+
return null;
|
|
5691
|
+
if (isHigh) {
|
|
5692
|
+
if (price >= pivotPrice)
|
|
5693
|
+
return null;
|
|
5694
|
+
} else {
|
|
5695
|
+
if (price <= pivotPrice)
|
|
5696
|
+
return null;
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
const pivotBarIndex = currentBarIndex - depth;
|
|
5700
|
+
const pivotTime = this.timeBuffer[pivotBufferIdx];
|
|
5701
|
+
if (pivotTime === void 0)
|
|
5702
|
+
return null;
|
|
5703
|
+
return {
|
|
5704
|
+
time: pivotTime,
|
|
5705
|
+
barIndex: pivotBarIndex,
|
|
5706
|
+
price: pivotPrice
|
|
5707
|
+
};
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Calculate percentage deviation from base price to new price
|
|
5711
|
+
*/
|
|
5712
|
+
calcDev(basePrice, price) {
|
|
5713
|
+
return 100 * (price - basePrice) / Math.abs(basePrice);
|
|
5714
|
+
}
|
|
5715
|
+
/**
|
|
5716
|
+
* Handle a newly detected pivot point.
|
|
5717
|
+
* Either updates existing pivot (if same direction) or adds new pivot (if direction changed).
|
|
5718
|
+
*/
|
|
5719
|
+
newPivotPointFound(isHigh, point) {
|
|
5720
|
+
const last2 = this.lastPivot();
|
|
5721
|
+
if (!last2) {
|
|
5722
|
+
this.pivots.push({
|
|
5723
|
+
isHigh,
|
|
5724
|
+
volume: this.sumVol,
|
|
5725
|
+
start: point,
|
|
5726
|
+
end: point
|
|
5727
|
+
});
|
|
5728
|
+
this.sumVol = 0;
|
|
5729
|
+
return true;
|
|
5730
|
+
}
|
|
5731
|
+
if (last2.isHigh === isHigh) {
|
|
5732
|
+
const isMoreExtreme = isHigh ? point.price > last2.end.price : point.price < last2.end.price;
|
|
5733
|
+
if (isMoreExtreme) {
|
|
5734
|
+
last2.end = point;
|
|
5735
|
+
last2.volume += this.sumVol;
|
|
5736
|
+
this.sumVol = 0;
|
|
5737
|
+
return true;
|
|
5738
|
+
}
|
|
5739
|
+
} else {
|
|
5740
|
+
const dev2 = this.calcDev(last2.end.price, point.price);
|
|
5741
|
+
const threshold = this.settings.devThreshold;
|
|
5742
|
+
const meetsThreshold = last2.isHigh ? dev2 <= -threshold : dev2 >= threshold;
|
|
5743
|
+
if (meetsThreshold) {
|
|
5744
|
+
this.pivots.push({
|
|
5745
|
+
isHigh,
|
|
5746
|
+
volume: this.sumVol,
|
|
5747
|
+
start: last2.end,
|
|
5748
|
+
end: point
|
|
5749
|
+
});
|
|
5750
|
+
this.sumVol = 0;
|
|
5751
|
+
return true;
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
return false;
|
|
5755
|
+
}
|
|
5756
|
+
};
|
|
5757
|
+
function calculateZigZag(bars, settings) {
|
|
5758
|
+
if (bars.length === 0) {
|
|
5759
|
+
return { pivots: [], extension: null };
|
|
5760
|
+
}
|
|
5761
|
+
const zz = new ZigZag(settings);
|
|
5762
|
+
for (let i = 0; i < bars.length; i++) {
|
|
5763
|
+
const bar = bars[i];
|
|
5764
|
+
if (bar !== void 0) {
|
|
5765
|
+
zz.update(bar, i);
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
const lastBar = bars[bars.length - 1];
|
|
5769
|
+
if (lastBar === void 0) {
|
|
5770
|
+
return { pivots: zz.pivots, extension: null };
|
|
5771
|
+
}
|
|
5772
|
+
const extension = zz.getExtension(lastBar, bars.length - 1);
|
|
5773
|
+
return {
|
|
5774
|
+
pivots: zz.pivots,
|
|
5775
|
+
extension
|
|
5776
|
+
};
|
|
5777
|
+
}
|
|
5463
5778
|
|
|
5464
5779
|
// src/indicator.ts
|
|
5465
5780
|
function indicator(metadata, setup) {
|
|
@@ -5579,7 +5894,7 @@ var input = {
|
|
|
5579
5894
|
defaultValue,
|
|
5580
5895
|
value: defaultValue,
|
|
5581
5896
|
title: options.title,
|
|
5582
|
-
options: ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4"]
|
|
5897
|
+
options: ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4", "hlcc4"]
|
|
5583
5898
|
};
|
|
5584
5899
|
},
|
|
5585
5900
|
/**
|
|
@@ -6175,3 +6490,5 @@ var info = {
|
|
|
6175
6490
|
indicator: ["indicator", "input", "plotHelper", "createPlot"]
|
|
6176
6491
|
}
|
|
6177
6492
|
};
|
|
6493
|
+
function alertcondition(_condition, _title, _message) {
|
|
6494
|
+
}
|