oakscriptjs 0.7.2 → 0.8.1

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
@@ -85,13 +85,47 @@ __export(ta_exports, {
85
85
  wvad: () => wvad,
86
86
  zigzag: () => zigzag
87
87
  });
88
+
89
+ // src/compare/index.ts
90
+ var compare_exports = {};
91
+ __export(compare_exports, {
92
+ EPSILON: () => EPSILON,
93
+ eq: () => eq,
94
+ ge: () => ge,
95
+ gt: () => gt,
96
+ le: () => le,
97
+ lt: () => lt,
98
+ ne: () => ne
99
+ });
100
+ var EPSILON = 1e-10;
101
+ var valid = (a, b2) => !Number.isNaN(a) && !Number.isNaN(b2);
102
+ function eq(a, b2) {
103
+ return valid(a, b2) && Math.abs(a - b2) <= EPSILON;
104
+ }
105
+ function ne(a, b2) {
106
+ return valid(a, b2) && Math.abs(a - b2) > EPSILON;
107
+ }
108
+ function lt(a, b2) {
109
+ return valid(a, b2) && b2 - a > EPSILON;
110
+ }
111
+ function le(a, b2) {
112
+ return valid(a, b2) && a - b2 <= EPSILON;
113
+ }
114
+ function gt(a, b2) {
115
+ return valid(a, b2) && a - b2 > EPSILON;
116
+ }
117
+ function ge(a, b2) {
118
+ return valid(a, b2) && b2 - a <= EPSILON;
119
+ }
120
+
121
+ // src/ta/index.ts
88
122
  function sma(source, length2) {
89
123
  const result = [];
90
124
  const len = Math.floor(length2);
91
125
  const window = [];
92
126
  for (let i = 0; i < source.length; i++) {
93
127
  const v = source[i];
94
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
128
+ if (v !== void 0 && v !== null && Number.isFinite(v)) {
95
129
  window.push(v);
96
130
  if (window.length > len) window.shift();
97
131
  }
@@ -276,7 +310,7 @@ function tr(handle_na = false, high, low, close) {
276
310
  const result = [];
277
311
  for (let i = 0; i < high.length; i++) {
278
312
  if (i === 0) {
279
- result.push(high[i] - low[i]);
313
+ result.push(handle_na ? high[i] - low[i] : NaN);
280
314
  } else {
281
315
  const prevClose = close[i - 1];
282
316
  if (isNaN(prevClose)) {
@@ -303,7 +337,7 @@ function atr(length2, high, low, close) {
303
337
  "ta.atr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
304
338
  );
305
339
  }
306
- const trueRange = tr(false, high, low, close);
340
+ const trueRange = tr(true, high, low, close);
307
341
  return rma(trueRange, length2);
308
342
  }
309
343
  function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
@@ -435,7 +469,7 @@ function lowest(source, length2) {
435
469
  function cum(source) {
436
470
  let sum4 = 0;
437
471
  return source.map((v) => {
438
- if (Number.isNaN(v)) return NaN;
472
+ if (!Number.isFinite(v)) return NaN;
439
473
  sum4 += v;
440
474
  return sum4;
441
475
  });
@@ -453,41 +487,24 @@ function cross(source1, source2) {
453
487
  }
454
488
  return result;
455
489
  }
456
- function rising(source, length2) {
490
+ function monotonic(source, length2, step) {
457
491
  const result = [];
492
+ const values2 = [];
458
493
  for (let i = 0; i < source.length; i++) {
459
- if (i < length2) {
460
- result.push(false);
461
- } else {
462
- let isRising = true;
463
- for (let j = 1; j <= length2; j++) {
464
- if (source[i - j + 1] <= source[i - j]) {
465
- isRising = false;
466
- break;
467
- }
468
- }
469
- result.push(isRising);
470
- }
494
+ const v = source[i];
495
+ if (!Number.isNaN(v)) values2.push(v);
496
+ const n = values2.length;
497
+ let ok = n > length2;
498
+ for (let j = 1; ok && j <= length2; j++) ok = step(values2[n - j], values2[n - j - 1]);
499
+ result.push(ok);
471
500
  }
472
501
  return result;
473
502
  }
503
+ function rising(source, length2) {
504
+ return monotonic(source, length2, gt);
505
+ }
474
506
  function falling(source, length2) {
475
- const result = [];
476
- for (let i = 0; i < source.length; i++) {
477
- if (i < length2) {
478
- result.push(false);
479
- } else {
480
- let isFalling = true;
481
- for (let j = 1; j <= length2; j++) {
482
- if (source[i - j + 1] >= source[i - j]) {
483
- isFalling = false;
484
- break;
485
- }
486
- }
487
- result.push(isFalling);
488
- }
489
- }
490
- return result;
507
+ return monotonic(source, length2, lt);
491
508
  }
492
509
  function roc(source, length2) {
493
510
  const result = [];
@@ -711,33 +728,16 @@ function correlation(source1, source2, length2) {
711
728
  function percentrank(source, length2) {
712
729
  const result = [];
713
730
  for (let i = 0; i < source.length; i++) {
714
- if (i < length2) {
731
+ const currentValue = source[i];
732
+ if (i < length2 || Number.isNaN(currentValue)) {
715
733
  result.push(NaN);
716
- } else {
717
- if (isNaN(source[i])) {
718
- result.push(NaN);
719
- continue;
720
- }
721
- let hasNaN = false;
722
- for (let j = 1; j <= length2; j++) {
723
- if (isNaN(source[i - j])) {
724
- hasNaN = true;
725
- break;
726
- }
727
- }
728
- if (hasNaN) {
729
- result.push(NaN);
730
- } else {
731
- const currentValue = source[i];
732
- let countLessOrEqual = 0;
733
- for (let j = 1; j <= length2; j++) {
734
- if (source[i - j] <= currentValue) {
735
- countLessOrEqual++;
736
- }
737
- }
738
- result.push(countLessOrEqual / length2 * 100);
739
- }
734
+ continue;
735
+ }
736
+ let countLessOrEqual = 0;
737
+ for (let j = 1; j <= length2; j++) {
738
+ if (le(source[i - j], currentValue)) countLessOrEqual++;
740
739
  }
740
+ result.push(countLessOrEqual / length2 * 100);
741
741
  }
742
742
  return result;
743
743
  }
@@ -780,48 +780,26 @@ function mfi(source, length2, volume) {
780
780
  "ta.mfi() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
781
781
  );
782
782
  }
783
- const result = [];
784
- const changes = [NaN];
785
- for (let i = 1; i < source.length; i++) {
786
- changes.push(source[i] - source[i - 1]);
787
- }
788
- const positiveFlow = [];
789
- const negativeFlow = [];
783
+ const upperFlow = [];
784
+ const lowerFlow = [];
790
785
  for (let i = 0; i < source.length; i++) {
791
- if (i === 0 || isNaN(changes[i])) {
792
- positiveFlow.push(0);
793
- negativeFlow.push(0);
794
- } else if (changes[i] > 0) {
795
- positiveFlow.push(volume[i] * source[i]);
796
- negativeFlow.push(0);
797
- } else if (changes[i] < 0) {
798
- positiveFlow.push(0);
799
- negativeFlow.push(volume[i] * source[i]);
800
- } else {
801
- positiveFlow.push(0);
802
- negativeFlow.push(0);
803
- }
804
- }
805
- for (let i = 0; i < source.length; i++) {
806
- if (i < length2) {
807
- result.push(NaN);
808
- } else {
809
- let posSum = 0;
810
- let negSum = 0;
811
- for (let j = 0; j < length2; j++) {
812
- posSum += positiveFlow[i - j];
813
- negSum += negativeFlow[i - j];
814
- }
815
- if (negSum === 0) {
816
- result.push(100);
817
- } else {
818
- const moneyRatio = posSum / negSum;
819
- const mfiValue = 100 - 100 / (1 + moneyRatio);
820
- result.push(mfiValue);
821
- }
822
- }
823
- }
824
- return result;
786
+ const change3 = i === 0 ? NaN : source[i] - source[i - 1];
787
+ const flow = volume[i] * source[i];
788
+ upperFlow.push(le(change3, 0) ? 0 : flow);
789
+ lowerFlow.push(ge(change3, 0) ? 0 : flow);
790
+ }
791
+ const sumAt = (flow, i) => {
792
+ const values2 = lastValues(flow, i, Math.floor(length2));
793
+ return values2 ? values2.reduce((a, b2) => a + b2, 0) : NaN;
794
+ };
795
+ const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
796
+ const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
797
+ return upper2.map((u, i) => {
798
+ const l = lower2[i];
799
+ if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
800
+ if (l === 0) return 100;
801
+ return 100 - 100 / (1 + u / l);
802
+ });
825
803
  }
826
804
  function hma(source, length2) {
827
805
  const halfLength = Math.floor(length2 / 2);
@@ -985,50 +963,32 @@ function dmi(diLength, adxSmoothing, high, low, close) {
985
963
  const len = Math.max(high.length, low.length, close.length);
986
964
  const plusDM = [];
987
965
  const minusDM = [];
988
- const trueRangeValues = tr(false, high, low, close);
989
966
  for (let i = 0; i < len; i++) {
990
- if (i === 0) {
991
- plusDM.push(0);
992
- minusDM.push(0);
993
- } else {
994
- const upMove = high[i] - high[i - 1];
995
- const downMove = low[i - 1] - low[i];
996
- let plusDMVal = 0;
997
- let minusDMVal = 0;
998
- if (upMove > downMove && upMove > 0) {
999
- plusDMVal = upMove;
1000
- }
1001
- if (downMove > upMove && downMove > 0) {
1002
- minusDMVal = downMove;
1003
- }
1004
- plusDM.push(plusDMVal);
1005
- minusDM.push(minusDMVal);
1006
- }
1007
- }
1008
- const smoothedPlusDM = rma(plusDM, diLength);
1009
- const smoothedMinusDM = rma(minusDM, diLength);
1010
- const smoothedTR = rma(trueRangeValues, diLength);
1011
- const plusDI = [];
1012
- const minusDI = [];
1013
- for (let i = 0; i < len; i++) {
1014
- if (smoothedTR[i] === 0) {
1015
- plusDI.push(0);
1016
- minusDI.push(0);
1017
- } else {
1018
- plusDI.push(smoothedPlusDM[i] / smoothedTR[i] * 100);
1019
- minusDI.push(smoothedMinusDM[i] / smoothedTR[i] * 100);
1020
- }
1021
- }
1022
- const dx = [];
1023
- for (let i = 0; i < len; i++) {
1024
- const sum4 = plusDI[i] + minusDI[i];
1025
- if (sum4 === 0) {
1026
- dx.push(0);
1027
- } else {
1028
- dx.push(Math.abs(plusDI[i] - minusDI[i]) / sum4 * 100);
1029
- }
1030
- }
1031
- const adx = rma(dx, adxSmoothing);
967
+ const up = i === 0 ? NaN : high[i] - high[i - 1];
968
+ const down = i === 0 ? NaN : low[i - 1] - low[i];
969
+ plusDM.push(Number.isNaN(up) ? NaN : gt(up, down) && gt(up, 0) ? up : 0);
970
+ minusDM.push(Number.isNaN(down) ? NaN : gt(down, up) && gt(down, 0) ? down : 0);
971
+ }
972
+ const trur = rma(tr(false, high, low, close), diLength);
973
+ const smoothedPlus = rma(plusDM, diLength);
974
+ const smoothedMinus = rma(minusDM, diLength);
975
+ const directional = (smoothed) => {
976
+ let last2 = NaN;
977
+ return smoothed.map((v, i) => {
978
+ const t2 = trur[i];
979
+ const value = t2 === 0 ? NaN : 100 * v / t2;
980
+ if (!Number.isNaN(value)) last2 = value;
981
+ return last2;
982
+ });
983
+ };
984
+ const plusDI = directional(smoothedPlus);
985
+ const minusDI = directional(smoothedMinus);
986
+ const dx = plusDI.map((plus, i) => {
987
+ const minus = minusDI[i];
988
+ const sum4 = plus + minus;
989
+ return Math.abs(plus - minus) / (eq(sum4, 0) ? 1 : sum4);
990
+ });
991
+ const adx = rma(dx, adxSmoothing).map((v) => 100 * v);
1032
992
  return [plusDI, minusDI, adx];
1033
993
  }
1034
994
  function tsi(source, shortLength, longLength) {
@@ -1304,63 +1264,36 @@ function mode(source, length2) {
1304
1264
  return result;
1305
1265
  }
1306
1266
  function percentile_linear_interpolation(source, length2, percentage) {
1267
+ return sortedWindow(source, length2, (s) => {
1268
+ const pos2 = percentage / 100 * length2 - 0.5;
1269
+ if (pos2 <= 0) return s[0];
1270
+ if (pos2 >= length2 - 1) return s[length2 - 1];
1271
+ const lo = Math.floor(pos2);
1272
+ return s[lo] + (pos2 - lo) * (s[lo + 1] - s[lo]);
1273
+ });
1274
+ }
1275
+ function sortedWindow(source, length2, fn) {
1307
1276
  const result = [];
1277
+ const values2 = [];
1278
+ const bars = [];
1308
1279
  for (let i = 0; i < source.length; i++) {
1309
- if (i < length2 - 1) {
1310
- result.push(NaN);
1311
- continue;
1312
- }
1313
- const values2 = [];
1314
- for (let j = 0; j < length2; j++) {
1315
- const value = source[i - j];
1316
- values2.push(value);
1317
- }
1318
- if (values2.some((v) => isNaN(v))) {
1319
- result.push(NaN);
1320
- continue;
1321
- }
1322
- const sorted = [...values2].sort((a, b2) => a - b2);
1323
- const position = percentage / 100 * (sorted.length - 1);
1324
- const lowerIndex = Math.floor(position);
1325
- const upperIndex = Math.ceil(position);
1326
- if (lowerIndex === upperIndex) {
1327
- result.push(sorted[lowerIndex]);
1328
- } else {
1329
- const fraction = position - lowerIndex;
1330
- const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
1331
- result.push(interpolated);
1280
+ const v = source[i];
1281
+ let k = 0;
1282
+ while (k < values2.length && !(values2[k] >= v)) k++;
1283
+ values2.splice(k, 0, v);
1284
+ bars.splice(k, 0, i);
1285
+ if (i >= length2) {
1286
+ const old = bars.indexOf(i - length2);
1287
+ values2.splice(old, 1);
1288
+ bars.splice(old, 1);
1332
1289
  }
1290
+ result.push(i < length2 - 1 ? NaN : fn(values2));
1333
1291
  }
1334
1292
  return result;
1335
1293
  }
1336
1294
  function percentile_nearest_rank(source, length2, percentage) {
1337
- const result = [];
1338
- for (let i = 0; i < source.length; i++) {
1339
- if (i < length2 - 1) {
1340
- result.push(NaN);
1341
- continue;
1342
- }
1343
- const values2 = [];
1344
- for (let j = 0; j < length2; j++) {
1345
- const value = source[i - j];
1346
- if (!isNaN(value)) {
1347
- values2.push(value);
1348
- }
1349
- }
1350
- if (values2.length === 0) {
1351
- result.push(NaN);
1352
- continue;
1353
- }
1354
- const sorted = [...values2].sort((a, b2) => a - b2);
1355
- if (percentage >= 100) {
1356
- result.push(sorted[sorted.length - 1]);
1357
- continue;
1358
- }
1359
- const rank2 = Math.ceil(percentage / 100 * sorted.length);
1360
- const index = Math.max(0, rank2 - 1);
1361
- result.push(sorted[index]);
1362
- }
1363
- return result;
1295
+ const rank2 = Math.min(length2, Math.max(1, Math.ceil(percentage / 100 * length2)));
1296
+ return sortedWindow(source, length2, (s) => s[rank2 - 1]);
1364
1297
  }
1365
1298
  function rci(source, length2) {
1366
1299
  const result = [];
@@ -1855,38 +1788,6 @@ __export(math_exports, {
1855
1788
  toradians: () => toradians
1856
1789
  });
1857
1790
 
1858
- // src/compare/index.ts
1859
- var compare_exports = {};
1860
- __export(compare_exports, {
1861
- EPSILON: () => EPSILON,
1862
- eq: () => eq,
1863
- ge: () => ge,
1864
- gt: () => gt,
1865
- le: () => le,
1866
- lt: () => lt,
1867
- ne: () => ne
1868
- });
1869
- var EPSILON = 1e-10;
1870
- var valid = (a, b2) => !Number.isNaN(a) && !Number.isNaN(b2);
1871
- function eq(a, b2) {
1872
- return valid(a, b2) && Math.abs(a - b2) <= EPSILON;
1873
- }
1874
- function ne(a, b2) {
1875
- return valid(a, b2) && Math.abs(a - b2) > EPSILON;
1876
- }
1877
- function lt(a, b2) {
1878
- return valid(a, b2) && b2 - a > EPSILON;
1879
- }
1880
- function le(a, b2) {
1881
- return valid(a, b2) && a - b2 <= EPSILON;
1882
- }
1883
- function gt(a, b2) {
1884
- return valid(a, b2) && a - b2 > EPSILON;
1885
- }
1886
- function ge(a, b2) {
1887
- return valid(a, b2) && b2 - a <= EPSILON;
1888
- }
1889
-
1890
1791
  // src/runtime/series.ts
1891
1792
  var BarData = class _BarData {
1892
1793
  _bars;
@@ -2110,7 +2011,7 @@ var Series = class _Series {
2110
2011
  return new _Series(this.dataSource, (bar, i, data) => {
2111
2012
  const a = this.extractor(bar, i, data);
2112
2013
  const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
2113
- return b2 !== 0 ? a / b2 : NaN;
2014
+ return a / b2;
2114
2015
  });
2115
2016
  }
2116
2017
  /**
@@ -3117,15 +3018,27 @@ function percentile_linear_interpolation2(id, percentage) {
3117
3018
  if (id.length === 0) {
3118
3019
  return NaN;
3119
3020
  }
3120
- const result = percentile_linear_interpolation(id, id.length, percentage);
3121
- return result[result.length - 1];
3021
+ const s = sortedNaLast(id);
3022
+ const n = s.length;
3023
+ const pos2 = percentage / 100 * n - 0.5;
3024
+ if (pos2 <= 0) return s[0];
3025
+ if (pos2 >= n - 1) return s[n - 1];
3026
+ const lo = Math.floor(pos2);
3027
+ const f = pos2 - lo;
3028
+ if (f === 0) return s[lo];
3029
+ return Number.isNaN(s[n - 1]) ? NaN : s[lo] + f * (s[lo + 1] - s[lo]);
3030
+ }
3031
+ function sortedNaLast(id) {
3032
+ const numbers = id.filter((v) => !Number.isNaN(v)).sort((a, b2) => a - b2);
3033
+ return [...numbers, ...Array(id.length - numbers.length).fill(NaN)];
3122
3034
  }
3123
3035
  function percentile_nearest_rank2(id, percentage) {
3124
3036
  if (id.length === 0) {
3125
3037
  return NaN;
3126
3038
  }
3127
- const result = percentile_nearest_rank(id, id.length, percentage);
3128
- return result[result.length - 1];
3039
+ const s = sortedNaLast(id);
3040
+ const rank2 = Math.min(s.length, Math.max(1, Math.ceil(percentage / 100 * s.length)));
3041
+ return s[rank2 - 1];
3129
3042
  }
3130
3043
  function percentrank2(id, index) {
3131
3044
  if (id.length === 0 || index < 0 || index >= id.length) {
@@ -3836,11 +3749,13 @@ __export(color_exports, {
3836
3749
  white: () => white,
3837
3750
  yellow: () => yellow
3838
3751
  });
3752
+ var channel = (v) => v === null || v === void 0 || Number.isNaN(v) ? 0 : Math.max(0, Math.min(255, Math.trunc(v)));
3753
+ var alphaOf = (transp) => transp === null || Number.isNaN(transp) ? 0 : 1 - Math.max(0, Math.min(100, transp)) / 100;
3839
3754
  function rgb(red2, green2, blue2, transp) {
3840
- const r2 = Math.max(0, Math.min(255, red2));
3841
- const g2 = Math.max(0, Math.min(255, green2));
3842
- const b2 = Math.max(0, Math.min(255, blue2));
3843
- const a = transp !== void 0 ? 1 - transp / 100 : 1;
3755
+ const r2 = channel(red2);
3756
+ const g2 = channel(green2);
3757
+ const b2 = channel(blue2);
3758
+ const a = transp !== void 0 ? alphaOf(transp) : 1;
3844
3759
  if (a === 1) {
3845
3760
  return `rgb(${r2}, ${g2}, ${b2})`;
3846
3761
  }
@@ -3867,7 +3782,8 @@ function b(clr) {
3867
3782
  return parseColor(clr).b;
3868
3783
  }
3869
3784
  function t(clr) {
3870
- return parseColor(clr).t;
3785
+ const alpha = Math.round((1 - parseColor(clr).t / 100) * 255);
3786
+ return Math.round((1 - alpha / 255) * 100);
3871
3787
  }
3872
3788
  function parseColor(clr) {
3873
3789
  if (typeof clr === "string") {
@@ -3920,13 +3836,12 @@ function from_gradient(value, bottom_value, top_value, bottom_color, top_color)
3920
3836
  };
3921
3837
  const c1 = rgba(bottom_color);
3922
3838
  const c2 = rgba(top_color);
3923
- const w1 = c1.a * (1 - k);
3924
- const w2 = c2.a * k;
3925
- const a = w1 + w2;
3839
+ const a = c1.a * (1 - k) + c2.a * k;
3926
3840
  if (a === 0) return transparent;
3927
- const mix = (x1, x2) => Math.floor((x1 * w1 + x2 * w2) / a);
3841
+ const mix = (x1, x2) => Math.floor((x1 / 255 * c1.a * (1 - k) + x2 / 255 * c2.a * k) / (a / 255));
3928
3842
  const [r2, g2, b2] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
3929
- return a === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${a / 255})`;
3843
+ const alpha = Math.floor(a);
3844
+ return alpha === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${alpha / 255})`;
3930
3845
  }
3931
3846
 
3932
3847
  // src/time/index.ts
@@ -6134,13 +6049,60 @@ var format_none = 0;
6134
6049
  var format_bold = 1;
6135
6050
  var format_italic = 2;
6136
6051
 
6052
+ // src/callsite/index.ts
6053
+ var callsite_exports = {};
6054
+ __export(callsite_exports, {
6055
+ barssince: () => barssince2,
6056
+ cross: () => cross2,
6057
+ crossover: () => crossover2,
6058
+ crossunder: () => crossunder2,
6059
+ whenCalled: () => whenCalled
6060
+ });
6061
+ function whenCalled(called, fn, ...sources) {
6062
+ const bars = [];
6063
+ for (let i = 0; i < called.length; i++) if (called[i]) bars.push(i);
6064
+ const result = fn(...sources.map((s) => bars.map((i) => s[i] ?? NaN)));
6065
+ const out = new Array(called.length).fill(NaN);
6066
+ bars.forEach((bar, k) => {
6067
+ out[bar] = result[k];
6068
+ });
6069
+ return out;
6070
+ }
6071
+ function twoSeriesSite(test) {
6072
+ let prevA = NaN;
6073
+ let prevB = NaN;
6074
+ return (a, b2) => {
6075
+ const result = test(a, b2, prevA, prevB);
6076
+ prevA = a;
6077
+ prevB = b2;
6078
+ return result;
6079
+ };
6080
+ }
6081
+ function crossover2() {
6082
+ return twoSeriesSite((a, b2, pa, pb) => a > b2 && pa <= pb);
6083
+ }
6084
+ function crossunder2() {
6085
+ return twoSeriesSite((a, b2, pa, pb) => a < b2 && pa >= pb);
6086
+ }
6087
+ function barssince2() {
6088
+ let count = NaN;
6089
+ return (condition) => {
6090
+ if (condition && !Number.isNaN(condition)) count = 0;
6091
+ else if (!Number.isNaN(count)) count++;
6092
+ return count;
6093
+ };
6094
+ }
6095
+ function cross2() {
6096
+ return twoSeriesSite((a, b2, pa, pb) => a > b2 && pa <= pb || a < b2 && pa >= pb);
6097
+ }
6098
+
6137
6099
  // src/ta-series.ts
6138
6100
  var ta_series_exports = {};
6139
6101
  __export(ta_series_exports, {
6140
6102
  accdist: () => accdist2,
6141
6103
  alma: () => alma2,
6142
6104
  atr: () => atr2,
6143
- barssince: () => barssince2,
6105
+ barssince: () => barssince3,
6144
6106
  bb: () => bb2,
6145
6107
  bbw: () => bbw2,
6146
6108
  cci: () => cci2,
@@ -6148,9 +6110,9 @@ __export(ta_series_exports, {
6148
6110
  cmo: () => cmo2,
6149
6111
  cog: () => cog2,
6150
6112
  correlation: () => correlation2,
6151
- cross: () => cross2,
6152
- crossover: () => crossover2,
6153
- crossunder: () => crossunder2,
6113
+ cross: () => cross3,
6114
+ crossover: () => crossover3,
6115
+ crossunder: () => crossunder3,
6154
6116
  cum: () => cum2,
6155
6117
  dev: () => dev2,
6156
6118
  dmi: () => dmi2,
@@ -6316,7 +6278,7 @@ function stoch2(source, high, low, length2) {
6316
6278
  const result = stoch(sourceValues, highValues, lowValues, length2);
6317
6279
  return Series.fromArray(bars, result);
6318
6280
  }
6319
- function crossover2(source1, source2) {
6281
+ function crossover3(source1, source2) {
6320
6282
  const bars = source1.bars;
6321
6283
  const vals1 = source1.toArray();
6322
6284
  const vals2 = typeof source2 === "number" ? Array(bars.length).fill(source2) : source2.toArray();
@@ -6324,7 +6286,7 @@ function crossover2(source1, source2) {
6324
6286
  const numResult = result.map((b2) => b2 ? 1 : 0);
6325
6287
  return Series.fromArray(bars, numResult);
6326
6288
  }
6327
- function crossunder2(source1, source2) {
6289
+ function crossunder3(source1, source2) {
6328
6290
  const bars = source1.bars;
6329
6291
  const vals1 = source1.toArray();
6330
6292
  const vals2 = typeof source2 === "number" ? Array(bars.length).fill(source2) : source2.toArray();
@@ -6332,7 +6294,7 @@ function crossunder2(source1, source2) {
6332
6294
  const numResult = result.map((b2) => b2 ? 1 : 0);
6333
6295
  return Series.fromArray(bars, numResult);
6334
6296
  }
6335
- function cross2(source1, source2) {
6297
+ function cross3(source1, source2) {
6336
6298
  const bars = source1.bars;
6337
6299
  const vals1 = source1.toArray();
6338
6300
  const vals2 = typeof source2 === "number" ? Array(bars.length).fill(source2) : source2.toArray();
@@ -6528,7 +6490,7 @@ function pivotlow2(source, leftbars, rightbars) {
6528
6490
  const result = pivotlow(source.toArray(), leftbars, rightbars);
6529
6491
  return Series.fromArray(bars, result);
6530
6492
  }
6531
- function barssince2(condition) {
6493
+ function barssince3(condition) {
6532
6494
  const bars = condition.bars;
6533
6495
  const boolValues = condition.toArray().map((v) => v === 1);
6534
6496
  const result = barssince(boolValues);
@@ -6651,9 +6613,9 @@ var ta = {
6651
6613
  atr: atr2,
6652
6614
  tr: tr2,
6653
6615
  stoch: stoch2,
6654
- crossover: crossover2,
6655
- crossunder: crossunder2,
6656
- cross: cross2,
6616
+ crossover: crossover3,
6617
+ crossunder: crossunder3,
6618
+ cross: cross3,
6657
6619
  change: change2,
6658
6620
  mom: mom2,
6659
6621
  roc: roc2,
@@ -6680,7 +6642,7 @@ var ta = {
6680
6642
  hma: hma2,
6681
6643
  pivothigh: pivothigh2,
6682
6644
  pivotlow: pivotlow2,
6683
- barssince: barssince2,
6645
+ barssince: barssince3,
6684
6646
  valuewhen: valuewhen2,
6685
6647
  dmi: dmi2,
6686
6648
  tsi: tsi2,
@@ -6712,7 +6674,7 @@ var ta = {
6712
6674
 
6713
6675
  // src/utils/index.ts
6714
6676
  function isNA(value) {
6715
- return value === null || value === void 0 || Number.isNaN(value) || isDeleted(value);
6677
+ return value === null || value === void 0 || Number.isNaN(value) || value === Infinity || value === -Infinity || isDeleted(value);
6716
6678
  }
6717
6679
  function nz(value, defaultValue = 0) {
6718
6680
  return isNA(value) ? defaultValue : value;
@@ -6721,7 +6683,7 @@ function fixnan(source) {
6721
6683
  let last2;
6722
6684
  let seen = false;
6723
6685
  return source.map((v) => {
6724
- if (!isNA(v)) {
6686
+ if (!(v === null || v === void 0 || Number.isNaN(v) || isDeleted(v))) {
6725
6687
  last2 = v;
6726
6688
  seen = true;
6727
6689
  return v;
@@ -6729,6 +6691,16 @@ function fixnan(source) {
6729
6691
  return seen ? last2 : v;
6730
6692
  });
6731
6693
  }
6694
+ function at(source, i, offset = 0) {
6695
+ if (offset < 0) {
6696
+ throw new RangeError(`at(): the offset must be >= 0 (got ${offset}); a negative offset reads a later bar.`);
6697
+ }
6698
+ const j = i - offset;
6699
+ return j >= 0 && j < source.length ? source[j] ?? NaN : NaN;
6700
+ }
6701
+ function div(a, b2) {
6702
+ return a / b2;
6703
+ }
6732
6704
  function ohlcFromBars(bars) {
6733
6705
  return {
6734
6706
  open: bars.map((b2) => b2.open),
@@ -6749,6 +6721,50 @@ function getLow(ohlc) {
6749
6721
  function getOpen(ohlc) {
6750
6722
  return ohlc.open;
6751
6723
  }
6724
+ function getSource(data, source = "close") {
6725
+ const isOHLC = "open" in data && Array.isArray(data.open);
6726
+ if (isOHLC) {
6727
+ const ohlc = data;
6728
+ switch (source) {
6729
+ case "close":
6730
+ return ohlc.close;
6731
+ case "open":
6732
+ return ohlc.open;
6733
+ case "high":
6734
+ return ohlc.high;
6735
+ case "low":
6736
+ return ohlc.low;
6737
+ case "hl2":
6738
+ return ohlc.high.map((h, i) => (h + ohlc.low[i]) / 2);
6739
+ case "hlc3":
6740
+ return ohlc.high.map((h, i) => (h + ohlc.low[i] + ohlc.close[i]) / 3);
6741
+ case "ohlc4":
6742
+ return ohlc.open.map((o, i) => (o + ohlc.high[i] + ohlc.low[i] + ohlc.close[i]) / 4);
6743
+ case "hlcc4":
6744
+ return ohlc.high.map((h, i) => (h + ohlc.low[i] + ohlc.close[i] + ohlc.close[i]) / 4);
6745
+ }
6746
+ } else {
6747
+ const bars = data;
6748
+ switch (source) {
6749
+ case "close":
6750
+ return bars.map((b2) => b2.close);
6751
+ case "open":
6752
+ return bars.map((b2) => b2.open);
6753
+ case "high":
6754
+ return bars.map((b2) => b2.high);
6755
+ case "low":
6756
+ return bars.map((b2) => b2.low);
6757
+ case "hl2":
6758
+ return bars.map((b2) => (b2.high + b2.low) / 2);
6759
+ case "hlc3":
6760
+ return bars.map((b2) => (b2.high + b2.low + b2.close) / 3);
6761
+ case "ohlc4":
6762
+ return bars.map((b2) => (b2.open + b2.high + b2.low + b2.close) / 4);
6763
+ case "hlcc4":
6764
+ return bars.map((b2) => (b2.high + b2.low + b2.close + b2.close) / 4);
6765
+ }
6766
+ }
6767
+ }
6752
6768
  function getSourceSeries(bars, source = "close") {
6753
6769
  const open = new Series(bars, (bar) => bar.open);
6754
6770
  const high = new Series(bars, (bar) => bar.high);
@@ -7317,7 +7333,7 @@ function hline(price, title, color, linestyle, linewidth) {
7317
7333
  const options = {
7318
7334
  color,
7319
7335
  lineWidth: linewidth,
7320
- lineStyle: getLineStyle(linestyle)
7336
+ lineStyle: getLineStyle(linestyle ?? "dashed")
7321
7337
  };
7322
7338
  const seriesHandle = context.chart.addSeries("line", options);
7323
7339
  const data = context.ohlcv.time.map((time) => ({
@@ -7548,6 +7564,73 @@ function getSourceData(ohlcv, sourceName) {
7548
7564
  }
7549
7565
  }
7550
7566
 
7567
+ // src/strategy/index.ts
7568
+ var STRATEGY_CONSTANTS = {
7569
+ long: "long",
7570
+ short: "short",
7571
+ fixed: "fixed",
7572
+ cash: "cash",
7573
+ percent_of_equity: "percent_of_equity",
7574
+ commission: { percent: "percent", cash_per_contract: "cash_per_contract", cash_per_order: "cash_per_order" },
7575
+ direction: { all: "all", long: "long", short: "short" },
7576
+ oca: { none: "none", cancel: "cancel", reduce: "reduce" }
7577
+ };
7578
+ var STRATEGY_DEFAULTS = {
7579
+ pyramiding: 0,
7580
+ calc_on_order_fills: false,
7581
+ calc_on_every_tick: false,
7582
+ backtest_fill_limits_assumption: 0,
7583
+ // v6 values; PineScript v5: fixed / 1 / 1000000 and margins 0
7584
+ default_qty_type: "percent_of_equity",
7585
+ default_qty_value: 100,
7586
+ initial_capital: 1e5,
7587
+ slippage: 0,
7588
+ commission_type: "percent",
7589
+ commission_value: 0,
7590
+ process_orders_on_close: false,
7591
+ close_entries_rule: "FIFO",
7592
+ margin_long: 100,
7593
+ margin_short: 100,
7594
+ calc_bars_count: 0,
7595
+ risk_free_rate: 2,
7596
+ use_bar_magnifier: false,
7597
+ fill_orders_on_standard_ohlc: false
7598
+ };
7599
+ var STRATEGY_NUMBER_VARIABLES = [
7600
+ "position_size",
7601
+ "position_avg_price",
7602
+ "equity",
7603
+ "initial_capital",
7604
+ "netprofit",
7605
+ "netprofit_percent",
7606
+ "openprofit",
7607
+ "openprofit_percent",
7608
+ "grossprofit",
7609
+ "grossprofit_percent",
7610
+ "grossloss",
7611
+ "grossloss_percent",
7612
+ "opentrades",
7613
+ "closedtrades",
7614
+ "wintrades",
7615
+ "losstrades",
7616
+ "eventrades",
7617
+ "avg_trade",
7618
+ "avg_trade_percent",
7619
+ "avg_winning_trade",
7620
+ "avg_winning_trade_percent",
7621
+ "avg_losing_trade",
7622
+ "avg_losing_trade_percent",
7623
+ "max_drawdown",
7624
+ "max_drawdown_percent",
7625
+ "max_runup",
7626
+ "max_runup_percent",
7627
+ "max_contracts_held_all",
7628
+ "max_contracts_held_long",
7629
+ "max_contracts_held_short",
7630
+ "margin_liquidation_price"
7631
+ ];
7632
+ var STRATEGY_TEXT_VARIABLES = ["position_entry_name", "account_currency"];
7633
+
7551
7634
  // src/runtime/adapters/LightweightChartsAdapter.ts
7552
7635
  var LightweightChartsAdapter = class {
7553
7636
  chart;
@@ -7756,7 +7839,7 @@ var SimpleInputAdapter = class {
7756
7839
  };
7757
7840
 
7758
7841
  // src/index.ts
7759
- var VERSION = "0.7.2";
7842
+ var VERSION = "0.8.1";
7760
7843
  var info2 = {
7761
7844
  name: "OakScriptJS",
7762
7845
  version: VERSION,
@@ -7781,6 +7864,10 @@ function alertcondition(_condition, _title, _message) {
7781
7864
  export {
7782
7865
  BarData,
7783
7866
  LightweightChartsAdapter,
7867
+ STRATEGY_CONSTANTS,
7868
+ STRATEGY_DEFAULTS,
7869
+ STRATEGY_NUMBER_VARIABLES,
7870
+ STRATEGY_TEXT_VARIABLES,
7784
7871
  Series,
7785
7872
  SimpleInputAdapter,
7786
7873
  VERSION,
@@ -7788,11 +7875,13 @@ export {
7788
7875
  abs,
7789
7876
  alertcondition,
7790
7877
  array_exports as array,
7878
+ at,
7791
7879
  atr2 as atr,
7792
7880
  avg,
7793
7881
  bb2 as bb,
7794
7882
  box_exports as box,
7795
7883
  calculateZigZag,
7884
+ callsite_exports as callsite,
7796
7885
  ceil,
7797
7886
  change2 as change,
7798
7887
  chartpoint_exports as chartPoint,
@@ -7803,9 +7892,10 @@ export {
7803
7892
  compare_exports as compare,
7804
7893
  cos,
7805
7894
  createPlot,
7806
- crossover2 as crossover,
7807
- crossunder2 as crossunder,
7895
+ crossover3 as crossover,
7896
+ crossunder3 as crossunder,
7808
7897
  cum2 as cum,
7898
+ div,
7809
7899
  ema2 as ema,
7810
7900
  exp,
7811
7901
  fixnan,
@@ -7815,6 +7905,7 @@ export {
7815
7905
  getHigh,
7816
7906
  getLow,
7817
7907
  getOpen,
7908
+ getSource,
7818
7909
  getSourceSeries,
7819
7910
  hline,
7820
7911
  indicator,