oakscriptjs 0.8.0 → 0.8.2

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 CHANGED
@@ -234,26 +234,86 @@ function ge(a, b2) {
234
234
  return valid(a, b2) && b2 - a <= EPSILON;
235
235
  }
236
236
 
237
- // src/ta/index.ts
238
- function sma(source, length2) {
239
- const result = [];
237
+ // src/ta/running-sum.ts
238
+ var view = new DataView(new ArrayBuffer(8));
239
+ function exponentOf(x) {
240
+ view.setFloat64(0, x);
241
+ const e2 = view.getUint16(0) >> 4 & 2047;
242
+ return e2 === 0 ? -1074 : e2 - 1023;
243
+ }
244
+ function compensationLost(x, c) {
245
+ if (c === 0 || x === 0) return false;
246
+ const ulp = 2 ** (exponentOf(x) - 52);
247
+ const q = c / ulp;
248
+ const mantissa = x / ulp;
249
+ const f = Math.floor(q);
250
+ const d = q - f;
251
+ const applied = d > 0.5 ? f + 1 : d < 0.5 ? f : (mantissa - f) % 2 === 0 ? f : f + 1;
252
+ return Math.abs(applied) < Math.abs(q);
253
+ }
254
+ function isNa(v) {
255
+ return v === void 0 || v === null || !Number.isFinite(v);
256
+ }
257
+ function runningSum(source, length2) {
240
258
  const len = Math.floor(length2);
241
- const window = [];
259
+ const out = new Array(source.length).fill(NaN);
260
+ if (!(len >= 1)) return out;
261
+ const values2 = [];
262
+ const stored = [];
263
+ let s = 0;
264
+ let c = 0;
265
+ let previous = NaN;
266
+ const kahanAdd = (v) => {
267
+ const y = v - c;
268
+ const t2 = s + y;
269
+ c = t2 - s - y;
270
+ s = t2;
271
+ return y;
272
+ };
242
273
  for (let i = 0; i < source.length; i++) {
243
274
  const v = source[i];
244
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
245
- window.push(v);
246
- if (window.length > len) window.shift();
275
+ if (isNa(v)) {
276
+ out[i] = previous;
277
+ continue;
247
278
  }
248
- if (window.length < len) {
249
- result.push(NaN);
279
+ const x = v;
280
+ const c0 = c;
281
+ const n = values2.length;
282
+ if (n >= len) kahanAdd(-stored[n - len]);
283
+ values2.push(x);
284
+ if (compensationLost(x, c0)) {
285
+ let fresh = 0;
286
+ for (let k = values2.length - 1; k >= Math.max(0, values2.length - len); k--) fresh += values2[k];
287
+ s = fresh;
288
+ c = 0;
289
+ stored.push(x);
250
290
  } else {
251
- let sum4 = 0;
252
- for (const x of window) sum4 += x;
253
- result.push(sum4 / len);
291
+ stored.push(kahanAdd(x));
254
292
  }
293
+ previous = values2.length >= len ? s : NaN;
294
+ out[i] = previous;
255
295
  }
256
- return result;
296
+ return out;
297
+ }
298
+ function runningVariance(source, length2, biased) {
299
+ const len = Math.floor(length2);
300
+ const squares = Array.from({ length: source.length }, (_, i) => {
301
+ const v = source[i];
302
+ return isNa(v) ? NaN : v * v;
303
+ });
304
+ const sums = runningSum(source, len);
305
+ const sumSquares = runningSum(squares, len);
306
+ return sums.map((sum4, i) => {
307
+ const mean = sum4 / len;
308
+ const sq = sumSquares[i];
309
+ return biased ? sq / len - mean * mean : sq / (len - 1) - mean * sum4 / (len - 1);
310
+ });
311
+ }
312
+
313
+ // src/ta/index.ts
314
+ function sma(source, length2) {
315
+ const len = Math.floor(length2);
316
+ return runningSum(source, len).map((sum4) => sum4 / len);
257
317
  }
258
318
  function strictWindowMean(source, length2) {
259
319
  const len = Math.floor(length2);
@@ -273,7 +333,7 @@ function ema(source, length2) {
273
333
  let initSum = 0;
274
334
  for (let i = 0; i < source.length; i++) {
275
335
  const val = source[i];
276
- if (val !== void 0 && !isNaN(val)) {
336
+ if (val !== void 0 && Number.isFinite(val)) {
277
337
  initSum += val;
278
338
  validCount++;
279
339
  if (validCount === len) {
@@ -291,7 +351,7 @@ function ema(source, length2) {
291
351
  result.push(emaValue);
292
352
  } else {
293
353
  const val = source[i];
294
- if (val !== void 0 && !isNaN(val)) {
354
+ if (val !== void 0 && Number.isFinite(val)) {
295
355
  emaValue = (val - emaValue) * multiplier + emaValue;
296
356
  result.push(emaValue);
297
357
  } else {
@@ -348,14 +408,7 @@ function bb(series, length2, mult2) {
348
408
  return [basis, upper2, lower2];
349
409
  }
350
410
  function stdev(source, length2, biased = true) {
351
- const len = Math.floor(length2);
352
- return source.map((_, i) => {
353
- const values2 = lastValues(source, i, len);
354
- if (!values2) return NaN;
355
- const mean = values2.reduce((a, b2) => a + b2, 0) / len;
356
- const squares = values2.reduce((a, b2) => a + (b2 - mean) * (b2 - mean), 0);
357
- return Math.sqrt(squares / (biased ? len : len - 1));
358
- });
411
+ return variance(source, length2, biased).map((v) => Number.isNaN(v) ? NaN : v > 0 ? Math.sqrt(v) : 0);
359
412
  }
360
413
  function lastValues(source, i, length2) {
361
414
  const values2 = [];
@@ -509,13 +562,12 @@ function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
509
562
  function rma(source, length2) {
510
563
  const result = [];
511
564
  const len = Math.floor(length2);
512
- const alpha = 1 / len;
513
565
  let firstValidIndex = -1;
514
566
  let validCount = 0;
515
567
  let initSum = 0;
516
568
  for (let i = 0; i < source.length; i++) {
517
569
  const val = source[i];
518
- if (val !== void 0 && !isNaN(val)) {
570
+ if (val !== void 0 && Number.isFinite(val)) {
519
571
  initSum += val;
520
572
  validCount++;
521
573
  if (validCount === len) {
@@ -533,8 +585,8 @@ function rma(source, length2) {
533
585
  result.push(rmaValue);
534
586
  } else {
535
587
  const val = source[i];
536
- if (val !== void 0 && !isNaN(val)) {
537
- rmaValue = alpha * val + (1 - alpha) * rmaValue;
588
+ if (val !== void 0 && Number.isFinite(val)) {
589
+ rmaValue = (val + (len - 1) * rmaValue) / len;
538
590
  result.push(rmaValue);
539
591
  } else {
540
592
  result.push(NaN);
@@ -548,18 +600,22 @@ function wma(source, length2) {
548
600
  const len = Math.floor(length2);
549
601
  const filled = [];
550
602
  let last2 = NaN;
603
+ let count = 0;
551
604
  for (let i = 0; i < source.length; i++) {
552
605
  const v = source[i];
553
- const isNa = v === void 0 || v === null || Number.isNaN(v);
554
- if (!isNa) last2 = v;
606
+ const isNa2 = v === void 0 || v === null || !Number.isFinite(v);
607
+ if (!isNa2) {
608
+ last2 = v;
609
+ count++;
610
+ }
555
611
  filled.push(last2);
556
- if (isNa || i < len - 1) {
612
+ if (isNa2 || count < len) {
557
613
  result.push(NaN);
558
614
  continue;
559
615
  }
560
616
  let sum4 = 0;
561
617
  let weightSum = 0;
562
- for (let j = 0; j < len; j++) {
618
+ for (let j = len - 1; j >= 0; j--) {
563
619
  const weight = len - j;
564
620
  sum4 += filled[i - j] * weight;
565
621
  weightSum += weight;
@@ -585,7 +641,7 @@ function lowest(source, length2) {
585
641
  function cum(source) {
586
642
  let sum4 = 0;
587
643
  return source.map((v) => {
588
- if (Number.isNaN(v)) return NaN;
644
+ if (!Number.isFinite(v)) return NaN;
589
645
  sum4 += v;
590
646
  return sum4;
591
647
  });
@@ -603,41 +659,24 @@ function cross(source1, source2) {
603
659
  }
604
660
  return result;
605
661
  }
606
- function rising(source, length2) {
662
+ function monotonic(source, length2, step) {
607
663
  const result = [];
664
+ const values2 = [];
608
665
  for (let i = 0; i < source.length; i++) {
609
- if (i < length2) {
610
- result.push(false);
611
- } else {
612
- let isRising = true;
613
- for (let j = 1; j <= length2; j++) {
614
- if (source[i - j + 1] <= source[i - j]) {
615
- isRising = false;
616
- break;
617
- }
618
- }
619
- result.push(isRising);
620
- }
666
+ const v = source[i];
667
+ if (!Number.isNaN(v)) values2.push(v);
668
+ const n = values2.length;
669
+ let ok = n > length2;
670
+ for (let j = 1; ok && j <= length2; j++) ok = step(values2[n - j], values2[n - j - 1]);
671
+ result.push(ok);
621
672
  }
622
673
  return result;
623
674
  }
675
+ function rising(source, length2) {
676
+ return monotonic(source, length2, gt);
677
+ }
624
678
  function falling(source, length2) {
625
- const result = [];
626
- for (let i = 0; i < source.length; i++) {
627
- if (i < length2) {
628
- result.push(false);
629
- } else {
630
- let isFalling = true;
631
- for (let j = 1; j <= length2; j++) {
632
- if (source[i - j + 1] >= source[i - j]) {
633
- isFalling = false;
634
- break;
635
- }
636
- }
637
- result.push(isFalling);
638
- }
639
- }
640
- return result;
679
+ return monotonic(source, length2, lt);
641
680
  }
642
681
  function roc(source, length2) {
643
682
  const result = [];
@@ -690,53 +729,19 @@ function dev(source, length2) {
690
729
  return result;
691
730
  }
692
731
  function variance(source, length2, biased = true) {
693
- const result = [];
694
732
  const len = Math.floor(length2);
695
- const window = [];
696
- for (let i = 0; i < source.length; i++) {
697
- const v = source[i];
698
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
699
- window.push(v);
700
- if (window.length > len) window.shift();
701
- }
702
- const divisor = biased ? len : len - 1;
703
- if (window.length < len || divisor <= 0) {
704
- result.push(NaN);
705
- continue;
706
- }
707
- const mean = window.reduce((a, x) => a + x, 0) / len;
708
- let sumSquares = 0;
709
- for (const x of window) sumSquares += (x - mean) * (x - mean);
710
- result.push(sumSquares / divisor);
711
- }
712
- return result;
733
+ if ((biased ? len : len - 1) <= 0) return source.map(() => NaN);
734
+ return runningVariance(source, len, biased);
713
735
  }
714
736
  function median(source, length2) {
715
- const result = [];
716
- for (let i = 0; i < source.length; i++) {
717
- if (i < length2 - 1) {
718
- result.push(NaN);
719
- } else {
720
- const values2 = [];
721
- for (let j = 0; j < length2; j++) {
722
- if (!isNaN(source[i - j])) {
723
- values2.push(source[i - j]);
724
- }
725
- }
726
- if (values2.length === 0) {
727
- result.push(NaN);
728
- } else {
729
- values2.sort((a, b2) => a - b2);
730
- const mid = Math.floor(values2.length / 2);
731
- if (values2.length % 2 === 0) {
732
- result.push((values2[mid - 1] + values2[mid]) / 2);
733
- } else {
734
- result.push(values2[mid]);
735
- }
736
- }
737
- }
738
- }
739
- return result;
737
+ const len = Math.floor(length2);
738
+ return source.map((_, i) => {
739
+ const values2 = lastValues(source, i, len);
740
+ if (!values2) return NaN;
741
+ values2.sort((a, b2) => a - b2);
742
+ const mid = Math.floor(len / 2);
743
+ return len % 2 === 0 ? (values2[mid - 1] + values2[mid]) / 2 : values2[mid];
744
+ });
740
745
  }
741
746
  function swma(source) {
742
747
  const result = [];
@@ -861,33 +866,16 @@ function correlation(source1, source2, length2) {
861
866
  function percentrank(source, length2) {
862
867
  const result = [];
863
868
  for (let i = 0; i < source.length; i++) {
864
- if (i < length2) {
869
+ const currentValue = source[i];
870
+ if (i < length2 || Number.isNaN(currentValue)) {
865
871
  result.push(NaN);
866
- } else {
867
- if (isNaN(source[i])) {
868
- result.push(NaN);
869
- continue;
870
- }
871
- let hasNaN = false;
872
- for (let j = 1; j <= length2; j++) {
873
- if (isNaN(source[i - j])) {
874
- hasNaN = true;
875
- break;
876
- }
877
- }
878
- if (hasNaN) {
879
- result.push(NaN);
880
- } else {
881
- const currentValue = source[i];
882
- let countLessOrEqual = 0;
883
- for (let j = 1; j <= length2; j++) {
884
- if (source[i - j] <= currentValue) {
885
- countLessOrEqual++;
886
- }
887
- }
888
- result.push(countLessOrEqual / length2 * 100);
889
- }
872
+ continue;
890
873
  }
874
+ let countLessOrEqual = 0;
875
+ for (let j = 1; j <= length2; j++) {
876
+ if (le(source[i - j], currentValue)) countLessOrEqual++;
877
+ }
878
+ result.push(countLessOrEqual / length2 * 100);
891
879
  }
892
880
  return result;
893
881
  }
@@ -938,12 +926,8 @@ function mfi(source, length2, volume) {
938
926
  upperFlow.push(le(change3, 0) ? 0 : flow);
939
927
  lowerFlow.push(ge(change3, 0) ? 0 : flow);
940
928
  }
941
- const sumAt = (flow, i) => {
942
- const values2 = lastValues(flow, i, Math.floor(length2));
943
- return values2 ? values2.reduce((a, b2) => a + b2, 0) : NaN;
944
- };
945
- const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
946
- const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
929
+ const upper2 = runningSum(upperFlow, length2);
930
+ const lower2 = runningSum(lowerFlow, length2);
947
931
  return upper2.map((u, i) => {
948
932
  const l = lower2[i];
949
933
  if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
@@ -1414,63 +1398,36 @@ function mode(source, length2) {
1414
1398
  return result;
1415
1399
  }
1416
1400
  function percentile_linear_interpolation(source, length2, percentage) {
1401
+ return sortedWindow(source, length2, (s) => {
1402
+ const pos2 = percentage / 100 * length2 - 0.5;
1403
+ if (pos2 <= 0) return s[0];
1404
+ if (pos2 >= length2 - 1) return s[length2 - 1];
1405
+ const lo = Math.floor(pos2);
1406
+ return s[lo] + (pos2 - lo) * (s[lo + 1] - s[lo]);
1407
+ });
1408
+ }
1409
+ function sortedWindow(source, length2, fn) {
1417
1410
  const result = [];
1411
+ const values2 = [];
1412
+ const bars = [];
1418
1413
  for (let i = 0; i < source.length; i++) {
1419
- if (i < length2 - 1) {
1420
- result.push(NaN);
1421
- continue;
1422
- }
1423
- const values2 = [];
1424
- for (let j = 0; j < length2; j++) {
1425
- const value = source[i - j];
1426
- values2.push(value);
1427
- }
1428
- if (values2.some((v) => isNaN(v))) {
1429
- result.push(NaN);
1430
- continue;
1431
- }
1432
- const sorted = [...values2].sort((a, b2) => a - b2);
1433
- const position = percentage / 100 * (sorted.length - 1);
1434
- const lowerIndex = Math.floor(position);
1435
- const upperIndex = Math.ceil(position);
1436
- if (lowerIndex === upperIndex) {
1437
- result.push(sorted[lowerIndex]);
1438
- } else {
1439
- const fraction = position - lowerIndex;
1440
- const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
1441
- result.push(interpolated);
1414
+ const v = source[i];
1415
+ let k = 0;
1416
+ while (k < values2.length && !(values2[k] >= v)) k++;
1417
+ values2.splice(k, 0, v);
1418
+ bars.splice(k, 0, i);
1419
+ if (i >= length2) {
1420
+ const old = bars.indexOf(i - length2);
1421
+ values2.splice(old, 1);
1422
+ bars.splice(old, 1);
1442
1423
  }
1424
+ result.push(i < length2 - 1 ? NaN : fn(values2));
1443
1425
  }
1444
1426
  return result;
1445
1427
  }
1446
1428
  function percentile_nearest_rank(source, length2, percentage) {
1447
- const result = [];
1448
- for (let i = 0; i < source.length; i++) {
1449
- if (i < length2 - 1) {
1450
- result.push(NaN);
1451
- continue;
1452
- }
1453
- const values2 = [];
1454
- for (let j = 0; j < length2; j++) {
1455
- const value = source[i - j];
1456
- if (!isNaN(value)) {
1457
- values2.push(value);
1458
- }
1459
- }
1460
- if (values2.length === 0) {
1461
- result.push(NaN);
1462
- continue;
1463
- }
1464
- const sorted = [...values2].sort((a, b2) => a - b2);
1465
- if (percentage >= 100) {
1466
- result.push(sorted[sorted.length - 1]);
1467
- continue;
1468
- }
1469
- const rank2 = Math.ceil(percentage / 100 * sorted.length);
1470
- const index = Math.max(0, rank2 - 1);
1471
- result.push(sorted[index]);
1472
- }
1473
- return result;
1429
+ const rank2 = Math.min(length2, Math.max(1, Math.ceil(percentage / 100 * length2)));
1430
+ return sortedWindow(source, length2, (s) => s[rank2 - 1]);
1474
1431
  }
1475
1432
  function rci(source, length2) {
1476
1433
  const result = [];
@@ -2188,7 +2145,7 @@ var Series = class _Series {
2188
2145
  return new _Series(this.dataSource, (bar, i, data) => {
2189
2146
  const a = this.extractor(bar, i, data);
2190
2147
  const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
2191
- return b2 !== 0 ? a / b2 : NaN;
2148
+ return a / b2;
2192
2149
  });
2193
2150
  }
2194
2151
  /**
@@ -2611,23 +2568,7 @@ function avg(...values2) {
2611
2568
  }
2612
2569
  function sum(source, length2) {
2613
2570
  const sourceArray = source instanceof Series ? source.toArray() : source;
2614
- const result = [];
2615
- const len = Math.floor(length2);
2616
- const window = [];
2617
- for (let i = 0; i < sourceArray.length; i++) {
2618
- const v = sourceArray[i];
2619
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
2620
- window.push(v);
2621
- if (window.length > len) window.shift();
2622
- }
2623
- if (window.length < len) {
2624
- result.push(NaN);
2625
- } else {
2626
- let total = 0;
2627
- for (const x of window) total += x;
2628
- result.push(total);
2629
- }
2630
- }
2571
+ const result = runningSum(sourceArray, length2);
2631
2572
  if (source instanceof Series) {
2632
2573
  return Series.fromArray(source.bars, result);
2633
2574
  }
@@ -3195,15 +3136,27 @@ function percentile_linear_interpolation2(id, percentage) {
3195
3136
  if (id.length === 0) {
3196
3137
  return NaN;
3197
3138
  }
3198
- const result = percentile_linear_interpolation(id, id.length, percentage);
3199
- return result[result.length - 1];
3139
+ const s = sortedNaLast(id);
3140
+ const n = s.length;
3141
+ const pos2 = percentage / 100 * n - 0.5;
3142
+ if (pos2 <= 0) return s[0];
3143
+ if (pos2 >= n - 1) return s[n - 1];
3144
+ const lo = Math.floor(pos2);
3145
+ const f = pos2 - lo;
3146
+ if (f === 0) return s[lo];
3147
+ return Number.isNaN(s[n - 1]) ? NaN : s[lo] + f * (s[lo + 1] - s[lo]);
3148
+ }
3149
+ function sortedNaLast(id) {
3150
+ const numbers = id.filter((v) => !Number.isNaN(v)).sort((a, b2) => a - b2);
3151
+ return [...numbers, ...Array(id.length - numbers.length).fill(NaN)];
3200
3152
  }
3201
3153
  function percentile_nearest_rank2(id, percentage) {
3202
3154
  if (id.length === 0) {
3203
3155
  return NaN;
3204
3156
  }
3205
- const result = percentile_nearest_rank(id, id.length, percentage);
3206
- return result[result.length - 1];
3157
+ const s = sortedNaLast(id);
3158
+ const rank2 = Math.min(s.length, Math.max(1, Math.ceil(percentage / 100 * s.length)));
3159
+ return s[rank2 - 1];
3207
3160
  }
3208
3161
  function percentrank2(id, index) {
3209
3162
  if (id.length === 0 || index < 0 || index >= id.length) {
@@ -3400,9 +3353,9 @@ function shortestDigits(x) {
3400
3353
  function exactDigits(x) {
3401
3354
  const abs3 = Math.abs(x);
3402
3355
  if (abs3 === 0) return { digits: "", intLen: 0 };
3403
- const view = new DataView(new ArrayBuffer(8));
3404
- view.setFloat64(0, abs3);
3405
- const bits = view.getBigUint64(0);
3356
+ const view2 = new DataView(new ArrayBuffer(8));
3357
+ view2.setFloat64(0, abs3);
3358
+ const bits = view2.getBigUint64(0);
3406
3359
  const expBits = Number(bits >> 52n & 0x7ffn);
3407
3360
  let mant = bits & 0xfffffffffffffn;
3408
3361
  const e2 = expBits === 0 ? -1074 : expBits - 1075;
@@ -3914,11 +3867,13 @@ __export(color_exports, {
3914
3867
  white: () => white,
3915
3868
  yellow: () => yellow
3916
3869
  });
3870
+ var channel = (v) => v === null || v === void 0 || Number.isNaN(v) ? 0 : Math.max(0, Math.min(255, Math.trunc(v)));
3871
+ var alphaOf = (transp) => transp === null || Number.isNaN(transp) ? 0 : 1 - Math.max(0, Math.min(100, transp)) / 100;
3917
3872
  function rgb(red2, green2, blue2, transp) {
3918
- const r2 = Math.max(0, Math.min(255, red2));
3919
- const g2 = Math.max(0, Math.min(255, green2));
3920
- const b2 = Math.max(0, Math.min(255, blue2));
3921
- const a = transp !== void 0 ? 1 - transp / 100 : 1;
3873
+ const r2 = channel(red2);
3874
+ const g2 = channel(green2);
3875
+ const b2 = channel(blue2);
3876
+ const a = transp !== void 0 ? alphaOf(transp) : 1;
3922
3877
  if (a === 1) {
3923
3878
  return `rgb(${r2}, ${g2}, ${b2})`;
3924
3879
  }
@@ -3945,7 +3900,8 @@ function b(clr) {
3945
3900
  return parseColor(clr).b;
3946
3901
  }
3947
3902
  function t(clr) {
3948
- return parseColor(clr).t;
3903
+ const alpha = Math.round((1 - parseColor(clr).t / 100) * 255);
3904
+ return Math.round((1 - alpha / 255) * 100);
3949
3905
  }
3950
3906
  function parseColor(clr) {
3951
3907
  if (typeof clr === "string") {
@@ -3998,13 +3954,12 @@ function from_gradient(value, bottom_value, top_value, bottom_color, top_color)
3998
3954
  };
3999
3955
  const c1 = rgba(bottom_color);
4000
3956
  const c2 = rgba(top_color);
4001
- const w1 = c1.a * (1 - k);
4002
- const w2 = c2.a * k;
4003
- const a = w1 + w2;
3957
+ const a = c1.a * (1 - k) + c2.a * k;
4004
3958
  if (a === 0) return transparent;
4005
- const mix = (x1, x2) => Math.floor((x1 * w1 + x2 * w2) / a);
3959
+ const mix = (x1, x2) => Math.floor((x1 / 255 * c1.a * (1 - k) + x2 / 255 * c2.a * k) / (a / 255));
4006
3960
  const [r2, g2, b2] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
4007
- return a === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${a / 255})`;
3961
+ const alpha = Math.floor(a);
3962
+ return alpha === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${alpha / 255})`;
4008
3963
  }
4009
3964
 
4010
3965
  // src/time/index.ts
@@ -6215,6 +6170,7 @@ var format_italic = 2;
6215
6170
  // src/callsite/index.ts
6216
6171
  var callsite_exports = {};
6217
6172
  __export(callsite_exports, {
6173
+ barssince: () => barssince2,
6218
6174
  cross: () => cross2,
6219
6175
  crossover: () => crossover2,
6220
6176
  crossunder: () => crossunder2,
@@ -6246,6 +6202,14 @@ function crossover2() {
6246
6202
  function crossunder2() {
6247
6203
  return twoSeriesSite((a, b2, pa, pb) => a < b2 && pa >= pb);
6248
6204
  }
6205
+ function barssince2() {
6206
+ let count = NaN;
6207
+ return (condition) => {
6208
+ if (condition && !Number.isNaN(condition)) count = 0;
6209
+ else if (!Number.isNaN(count)) count++;
6210
+ return count;
6211
+ };
6212
+ }
6249
6213
  function cross2() {
6250
6214
  return twoSeriesSite((a, b2, pa, pb) => a > b2 && pa <= pb || a < b2 && pa >= pb);
6251
6215
  }
@@ -6256,7 +6220,7 @@ __export(ta_series_exports, {
6256
6220
  accdist: () => accdist2,
6257
6221
  alma: () => alma2,
6258
6222
  atr: () => atr2,
6259
- barssince: () => barssince2,
6223
+ barssince: () => barssince3,
6260
6224
  bb: () => bb2,
6261
6225
  bbw: () => bbw2,
6262
6226
  cci: () => cci2,
@@ -6644,7 +6608,7 @@ function pivotlow2(source, leftbars, rightbars) {
6644
6608
  const result = pivotlow(source.toArray(), leftbars, rightbars);
6645
6609
  return Series.fromArray(bars, result);
6646
6610
  }
6647
- function barssince2(condition) {
6611
+ function barssince3(condition) {
6648
6612
  const bars = condition.bars;
6649
6613
  const boolValues = condition.toArray().map((v) => v === 1);
6650
6614
  const result = barssince(boolValues);
@@ -6796,7 +6760,7 @@ var ta = {
6796
6760
  hma: hma2,
6797
6761
  pivothigh: pivothigh2,
6798
6762
  pivotlow: pivotlow2,
6799
- barssince: barssince2,
6763
+ barssince: barssince3,
6800
6764
  valuewhen: valuewhen2,
6801
6765
  dmi: dmi2,
6802
6766
  tsi: tsi2,
@@ -6828,7 +6792,7 @@ var ta = {
6828
6792
 
6829
6793
  // src/utils/index.ts
6830
6794
  function isNA(value) {
6831
- return value === null || value === void 0 || Number.isNaN(value) || isDeleted(value);
6795
+ return value === null || value === void 0 || Number.isNaN(value) || value === Infinity || value === -Infinity || isDeleted(value);
6832
6796
  }
6833
6797
  function nz(value, defaultValue = 0) {
6834
6798
  return isNA(value) ? defaultValue : value;
@@ -6837,7 +6801,7 @@ function fixnan(source) {
6837
6801
  let last2;
6838
6802
  let seen = false;
6839
6803
  return source.map((v) => {
6840
- if (!isNA(v)) {
6804
+ if (!(v === null || v === void 0 || Number.isNaN(v) || isDeleted(v))) {
6841
6805
  last2 = v;
6842
6806
  seen = true;
6843
6807
  return v;
@@ -6853,7 +6817,7 @@ function at(source, i, offset = 0) {
6853
6817
  return j >= 0 && j < source.length ? source[j] ?? NaN : NaN;
6854
6818
  }
6855
6819
  function div(a, b2) {
6856
- return b2 === 0 ? NaN : a / b2;
6820
+ return a / b2;
6857
6821
  }
6858
6822
  function ohlcFromBars(bars) {
6859
6823
  return {
@@ -7487,7 +7451,7 @@ function hline(price, title, color, linestyle, linewidth) {
7487
7451
  const options = {
7488
7452
  color,
7489
7453
  lineWidth: linewidth,
7490
- lineStyle: getLineStyle(linestyle)
7454
+ lineStyle: getLineStyle(linestyle ?? "dashed")
7491
7455
  };
7492
7456
  const seriesHandle = context.chart.addSeries("line", options);
7493
7457
  const data = context.ohlcv.time.map((time) => ({
@@ -7734,9 +7698,10 @@ var STRATEGY_DEFAULTS = {
7734
7698
  calc_on_order_fills: false,
7735
7699
  calc_on_every_tick: false,
7736
7700
  backtest_fill_limits_assumption: 0,
7737
- default_qty_type: "fixed",
7738
- default_qty_value: 1,
7739
- initial_capital: 1e6,
7701
+ // v6 values; PineScript v5: fixed / 1 / 1000000 and margins 0
7702
+ default_qty_type: "percent_of_equity",
7703
+ default_qty_value: 100,
7704
+ initial_capital: 1e5,
7740
7705
  slippage: 0,
7741
7706
  commission_type: "percent",
7742
7707
  commission_value: 0,
@@ -7992,7 +7957,7 @@ var SimpleInputAdapter = class {
7992
7957
  };
7993
7958
 
7994
7959
  // src/index.ts
7995
- var VERSION = "0.8.0";
7960
+ var VERSION = "0.8.2";
7996
7961
  var info2 = {
7997
7962
  name: "OakScriptJS",
7998
7963
  version: VERSION,