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.cjs CHANGED
@@ -22,6 +22,10 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  BarData: () => BarData,
24
24
  LightweightChartsAdapter: () => LightweightChartsAdapter,
25
+ STRATEGY_CONSTANTS: () => STRATEGY_CONSTANTS,
26
+ STRATEGY_DEFAULTS: () => STRATEGY_DEFAULTS,
27
+ STRATEGY_NUMBER_VARIABLES: () => STRATEGY_NUMBER_VARIABLES,
28
+ STRATEGY_TEXT_VARIABLES: () => STRATEGY_TEXT_VARIABLES,
25
29
  Series: () => Series,
26
30
  SimpleInputAdapter: () => SimpleInputAdapter,
27
31
  VERSION: () => VERSION,
@@ -29,11 +33,13 @@ __export(index_exports, {
29
33
  abs: () => abs,
30
34
  alertcondition: () => alertcondition,
31
35
  array: () => array_exports,
36
+ at: () => at,
32
37
  atr: () => atr2,
33
38
  avg: () => avg,
34
39
  bb: () => bb2,
35
40
  box: () => box_exports,
36
41
  calculateZigZag: () => calculateZigZag,
42
+ callsite: () => callsite_exports,
37
43
  ceil: () => ceil,
38
44
  change: () => change2,
39
45
  chartPoint: () => chartpoint_exports,
@@ -44,9 +50,10 @@ __export(index_exports, {
44
50
  compare: () => compare_exports,
45
51
  cos: () => cos,
46
52
  createPlot: () => createPlot,
47
- crossover: () => crossover2,
48
- crossunder: () => crossunder2,
53
+ crossover: () => crossover3,
54
+ crossunder: () => crossunder3,
49
55
  cum: () => cum2,
56
+ div: () => div,
50
57
  ema: () => ema2,
51
58
  exp: () => exp,
52
59
  fixnan: () => fixnan,
@@ -56,6 +63,7 @@ __export(index_exports, {
56
63
  getHigh: () => getHigh,
57
64
  getLow: () => getLow,
58
65
  getOpen: () => getOpen,
66
+ getSource: () => getSource,
59
67
  getSourceSeries: () => getSourceSeries,
60
68
  hline: () => hline,
61
69
  indicator: () => indicator,
@@ -193,13 +201,47 @@ __export(ta_exports, {
193
201
  wvad: () => wvad,
194
202
  zigzag: () => zigzag
195
203
  });
204
+
205
+ // src/compare/index.ts
206
+ var compare_exports = {};
207
+ __export(compare_exports, {
208
+ EPSILON: () => EPSILON,
209
+ eq: () => eq,
210
+ ge: () => ge,
211
+ gt: () => gt,
212
+ le: () => le,
213
+ lt: () => lt,
214
+ ne: () => ne
215
+ });
216
+ var EPSILON = 1e-10;
217
+ var valid = (a, b2) => !Number.isNaN(a) && !Number.isNaN(b2);
218
+ function eq(a, b2) {
219
+ return valid(a, b2) && Math.abs(a - b2) <= EPSILON;
220
+ }
221
+ function ne(a, b2) {
222
+ return valid(a, b2) && Math.abs(a - b2) > EPSILON;
223
+ }
224
+ function lt(a, b2) {
225
+ return valid(a, b2) && b2 - a > EPSILON;
226
+ }
227
+ function le(a, b2) {
228
+ return valid(a, b2) && a - b2 <= EPSILON;
229
+ }
230
+ function gt(a, b2) {
231
+ return valid(a, b2) && a - b2 > EPSILON;
232
+ }
233
+ function ge(a, b2) {
234
+ return valid(a, b2) && b2 - a <= EPSILON;
235
+ }
236
+
237
+ // src/ta/index.ts
196
238
  function sma(source, length2) {
197
239
  const result = [];
198
240
  const len = Math.floor(length2);
199
241
  const window = [];
200
242
  for (let i = 0; i < source.length; i++) {
201
243
  const v = source[i];
202
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
244
+ if (v !== void 0 && v !== null && Number.isFinite(v)) {
203
245
  window.push(v);
204
246
  if (window.length > len) window.shift();
205
247
  }
@@ -384,7 +426,7 @@ function tr(handle_na = false, high, low, close) {
384
426
  const result = [];
385
427
  for (let i = 0; i < high.length; i++) {
386
428
  if (i === 0) {
387
- result.push(high[i] - low[i]);
429
+ result.push(handle_na ? high[i] - low[i] : NaN);
388
430
  } else {
389
431
  const prevClose = close[i - 1];
390
432
  if (isNaN(prevClose)) {
@@ -411,7 +453,7 @@ function atr(length2, high, low, close) {
411
453
  "ta.atr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
412
454
  );
413
455
  }
414
- const trueRange = tr(false, high, low, close);
456
+ const trueRange = tr(true, high, low, close);
415
457
  return rma(trueRange, length2);
416
458
  }
417
459
  function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
@@ -543,7 +585,7 @@ function lowest(source, length2) {
543
585
  function cum(source) {
544
586
  let sum4 = 0;
545
587
  return source.map((v) => {
546
- if (Number.isNaN(v)) return NaN;
588
+ if (!Number.isFinite(v)) return NaN;
547
589
  sum4 += v;
548
590
  return sum4;
549
591
  });
@@ -561,41 +603,24 @@ function cross(source1, source2) {
561
603
  }
562
604
  return result;
563
605
  }
564
- function rising(source, length2) {
606
+ function monotonic(source, length2, step) {
565
607
  const result = [];
608
+ const values2 = [];
566
609
  for (let i = 0; i < source.length; i++) {
567
- if (i < length2) {
568
- result.push(false);
569
- } else {
570
- let isRising = true;
571
- for (let j = 1; j <= length2; j++) {
572
- if (source[i - j + 1] <= source[i - j]) {
573
- isRising = false;
574
- break;
575
- }
576
- }
577
- result.push(isRising);
578
- }
610
+ const v = source[i];
611
+ if (!Number.isNaN(v)) values2.push(v);
612
+ const n = values2.length;
613
+ let ok = n > length2;
614
+ for (let j = 1; ok && j <= length2; j++) ok = step(values2[n - j], values2[n - j - 1]);
615
+ result.push(ok);
579
616
  }
580
617
  return result;
581
618
  }
619
+ function rising(source, length2) {
620
+ return monotonic(source, length2, gt);
621
+ }
582
622
  function falling(source, length2) {
583
- const result = [];
584
- for (let i = 0; i < source.length; i++) {
585
- if (i < length2) {
586
- result.push(false);
587
- } else {
588
- let isFalling = true;
589
- for (let j = 1; j <= length2; j++) {
590
- if (source[i - j + 1] >= source[i - j]) {
591
- isFalling = false;
592
- break;
593
- }
594
- }
595
- result.push(isFalling);
596
- }
597
- }
598
- return result;
623
+ return monotonic(source, length2, lt);
599
624
  }
600
625
  function roc(source, length2) {
601
626
  const result = [];
@@ -819,33 +844,16 @@ function correlation(source1, source2, length2) {
819
844
  function percentrank(source, length2) {
820
845
  const result = [];
821
846
  for (let i = 0; i < source.length; i++) {
822
- if (i < length2) {
847
+ const currentValue = source[i];
848
+ if (i < length2 || Number.isNaN(currentValue)) {
823
849
  result.push(NaN);
824
- } else {
825
- if (isNaN(source[i])) {
826
- result.push(NaN);
827
- continue;
828
- }
829
- let hasNaN = false;
830
- for (let j = 1; j <= length2; j++) {
831
- if (isNaN(source[i - j])) {
832
- hasNaN = true;
833
- break;
834
- }
835
- }
836
- if (hasNaN) {
837
- result.push(NaN);
838
- } else {
839
- const currentValue = source[i];
840
- let countLessOrEqual = 0;
841
- for (let j = 1; j <= length2; j++) {
842
- if (source[i - j] <= currentValue) {
843
- countLessOrEqual++;
844
- }
845
- }
846
- result.push(countLessOrEqual / length2 * 100);
847
- }
850
+ continue;
848
851
  }
852
+ let countLessOrEqual = 0;
853
+ for (let j = 1; j <= length2; j++) {
854
+ if (le(source[i - j], currentValue)) countLessOrEqual++;
855
+ }
856
+ result.push(countLessOrEqual / length2 * 100);
849
857
  }
850
858
  return result;
851
859
  }
@@ -888,48 +896,26 @@ function mfi(source, length2, volume) {
888
896
  "ta.mfi() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
889
897
  );
890
898
  }
891
- const result = [];
892
- const changes = [NaN];
893
- for (let i = 1; i < source.length; i++) {
894
- changes.push(source[i] - source[i - 1]);
895
- }
896
- const positiveFlow = [];
897
- const negativeFlow = [];
899
+ const upperFlow = [];
900
+ const lowerFlow = [];
898
901
  for (let i = 0; i < source.length; i++) {
899
- if (i === 0 || isNaN(changes[i])) {
900
- positiveFlow.push(0);
901
- negativeFlow.push(0);
902
- } else if (changes[i] > 0) {
903
- positiveFlow.push(volume[i] * source[i]);
904
- negativeFlow.push(0);
905
- } else if (changes[i] < 0) {
906
- positiveFlow.push(0);
907
- negativeFlow.push(volume[i] * source[i]);
908
- } else {
909
- positiveFlow.push(0);
910
- negativeFlow.push(0);
911
- }
912
- }
913
- for (let i = 0; i < source.length; i++) {
914
- if (i < length2) {
915
- result.push(NaN);
916
- } else {
917
- let posSum = 0;
918
- let negSum = 0;
919
- for (let j = 0; j < length2; j++) {
920
- posSum += positiveFlow[i - j];
921
- negSum += negativeFlow[i - j];
922
- }
923
- if (negSum === 0) {
924
- result.push(100);
925
- } else {
926
- const moneyRatio = posSum / negSum;
927
- const mfiValue = 100 - 100 / (1 + moneyRatio);
928
- result.push(mfiValue);
929
- }
930
- }
931
- }
932
- return result;
902
+ const change3 = i === 0 ? NaN : source[i] - source[i - 1];
903
+ const flow = volume[i] * source[i];
904
+ upperFlow.push(le(change3, 0) ? 0 : flow);
905
+ lowerFlow.push(ge(change3, 0) ? 0 : flow);
906
+ }
907
+ const sumAt = (flow, i) => {
908
+ const values2 = lastValues(flow, i, Math.floor(length2));
909
+ return values2 ? values2.reduce((a, b2) => a + b2, 0) : NaN;
910
+ };
911
+ const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
912
+ const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
913
+ return upper2.map((u, i) => {
914
+ const l = lower2[i];
915
+ if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
916
+ if (l === 0) return 100;
917
+ return 100 - 100 / (1 + u / l);
918
+ });
933
919
  }
934
920
  function hma(source, length2) {
935
921
  const halfLength = Math.floor(length2 / 2);
@@ -1093,50 +1079,32 @@ function dmi(diLength, adxSmoothing, high, low, close) {
1093
1079
  const len = Math.max(high.length, low.length, close.length);
1094
1080
  const plusDM = [];
1095
1081
  const minusDM = [];
1096
- const trueRangeValues = tr(false, high, low, close);
1097
- for (let i = 0; i < len; i++) {
1098
- if (i === 0) {
1099
- plusDM.push(0);
1100
- minusDM.push(0);
1101
- } else {
1102
- const upMove = high[i] - high[i - 1];
1103
- const downMove = low[i - 1] - low[i];
1104
- let plusDMVal = 0;
1105
- let minusDMVal = 0;
1106
- if (upMove > downMove && upMove > 0) {
1107
- plusDMVal = upMove;
1108
- }
1109
- if (downMove > upMove && downMove > 0) {
1110
- minusDMVal = downMove;
1111
- }
1112
- plusDM.push(plusDMVal);
1113
- minusDM.push(minusDMVal);
1114
- }
1115
- }
1116
- const smoothedPlusDM = rma(plusDM, diLength);
1117
- const smoothedMinusDM = rma(minusDM, diLength);
1118
- const smoothedTR = rma(trueRangeValues, diLength);
1119
- const plusDI = [];
1120
- const minusDI = [];
1121
- for (let i = 0; i < len; i++) {
1122
- if (smoothedTR[i] === 0) {
1123
- plusDI.push(0);
1124
- minusDI.push(0);
1125
- } else {
1126
- plusDI.push(smoothedPlusDM[i] / smoothedTR[i] * 100);
1127
- minusDI.push(smoothedMinusDM[i] / smoothedTR[i] * 100);
1128
- }
1129
- }
1130
- const dx = [];
1131
1082
  for (let i = 0; i < len; i++) {
1132
- const sum4 = plusDI[i] + minusDI[i];
1133
- if (sum4 === 0) {
1134
- dx.push(0);
1135
- } else {
1136
- dx.push(Math.abs(plusDI[i] - minusDI[i]) / sum4 * 100);
1137
- }
1138
- }
1139
- const adx = rma(dx, adxSmoothing);
1083
+ const up = i === 0 ? NaN : high[i] - high[i - 1];
1084
+ const down = i === 0 ? NaN : low[i - 1] - low[i];
1085
+ plusDM.push(Number.isNaN(up) ? NaN : gt(up, down) && gt(up, 0) ? up : 0);
1086
+ minusDM.push(Number.isNaN(down) ? NaN : gt(down, up) && gt(down, 0) ? down : 0);
1087
+ }
1088
+ const trur = rma(tr(false, high, low, close), diLength);
1089
+ const smoothedPlus = rma(plusDM, diLength);
1090
+ const smoothedMinus = rma(minusDM, diLength);
1091
+ const directional = (smoothed) => {
1092
+ let last2 = NaN;
1093
+ return smoothed.map((v, i) => {
1094
+ const t2 = trur[i];
1095
+ const value = t2 === 0 ? NaN : 100 * v / t2;
1096
+ if (!Number.isNaN(value)) last2 = value;
1097
+ return last2;
1098
+ });
1099
+ };
1100
+ const plusDI = directional(smoothedPlus);
1101
+ const minusDI = directional(smoothedMinus);
1102
+ const dx = plusDI.map((plus, i) => {
1103
+ const minus = minusDI[i];
1104
+ const sum4 = plus + minus;
1105
+ return Math.abs(plus - minus) / (eq(sum4, 0) ? 1 : sum4);
1106
+ });
1107
+ const adx = rma(dx, adxSmoothing).map((v) => 100 * v);
1140
1108
  return [plusDI, minusDI, adx];
1141
1109
  }
1142
1110
  function tsi(source, shortLength, longLength) {
@@ -1412,63 +1380,36 @@ function mode(source, length2) {
1412
1380
  return result;
1413
1381
  }
1414
1382
  function percentile_linear_interpolation(source, length2, percentage) {
1383
+ return sortedWindow(source, length2, (s) => {
1384
+ const pos2 = percentage / 100 * length2 - 0.5;
1385
+ if (pos2 <= 0) return s[0];
1386
+ if (pos2 >= length2 - 1) return s[length2 - 1];
1387
+ const lo = Math.floor(pos2);
1388
+ return s[lo] + (pos2 - lo) * (s[lo + 1] - s[lo]);
1389
+ });
1390
+ }
1391
+ function sortedWindow(source, length2, fn) {
1415
1392
  const result = [];
1393
+ const values2 = [];
1394
+ const bars = [];
1416
1395
  for (let i = 0; i < source.length; i++) {
1417
- if (i < length2 - 1) {
1418
- result.push(NaN);
1419
- continue;
1420
- }
1421
- const values2 = [];
1422
- for (let j = 0; j < length2; j++) {
1423
- const value = source[i - j];
1424
- values2.push(value);
1425
- }
1426
- if (values2.some((v) => isNaN(v))) {
1427
- result.push(NaN);
1428
- continue;
1429
- }
1430
- const sorted = [...values2].sort((a, b2) => a - b2);
1431
- const position = percentage / 100 * (sorted.length - 1);
1432
- const lowerIndex = Math.floor(position);
1433
- const upperIndex = Math.ceil(position);
1434
- if (lowerIndex === upperIndex) {
1435
- result.push(sorted[lowerIndex]);
1436
- } else {
1437
- const fraction = position - lowerIndex;
1438
- const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
1439
- result.push(interpolated);
1396
+ const v = source[i];
1397
+ let k = 0;
1398
+ while (k < values2.length && !(values2[k] >= v)) k++;
1399
+ values2.splice(k, 0, v);
1400
+ bars.splice(k, 0, i);
1401
+ if (i >= length2) {
1402
+ const old = bars.indexOf(i - length2);
1403
+ values2.splice(old, 1);
1404
+ bars.splice(old, 1);
1440
1405
  }
1406
+ result.push(i < length2 - 1 ? NaN : fn(values2));
1441
1407
  }
1442
1408
  return result;
1443
1409
  }
1444
1410
  function percentile_nearest_rank(source, length2, percentage) {
1445
- const result = [];
1446
- for (let i = 0; i < source.length; i++) {
1447
- if (i < length2 - 1) {
1448
- result.push(NaN);
1449
- continue;
1450
- }
1451
- const values2 = [];
1452
- for (let j = 0; j < length2; j++) {
1453
- const value = source[i - j];
1454
- if (!isNaN(value)) {
1455
- values2.push(value);
1456
- }
1457
- }
1458
- if (values2.length === 0) {
1459
- result.push(NaN);
1460
- continue;
1461
- }
1462
- const sorted = [...values2].sort((a, b2) => a - b2);
1463
- if (percentage >= 100) {
1464
- result.push(sorted[sorted.length - 1]);
1465
- continue;
1466
- }
1467
- const rank2 = Math.ceil(percentage / 100 * sorted.length);
1468
- const index = Math.max(0, rank2 - 1);
1469
- result.push(sorted[index]);
1470
- }
1471
- return result;
1411
+ const rank2 = Math.min(length2, Math.max(1, Math.ceil(percentage / 100 * length2)));
1412
+ return sortedWindow(source, length2, (s) => s[rank2 - 1]);
1472
1413
  }
1473
1414
  function rci(source, length2) {
1474
1415
  const result = [];
@@ -1963,38 +1904,6 @@ __export(math_exports, {
1963
1904
  toradians: () => toradians
1964
1905
  });
1965
1906
 
1966
- // src/compare/index.ts
1967
- var compare_exports = {};
1968
- __export(compare_exports, {
1969
- EPSILON: () => EPSILON,
1970
- eq: () => eq,
1971
- ge: () => ge,
1972
- gt: () => gt,
1973
- le: () => le,
1974
- lt: () => lt,
1975
- ne: () => ne
1976
- });
1977
- var EPSILON = 1e-10;
1978
- var valid = (a, b2) => !Number.isNaN(a) && !Number.isNaN(b2);
1979
- function eq(a, b2) {
1980
- return valid(a, b2) && Math.abs(a - b2) <= EPSILON;
1981
- }
1982
- function ne(a, b2) {
1983
- return valid(a, b2) && Math.abs(a - b2) > EPSILON;
1984
- }
1985
- function lt(a, b2) {
1986
- return valid(a, b2) && b2 - a > EPSILON;
1987
- }
1988
- function le(a, b2) {
1989
- return valid(a, b2) && a - b2 <= EPSILON;
1990
- }
1991
- function gt(a, b2) {
1992
- return valid(a, b2) && a - b2 > EPSILON;
1993
- }
1994
- function ge(a, b2) {
1995
- return valid(a, b2) && b2 - a <= EPSILON;
1996
- }
1997
-
1998
1907
  // src/runtime/series.ts
1999
1908
  var BarData = class _BarData {
2000
1909
  _bars;
@@ -2218,7 +2127,7 @@ var Series = class _Series {
2218
2127
  return new _Series(this.dataSource, (bar, i, data) => {
2219
2128
  const a = this.extractor(bar, i, data);
2220
2129
  const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
2221
- return b2 !== 0 ? a / b2 : NaN;
2130
+ return a / b2;
2222
2131
  });
2223
2132
  }
2224
2133
  /**
@@ -3225,15 +3134,27 @@ function percentile_linear_interpolation2(id, percentage) {
3225
3134
  if (id.length === 0) {
3226
3135
  return NaN;
3227
3136
  }
3228
- const result = percentile_linear_interpolation(id, id.length, percentage);
3229
- return result[result.length - 1];
3137
+ const s = sortedNaLast(id);
3138
+ const n = s.length;
3139
+ const pos2 = percentage / 100 * n - 0.5;
3140
+ if (pos2 <= 0) return s[0];
3141
+ if (pos2 >= n - 1) return s[n - 1];
3142
+ const lo = Math.floor(pos2);
3143
+ const f = pos2 - lo;
3144
+ if (f === 0) return s[lo];
3145
+ return Number.isNaN(s[n - 1]) ? NaN : s[lo] + f * (s[lo + 1] - s[lo]);
3146
+ }
3147
+ function sortedNaLast(id) {
3148
+ const numbers = id.filter((v) => !Number.isNaN(v)).sort((a, b2) => a - b2);
3149
+ return [...numbers, ...Array(id.length - numbers.length).fill(NaN)];
3230
3150
  }
3231
3151
  function percentile_nearest_rank2(id, percentage) {
3232
3152
  if (id.length === 0) {
3233
3153
  return NaN;
3234
3154
  }
3235
- const result = percentile_nearest_rank(id, id.length, percentage);
3236
- return result[result.length - 1];
3155
+ const s = sortedNaLast(id);
3156
+ const rank2 = Math.min(s.length, Math.max(1, Math.ceil(percentage / 100 * s.length)));
3157
+ return s[rank2 - 1];
3237
3158
  }
3238
3159
  function percentrank2(id, index) {
3239
3160
  if (id.length === 0 || index < 0 || index >= id.length) {
@@ -3944,11 +3865,13 @@ __export(color_exports, {
3944
3865
  white: () => white,
3945
3866
  yellow: () => yellow
3946
3867
  });
3868
+ var channel = (v) => v === null || v === void 0 || Number.isNaN(v) ? 0 : Math.max(0, Math.min(255, Math.trunc(v)));
3869
+ var alphaOf = (transp) => transp === null || Number.isNaN(transp) ? 0 : 1 - Math.max(0, Math.min(100, transp)) / 100;
3947
3870
  function rgb(red2, green2, blue2, transp) {
3948
- const r2 = Math.max(0, Math.min(255, red2));
3949
- const g2 = Math.max(0, Math.min(255, green2));
3950
- const b2 = Math.max(0, Math.min(255, blue2));
3951
- const a = transp !== void 0 ? 1 - transp / 100 : 1;
3871
+ const r2 = channel(red2);
3872
+ const g2 = channel(green2);
3873
+ const b2 = channel(blue2);
3874
+ const a = transp !== void 0 ? alphaOf(transp) : 1;
3952
3875
  if (a === 1) {
3953
3876
  return `rgb(${r2}, ${g2}, ${b2})`;
3954
3877
  }
@@ -3975,7 +3898,8 @@ function b(clr) {
3975
3898
  return parseColor(clr).b;
3976
3899
  }
3977
3900
  function t(clr) {
3978
- return parseColor(clr).t;
3901
+ const alpha = Math.round((1 - parseColor(clr).t / 100) * 255);
3902
+ return Math.round((1 - alpha / 255) * 100);
3979
3903
  }
3980
3904
  function parseColor(clr) {
3981
3905
  if (typeof clr === "string") {
@@ -4028,13 +3952,12 @@ function from_gradient(value, bottom_value, top_value, bottom_color, top_color)
4028
3952
  };
4029
3953
  const c1 = rgba(bottom_color);
4030
3954
  const c2 = rgba(top_color);
4031
- const w1 = c1.a * (1 - k);
4032
- const w2 = c2.a * k;
4033
- const a = w1 + w2;
3955
+ const a = c1.a * (1 - k) + c2.a * k;
4034
3956
  if (a === 0) return transparent;
4035
- const mix = (x1, x2) => Math.floor((x1 * w1 + x2 * w2) / a);
3957
+ const mix = (x1, x2) => Math.floor((x1 / 255 * c1.a * (1 - k) + x2 / 255 * c2.a * k) / (a / 255));
4036
3958
  const [r2, g2, b2] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
4037
- return a === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${a / 255})`;
3959
+ const alpha = Math.floor(a);
3960
+ return alpha === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${alpha / 255})`;
4038
3961
  }
4039
3962
 
4040
3963
  // src/time/index.ts
@@ -6242,13 +6165,60 @@ var format_none = 0;
6242
6165
  var format_bold = 1;
6243
6166
  var format_italic = 2;
6244
6167
 
6168
+ // src/callsite/index.ts
6169
+ var callsite_exports = {};
6170
+ __export(callsite_exports, {
6171
+ barssince: () => barssince2,
6172
+ cross: () => cross2,
6173
+ crossover: () => crossover2,
6174
+ crossunder: () => crossunder2,
6175
+ whenCalled: () => whenCalled
6176
+ });
6177
+ function whenCalled(called, fn, ...sources) {
6178
+ const bars = [];
6179
+ for (let i = 0; i < called.length; i++) if (called[i]) bars.push(i);
6180
+ const result = fn(...sources.map((s) => bars.map((i) => s[i] ?? NaN)));
6181
+ const out = new Array(called.length).fill(NaN);
6182
+ bars.forEach((bar, k) => {
6183
+ out[bar] = result[k];
6184
+ });
6185
+ return out;
6186
+ }
6187
+ function twoSeriesSite(test) {
6188
+ let prevA = NaN;
6189
+ let prevB = NaN;
6190
+ return (a, b2) => {
6191
+ const result = test(a, b2, prevA, prevB);
6192
+ prevA = a;
6193
+ prevB = b2;
6194
+ return result;
6195
+ };
6196
+ }
6197
+ function crossover2() {
6198
+ return twoSeriesSite((a, b2, pa, pb) => a > b2 && pa <= pb);
6199
+ }
6200
+ function crossunder2() {
6201
+ return twoSeriesSite((a, b2, pa, pb) => a < b2 && pa >= pb);
6202
+ }
6203
+ function barssince2() {
6204
+ let count = NaN;
6205
+ return (condition) => {
6206
+ if (condition && !Number.isNaN(condition)) count = 0;
6207
+ else if (!Number.isNaN(count)) count++;
6208
+ return count;
6209
+ };
6210
+ }
6211
+ function cross2() {
6212
+ return twoSeriesSite((a, b2, pa, pb) => a > b2 && pa <= pb || a < b2 && pa >= pb);
6213
+ }
6214
+
6245
6215
  // src/ta-series.ts
6246
6216
  var ta_series_exports = {};
6247
6217
  __export(ta_series_exports, {
6248
6218
  accdist: () => accdist2,
6249
6219
  alma: () => alma2,
6250
6220
  atr: () => atr2,
6251
- barssince: () => barssince2,
6221
+ barssince: () => barssince3,
6252
6222
  bb: () => bb2,
6253
6223
  bbw: () => bbw2,
6254
6224
  cci: () => cci2,
@@ -6256,9 +6226,9 @@ __export(ta_series_exports, {
6256
6226
  cmo: () => cmo2,
6257
6227
  cog: () => cog2,
6258
6228
  correlation: () => correlation2,
6259
- cross: () => cross2,
6260
- crossover: () => crossover2,
6261
- crossunder: () => crossunder2,
6229
+ cross: () => cross3,
6230
+ crossover: () => crossover3,
6231
+ crossunder: () => crossunder3,
6262
6232
  cum: () => cum2,
6263
6233
  dev: () => dev2,
6264
6234
  dmi: () => dmi2,
@@ -6424,7 +6394,7 @@ function stoch2(source, high, low, length2) {
6424
6394
  const result = stoch(sourceValues, highValues, lowValues, length2);
6425
6395
  return Series.fromArray(bars, result);
6426
6396
  }
6427
- function crossover2(source1, source2) {
6397
+ function crossover3(source1, source2) {
6428
6398
  const bars = source1.bars;
6429
6399
  const vals1 = source1.toArray();
6430
6400
  const vals2 = typeof source2 === "number" ? Array(bars.length).fill(source2) : source2.toArray();
@@ -6432,7 +6402,7 @@ function crossover2(source1, source2) {
6432
6402
  const numResult = result.map((b2) => b2 ? 1 : 0);
6433
6403
  return Series.fromArray(bars, numResult);
6434
6404
  }
6435
- function crossunder2(source1, source2) {
6405
+ function crossunder3(source1, source2) {
6436
6406
  const bars = source1.bars;
6437
6407
  const vals1 = source1.toArray();
6438
6408
  const vals2 = typeof source2 === "number" ? Array(bars.length).fill(source2) : source2.toArray();
@@ -6440,7 +6410,7 @@ function crossunder2(source1, source2) {
6440
6410
  const numResult = result.map((b2) => b2 ? 1 : 0);
6441
6411
  return Series.fromArray(bars, numResult);
6442
6412
  }
6443
- function cross2(source1, source2) {
6413
+ function cross3(source1, source2) {
6444
6414
  const bars = source1.bars;
6445
6415
  const vals1 = source1.toArray();
6446
6416
  const vals2 = typeof source2 === "number" ? Array(bars.length).fill(source2) : source2.toArray();
@@ -6636,7 +6606,7 @@ function pivotlow2(source, leftbars, rightbars) {
6636
6606
  const result = pivotlow(source.toArray(), leftbars, rightbars);
6637
6607
  return Series.fromArray(bars, result);
6638
6608
  }
6639
- function barssince2(condition) {
6609
+ function barssince3(condition) {
6640
6610
  const bars = condition.bars;
6641
6611
  const boolValues = condition.toArray().map((v) => v === 1);
6642
6612
  const result = barssince(boolValues);
@@ -6759,9 +6729,9 @@ var ta = {
6759
6729
  atr: atr2,
6760
6730
  tr: tr2,
6761
6731
  stoch: stoch2,
6762
- crossover: crossover2,
6763
- crossunder: crossunder2,
6764
- cross: cross2,
6732
+ crossover: crossover3,
6733
+ crossunder: crossunder3,
6734
+ cross: cross3,
6765
6735
  change: change2,
6766
6736
  mom: mom2,
6767
6737
  roc: roc2,
@@ -6788,7 +6758,7 @@ var ta = {
6788
6758
  hma: hma2,
6789
6759
  pivothigh: pivothigh2,
6790
6760
  pivotlow: pivotlow2,
6791
- barssince: barssince2,
6761
+ barssince: barssince3,
6792
6762
  valuewhen: valuewhen2,
6793
6763
  dmi: dmi2,
6794
6764
  tsi: tsi2,
@@ -6820,7 +6790,7 @@ var ta = {
6820
6790
 
6821
6791
  // src/utils/index.ts
6822
6792
  function isNA(value) {
6823
- return value === null || value === void 0 || Number.isNaN(value) || isDeleted(value);
6793
+ return value === null || value === void 0 || Number.isNaN(value) || value === Infinity || value === -Infinity || isDeleted(value);
6824
6794
  }
6825
6795
  function nz(value, defaultValue = 0) {
6826
6796
  return isNA(value) ? defaultValue : value;
@@ -6829,7 +6799,7 @@ function fixnan(source) {
6829
6799
  let last2;
6830
6800
  let seen = false;
6831
6801
  return source.map((v) => {
6832
- if (!isNA(v)) {
6802
+ if (!(v === null || v === void 0 || Number.isNaN(v) || isDeleted(v))) {
6833
6803
  last2 = v;
6834
6804
  seen = true;
6835
6805
  return v;
@@ -6837,6 +6807,16 @@ function fixnan(source) {
6837
6807
  return seen ? last2 : v;
6838
6808
  });
6839
6809
  }
6810
+ function at(source, i, offset = 0) {
6811
+ if (offset < 0) {
6812
+ throw new RangeError(`at(): the offset must be >= 0 (got ${offset}); a negative offset reads a later bar.`);
6813
+ }
6814
+ const j = i - offset;
6815
+ return j >= 0 && j < source.length ? source[j] ?? NaN : NaN;
6816
+ }
6817
+ function div(a, b2) {
6818
+ return a / b2;
6819
+ }
6840
6820
  function ohlcFromBars(bars) {
6841
6821
  return {
6842
6822
  open: bars.map((b2) => b2.open),
@@ -6857,6 +6837,50 @@ function getLow(ohlc) {
6857
6837
  function getOpen(ohlc) {
6858
6838
  return ohlc.open;
6859
6839
  }
6840
+ function getSource(data, source = "close") {
6841
+ const isOHLC = "open" in data && Array.isArray(data.open);
6842
+ if (isOHLC) {
6843
+ const ohlc = data;
6844
+ switch (source) {
6845
+ case "close":
6846
+ return ohlc.close;
6847
+ case "open":
6848
+ return ohlc.open;
6849
+ case "high":
6850
+ return ohlc.high;
6851
+ case "low":
6852
+ return ohlc.low;
6853
+ case "hl2":
6854
+ return ohlc.high.map((h, i) => (h + ohlc.low[i]) / 2);
6855
+ case "hlc3":
6856
+ return ohlc.high.map((h, i) => (h + ohlc.low[i] + ohlc.close[i]) / 3);
6857
+ case "ohlc4":
6858
+ return ohlc.open.map((o, i) => (o + ohlc.high[i] + ohlc.low[i] + ohlc.close[i]) / 4);
6859
+ case "hlcc4":
6860
+ return ohlc.high.map((h, i) => (h + ohlc.low[i] + ohlc.close[i] + ohlc.close[i]) / 4);
6861
+ }
6862
+ } else {
6863
+ const bars = data;
6864
+ switch (source) {
6865
+ case "close":
6866
+ return bars.map((b2) => b2.close);
6867
+ case "open":
6868
+ return bars.map((b2) => b2.open);
6869
+ case "high":
6870
+ return bars.map((b2) => b2.high);
6871
+ case "low":
6872
+ return bars.map((b2) => b2.low);
6873
+ case "hl2":
6874
+ return bars.map((b2) => (b2.high + b2.low) / 2);
6875
+ case "hlc3":
6876
+ return bars.map((b2) => (b2.high + b2.low + b2.close) / 3);
6877
+ case "ohlc4":
6878
+ return bars.map((b2) => (b2.open + b2.high + b2.low + b2.close) / 4);
6879
+ case "hlcc4":
6880
+ return bars.map((b2) => (b2.high + b2.low + b2.close + b2.close) / 4);
6881
+ }
6882
+ }
6883
+ }
6860
6884
  function getSourceSeries(bars, source = "close") {
6861
6885
  const open = new Series(bars, (bar) => bar.open);
6862
6886
  const high = new Series(bars, (bar) => bar.high);
@@ -7425,7 +7449,7 @@ function hline(price, title, color, linestyle, linewidth) {
7425
7449
  const options = {
7426
7450
  color,
7427
7451
  lineWidth: linewidth,
7428
- lineStyle: getLineStyle(linestyle)
7452
+ lineStyle: getLineStyle(linestyle ?? "dashed")
7429
7453
  };
7430
7454
  const seriesHandle = context.chart.addSeries("line", options);
7431
7455
  const data = context.ohlcv.time.map((time) => ({
@@ -7656,6 +7680,73 @@ function getSourceData(ohlcv, sourceName) {
7656
7680
  }
7657
7681
  }
7658
7682
 
7683
+ // src/strategy/index.ts
7684
+ var STRATEGY_CONSTANTS = {
7685
+ long: "long",
7686
+ short: "short",
7687
+ fixed: "fixed",
7688
+ cash: "cash",
7689
+ percent_of_equity: "percent_of_equity",
7690
+ commission: { percent: "percent", cash_per_contract: "cash_per_contract", cash_per_order: "cash_per_order" },
7691
+ direction: { all: "all", long: "long", short: "short" },
7692
+ oca: { none: "none", cancel: "cancel", reduce: "reduce" }
7693
+ };
7694
+ var STRATEGY_DEFAULTS = {
7695
+ pyramiding: 0,
7696
+ calc_on_order_fills: false,
7697
+ calc_on_every_tick: false,
7698
+ backtest_fill_limits_assumption: 0,
7699
+ // v6 values; PineScript v5: fixed / 1 / 1000000 and margins 0
7700
+ default_qty_type: "percent_of_equity",
7701
+ default_qty_value: 100,
7702
+ initial_capital: 1e5,
7703
+ slippage: 0,
7704
+ commission_type: "percent",
7705
+ commission_value: 0,
7706
+ process_orders_on_close: false,
7707
+ close_entries_rule: "FIFO",
7708
+ margin_long: 100,
7709
+ margin_short: 100,
7710
+ calc_bars_count: 0,
7711
+ risk_free_rate: 2,
7712
+ use_bar_magnifier: false,
7713
+ fill_orders_on_standard_ohlc: false
7714
+ };
7715
+ var STRATEGY_NUMBER_VARIABLES = [
7716
+ "position_size",
7717
+ "position_avg_price",
7718
+ "equity",
7719
+ "initial_capital",
7720
+ "netprofit",
7721
+ "netprofit_percent",
7722
+ "openprofit",
7723
+ "openprofit_percent",
7724
+ "grossprofit",
7725
+ "grossprofit_percent",
7726
+ "grossloss",
7727
+ "grossloss_percent",
7728
+ "opentrades",
7729
+ "closedtrades",
7730
+ "wintrades",
7731
+ "losstrades",
7732
+ "eventrades",
7733
+ "avg_trade",
7734
+ "avg_trade_percent",
7735
+ "avg_winning_trade",
7736
+ "avg_winning_trade_percent",
7737
+ "avg_losing_trade",
7738
+ "avg_losing_trade_percent",
7739
+ "max_drawdown",
7740
+ "max_drawdown_percent",
7741
+ "max_runup",
7742
+ "max_runup_percent",
7743
+ "max_contracts_held_all",
7744
+ "max_contracts_held_long",
7745
+ "max_contracts_held_short",
7746
+ "margin_liquidation_price"
7747
+ ];
7748
+ var STRATEGY_TEXT_VARIABLES = ["position_entry_name", "account_currency"];
7749
+
7659
7750
  // src/runtime/adapters/LightweightChartsAdapter.ts
7660
7751
  var LightweightChartsAdapter = class {
7661
7752
  chart;
@@ -7864,7 +7955,7 @@ var SimpleInputAdapter = class {
7864
7955
  };
7865
7956
 
7866
7957
  // src/index.ts
7867
- var VERSION = "0.7.2";
7958
+ var VERSION = "0.8.1";
7868
7959
  var info2 = {
7869
7960
  name: "OakScriptJS",
7870
7961
  version: VERSION,