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.mjs CHANGED
@@ -118,26 +118,86 @@ function ge(a, b2) {
118
118
  return valid(a, b2) && b2 - a <= EPSILON;
119
119
  }
120
120
 
121
- // src/ta/index.ts
122
- function sma(source, length2) {
123
- const result = [];
121
+ // src/ta/running-sum.ts
122
+ var view = new DataView(new ArrayBuffer(8));
123
+ function exponentOf(x) {
124
+ view.setFloat64(0, x);
125
+ const e2 = view.getUint16(0) >> 4 & 2047;
126
+ return e2 === 0 ? -1074 : e2 - 1023;
127
+ }
128
+ function compensationLost(x, c) {
129
+ if (c === 0 || x === 0) return false;
130
+ const ulp = 2 ** (exponentOf(x) - 52);
131
+ const q = c / ulp;
132
+ const mantissa = x / ulp;
133
+ const f = Math.floor(q);
134
+ const d = q - f;
135
+ const applied = d > 0.5 ? f + 1 : d < 0.5 ? f : (mantissa - f) % 2 === 0 ? f : f + 1;
136
+ return Math.abs(applied) < Math.abs(q);
137
+ }
138
+ function isNa(v) {
139
+ return v === void 0 || v === null || !Number.isFinite(v);
140
+ }
141
+ function runningSum(source, length2) {
124
142
  const len = Math.floor(length2);
125
- const window = [];
143
+ const out = new Array(source.length).fill(NaN);
144
+ if (!(len >= 1)) return out;
145
+ const values2 = [];
146
+ const stored = [];
147
+ let s = 0;
148
+ let c = 0;
149
+ let previous = NaN;
150
+ const kahanAdd = (v) => {
151
+ const y = v - c;
152
+ const t2 = s + y;
153
+ c = t2 - s - y;
154
+ s = t2;
155
+ return y;
156
+ };
126
157
  for (let i = 0; i < source.length; i++) {
127
158
  const v = source[i];
128
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
129
- window.push(v);
130
- if (window.length > len) window.shift();
159
+ if (isNa(v)) {
160
+ out[i] = previous;
161
+ continue;
131
162
  }
132
- if (window.length < len) {
133
- result.push(NaN);
163
+ const x = v;
164
+ const c0 = c;
165
+ const n = values2.length;
166
+ if (n >= len) kahanAdd(-stored[n - len]);
167
+ values2.push(x);
168
+ if (compensationLost(x, c0)) {
169
+ let fresh = 0;
170
+ for (let k = values2.length - 1; k >= Math.max(0, values2.length - len); k--) fresh += values2[k];
171
+ s = fresh;
172
+ c = 0;
173
+ stored.push(x);
134
174
  } else {
135
- let sum4 = 0;
136
- for (const x of window) sum4 += x;
137
- result.push(sum4 / len);
175
+ stored.push(kahanAdd(x));
138
176
  }
177
+ previous = values2.length >= len ? s : NaN;
178
+ out[i] = previous;
139
179
  }
140
- return result;
180
+ return out;
181
+ }
182
+ function runningVariance(source, length2, biased) {
183
+ const len = Math.floor(length2);
184
+ const squares = Array.from({ length: source.length }, (_, i) => {
185
+ const v = source[i];
186
+ return isNa(v) ? NaN : v * v;
187
+ });
188
+ const sums = runningSum(source, len);
189
+ const sumSquares = runningSum(squares, len);
190
+ return sums.map((sum4, i) => {
191
+ const mean = sum4 / len;
192
+ const sq = sumSquares[i];
193
+ return biased ? sq / len - mean * mean : sq / (len - 1) - mean * sum4 / (len - 1);
194
+ });
195
+ }
196
+
197
+ // src/ta/index.ts
198
+ function sma(source, length2) {
199
+ const len = Math.floor(length2);
200
+ return runningSum(source, len).map((sum4) => sum4 / len);
141
201
  }
142
202
  function strictWindowMean(source, length2) {
143
203
  const len = Math.floor(length2);
@@ -157,7 +217,7 @@ function ema(source, length2) {
157
217
  let initSum = 0;
158
218
  for (let i = 0; i < source.length; i++) {
159
219
  const val = source[i];
160
- if (val !== void 0 && !isNaN(val)) {
220
+ if (val !== void 0 && Number.isFinite(val)) {
161
221
  initSum += val;
162
222
  validCount++;
163
223
  if (validCount === len) {
@@ -175,7 +235,7 @@ function ema(source, length2) {
175
235
  result.push(emaValue);
176
236
  } else {
177
237
  const val = source[i];
178
- if (val !== void 0 && !isNaN(val)) {
238
+ if (val !== void 0 && Number.isFinite(val)) {
179
239
  emaValue = (val - emaValue) * multiplier + emaValue;
180
240
  result.push(emaValue);
181
241
  } else {
@@ -232,14 +292,7 @@ function bb(series, length2, mult2) {
232
292
  return [basis, upper2, lower2];
233
293
  }
234
294
  function stdev(source, length2, biased = true) {
235
- const len = Math.floor(length2);
236
- return source.map((_, i) => {
237
- const values2 = lastValues(source, i, len);
238
- if (!values2) return NaN;
239
- const mean = values2.reduce((a, b2) => a + b2, 0) / len;
240
- const squares = values2.reduce((a, b2) => a + (b2 - mean) * (b2 - mean), 0);
241
- return Math.sqrt(squares / (biased ? len : len - 1));
242
- });
295
+ return variance(source, length2, biased).map((v) => Number.isNaN(v) ? NaN : v > 0 ? Math.sqrt(v) : 0);
243
296
  }
244
297
  function lastValues(source, i, length2) {
245
298
  const values2 = [];
@@ -393,13 +446,12 @@ function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
393
446
  function rma(source, length2) {
394
447
  const result = [];
395
448
  const len = Math.floor(length2);
396
- const alpha = 1 / len;
397
449
  let firstValidIndex = -1;
398
450
  let validCount = 0;
399
451
  let initSum = 0;
400
452
  for (let i = 0; i < source.length; i++) {
401
453
  const val = source[i];
402
- if (val !== void 0 && !isNaN(val)) {
454
+ if (val !== void 0 && Number.isFinite(val)) {
403
455
  initSum += val;
404
456
  validCount++;
405
457
  if (validCount === len) {
@@ -417,8 +469,8 @@ function rma(source, length2) {
417
469
  result.push(rmaValue);
418
470
  } else {
419
471
  const val = source[i];
420
- if (val !== void 0 && !isNaN(val)) {
421
- rmaValue = alpha * val + (1 - alpha) * rmaValue;
472
+ if (val !== void 0 && Number.isFinite(val)) {
473
+ rmaValue = (val + (len - 1) * rmaValue) / len;
422
474
  result.push(rmaValue);
423
475
  } else {
424
476
  result.push(NaN);
@@ -432,18 +484,22 @@ function wma(source, length2) {
432
484
  const len = Math.floor(length2);
433
485
  const filled = [];
434
486
  let last2 = NaN;
487
+ let count = 0;
435
488
  for (let i = 0; i < source.length; i++) {
436
489
  const v = source[i];
437
- const isNa = v === void 0 || v === null || Number.isNaN(v);
438
- if (!isNa) last2 = v;
490
+ const isNa2 = v === void 0 || v === null || !Number.isFinite(v);
491
+ if (!isNa2) {
492
+ last2 = v;
493
+ count++;
494
+ }
439
495
  filled.push(last2);
440
- if (isNa || i < len - 1) {
496
+ if (isNa2 || count < len) {
441
497
  result.push(NaN);
442
498
  continue;
443
499
  }
444
500
  let sum4 = 0;
445
501
  let weightSum = 0;
446
- for (let j = 0; j < len; j++) {
502
+ for (let j = len - 1; j >= 0; j--) {
447
503
  const weight = len - j;
448
504
  sum4 += filled[i - j] * weight;
449
505
  weightSum += weight;
@@ -469,7 +525,7 @@ function lowest(source, length2) {
469
525
  function cum(source) {
470
526
  let sum4 = 0;
471
527
  return source.map((v) => {
472
- if (Number.isNaN(v)) return NaN;
528
+ if (!Number.isFinite(v)) return NaN;
473
529
  sum4 += v;
474
530
  return sum4;
475
531
  });
@@ -487,41 +543,24 @@ function cross(source1, source2) {
487
543
  }
488
544
  return result;
489
545
  }
490
- function rising(source, length2) {
546
+ function monotonic(source, length2, step) {
491
547
  const result = [];
548
+ const values2 = [];
492
549
  for (let i = 0; i < source.length; i++) {
493
- if (i < length2) {
494
- result.push(false);
495
- } else {
496
- let isRising = true;
497
- for (let j = 1; j <= length2; j++) {
498
- if (source[i - j + 1] <= source[i - j]) {
499
- isRising = false;
500
- break;
501
- }
502
- }
503
- result.push(isRising);
504
- }
550
+ const v = source[i];
551
+ if (!Number.isNaN(v)) values2.push(v);
552
+ const n = values2.length;
553
+ let ok = n > length2;
554
+ for (let j = 1; ok && j <= length2; j++) ok = step(values2[n - j], values2[n - j - 1]);
555
+ result.push(ok);
505
556
  }
506
557
  return result;
507
558
  }
559
+ function rising(source, length2) {
560
+ return monotonic(source, length2, gt);
561
+ }
508
562
  function falling(source, length2) {
509
- const result = [];
510
- for (let i = 0; i < source.length; i++) {
511
- if (i < length2) {
512
- result.push(false);
513
- } else {
514
- let isFalling = true;
515
- for (let j = 1; j <= length2; j++) {
516
- if (source[i - j + 1] >= source[i - j]) {
517
- isFalling = false;
518
- break;
519
- }
520
- }
521
- result.push(isFalling);
522
- }
523
- }
524
- return result;
563
+ return monotonic(source, length2, lt);
525
564
  }
526
565
  function roc(source, length2) {
527
566
  const result = [];
@@ -574,53 +613,19 @@ function dev(source, length2) {
574
613
  return result;
575
614
  }
576
615
  function variance(source, length2, biased = true) {
577
- const result = [];
578
616
  const len = Math.floor(length2);
579
- const window = [];
580
- for (let i = 0; i < source.length; i++) {
581
- const v = source[i];
582
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
583
- window.push(v);
584
- if (window.length > len) window.shift();
585
- }
586
- const divisor = biased ? len : len - 1;
587
- if (window.length < len || divisor <= 0) {
588
- result.push(NaN);
589
- continue;
590
- }
591
- const mean = window.reduce((a, x) => a + x, 0) / len;
592
- let sumSquares = 0;
593
- for (const x of window) sumSquares += (x - mean) * (x - mean);
594
- result.push(sumSquares / divisor);
595
- }
596
- return result;
617
+ if ((biased ? len : len - 1) <= 0) return source.map(() => NaN);
618
+ return runningVariance(source, len, biased);
597
619
  }
598
620
  function median(source, length2) {
599
- const result = [];
600
- for (let i = 0; i < source.length; i++) {
601
- if (i < length2 - 1) {
602
- result.push(NaN);
603
- } else {
604
- const values2 = [];
605
- for (let j = 0; j < length2; j++) {
606
- if (!isNaN(source[i - j])) {
607
- values2.push(source[i - j]);
608
- }
609
- }
610
- if (values2.length === 0) {
611
- result.push(NaN);
612
- } else {
613
- values2.sort((a, b2) => a - b2);
614
- const mid = Math.floor(values2.length / 2);
615
- if (values2.length % 2 === 0) {
616
- result.push((values2[mid - 1] + values2[mid]) / 2);
617
- } else {
618
- result.push(values2[mid]);
619
- }
620
- }
621
- }
622
- }
623
- return result;
621
+ const len = Math.floor(length2);
622
+ return source.map((_, i) => {
623
+ const values2 = lastValues(source, i, len);
624
+ if (!values2) return NaN;
625
+ values2.sort((a, b2) => a - b2);
626
+ const mid = Math.floor(len / 2);
627
+ return len % 2 === 0 ? (values2[mid - 1] + values2[mid]) / 2 : values2[mid];
628
+ });
624
629
  }
625
630
  function swma(source) {
626
631
  const result = [];
@@ -745,33 +750,16 @@ function correlation(source1, source2, length2) {
745
750
  function percentrank(source, length2) {
746
751
  const result = [];
747
752
  for (let i = 0; i < source.length; i++) {
748
- if (i < length2) {
753
+ const currentValue = source[i];
754
+ if (i < length2 || Number.isNaN(currentValue)) {
749
755
  result.push(NaN);
750
- } else {
751
- if (isNaN(source[i])) {
752
- result.push(NaN);
753
- continue;
754
- }
755
- let hasNaN = false;
756
- for (let j = 1; j <= length2; j++) {
757
- if (isNaN(source[i - j])) {
758
- hasNaN = true;
759
- break;
760
- }
761
- }
762
- if (hasNaN) {
763
- result.push(NaN);
764
- } else {
765
- const currentValue = source[i];
766
- let countLessOrEqual = 0;
767
- for (let j = 1; j <= length2; j++) {
768
- if (source[i - j] <= currentValue) {
769
- countLessOrEqual++;
770
- }
771
- }
772
- result.push(countLessOrEqual / length2 * 100);
773
- }
756
+ continue;
774
757
  }
758
+ let countLessOrEqual = 0;
759
+ for (let j = 1; j <= length2; j++) {
760
+ if (le(source[i - j], currentValue)) countLessOrEqual++;
761
+ }
762
+ result.push(countLessOrEqual / length2 * 100);
775
763
  }
776
764
  return result;
777
765
  }
@@ -822,12 +810,8 @@ function mfi(source, length2, volume) {
822
810
  upperFlow.push(le(change3, 0) ? 0 : flow);
823
811
  lowerFlow.push(ge(change3, 0) ? 0 : flow);
824
812
  }
825
- const sumAt = (flow, i) => {
826
- const values2 = lastValues(flow, i, Math.floor(length2));
827
- return values2 ? values2.reduce((a, b2) => a + b2, 0) : NaN;
828
- };
829
- const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
830
- const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
813
+ const upper2 = runningSum(upperFlow, length2);
814
+ const lower2 = runningSum(lowerFlow, length2);
831
815
  return upper2.map((u, i) => {
832
816
  const l = lower2[i];
833
817
  if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
@@ -1298,63 +1282,36 @@ function mode(source, length2) {
1298
1282
  return result;
1299
1283
  }
1300
1284
  function percentile_linear_interpolation(source, length2, percentage) {
1285
+ return sortedWindow(source, length2, (s) => {
1286
+ const pos2 = percentage / 100 * length2 - 0.5;
1287
+ if (pos2 <= 0) return s[0];
1288
+ if (pos2 >= length2 - 1) return s[length2 - 1];
1289
+ const lo = Math.floor(pos2);
1290
+ return s[lo] + (pos2 - lo) * (s[lo + 1] - s[lo]);
1291
+ });
1292
+ }
1293
+ function sortedWindow(source, length2, fn) {
1301
1294
  const result = [];
1295
+ const values2 = [];
1296
+ const bars = [];
1302
1297
  for (let i = 0; i < source.length; i++) {
1303
- if (i < length2 - 1) {
1304
- result.push(NaN);
1305
- continue;
1306
- }
1307
- const values2 = [];
1308
- for (let j = 0; j < length2; j++) {
1309
- const value = source[i - j];
1310
- values2.push(value);
1311
- }
1312
- if (values2.some((v) => isNaN(v))) {
1313
- result.push(NaN);
1314
- continue;
1315
- }
1316
- const sorted = [...values2].sort((a, b2) => a - b2);
1317
- const position = percentage / 100 * (sorted.length - 1);
1318
- const lowerIndex = Math.floor(position);
1319
- const upperIndex = Math.ceil(position);
1320
- if (lowerIndex === upperIndex) {
1321
- result.push(sorted[lowerIndex]);
1322
- } else {
1323
- const fraction = position - lowerIndex;
1324
- const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
1325
- result.push(interpolated);
1298
+ const v = source[i];
1299
+ let k = 0;
1300
+ while (k < values2.length && !(values2[k] >= v)) k++;
1301
+ values2.splice(k, 0, v);
1302
+ bars.splice(k, 0, i);
1303
+ if (i >= length2) {
1304
+ const old = bars.indexOf(i - length2);
1305
+ values2.splice(old, 1);
1306
+ bars.splice(old, 1);
1326
1307
  }
1308
+ result.push(i < length2 - 1 ? NaN : fn(values2));
1327
1309
  }
1328
1310
  return result;
1329
1311
  }
1330
1312
  function percentile_nearest_rank(source, length2, percentage) {
1331
- const result = [];
1332
- for (let i = 0; i < source.length; i++) {
1333
- if (i < length2 - 1) {
1334
- result.push(NaN);
1335
- continue;
1336
- }
1337
- const values2 = [];
1338
- for (let j = 0; j < length2; j++) {
1339
- const value = source[i - j];
1340
- if (!isNaN(value)) {
1341
- values2.push(value);
1342
- }
1343
- }
1344
- if (values2.length === 0) {
1345
- result.push(NaN);
1346
- continue;
1347
- }
1348
- const sorted = [...values2].sort((a, b2) => a - b2);
1349
- if (percentage >= 100) {
1350
- result.push(sorted[sorted.length - 1]);
1351
- continue;
1352
- }
1353
- const rank2 = Math.ceil(percentage / 100 * sorted.length);
1354
- const index = Math.max(0, rank2 - 1);
1355
- result.push(sorted[index]);
1356
- }
1357
- return result;
1313
+ const rank2 = Math.min(length2, Math.max(1, Math.ceil(percentage / 100 * length2)));
1314
+ return sortedWindow(source, length2, (s) => s[rank2 - 1]);
1358
1315
  }
1359
1316
  function rci(source, length2) {
1360
1317
  const result = [];
@@ -2072,7 +2029,7 @@ var Series = class _Series {
2072
2029
  return new _Series(this.dataSource, (bar, i, data) => {
2073
2030
  const a = this.extractor(bar, i, data);
2074
2031
  const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
2075
- return b2 !== 0 ? a / b2 : NaN;
2032
+ return a / b2;
2076
2033
  });
2077
2034
  }
2078
2035
  /**
@@ -2495,23 +2452,7 @@ function avg(...values2) {
2495
2452
  }
2496
2453
  function sum(source, length2) {
2497
2454
  const sourceArray = source instanceof Series ? source.toArray() : source;
2498
- const result = [];
2499
- const len = Math.floor(length2);
2500
- const window = [];
2501
- for (let i = 0; i < sourceArray.length; i++) {
2502
- const v = sourceArray[i];
2503
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
2504
- window.push(v);
2505
- if (window.length > len) window.shift();
2506
- }
2507
- if (window.length < len) {
2508
- result.push(NaN);
2509
- } else {
2510
- let total = 0;
2511
- for (const x of window) total += x;
2512
- result.push(total);
2513
- }
2514
- }
2455
+ const result = runningSum(sourceArray, length2);
2515
2456
  if (source instanceof Series) {
2516
2457
  return Series.fromArray(source.bars, result);
2517
2458
  }
@@ -3079,15 +3020,27 @@ function percentile_linear_interpolation2(id, percentage) {
3079
3020
  if (id.length === 0) {
3080
3021
  return NaN;
3081
3022
  }
3082
- const result = percentile_linear_interpolation(id, id.length, percentage);
3083
- return result[result.length - 1];
3023
+ const s = sortedNaLast(id);
3024
+ const n = s.length;
3025
+ const pos2 = percentage / 100 * n - 0.5;
3026
+ if (pos2 <= 0) return s[0];
3027
+ if (pos2 >= n - 1) return s[n - 1];
3028
+ const lo = Math.floor(pos2);
3029
+ const f = pos2 - lo;
3030
+ if (f === 0) return s[lo];
3031
+ return Number.isNaN(s[n - 1]) ? NaN : s[lo] + f * (s[lo + 1] - s[lo]);
3032
+ }
3033
+ function sortedNaLast(id) {
3034
+ const numbers = id.filter((v) => !Number.isNaN(v)).sort((a, b2) => a - b2);
3035
+ return [...numbers, ...Array(id.length - numbers.length).fill(NaN)];
3084
3036
  }
3085
3037
  function percentile_nearest_rank2(id, percentage) {
3086
3038
  if (id.length === 0) {
3087
3039
  return NaN;
3088
3040
  }
3089
- const result = percentile_nearest_rank(id, id.length, percentage);
3090
- return result[result.length - 1];
3041
+ const s = sortedNaLast(id);
3042
+ const rank2 = Math.min(s.length, Math.max(1, Math.ceil(percentage / 100 * s.length)));
3043
+ return s[rank2 - 1];
3091
3044
  }
3092
3045
  function percentrank2(id, index) {
3093
3046
  if (id.length === 0 || index < 0 || index >= id.length) {
@@ -3284,9 +3237,9 @@ function shortestDigits(x) {
3284
3237
  function exactDigits(x) {
3285
3238
  const abs3 = Math.abs(x);
3286
3239
  if (abs3 === 0) return { digits: "", intLen: 0 };
3287
- const view = new DataView(new ArrayBuffer(8));
3288
- view.setFloat64(0, abs3);
3289
- const bits = view.getBigUint64(0);
3240
+ const view2 = new DataView(new ArrayBuffer(8));
3241
+ view2.setFloat64(0, abs3);
3242
+ const bits = view2.getBigUint64(0);
3290
3243
  const expBits = Number(bits >> 52n & 0x7ffn);
3291
3244
  let mant = bits & 0xfffffffffffffn;
3292
3245
  const e2 = expBits === 0 ? -1074 : expBits - 1075;
@@ -3798,11 +3751,13 @@ __export(color_exports, {
3798
3751
  white: () => white,
3799
3752
  yellow: () => yellow
3800
3753
  });
3754
+ var channel = (v) => v === null || v === void 0 || Number.isNaN(v) ? 0 : Math.max(0, Math.min(255, Math.trunc(v)));
3755
+ var alphaOf = (transp) => transp === null || Number.isNaN(transp) ? 0 : 1 - Math.max(0, Math.min(100, transp)) / 100;
3801
3756
  function rgb(red2, green2, blue2, transp) {
3802
- const r2 = Math.max(0, Math.min(255, red2));
3803
- const g2 = Math.max(0, Math.min(255, green2));
3804
- const b2 = Math.max(0, Math.min(255, blue2));
3805
- const a = transp !== void 0 ? 1 - transp / 100 : 1;
3757
+ const r2 = channel(red2);
3758
+ const g2 = channel(green2);
3759
+ const b2 = channel(blue2);
3760
+ const a = transp !== void 0 ? alphaOf(transp) : 1;
3806
3761
  if (a === 1) {
3807
3762
  return `rgb(${r2}, ${g2}, ${b2})`;
3808
3763
  }
@@ -3829,7 +3784,8 @@ function b(clr) {
3829
3784
  return parseColor(clr).b;
3830
3785
  }
3831
3786
  function t(clr) {
3832
- return parseColor(clr).t;
3787
+ const alpha = Math.round((1 - parseColor(clr).t / 100) * 255);
3788
+ return Math.round((1 - alpha / 255) * 100);
3833
3789
  }
3834
3790
  function parseColor(clr) {
3835
3791
  if (typeof clr === "string") {
@@ -3882,13 +3838,12 @@ function from_gradient(value, bottom_value, top_value, bottom_color, top_color)
3882
3838
  };
3883
3839
  const c1 = rgba(bottom_color);
3884
3840
  const c2 = rgba(top_color);
3885
- const w1 = c1.a * (1 - k);
3886
- const w2 = c2.a * k;
3887
- const a = w1 + w2;
3841
+ const a = c1.a * (1 - k) + c2.a * k;
3888
3842
  if (a === 0) return transparent;
3889
- const mix = (x1, x2) => Math.floor((x1 * w1 + x2 * w2) / a);
3843
+ const mix = (x1, x2) => Math.floor((x1 / 255 * c1.a * (1 - k) + x2 / 255 * c2.a * k) / (a / 255));
3890
3844
  const [r2, g2, b2] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
3891
- return a === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${a / 255})`;
3845
+ const alpha = Math.floor(a);
3846
+ return alpha === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${alpha / 255})`;
3892
3847
  }
3893
3848
 
3894
3849
  // src/time/index.ts
@@ -6099,6 +6054,7 @@ var format_italic = 2;
6099
6054
  // src/callsite/index.ts
6100
6055
  var callsite_exports = {};
6101
6056
  __export(callsite_exports, {
6057
+ barssince: () => barssince2,
6102
6058
  cross: () => cross2,
6103
6059
  crossover: () => crossover2,
6104
6060
  crossunder: () => crossunder2,
@@ -6130,6 +6086,14 @@ function crossover2() {
6130
6086
  function crossunder2() {
6131
6087
  return twoSeriesSite((a, b2, pa, pb) => a < b2 && pa >= pb);
6132
6088
  }
6089
+ function barssince2() {
6090
+ let count = NaN;
6091
+ return (condition) => {
6092
+ if (condition && !Number.isNaN(condition)) count = 0;
6093
+ else if (!Number.isNaN(count)) count++;
6094
+ return count;
6095
+ };
6096
+ }
6133
6097
  function cross2() {
6134
6098
  return twoSeriesSite((a, b2, pa, pb) => a > b2 && pa <= pb || a < b2 && pa >= pb);
6135
6099
  }
@@ -6140,7 +6104,7 @@ __export(ta_series_exports, {
6140
6104
  accdist: () => accdist2,
6141
6105
  alma: () => alma2,
6142
6106
  atr: () => atr2,
6143
- barssince: () => barssince2,
6107
+ barssince: () => barssince3,
6144
6108
  bb: () => bb2,
6145
6109
  bbw: () => bbw2,
6146
6110
  cci: () => cci2,
@@ -6528,7 +6492,7 @@ function pivotlow2(source, leftbars, rightbars) {
6528
6492
  const result = pivotlow(source.toArray(), leftbars, rightbars);
6529
6493
  return Series.fromArray(bars, result);
6530
6494
  }
6531
- function barssince2(condition) {
6495
+ function barssince3(condition) {
6532
6496
  const bars = condition.bars;
6533
6497
  const boolValues = condition.toArray().map((v) => v === 1);
6534
6498
  const result = barssince(boolValues);
@@ -6680,7 +6644,7 @@ var ta = {
6680
6644
  hma: hma2,
6681
6645
  pivothigh: pivothigh2,
6682
6646
  pivotlow: pivotlow2,
6683
- barssince: barssince2,
6647
+ barssince: barssince3,
6684
6648
  valuewhen: valuewhen2,
6685
6649
  dmi: dmi2,
6686
6650
  tsi: tsi2,
@@ -6712,7 +6676,7 @@ var ta = {
6712
6676
 
6713
6677
  // src/utils/index.ts
6714
6678
  function isNA(value) {
6715
- return value === null || value === void 0 || Number.isNaN(value) || isDeleted(value);
6679
+ return value === null || value === void 0 || Number.isNaN(value) || value === Infinity || value === -Infinity || isDeleted(value);
6716
6680
  }
6717
6681
  function nz(value, defaultValue = 0) {
6718
6682
  return isNA(value) ? defaultValue : value;
@@ -6721,7 +6685,7 @@ function fixnan(source) {
6721
6685
  let last2;
6722
6686
  let seen = false;
6723
6687
  return source.map((v) => {
6724
- if (!isNA(v)) {
6688
+ if (!(v === null || v === void 0 || Number.isNaN(v) || isDeleted(v))) {
6725
6689
  last2 = v;
6726
6690
  seen = true;
6727
6691
  return v;
@@ -6737,7 +6701,7 @@ function at(source, i, offset = 0) {
6737
6701
  return j >= 0 && j < source.length ? source[j] ?? NaN : NaN;
6738
6702
  }
6739
6703
  function div(a, b2) {
6740
- return b2 === 0 ? NaN : a / b2;
6704
+ return a / b2;
6741
6705
  }
6742
6706
  function ohlcFromBars(bars) {
6743
6707
  return {
@@ -7371,7 +7335,7 @@ function hline(price, title, color, linestyle, linewidth) {
7371
7335
  const options = {
7372
7336
  color,
7373
7337
  lineWidth: linewidth,
7374
- lineStyle: getLineStyle(linestyle)
7338
+ lineStyle: getLineStyle(linestyle ?? "dashed")
7375
7339
  };
7376
7340
  const seriesHandle = context.chart.addSeries("line", options);
7377
7341
  const data = context.ohlcv.time.map((time) => ({
@@ -7618,9 +7582,10 @@ var STRATEGY_DEFAULTS = {
7618
7582
  calc_on_order_fills: false,
7619
7583
  calc_on_every_tick: false,
7620
7584
  backtest_fill_limits_assumption: 0,
7621
- default_qty_type: "fixed",
7622
- default_qty_value: 1,
7623
- initial_capital: 1e6,
7585
+ // v6 values; PineScript v5: fixed / 1 / 1000000 and margins 0
7586
+ default_qty_type: "percent_of_equity",
7587
+ default_qty_value: 100,
7588
+ initial_capital: 1e5,
7624
7589
  slippage: 0,
7625
7590
  commission_type: "percent",
7626
7591
  commission_value: 0,
@@ -7876,7 +7841,7 @@ var SimpleInputAdapter = class {
7876
7841
  };
7877
7842
 
7878
7843
  // src/index.ts
7879
- var VERSION = "0.8.0";
7844
+ var VERSION = "0.8.2";
7880
7845
  var info2 = {
7881
7846
  name: "OakScriptJS",
7882
7847
  version: VERSION,