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.mjs
CHANGED
|
@@ -5,7 +5,6 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/types/index.ts
|
|
8
|
-
var na = null;
|
|
9
8
|
var xloc = {
|
|
10
9
|
/** X-coordinates are bar indices */
|
|
11
10
|
bar_index: "bar_index",
|
|
@@ -80,34 +79,51 @@ __export(ta_exports, {
|
|
|
80
79
|
});
|
|
81
80
|
function sma(source, length2) {
|
|
82
81
|
const result = [];
|
|
82
|
+
const len = Math.floor(length2);
|
|
83
83
|
for (let i = 0; i < source.length; i++) {
|
|
84
|
-
if (i <
|
|
84
|
+
if (i < len - 1) {
|
|
85
85
|
result.push(NaN);
|
|
86
86
|
} else {
|
|
87
87
|
let sum4 = 0;
|
|
88
|
-
for (let j = 0; j <
|
|
88
|
+
for (let j = 0; j < len; j++) {
|
|
89
89
|
sum4 += source[i - j];
|
|
90
90
|
}
|
|
91
|
-
result.push(sum4 /
|
|
91
|
+
result.push(sum4 / len);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
return result;
|
|
95
95
|
}
|
|
96
96
|
function ema(source, length2) {
|
|
97
97
|
const result = [];
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
const len = Math.floor(length2);
|
|
99
|
+
const multiplier = 2 / (len + 1);
|
|
100
|
+
let firstValidIndex = -1;
|
|
101
|
+
let validCount = 0;
|
|
102
|
+
let initSum = 0;
|
|
103
|
+
for (let i = 0; i < source.length; i++) {
|
|
104
|
+
const val = source[i];
|
|
105
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
106
|
+
initSum += val;
|
|
107
|
+
validCount++;
|
|
108
|
+
if (validCount === len) {
|
|
109
|
+
firstValidIndex = i;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
102
113
|
}
|
|
103
|
-
|
|
114
|
+
let emaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
115
|
+
let emaInitialized = firstValidIndex >= 0;
|
|
104
116
|
for (let i = 0; i < source.length; i++) {
|
|
105
|
-
if (i
|
|
106
|
-
result.push(
|
|
107
|
-
|
|
117
|
+
if (!emaInitialized || i < firstValidIndex) {
|
|
118
|
+
result.push(NaN);
|
|
119
|
+
} else if (i === firstValidIndex) {
|
|
120
|
+
result.push(emaValue);
|
|
108
121
|
} else {
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
const val = source[i];
|
|
123
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
124
|
+
emaValue = (val - emaValue) * multiplier + emaValue;
|
|
125
|
+
}
|
|
126
|
+
result.push(emaValue);
|
|
111
127
|
}
|
|
112
128
|
}
|
|
113
129
|
return result;
|
|
@@ -160,17 +176,18 @@ function bb(series, length2, mult2) {
|
|
|
160
176
|
}
|
|
161
177
|
function stdev(source, length2) {
|
|
162
178
|
const result = [];
|
|
163
|
-
const
|
|
179
|
+
const len = Math.floor(length2);
|
|
180
|
+
const avg4 = sma(source, len);
|
|
164
181
|
for (let i = 0; i < source.length; i++) {
|
|
165
|
-
if (i <
|
|
182
|
+
if (i < len - 1) {
|
|
166
183
|
result.push(NaN);
|
|
167
184
|
} else {
|
|
168
185
|
let sumSquares = 0;
|
|
169
|
-
for (let j = 0; j <
|
|
186
|
+
for (let j = 0; j < len; j++) {
|
|
170
187
|
const diff2 = source[i - j] - avg4[i];
|
|
171
188
|
sumSquares += diff2 * diff2;
|
|
172
189
|
}
|
|
173
|
-
result.push(Math.sqrt(sumSquares /
|
|
190
|
+
result.push(Math.sqrt(sumSquares / len));
|
|
174
191
|
}
|
|
175
192
|
}
|
|
176
193
|
return result;
|
|
@@ -299,17 +316,34 @@ function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
|
|
|
299
316
|
}
|
|
300
317
|
function rma(source, length2) {
|
|
301
318
|
const result = [];
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
319
|
+
const len = Math.floor(length2);
|
|
320
|
+
const alpha = 1 / len;
|
|
321
|
+
let firstValidIndex = -1;
|
|
322
|
+
let validCount = 0;
|
|
323
|
+
let initSum = 0;
|
|
324
|
+
for (let i = 0; i < source.length; i++) {
|
|
325
|
+
const val = source[i];
|
|
326
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
327
|
+
initSum += val;
|
|
328
|
+
validCount++;
|
|
329
|
+
if (validCount === len) {
|
|
330
|
+
firstValidIndex = i;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
306
334
|
}
|
|
307
|
-
rmaValue =
|
|
335
|
+
let rmaValue = validCount > 0 ? initSum / validCount : NaN;
|
|
336
|
+
let rmaInitialized = firstValidIndex >= 0;
|
|
308
337
|
for (let i = 0; i < source.length; i++) {
|
|
309
|
-
if (i
|
|
338
|
+
if (!rmaInitialized || i < firstValidIndex) {
|
|
339
|
+
result.push(NaN);
|
|
340
|
+
} else if (i === firstValidIndex) {
|
|
310
341
|
result.push(rmaValue);
|
|
311
342
|
} else {
|
|
312
|
-
|
|
343
|
+
const val = source[i];
|
|
344
|
+
if (val !== void 0 && !isNaN(val)) {
|
|
345
|
+
rmaValue = alpha * val + (1 - alpha) * rmaValue;
|
|
346
|
+
}
|
|
313
347
|
result.push(rmaValue);
|
|
314
348
|
}
|
|
315
349
|
}
|
|
@@ -317,14 +351,15 @@ function rma(source, length2) {
|
|
|
317
351
|
}
|
|
318
352
|
function wma(source, length2) {
|
|
319
353
|
const result = [];
|
|
354
|
+
const len = Math.floor(length2);
|
|
320
355
|
for (let i = 0; i < source.length; i++) {
|
|
321
|
-
if (i <
|
|
356
|
+
if (i < len - 1) {
|
|
322
357
|
result.push(NaN);
|
|
323
358
|
} else {
|
|
324
359
|
let sum4 = 0;
|
|
325
360
|
let weightSum = 0;
|
|
326
|
-
for (let j = 0; j <
|
|
327
|
-
const weight =
|
|
361
|
+
for (let j = 0; j < len; j++) {
|
|
362
|
+
const weight = len - j;
|
|
328
363
|
sum4 += source[i - j] * weight;
|
|
329
364
|
weightSum += weight;
|
|
330
365
|
}
|
|
@@ -564,12 +599,13 @@ function vwma(source, length2, volume) {
|
|
|
564
599
|
}
|
|
565
600
|
function linreg(source, length2, offset = 0) {
|
|
566
601
|
const result = [];
|
|
602
|
+
const len = Math.floor(length2);
|
|
567
603
|
for (let i = 0; i < source.length; i++) {
|
|
568
|
-
if (i <
|
|
604
|
+
if (i < len - 1) {
|
|
569
605
|
result.push(NaN);
|
|
570
606
|
} else {
|
|
571
607
|
let hasNaN = false;
|
|
572
|
-
for (let j = 0; j <
|
|
608
|
+
for (let j = 0; j < len; j++) {
|
|
573
609
|
if (isNaN(source[i - j])) {
|
|
574
610
|
hasNaN = true;
|
|
575
611
|
break;
|
|
@@ -582,17 +618,17 @@ function linreg(source, length2, offset = 0) {
|
|
|
582
618
|
let sumY = 0;
|
|
583
619
|
let sumXY = 0;
|
|
584
620
|
let sumX2 = 0;
|
|
585
|
-
for (let j = 0; j <
|
|
621
|
+
for (let j = 0; j < len; j++) {
|
|
586
622
|
const x2 = j;
|
|
587
|
-
const y = source[i - (
|
|
623
|
+
const y = source[i - (len - 1 - j)];
|
|
588
624
|
sumX += x2;
|
|
589
625
|
sumY += y;
|
|
590
626
|
sumXY += x2 * y;
|
|
591
627
|
sumX2 += x2 * x2;
|
|
592
628
|
}
|
|
593
|
-
const slope = (
|
|
594
|
-
const intercept = (sumY - slope * sumX) /
|
|
595
|
-
const x =
|
|
629
|
+
const slope = (len * sumXY - sumX * sumY) / (len * sumX2 - sumX * sumX);
|
|
630
|
+
const intercept = (sumY - slope * sumX) / len;
|
|
631
|
+
const x = len - 1 - offset;
|
|
596
632
|
result.push(intercept + slope * x);
|
|
597
633
|
}
|
|
598
634
|
}
|
|
@@ -681,8 +717,8 @@ function cci(source, length2) {
|
|
|
681
717
|
if (isNaN(smaValues[i]) || isNaN(devValues[i]) || devValues[i] === 0) {
|
|
682
718
|
result.push(NaN);
|
|
683
719
|
} else {
|
|
684
|
-
const
|
|
685
|
-
result.push(
|
|
720
|
+
const cci3 = (source[i] - smaValues[i]) / (0.015 * devValues[i]);
|
|
721
|
+
result.push(cci3);
|
|
686
722
|
}
|
|
687
723
|
}
|
|
688
724
|
return result;
|
|
@@ -1143,9 +1179,9 @@ function vwap(source, volume) {
|
|
|
1143
1179
|
}
|
|
1144
1180
|
return result;
|
|
1145
1181
|
}
|
|
1146
|
-
function alma(source, length2 = 9, offset = 0.85, sigma = 6) {
|
|
1182
|
+
function alma(source, length2 = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
1147
1183
|
const result = [];
|
|
1148
|
-
const m = Math.floor(offset * (length2 - 1));
|
|
1184
|
+
const m = floor2 ? Math.floor(offset * (length2 - 1)) : offset * (length2 - 1);
|
|
1149
1185
|
const s = length2 / sigma;
|
|
1150
1186
|
const weights = [];
|
|
1151
1187
|
let weightSum = 0;
|
|
@@ -2147,6 +2183,38 @@ var Series = class _Series {
|
|
|
2147
2183
|
});
|
|
2148
2184
|
}
|
|
2149
2185
|
// ============================================
|
|
2186
|
+
// Conditional Selection
|
|
2187
|
+
// ============================================
|
|
2188
|
+
/**
|
|
2189
|
+
* Bar-by-bar conditional selection (ternary operator for Series)
|
|
2190
|
+
* Uses this Series as the condition (truthy = nonzero, falsy = zero/NaN)
|
|
2191
|
+
*
|
|
2192
|
+
* @param trueValue - Value or Series to use when condition is truthy
|
|
2193
|
+
* @param falseValue - Value or Series to use when condition is falsy
|
|
2194
|
+
* @returns New Series with bar-by-bar selected values
|
|
2195
|
+
*
|
|
2196
|
+
* @example
|
|
2197
|
+
* ```typescript
|
|
2198
|
+
* // PineScript: result = condition ? trueVal : falseVal
|
|
2199
|
+
* const result = condition.iff(trueVal, falseVal);
|
|
2200
|
+
*
|
|
2201
|
+
* // Example: RSI formula uses ternary for edge cases
|
|
2202
|
+
* // rsi = down == 0 ? 100 : up == 0 ? 0 : formula
|
|
2203
|
+
* const rsi = down.eq(0).iff(100, up.eq(0).iff(0, formula));
|
|
2204
|
+
* ```
|
|
2205
|
+
*/
|
|
2206
|
+
iff(trueValue, falseValue) {
|
|
2207
|
+
return new _Series(this.dataSource, (bar, i, data) => {
|
|
2208
|
+
const condition = this.extractor(bar, i, data);
|
|
2209
|
+
const isTruthy = condition !== 0 && !Number.isNaN(condition);
|
|
2210
|
+
if (isTruthy) {
|
|
2211
|
+
return typeof trueValue === "number" ? trueValue : trueValue.extractor(bar, i, data);
|
|
2212
|
+
} else {
|
|
2213
|
+
return typeof falseValue === "number" ? falseValue : falseValue.extractor(bar, i, data);
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
// ============================================
|
|
2150
2218
|
// Offset/History
|
|
2151
2219
|
// ============================================
|
|
2152
2220
|
/**
|
|
@@ -3044,6 +3112,7 @@ __export(str_exports, {
|
|
|
3044
3112
|
lower: () => lower,
|
|
3045
3113
|
match: () => match,
|
|
3046
3114
|
pos: () => pos,
|
|
3115
|
+
repeat: () => repeat,
|
|
3047
3116
|
replace: () => replace,
|
|
3048
3117
|
replace_all: () => replace_all,
|
|
3049
3118
|
split: () => split,
|
|
@@ -3129,6 +3198,13 @@ function trimRight(str) {
|
|
|
3129
3198
|
function match(source, regex) {
|
|
3130
3199
|
return new RegExp(regex).test(source);
|
|
3131
3200
|
}
|
|
3201
|
+
function repeat(source, count, separator = "") {
|
|
3202
|
+
if (source == null)
|
|
3203
|
+
return null;
|
|
3204
|
+
if (count <= 0)
|
|
3205
|
+
return "";
|
|
3206
|
+
return Array(count).fill(source).join(separator);
|
|
3207
|
+
}
|
|
3132
3208
|
function format_time(time, format2) {
|
|
3133
3209
|
const date = new Date(time);
|
|
3134
3210
|
const monthNames = [
|
|
@@ -5072,6 +5148,7 @@ __export(ta_series_exports, {
|
|
|
5072
5148
|
alma: () => alma2,
|
|
5073
5149
|
atr: () => atr2,
|
|
5074
5150
|
bb: () => bb2,
|
|
5151
|
+
cci: () => cci2,
|
|
5075
5152
|
change: () => change2,
|
|
5076
5153
|
cross: () => cross2,
|
|
5077
5154
|
crossover: () => crossover2,
|
|
@@ -5089,8 +5166,10 @@ __export(ta_series_exports, {
|
|
|
5089
5166
|
rma: () => rma2,
|
|
5090
5167
|
roc: () => roc2,
|
|
5091
5168
|
rsi: () => rsi2,
|
|
5169
|
+
sar: () => sar2,
|
|
5092
5170
|
sma: () => sma2,
|
|
5093
5171
|
stdev: () => stdev3,
|
|
5172
|
+
stoch: () => stoch2,
|
|
5094
5173
|
supertrend: () => supertrend2,
|
|
5095
5174
|
ta: () => ta,
|
|
5096
5175
|
tr: () => tr2,
|
|
@@ -5160,11 +5239,19 @@ function atr2(bars, length2) {
|
|
|
5160
5239
|
const result = atr(length2, high, low, close);
|
|
5161
5240
|
return Series.fromArray(bars, result);
|
|
5162
5241
|
}
|
|
5163
|
-
function tr2(bars) {
|
|
5242
|
+
function tr2(bars, handle_na = false) {
|
|
5164
5243
|
const high = bars.map((b2) => b2.high);
|
|
5165
5244
|
const low = bars.map((b2) => b2.low);
|
|
5166
5245
|
const close = bars.map((b2) => b2.close);
|
|
5167
|
-
const result = tr(
|
|
5246
|
+
const result = tr(handle_na, high, low, close);
|
|
5247
|
+
return Series.fromArray(bars, result);
|
|
5248
|
+
}
|
|
5249
|
+
function stoch2(source, high, low, length2) {
|
|
5250
|
+
const bars = source.bars;
|
|
5251
|
+
const sourceValues = source.toArray();
|
|
5252
|
+
const highValues = high.toArray();
|
|
5253
|
+
const lowValues = low.toArray();
|
|
5254
|
+
const result = stoch(sourceValues, highValues, lowValues, length2);
|
|
5168
5255
|
return Series.fromArray(bars, result);
|
|
5169
5256
|
}
|
|
5170
5257
|
function crossover2(source1, source2) {
|
|
@@ -5291,10 +5378,10 @@ function linreg2(source, length2, offset = 0) {
|
|
|
5291
5378
|
const result = linreg(sourceValues, length2, offset);
|
|
5292
5379
|
return Series.fromArray(bars, result);
|
|
5293
5380
|
}
|
|
5294
|
-
function alma2(source, length2 = 9, offset = 0.85, sigma = 6) {
|
|
5381
|
+
function alma2(source, length2 = 9, offset = 0.85, sigma = 6, floor2 = false) {
|
|
5295
5382
|
const bars = source.bars;
|
|
5296
5383
|
const sourceValues = source.toArray();
|
|
5297
|
-
const result = alma(sourceValues, length2, offset, sigma);
|
|
5384
|
+
const result = alma(sourceValues, length2, offset, sigma, floor2);
|
|
5298
5385
|
return Series.fromArray(bars, result);
|
|
5299
5386
|
}
|
|
5300
5387
|
function zigzag2(bars, deviation = 5, depth = 10, backstep = 3) {
|
|
@@ -5314,6 +5401,19 @@ function zigzag2(bars, deviation = 5, depth = 10, backstep = 3) {
|
|
|
5314
5401
|
Series.fromArray(bars, pivotVals.map((b2) => b2 ? 1 : 0))
|
|
5315
5402
|
];
|
|
5316
5403
|
}
|
|
5404
|
+
function cci2(source, length2) {
|
|
5405
|
+
const bars = source.bars;
|
|
5406
|
+
const sourceValues = source.toArray();
|
|
5407
|
+
const result = cci(sourceValues, length2);
|
|
5408
|
+
return Series.fromArray(bars, result);
|
|
5409
|
+
}
|
|
5410
|
+
function sar2(bars, start = 0.02, inc = 0.02, max5 = 0.2) {
|
|
5411
|
+
const high = bars.map((b2) => b2.high);
|
|
5412
|
+
const low = bars.map((b2) => b2.low);
|
|
5413
|
+
const close = bars.map((b2) => b2.close);
|
|
5414
|
+
const result = sar(start, inc, max5, high, low, close);
|
|
5415
|
+
return Series.fromArray(bars, result);
|
|
5416
|
+
}
|
|
5317
5417
|
var ta = {
|
|
5318
5418
|
sma: sma2,
|
|
5319
5419
|
ema: ema2,
|
|
@@ -5326,6 +5426,7 @@ var ta = {
|
|
|
5326
5426
|
stdev: stdev3,
|
|
5327
5427
|
atr: atr2,
|
|
5328
5428
|
tr: tr2,
|
|
5429
|
+
stoch: stoch2,
|
|
5329
5430
|
crossover: crossover2,
|
|
5330
5431
|
crossunder: crossunder2,
|
|
5331
5432
|
cross: cross2,
|
|
@@ -5342,10 +5443,18 @@ var ta = {
|
|
|
5342
5443
|
ichimoku: ichimoku2,
|
|
5343
5444
|
linreg: linreg2,
|
|
5344
5445
|
alma: alma2,
|
|
5345
|
-
zigzag: zigzag2
|
|
5446
|
+
zigzag: zigzag2,
|
|
5447
|
+
cci: cci2,
|
|
5448
|
+
sar: sar2
|
|
5346
5449
|
};
|
|
5347
5450
|
|
|
5348
5451
|
// src/utils/index.ts
|
|
5452
|
+
function isNA(value) {
|
|
5453
|
+
return value === null || value === void 0 || Number.isNaN(value);
|
|
5454
|
+
}
|
|
5455
|
+
function nz(value, defaultValue = 0) {
|
|
5456
|
+
return isNA(value) ? defaultValue : value;
|
|
5457
|
+
}
|
|
5349
5458
|
function ohlcFromBars(bars) {
|
|
5350
5459
|
return {
|
|
5351
5460
|
open: bars.map((b2) => b2.open),
|
|
@@ -5366,6 +5475,229 @@ function getLow(ohlc) {
|
|
|
5366
5475
|
function getOpen(ohlc) {
|
|
5367
5476
|
return ohlc.open;
|
|
5368
5477
|
}
|
|
5478
|
+
function getSourceSeries(bars, source = "close") {
|
|
5479
|
+
const open = new Series(bars, (bar) => bar.open);
|
|
5480
|
+
const high = new Series(bars, (bar) => bar.high);
|
|
5481
|
+
const low = new Series(bars, (bar) => bar.low);
|
|
5482
|
+
const close = new Series(bars, (bar) => bar.close);
|
|
5483
|
+
switch (source) {
|
|
5484
|
+
case "open":
|
|
5485
|
+
return open;
|
|
5486
|
+
case "high":
|
|
5487
|
+
return high;
|
|
5488
|
+
case "low":
|
|
5489
|
+
return low;
|
|
5490
|
+
case "close":
|
|
5491
|
+
return close;
|
|
5492
|
+
case "hl2":
|
|
5493
|
+
return high.add(low).div(2);
|
|
5494
|
+
case "hlc3":
|
|
5495
|
+
return high.add(low).add(close).div(3);
|
|
5496
|
+
case "ohlc4":
|
|
5497
|
+
return open.add(high).add(low).add(close).div(4);
|
|
5498
|
+
case "hlcc4":
|
|
5499
|
+
return high.add(low).add(close).add(close).div(4);
|
|
5500
|
+
default:
|
|
5501
|
+
return close;
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5504
|
+
|
|
5505
|
+
// src/lib/zigzag/zigzag.ts
|
|
5506
|
+
var defaultSettings = {
|
|
5507
|
+
devThreshold: 5,
|
|
5508
|
+
depth: 10,
|
|
5509
|
+
lineColor: "#2962FF",
|
|
5510
|
+
extendLast: true,
|
|
5511
|
+
displayReversalPrice: true,
|
|
5512
|
+
displayCumulativeVolume: true,
|
|
5513
|
+
displayReversalPriceChange: true,
|
|
5514
|
+
differencePriceMode: "Absolute",
|
|
5515
|
+
allowZigZagOnOneBar: true
|
|
5516
|
+
};
|
|
5517
|
+
var ZigZag = class {
|
|
5518
|
+
settings;
|
|
5519
|
+
pivots = [];
|
|
5520
|
+
sumVol = 0;
|
|
5521
|
+
highBuffer = [];
|
|
5522
|
+
lowBuffer = [];
|
|
5523
|
+
timeBuffer = [];
|
|
5524
|
+
barCount = 0;
|
|
5525
|
+
constructor(settings) {
|
|
5526
|
+
this.settings = { ...defaultSettings, ...settings };
|
|
5527
|
+
}
|
|
5528
|
+
/**
|
|
5529
|
+
* Process a single bar, detecting pivots.
|
|
5530
|
+
* Must be called sequentially for each bar.
|
|
5531
|
+
* @returns true if a new pivot was detected or updated
|
|
5532
|
+
*/
|
|
5533
|
+
update(bar, barIndex) {
|
|
5534
|
+
this.highBuffer.push(bar.high);
|
|
5535
|
+
this.lowBuffer.push(bar.low);
|
|
5536
|
+
this.timeBuffer.push(bar.time);
|
|
5537
|
+
this.barCount++;
|
|
5538
|
+
const depth = Math.max(2, Math.floor(this.settings.depth / 2));
|
|
5539
|
+
this.sumVol += bar.volume ?? 0;
|
|
5540
|
+
if (this.barCount < depth * 2 + 1)
|
|
5541
|
+
return false;
|
|
5542
|
+
let somethingChanged = this.tryFindPivot(true, depth, barIndex);
|
|
5543
|
+
somethingChanged = this.tryFindPivot(
|
|
5544
|
+
false,
|
|
5545
|
+
depth,
|
|
5546
|
+
barIndex,
|
|
5547
|
+
this.settings.allowZigZagOnOneBar || !somethingChanged
|
|
5548
|
+
) || somethingChanged;
|
|
5549
|
+
return somethingChanged;
|
|
5550
|
+
}
|
|
5551
|
+
/**
|
|
5552
|
+
* Get the last confirmed pivot
|
|
5553
|
+
*/
|
|
5554
|
+
lastPivot() {
|
|
5555
|
+
if (this.pivots.length === 0)
|
|
5556
|
+
return null;
|
|
5557
|
+
const last2 = this.pivots[this.pivots.length - 1];
|
|
5558
|
+
return last2 !== void 0 ? last2 : null;
|
|
5559
|
+
}
|
|
5560
|
+
/**
|
|
5561
|
+
* Get extension line to current bar (if extendLast enabled)
|
|
5562
|
+
*/
|
|
5563
|
+
getExtension(currentBar, barIndex) {
|
|
5564
|
+
if (!this.settings.extendLast)
|
|
5565
|
+
return null;
|
|
5566
|
+
const last2 = this.lastPivot();
|
|
5567
|
+
if (!last2)
|
|
5568
|
+
return null;
|
|
5569
|
+
const isHigh = !last2.isHigh;
|
|
5570
|
+
const price = isHigh ? currentBar.high : currentBar.low;
|
|
5571
|
+
return {
|
|
5572
|
+
isHigh,
|
|
5573
|
+
volume: this.sumVol,
|
|
5574
|
+
start: last2.end,
|
|
5575
|
+
end: { time: currentBar.time, barIndex, price }
|
|
5576
|
+
};
|
|
5577
|
+
}
|
|
5578
|
+
// ============ Private Methods ============
|
|
5579
|
+
tryFindPivot(isHigh, depth, currentBarIndex, registerPivot = true) {
|
|
5580
|
+
const point = this.findPivotPoint(isHigh, depth, currentBarIndex);
|
|
5581
|
+
if (!point || !registerPivot)
|
|
5582
|
+
return false;
|
|
5583
|
+
return this.newPivotPointFound(isHigh, point);
|
|
5584
|
+
}
|
|
5585
|
+
/**
|
|
5586
|
+
* Check if a bar from `depth` bars back is a pivot point.
|
|
5587
|
+
* A pivot high has prices below it on both sides.
|
|
5588
|
+
* A pivot low has prices above it on both sides.
|
|
5589
|
+
*/
|
|
5590
|
+
findPivotPoint(isHigh, depth, currentBarIndex) {
|
|
5591
|
+
const buffer = isHigh ? this.highBuffer : this.lowBuffer;
|
|
5592
|
+
const pivotBufferIdx = buffer.length - 1 - depth;
|
|
5593
|
+
if (pivotBufferIdx < depth)
|
|
5594
|
+
return null;
|
|
5595
|
+
const pivotPrice = buffer[pivotBufferIdx];
|
|
5596
|
+
if (pivotPrice === void 0)
|
|
5597
|
+
return null;
|
|
5598
|
+
for (let i = pivotBufferIdx + 1; i < buffer.length; i++) {
|
|
5599
|
+
const price = buffer[i];
|
|
5600
|
+
if (price === void 0)
|
|
5601
|
+
return null;
|
|
5602
|
+
if (isHigh) {
|
|
5603
|
+
if (price > pivotPrice)
|
|
5604
|
+
return null;
|
|
5605
|
+
} else {
|
|
5606
|
+
if (price < pivotPrice)
|
|
5607
|
+
return null;
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
for (let i = pivotBufferIdx - depth; i < pivotBufferIdx; i++) {
|
|
5611
|
+
const price = buffer[i];
|
|
5612
|
+
if (price === void 0)
|
|
5613
|
+
return null;
|
|
5614
|
+
if (isHigh) {
|
|
5615
|
+
if (price >= pivotPrice)
|
|
5616
|
+
return null;
|
|
5617
|
+
} else {
|
|
5618
|
+
if (price <= pivotPrice)
|
|
5619
|
+
return null;
|
|
5620
|
+
}
|
|
5621
|
+
}
|
|
5622
|
+
const pivotBarIndex = currentBarIndex - depth;
|
|
5623
|
+
const pivotTime = this.timeBuffer[pivotBufferIdx];
|
|
5624
|
+
if (pivotTime === void 0)
|
|
5625
|
+
return null;
|
|
5626
|
+
return {
|
|
5627
|
+
time: pivotTime,
|
|
5628
|
+
barIndex: pivotBarIndex,
|
|
5629
|
+
price: pivotPrice
|
|
5630
|
+
};
|
|
5631
|
+
}
|
|
5632
|
+
/**
|
|
5633
|
+
* Calculate percentage deviation from base price to new price
|
|
5634
|
+
*/
|
|
5635
|
+
calcDev(basePrice, price) {
|
|
5636
|
+
return 100 * (price - basePrice) / Math.abs(basePrice);
|
|
5637
|
+
}
|
|
5638
|
+
/**
|
|
5639
|
+
* Handle a newly detected pivot point.
|
|
5640
|
+
* Either updates existing pivot (if same direction) or adds new pivot (if direction changed).
|
|
5641
|
+
*/
|
|
5642
|
+
newPivotPointFound(isHigh, point) {
|
|
5643
|
+
const last2 = this.lastPivot();
|
|
5644
|
+
if (!last2) {
|
|
5645
|
+
this.pivots.push({
|
|
5646
|
+
isHigh,
|
|
5647
|
+
volume: this.sumVol,
|
|
5648
|
+
start: point,
|
|
5649
|
+
end: point
|
|
5650
|
+
});
|
|
5651
|
+
this.sumVol = 0;
|
|
5652
|
+
return true;
|
|
5653
|
+
}
|
|
5654
|
+
if (last2.isHigh === isHigh) {
|
|
5655
|
+
const isMoreExtreme = isHigh ? point.price > last2.end.price : point.price < last2.end.price;
|
|
5656
|
+
if (isMoreExtreme) {
|
|
5657
|
+
last2.end = point;
|
|
5658
|
+
last2.volume += this.sumVol;
|
|
5659
|
+
this.sumVol = 0;
|
|
5660
|
+
return true;
|
|
5661
|
+
}
|
|
5662
|
+
} else {
|
|
5663
|
+
const dev2 = this.calcDev(last2.end.price, point.price);
|
|
5664
|
+
const threshold = this.settings.devThreshold;
|
|
5665
|
+
const meetsThreshold = last2.isHigh ? dev2 <= -threshold : dev2 >= threshold;
|
|
5666
|
+
if (meetsThreshold) {
|
|
5667
|
+
this.pivots.push({
|
|
5668
|
+
isHigh,
|
|
5669
|
+
volume: this.sumVol,
|
|
5670
|
+
start: last2.end,
|
|
5671
|
+
end: point
|
|
5672
|
+
});
|
|
5673
|
+
this.sumVol = 0;
|
|
5674
|
+
return true;
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
return false;
|
|
5678
|
+
}
|
|
5679
|
+
};
|
|
5680
|
+
function calculateZigZag(bars, settings) {
|
|
5681
|
+
if (bars.length === 0) {
|
|
5682
|
+
return { pivots: [], extension: null };
|
|
5683
|
+
}
|
|
5684
|
+
const zz = new ZigZag(settings);
|
|
5685
|
+
for (let i = 0; i < bars.length; i++) {
|
|
5686
|
+
const bar = bars[i];
|
|
5687
|
+
if (bar !== void 0) {
|
|
5688
|
+
zz.update(bar, i);
|
|
5689
|
+
}
|
|
5690
|
+
}
|
|
5691
|
+
const lastBar = bars[bars.length - 1];
|
|
5692
|
+
if (lastBar === void 0) {
|
|
5693
|
+
return { pivots: zz.pivots, extension: null };
|
|
5694
|
+
}
|
|
5695
|
+
const extension = zz.getExtension(lastBar, bars.length - 1);
|
|
5696
|
+
return {
|
|
5697
|
+
pivots: zz.pivots,
|
|
5698
|
+
extension
|
|
5699
|
+
};
|
|
5700
|
+
}
|
|
5369
5701
|
|
|
5370
5702
|
// src/indicator.ts
|
|
5371
5703
|
function indicator(metadata, setup) {
|
|
@@ -5485,7 +5817,7 @@ var input = {
|
|
|
5485
5817
|
defaultValue,
|
|
5486
5818
|
value: defaultValue,
|
|
5487
5819
|
title: options.title,
|
|
5488
|
-
options: ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4"]
|
|
5820
|
+
options: ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4", "hlcc4"]
|
|
5489
5821
|
};
|
|
5490
5822
|
},
|
|
5491
5823
|
/**
|
|
@@ -6081,18 +6413,23 @@ var info = {
|
|
|
6081
6413
|
indicator: ["indicator", "input", "plotHelper", "createPlot"]
|
|
6082
6414
|
}
|
|
6083
6415
|
};
|
|
6416
|
+
function alertcondition(_condition, _title, _message) {
|
|
6417
|
+
}
|
|
6084
6418
|
export {
|
|
6085
6419
|
BarData,
|
|
6086
6420
|
LightweightChartsAdapter,
|
|
6087
6421
|
Series,
|
|
6088
6422
|
SimpleInputAdapter,
|
|
6089
6423
|
VERSION,
|
|
6424
|
+
ZigZag,
|
|
6090
6425
|
abs,
|
|
6426
|
+
alertcondition,
|
|
6091
6427
|
array_exports as array,
|
|
6092
6428
|
atr2 as atr,
|
|
6093
6429
|
avg,
|
|
6094
6430
|
bb2 as bb,
|
|
6095
6431
|
box_exports as box,
|
|
6432
|
+
calculateZigZag,
|
|
6096
6433
|
ceil,
|
|
6097
6434
|
change2 as change,
|
|
6098
6435
|
chartpoint_exports as chartPoint,
|
|
@@ -6113,6 +6450,7 @@ export {
|
|
|
6113
6450
|
getHigh,
|
|
6114
6451
|
getLow,
|
|
6115
6452
|
getOpen,
|
|
6453
|
+
getSourceSeries,
|
|
6116
6454
|
hline,
|
|
6117
6455
|
indicator,
|
|
6118
6456
|
info,
|
|
@@ -6131,8 +6469,9 @@ export {
|
|
|
6131
6469
|
matrix_exports as matrix,
|
|
6132
6470
|
max2 as max,
|
|
6133
6471
|
min2 as min,
|
|
6134
|
-
na,
|
|
6472
|
+
isNA as na,
|
|
6135
6473
|
new_color2 as new_color,
|
|
6474
|
+
nz,
|
|
6136
6475
|
ohlcFromBars,
|
|
6137
6476
|
plot2 as plot,
|
|
6138
6477
|
plot as plotHelper,
|
|
@@ -6156,5 +6495,6 @@ export {
|
|
|
6156
6495
|
time_exports as time,
|
|
6157
6496
|
tr2 as tr,
|
|
6158
6497
|
vwap2 as vwap,
|
|
6159
|
-
xloc
|
|
6498
|
+
xloc,
|
|
6499
|
+
defaultSettings as zigZagDefaults
|
|
6160
6500
|
};
|
package/dist/input.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface StringInputOptions {
|
|
|
30
30
|
title?: string;
|
|
31
31
|
options?: string[];
|
|
32
32
|
}
|
|
33
|
-
export type SourceType = 'open' | 'high' | 'low' | 'close' | 'hl2' | 'hlc3' | 'ohlc4';
|
|
33
|
+
export type SourceType = 'open' | 'high' | 'low' | 'close' | 'hl2' | 'hlc3' | 'ohlc4' | 'hlcc4';
|
|
34
34
|
export declare const input: {
|
|
35
35
|
int(defaultValue: number, options?: IntInputOptions): InputValue<number>;
|
|
36
36
|
float(defaultValue: number, options?: FloatInputOptions): InputValue<number>;
|
package/dist/input.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU,CAAC,CAAC;IAE3B,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAErD,YAAY,EAAE,CAAC,CAAC;IAEhB,KAAK,EAAE,CAAC,CAAC;IAET,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAKD,MAAM,WAAW,eAAe;IAE9B,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,iBAAiB;IAEhC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,kBAAkB;IAEjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,gBAAgB;IAE/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,kBAAkB;IAEjC,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAKD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU,CAAC,CAAC;IAE3B,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAErD,YAAY,EAAE,CAAC,CAAC;IAEhB,KAAK,EAAE,CAAC,CAAC;IAET,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAKD,MAAM,WAAW,eAAe;IAE9B,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,iBAAiB;IAEhC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,kBAAkB;IAEjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,gBAAgB;IAE/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,kBAAkB;IAEjC,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAKD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAKhG,eAAO,MAAM,KAAK;sBAaE,MAAM,YAAW,eAAe,GAAQ,UAAU,CAAC,MAAM,CAAC;wBAwBxD,MAAM,YAAW,iBAAiB,GAAQ,UAAU,CAAC,MAAM,CAAC;0BAwB3D,UAAU,YAAqB,kBAAkB,GAAQ,UAAU,CAAC,UAAU,CAAC;uBAsBjF,OAAO,YAAW,gBAAgB,GAAQ,UAAU,CAAC,OAAO,CAAC;yBAqB3D,MAAM,YAAW,kBAAkB,GAAQ,UAAU,CAAC,MAAM,CAAC;CASnF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAOA,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/zigzag/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,MAAM,EACN,eAAe,EACf,eAAe,IAAI,cAAc,EACjC,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC"}
|