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.
@@ -21,10 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  BarData: () => BarData,
24
+ STRATEGY_DEFAULTS: () => STRATEGY_DEFAULTS,
24
25
  Series: () => Series,
25
26
  alertcondition: () => alertcondition,
26
27
  barcolor: () => barcolor,
27
28
  barmerge: () => barmerge,
29
+ barstate: () => barstate,
28
30
  bgcolor: () => bgcolor,
29
31
  box: () => box,
30
32
  chart: () => chart,
@@ -50,7 +52,7 @@ __export(index_exports, {
50
52
  line: () => line,
51
53
  linefill: () => linefill,
52
54
  low: () => low,
53
- math: () => math_exports,
55
+ math: () => math,
54
56
  minute: () => minute2,
55
57
  month: () => month2,
56
58
  na: () => isNA,
@@ -58,6 +60,7 @@ __export(index_exports, {
58
60
  ohlc4: () => ohlc4,
59
61
  open: () => open,
60
62
  plot: () => plot,
63
+ plotarrow: () => plotarrow,
61
64
  plotchar: () => plotchar,
62
65
  plotshape: () => plotshape,
63
66
  polyline: () => polyline,
@@ -66,6 +69,7 @@ __export(index_exports, {
66
69
  seriesOf: () => seriesOf,
67
70
  session: () => session,
68
71
  str: () => str,
72
+ strategy: () => strategy,
69
73
  syminfo: () => syminfo,
70
74
  ta: () => ta2,
71
75
  text: () => text,
@@ -336,7 +340,7 @@ var Series = class _Series {
336
340
  return new _Series(this.dataSource, (bar, i, data) => {
337
341
  const a = this.extractor(bar, i, data);
338
342
  const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
339
- return b2 !== 0 ? a / b2 : NaN;
343
+ return a / b2;
340
344
  });
341
345
  }
342
346
  /**
@@ -709,7 +713,7 @@ function sma(source, length2) {
709
713
  const window = [];
710
714
  for (let i = 0; i < source.length; i++) {
711
715
  const v = source[i];
712
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
716
+ if (v !== void 0 && v !== null && Number.isFinite(v)) {
713
717
  window.push(v);
714
718
  if (window.length > len) window.shift();
715
719
  }
@@ -894,7 +898,7 @@ function tr(handle_na = false, high2, low2, close2) {
894
898
  const result = [];
895
899
  for (let i = 0; i < high2.length; i++) {
896
900
  if (i === 0) {
897
- result.push(high2[i] - low2[i]);
901
+ result.push(handle_na ? high2[i] - low2[i] : NaN);
898
902
  } else {
899
903
  const prevClose = close2[i - 1];
900
904
  if (isNaN(prevClose)) {
@@ -921,7 +925,7 @@ function atr(length2, high2, low2, close2) {
921
925
  "ta.atr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
922
926
  );
923
927
  }
924
- const trueRange = tr(false, high2, low2, close2);
928
+ const trueRange = tr(true, high2, low2, close2);
925
929
  return rma(trueRange, length2);
926
930
  }
927
931
  function supertrend(factor, atrPeriod, high2, low2, close2, wicks = false) {
@@ -1053,7 +1057,7 @@ function lowest(source, length2) {
1053
1057
  function cum(source) {
1054
1058
  let sum2 = 0;
1055
1059
  return source.map((v) => {
1056
- if (Number.isNaN(v)) return NaN;
1060
+ if (!Number.isFinite(v)) return NaN;
1057
1061
  sum2 += v;
1058
1062
  return sum2;
1059
1063
  });
@@ -1071,41 +1075,24 @@ function cross(source1, source2) {
1071
1075
  }
1072
1076
  return result;
1073
1077
  }
1074
- function rising(source, length2) {
1078
+ function monotonic(source, length2, step) {
1075
1079
  const result = [];
1080
+ const values = [];
1076
1081
  for (let i = 0; i < source.length; i++) {
1077
- if (i < length2) {
1078
- result.push(false);
1079
- } else {
1080
- let isRising = true;
1081
- for (let j = 1; j <= length2; j++) {
1082
- if (source[i - j + 1] <= source[i - j]) {
1083
- isRising = false;
1084
- break;
1085
- }
1086
- }
1087
- result.push(isRising);
1088
- }
1082
+ const v = source[i];
1083
+ if (!Number.isNaN(v)) values.push(v);
1084
+ const n = values.length;
1085
+ let ok = n > length2;
1086
+ for (let j = 1; ok && j <= length2; j++) ok = step(values[n - j], values[n - j - 1]);
1087
+ result.push(ok);
1089
1088
  }
1090
1089
  return result;
1091
1090
  }
1091
+ function rising(source, length2) {
1092
+ return monotonic(source, length2, gt);
1093
+ }
1092
1094
  function falling(source, length2) {
1093
- const result = [];
1094
- for (let i = 0; i < source.length; i++) {
1095
- if (i < length2) {
1096
- result.push(false);
1097
- } else {
1098
- let isFalling = true;
1099
- for (let j = 1; j <= length2; j++) {
1100
- if (source[i - j + 1] >= source[i - j]) {
1101
- isFalling = false;
1102
- break;
1103
- }
1104
- }
1105
- result.push(isFalling);
1106
- }
1107
- }
1108
- return result;
1095
+ return monotonic(source, length2, lt);
1109
1096
  }
1110
1097
  function roc(source, length2) {
1111
1098
  const result = [];
@@ -1329,33 +1316,16 @@ function correlation(source1, source2, length2) {
1329
1316
  function percentrank(source, length2) {
1330
1317
  const result = [];
1331
1318
  for (let i = 0; i < source.length; i++) {
1332
- if (i < length2) {
1319
+ const currentValue = source[i];
1320
+ if (i < length2 || Number.isNaN(currentValue)) {
1333
1321
  result.push(NaN);
1334
- } else {
1335
- if (isNaN(source[i])) {
1336
- result.push(NaN);
1337
- continue;
1338
- }
1339
- let hasNaN = false;
1340
- for (let j = 1; j <= length2; j++) {
1341
- if (isNaN(source[i - j])) {
1342
- hasNaN = true;
1343
- break;
1344
- }
1345
- }
1346
- if (hasNaN) {
1347
- result.push(NaN);
1348
- } else {
1349
- const currentValue = source[i];
1350
- let countLessOrEqual = 0;
1351
- for (let j = 1; j <= length2; j++) {
1352
- if (source[i - j] <= currentValue) {
1353
- countLessOrEqual++;
1354
- }
1355
- }
1356
- result.push(countLessOrEqual / length2 * 100);
1357
- }
1322
+ continue;
1323
+ }
1324
+ let countLessOrEqual = 0;
1325
+ for (let j = 1; j <= length2; j++) {
1326
+ if (le(source[i - j], currentValue)) countLessOrEqual++;
1358
1327
  }
1328
+ result.push(countLessOrEqual / length2 * 100);
1359
1329
  }
1360
1330
  return result;
1361
1331
  }
@@ -1398,48 +1368,26 @@ function mfi(source, length2, volume2) {
1398
1368
  "ta.mfi() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
1399
1369
  );
1400
1370
  }
1401
- const result = [];
1402
- const changes = [NaN];
1403
- for (let i = 1; i < source.length; i++) {
1404
- changes.push(source[i] - source[i - 1]);
1405
- }
1406
- const positiveFlow = [];
1407
- const negativeFlow = [];
1371
+ const upperFlow = [];
1372
+ const lowerFlow = [];
1408
1373
  for (let i = 0; i < source.length; i++) {
1409
- if (i === 0 || isNaN(changes[i])) {
1410
- positiveFlow.push(0);
1411
- negativeFlow.push(0);
1412
- } else if (changes[i] > 0) {
1413
- positiveFlow.push(volume2[i] * source[i]);
1414
- negativeFlow.push(0);
1415
- } else if (changes[i] < 0) {
1416
- positiveFlow.push(0);
1417
- negativeFlow.push(volume2[i] * source[i]);
1418
- } else {
1419
- positiveFlow.push(0);
1420
- negativeFlow.push(0);
1421
- }
1422
- }
1423
- for (let i = 0; i < source.length; i++) {
1424
- if (i < length2) {
1425
- result.push(NaN);
1426
- } else {
1427
- let posSum = 0;
1428
- let negSum = 0;
1429
- for (let j = 0; j < length2; j++) {
1430
- posSum += positiveFlow[i - j];
1431
- negSum += negativeFlow[i - j];
1432
- }
1433
- if (negSum === 0) {
1434
- result.push(100);
1435
- } else {
1436
- const moneyRatio = posSum / negSum;
1437
- const mfiValue = 100 - 100 / (1 + moneyRatio);
1438
- result.push(mfiValue);
1439
- }
1440
- }
1441
- }
1442
- return result;
1374
+ const change3 = i === 0 ? NaN : source[i] - source[i - 1];
1375
+ const flow = volume2[i] * source[i];
1376
+ upperFlow.push(le(change3, 0) ? 0 : flow);
1377
+ lowerFlow.push(ge(change3, 0) ? 0 : flow);
1378
+ }
1379
+ const sumAt = (flow, i) => {
1380
+ const values = lastValues(flow, i, Math.floor(length2));
1381
+ return values ? values.reduce((a, b2) => a + b2, 0) : NaN;
1382
+ };
1383
+ const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
1384
+ const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
1385
+ return upper2.map((u, i) => {
1386
+ const l = lower2[i];
1387
+ if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
1388
+ if (l === 0) return 100;
1389
+ return 100 - 100 / (1 + u / l);
1390
+ });
1443
1391
  }
1444
1392
  function hma(source, length2) {
1445
1393
  const halfLength = Math.floor(length2 / 2);
@@ -1603,50 +1551,32 @@ function dmi(diLength, adxSmoothing, high2, low2, close2) {
1603
1551
  const len = Math.max(high2.length, low2.length, close2.length);
1604
1552
  const plusDM = [];
1605
1553
  const minusDM = [];
1606
- const trueRangeValues = tr(false, high2, low2, close2);
1607
1554
  for (let i = 0; i < len; i++) {
1608
- if (i === 0) {
1609
- plusDM.push(0);
1610
- minusDM.push(0);
1611
- } else {
1612
- const upMove = high2[i] - high2[i - 1];
1613
- const downMove = low2[i - 1] - low2[i];
1614
- let plusDMVal = 0;
1615
- let minusDMVal = 0;
1616
- if (upMove > downMove && upMove > 0) {
1617
- plusDMVal = upMove;
1618
- }
1619
- if (downMove > upMove && downMove > 0) {
1620
- minusDMVal = downMove;
1621
- }
1622
- plusDM.push(plusDMVal);
1623
- minusDM.push(minusDMVal);
1624
- }
1625
- }
1626
- const smoothedPlusDM = rma(plusDM, diLength);
1627
- const smoothedMinusDM = rma(minusDM, diLength);
1628
- const smoothedTR = rma(trueRangeValues, diLength);
1629
- const plusDI = [];
1630
- const minusDI = [];
1631
- for (let i = 0; i < len; i++) {
1632
- if (smoothedTR[i] === 0) {
1633
- plusDI.push(0);
1634
- minusDI.push(0);
1635
- } else {
1636
- plusDI.push(smoothedPlusDM[i] / smoothedTR[i] * 100);
1637
- minusDI.push(smoothedMinusDM[i] / smoothedTR[i] * 100);
1638
- }
1639
- }
1640
- const dx = [];
1641
- for (let i = 0; i < len; i++) {
1642
- const sum2 = plusDI[i] + minusDI[i];
1643
- if (sum2 === 0) {
1644
- dx.push(0);
1645
- } else {
1646
- dx.push(Math.abs(plusDI[i] - minusDI[i]) / sum2 * 100);
1647
- }
1648
- }
1649
- const adx = rma(dx, adxSmoothing);
1555
+ const up = i === 0 ? NaN : high2[i] - high2[i - 1];
1556
+ const down = i === 0 ? NaN : low2[i - 1] - low2[i];
1557
+ plusDM.push(Number.isNaN(up) ? NaN : gt(up, down) && gt(up, 0) ? up : 0);
1558
+ minusDM.push(Number.isNaN(down) ? NaN : gt(down, up) && gt(down, 0) ? down : 0);
1559
+ }
1560
+ const trur = rma(tr(false, high2, low2, close2), diLength);
1561
+ const smoothedPlus = rma(plusDM, diLength);
1562
+ const smoothedMinus = rma(minusDM, diLength);
1563
+ const directional = (smoothed) => {
1564
+ let last = NaN;
1565
+ return smoothed.map((v, i) => {
1566
+ const t2 = trur[i];
1567
+ const value = t2 === 0 ? NaN : 100 * v / t2;
1568
+ if (!Number.isNaN(value)) last = value;
1569
+ return last;
1570
+ });
1571
+ };
1572
+ const plusDI = directional(smoothedPlus);
1573
+ const minusDI = directional(smoothedMinus);
1574
+ const dx = plusDI.map((plus, i) => {
1575
+ const minus = minusDI[i];
1576
+ const sum2 = plus + minus;
1577
+ return Math.abs(plus - minus) / (eq(sum2, 0) ? 1 : sum2);
1578
+ });
1579
+ const adx = rma(dx, adxSmoothing).map((v) => 100 * v);
1650
1580
  return [plusDI, minusDI, adx];
1651
1581
  }
1652
1582
  function tsi(source, shortLength, longLength) {
@@ -1922,63 +1852,36 @@ function mode(source, length2) {
1922
1852
  return result;
1923
1853
  }
1924
1854
  function percentile_linear_interpolation(source, length2, percentage) {
1855
+ return sortedWindow(source, length2, (s) => {
1856
+ const pos2 = percentage / 100 * length2 - 0.5;
1857
+ if (pos2 <= 0) return s[0];
1858
+ if (pos2 >= length2 - 1) return s[length2 - 1];
1859
+ const lo = Math.floor(pos2);
1860
+ return s[lo] + (pos2 - lo) * (s[lo + 1] - s[lo]);
1861
+ });
1862
+ }
1863
+ function sortedWindow(source, length2, fn) {
1925
1864
  const result = [];
1865
+ const values = [];
1866
+ const bars2 = [];
1926
1867
  for (let i = 0; i < source.length; i++) {
1927
- if (i < length2 - 1) {
1928
- result.push(NaN);
1929
- continue;
1930
- }
1931
- const values = [];
1932
- for (let j = 0; j < length2; j++) {
1933
- const value = source[i - j];
1934
- values.push(value);
1935
- }
1936
- if (values.some((v) => isNaN(v))) {
1937
- result.push(NaN);
1938
- continue;
1939
- }
1940
- const sorted = [...values].sort((a, b2) => a - b2);
1941
- const position = percentage / 100 * (sorted.length - 1);
1942
- const lowerIndex = Math.floor(position);
1943
- const upperIndex = Math.ceil(position);
1944
- if (lowerIndex === upperIndex) {
1945
- result.push(sorted[lowerIndex]);
1946
- } else {
1947
- const fraction = position - lowerIndex;
1948
- const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
1949
- result.push(interpolated);
1868
+ const v = source[i];
1869
+ let k = 0;
1870
+ while (k < values.length && !(values[k] >= v)) k++;
1871
+ values.splice(k, 0, v);
1872
+ bars2.splice(k, 0, i);
1873
+ if (i >= length2) {
1874
+ const old = bars2.indexOf(i - length2);
1875
+ values.splice(old, 1);
1876
+ bars2.splice(old, 1);
1950
1877
  }
1878
+ result.push(i < length2 - 1 ? NaN : fn(values));
1951
1879
  }
1952
1880
  return result;
1953
1881
  }
1954
1882
  function percentile_nearest_rank(source, length2, percentage) {
1955
- const result = [];
1956
- for (let i = 0; i < source.length; i++) {
1957
- if (i < length2 - 1) {
1958
- result.push(NaN);
1959
- continue;
1960
- }
1961
- const values = [];
1962
- for (let j = 0; j < length2; j++) {
1963
- const value = source[i - j];
1964
- if (!isNaN(value)) {
1965
- values.push(value);
1966
- }
1967
- }
1968
- if (values.length === 0) {
1969
- result.push(NaN);
1970
- continue;
1971
- }
1972
- const sorted = [...values].sort((a, b2) => a - b2);
1973
- if (percentage >= 100) {
1974
- result.push(sorted[sorted.length - 1]);
1975
- continue;
1976
- }
1977
- const rank = Math.ceil(percentage / 100 * sorted.length);
1978
- const index = Math.max(0, rank - 1);
1979
- result.push(sorted[index]);
1980
- }
1981
- return result;
1883
+ const rank = Math.min(length2, Math.max(1, Math.ceil(percentage / 100 * length2)));
1884
+ return sortedWindow(source, length2, (s) => s[rank - 1]);
1982
1885
  }
1983
1886
  function rci(source, length2) {
1984
1887
  const result = [];
@@ -2844,11 +2747,13 @@ __export(color_exports, {
2844
2747
  white: () => white,
2845
2748
  yellow: () => yellow
2846
2749
  });
2750
+ var channel = (v) => v === null || v === void 0 || Number.isNaN(v) ? 0 : Math.max(0, Math.min(255, Math.trunc(v)));
2751
+ var alphaOf = (transp) => transp === null || Number.isNaN(transp) ? 0 : 1 - Math.max(0, Math.min(100, transp)) / 100;
2847
2752
  function rgb(red2, green2, blue2, transp) {
2848
- const r2 = Math.max(0, Math.min(255, red2));
2849
- const g2 = Math.max(0, Math.min(255, green2));
2850
- const b2 = Math.max(0, Math.min(255, blue2));
2851
- const a = transp !== void 0 ? 1 - transp / 100 : 1;
2753
+ const r2 = channel(red2);
2754
+ const g2 = channel(green2);
2755
+ const b2 = channel(blue2);
2756
+ const a = transp !== void 0 ? alphaOf(transp) : 1;
2852
2757
  if (a === 1) {
2853
2758
  return `rgb(${r2}, ${g2}, ${b2})`;
2854
2759
  }
@@ -2875,7 +2780,8 @@ function b(clr) {
2875
2780
  return parseColor(clr).b;
2876
2781
  }
2877
2782
  function t(clr) {
2878
- return parseColor(clr).t;
2783
+ const alpha = Math.round((1 - parseColor(clr).t / 100) * 255);
2784
+ return Math.round((1 - alpha / 255) * 100);
2879
2785
  }
2880
2786
  function parseColor(clr) {
2881
2787
  if (typeof clr === "string") {
@@ -2928,13 +2834,12 @@ function from_gradient(value, bottom_value, top_value, bottom_color, top_color)
2928
2834
  };
2929
2835
  const c1 = rgba(bottom_color);
2930
2836
  const c2 = rgba(top_color);
2931
- const w1 = c1.a * (1 - k);
2932
- const w2 = c2.a * k;
2933
- const a = w1 + w2;
2837
+ const a = c1.a * (1 - k) + c2.a * k;
2934
2838
  if (a === 0) return transparent;
2935
- const mix = (x1, x2) => Math.floor((x1 * w1 + x2 * w2) / a);
2839
+ const mix = (x1, x2) => Math.floor((x1 / 255 * c1.a * (1 - k) + x2 / 255 * c2.a * k) / (a / 255));
2936
2840
  const [r2, g2, b2] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
2937
- return a === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${a / 255})`;
2841
+ const alpha = Math.floor(a);
2842
+ return alpha === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${alpha / 255})`;
2938
2843
  }
2939
2844
 
2940
2845
  // src/drawing/registry.ts
@@ -2998,7 +2903,7 @@ function setDrawingLimits(newLimits) {
2998
2903
 
2999
2904
  // src/utils/index.ts
3000
2905
  function isNA(value) {
3001
- return value === null || value === void 0 || Number.isNaN(value) || isDeleted(value);
2906
+ return value === null || value === void 0 || Number.isNaN(value) || value === Infinity || value === -Infinity || isDeleted(value);
3002
2907
  }
3003
2908
  function nz(value, defaultValue = 0) {
3004
2909
  return isNA(value) ? defaultValue : value;
@@ -3007,7 +2912,7 @@ function fixnan(source) {
3007
2912
  let last;
3008
2913
  let seen = false;
3009
2914
  return source.map((v) => {
3010
- if (!isNA(v)) {
2915
+ if (!(v === null || v === void 0 || Number.isNaN(v) || isDeleted(v))) {
3011
2916
  last = v;
3012
2917
  seen = true;
3013
2918
  return v;
@@ -3731,1640 +3636,1707 @@ var format_none = 0;
3731
3636
  var format_bold = 1;
3732
3637
  var format_italic = 2;
3733
3638
 
3734
- // src/timeframe/index.ts
3735
- var timeframe_exports = {};
3736
- __export(timeframe_exports, {
3737
- from_seconds: () => from_seconds,
3738
- in_seconds: () => in_seconds,
3739
- info: () => info
3639
+ // src/math/index.ts
3640
+ var math_exports = {};
3641
+ __export(math_exports, {
3642
+ abs: () => abs,
3643
+ acos: () => acos,
3644
+ asin: () => asin,
3645
+ atan: () => atan,
3646
+ avg: () => avg,
3647
+ ceil: () => ceil,
3648
+ cos: () => cos,
3649
+ e: () => e,
3650
+ exp: () => exp,
3651
+ floor: () => floor,
3652
+ log: () => log,
3653
+ log10: () => log10,
3654
+ max: () => max3,
3655
+ min: () => min3,
3656
+ phi: () => phi,
3657
+ pi: () => pi,
3658
+ pow: () => pow,
3659
+ random: () => random,
3660
+ round: () => round,
3661
+ round_to_mintick: () => round_to_mintick,
3662
+ rphi: () => rphi,
3663
+ sign: () => sign,
3664
+ sin: () => sin,
3665
+ sqrt: () => sqrt,
3666
+ sum: () => sum,
3667
+ tan: () => tan,
3668
+ todegrees: () => todegrees,
3669
+ toradians: () => toradians
3740
3670
  });
3741
- var SECONDS_PER_MINUTE = 60;
3742
- var SECONDS_PER_DAY = 86400;
3743
- var SECONDS_PER_WEEK = 604800;
3744
- var SECONDS_PER_MONTH = 2628003;
3745
- var SECONDS_365_DAYS = 365 * SECONDS_PER_DAY;
3746
- var UNIT_SECONDS = {
3747
- S: 1,
3748
- "": SECONDS_PER_MINUTE,
3749
- D: SECONDS_PER_DAY,
3750
- W: SECONDS_PER_WEEK,
3751
- M: SECONDS_PER_MONTH
3752
- };
3753
- var SECOND_TIMEFRAMES = [1, 5, 10, 15, 30];
3754
- function in_seconds(timeframe2) {
3755
- const text2 = timeframe2.trim();
3756
- if (text2 === "") {
3757
- throw new RangeError("timeframe.in_seconds() needs a timeframe: the chart timeframe is not known here");
3671
+ function abs(value) {
3672
+ if (value instanceof Series) {
3673
+ const bars2 = value.bars;
3674
+ const valueArray = value.toArray();
3675
+ const length2 = bars2.length;
3676
+ const result = [];
3677
+ for (let i = 0; i < length2; i++) {
3678
+ const v = valueArray[i] ?? NaN;
3679
+ result.push(Math.abs(v));
3680
+ }
3681
+ return Series.fromArray(bars2, result);
3758
3682
  }
3759
- const before = /^(\d*)([SDWMT]?)$/.exec(text2);
3760
- const after = before ? null : /^([SDWMT])(\d+)$/.exec(text2);
3761
- if (!before && !after) throw new RangeError(`Invalid timeframe "${timeframe2}"`);
3762
- const [digits, unit] = before ? [before[1], before[2]] : [after[2], after[1]];
3763
- if (unit === "T") return NaN;
3764
- const multiplier = digits === "" ? 1 : Number(digits);
3765
- return multiplier * UNIT_SECONDS[unit];
3683
+ return Math.abs(value);
3766
3684
  }
3767
- function from_seconds(seconds) {
3768
- if (Number.isNaN(seconds)) throw new RangeError("timeframe.from_seconds() needs a number of seconds");
3769
- if (seconds >= SECONDS_365_DAYS) return "12M";
3770
- if (seconds > 0) {
3771
- if (seconds % SECONDS_PER_MONTH === 0) return `${seconds / SECONDS_PER_MONTH}M`;
3772
- if (seconds % SECONDS_PER_WEEK === 0) return `${seconds / SECONDS_PER_WEEK}W`;
3773
- if (seconds % SECONDS_PER_DAY === 0) return `${seconds / SECONDS_PER_DAY}D`;
3774
- if (seconds % SECONDS_PER_MINUTE === 0 && seconds < SECONDS_PER_DAY) return `${seconds / SECONDS_PER_MINUTE}`;
3685
+ function ceil(value) {
3686
+ if (value instanceof Series) {
3687
+ const bars2 = value.bars;
3688
+ const valueArray = value.toArray();
3689
+ const length2 = bars2.length;
3690
+ const result = [];
3691
+ for (let i = 0; i < length2; i++) {
3692
+ const v = valueArray[i] ?? NaN;
3693
+ result.push(Math.ceil(v));
3694
+ }
3695
+ return Series.fromArray(bars2, result);
3775
3696
  }
3776
- const second3 = SECOND_TIMEFRAMES.find((m) => seconds <= m);
3777
- if (second3 !== void 0) return `${second3}S`;
3778
- if (seconds < SECONDS_PER_DAY) return `${Math.ceil(seconds / SECONDS_PER_MINUTE)}`;
3779
- return `${Math.ceil(seconds / SECONDS_PER_DAY)}D`;
3697
+ return Math.ceil(value);
3780
3698
  }
3781
- function info(timeframe2) {
3782
- const text2 = timeframe2.trim();
3783
- const m = /^(\d*)([SDWMT]?)$/.exec(text2);
3784
- if (!m || text2 === "") throw new RangeError(`Invalid timeframe "${timeframe2}"`);
3785
- const unit = m[2];
3786
- const multiplier = m[1] === "" ? 1 : Number(m[1]);
3787
- return {
3788
- period: unit === "" ? String(multiplier) : `${multiplier}${unit}`,
3789
- multiplier,
3790
- isintraday: unit === "" || unit === "S" || unit === "T",
3791
- isdaily: unit === "D",
3792
- isweekly: unit === "W",
3793
- ismonthly: unit === "M",
3794
- isminutes: unit === "",
3795
- isseconds: unit === "S",
3796
- isticks: unit === "T",
3797
- isdwm: unit === "D" || unit === "W" || unit === "M"
3798
- };
3699
+ function floor(value) {
3700
+ if (value instanceof Series) {
3701
+ const bars2 = value.bars;
3702
+ const valueArray = value.toArray();
3703
+ const length2 = bars2.length;
3704
+ const result = [];
3705
+ for (let i = 0; i < length2; i++) {
3706
+ const v = valueArray[i] ?? NaN;
3707
+ result.push(Math.floor(v));
3708
+ }
3709
+ return Series.fromArray(bars2, result);
3710
+ }
3711
+ return Math.floor(value);
3799
3712
  }
3800
-
3801
- // src/time/timezone.ts
3802
- var MS_PER_SECOND = 1e3;
3803
- var MS_PER_DAY = 864e5;
3804
- var MAX_OFFSET_SECONDS = 18 * 3600;
3805
- var OFFSET_NOTATION = /^(?:UTC|GMT)(?:([+-])(\d{1,2})(?::?(\d{2})(?::?(\d{2}))?)?)?$/;
3806
- var REJECTED_NAMES = /* @__PURE__ */ new Set(["EST", "MST", "HST"]);
3807
- var canonicalNames;
3808
- function hasTimeZoneCase(name) {
3809
- if (name.split("/").some((segment) => !/^[A-Z]/.test(segment))) return false;
3810
- canonicalNames ??= new Map(Intl.supportedValuesOf("timeZone").map((n) => [n.toLowerCase(), n]));
3811
- const canonical = canonicalNames.get(name.toLowerCase());
3812
- return canonical === void 0 || canonical === name;
3813
- }
3814
- var zones = /* @__PURE__ */ new Map();
3815
- function resolveZone(timezone) {
3816
- let zone = zones.get(timezone);
3817
- if (zone) return zone;
3818
- const invalid = () => new RangeError(
3819
- `Invalid time zone "${timezone}": use UTC/GMT offset notation ("UTC-5", "GMT+0530") or an IANA name ("America/New_York")`
3820
- );
3821
- const m = OFFSET_NOTATION.exec(timezone);
3822
- if (m) {
3823
- const [, sign2, h = "0", mi = "0", s] = m;
3824
- const minutes = Number(mi);
3825
- const seconds = s === void 0 ? 0 : Number(s);
3826
- if (minutes > 59 || seconds > 59 || s !== void 0 && h.length !== 2) throw invalid();
3827
- const offsetSeconds = Number(h) * 3600 + minutes * 60 + seconds;
3828
- if (offsetSeconds > MAX_OFFSET_SECONDS) throw invalid();
3829
- zone = { kind: "fixed", offsetMs: (sign2 === "-" ? -offsetSeconds : offsetSeconds) * MS_PER_SECOND };
3830
- } else {
3831
- if (REJECTED_NAMES.has(timezone) || !hasTimeZoneCase(timezone)) throw invalid();
3832
- let format2;
3833
- try {
3834
- format2 = new Intl.DateTimeFormat("en-US", {
3835
- timeZone: timezone,
3836
- hourCycle: "h23",
3837
- year: "numeric",
3838
- month: "numeric",
3839
- day: "numeric",
3840
- hour: "numeric",
3841
- minute: "numeric",
3842
- second: "numeric"
3843
- });
3844
- } catch {
3845
- throw invalid();
3713
+ function round(value, precision) {
3714
+ if (value instanceof Series) {
3715
+ const bars2 = value.bars;
3716
+ const valueArray = value.toArray();
3717
+ const length2 = bars2.length;
3718
+ const result = [];
3719
+ if (precision === void 0) {
3720
+ for (let i = 0; i < length2; i++) {
3721
+ const v = valueArray[i] ?? NaN;
3722
+ result.push(Math.round(v));
3723
+ }
3724
+ } else {
3725
+ const multiplier2 = Math.pow(10, precision);
3726
+ for (let i = 0; i < length2; i++) {
3727
+ const v = valueArray[i] ?? NaN;
3728
+ result.push(Math.round(v * multiplier2) / multiplier2);
3729
+ }
3846
3730
  }
3847
- zone = { kind: "iana", format: format2 };
3731
+ return Series.fromArray(bars2, result);
3848
3732
  }
3849
- zones.set(timezone, zone);
3850
- return zone;
3851
- }
3852
- function offsetAt(zone, time2) {
3853
- if (zone.kind === "fixed") return zone.offsetMs;
3854
- const wholeSecond = Math.floor(time2 / 1e3) * 1e3;
3855
- const f = {};
3856
- for (const part of zone.format.formatToParts(wholeSecond)) {
3857
- if (part.type !== "literal") f[part.type] = Number(part.value);
3733
+ if (precision === void 0) {
3734
+ return Math.round(value);
3858
3735
  }
3859
- const local = Date.UTC(f.year, f.month - 1, f.day, f.hour, f.minute, f.second);
3860
- return local - wholeSecond;
3861
- }
3862
- function zoneOffset(time2, timezone) {
3863
- return offsetAt(resolveZone(timezone), time2);
3864
- }
3865
- function zonedFields(time2, timezone) {
3866
- const local = new Date(time2 + offsetAt(resolveZone(timezone), time2));
3867
- return {
3868
- year: local.getUTCFullYear(),
3869
- month: local.getUTCMonth() + 1,
3870
- day: local.getUTCDate(),
3871
- hour: local.getUTCHours(),
3872
- minute: local.getUTCMinutes(),
3873
- second: local.getUTCSeconds(),
3874
- dayOfWeek: local.getUTCDay() + 1
3875
- };
3736
+ const multiplier = Math.pow(10, precision);
3737
+ return Math.round(value * multiplier) / multiplier;
3876
3738
  }
3877
- function zonedToUnix(timezone, year3, month3, day, hour3 = 0, minute3 = 0, second3 = 0) {
3878
- const zone = resolveZone(timezone);
3879
- const local = Date.UTC(year3, month3 - 1, day, hour3, minute3, second3);
3880
- if (zone.kind === "fixed") return local - zone.offsetMs;
3881
- const before = offsetAt(zone, local - MS_PER_DAY);
3882
- const after = offsetAt(zone, local + MS_PER_DAY);
3883
- const candidates = [local - before, local - after].filter((t2) => local - offsetAt(zone, t2) === t2);
3884
- if (candidates.length) return Math.max(...candidates);
3885
- return local - before;
3739
+ function max3(...values) {
3740
+ const hasSeries = values.some((v) => v instanceof Series);
3741
+ if (!hasSeries) {
3742
+ return Math.max(...values);
3743
+ }
3744
+ const firstSeries = values.find((v) => v instanceof Series);
3745
+ const bars2 = firstSeries.bars;
3746
+ const length2 = bars2.length;
3747
+ const valueArrays = values.map(
3748
+ (v) => v instanceof Series ? v.toArray() : null
3749
+ );
3750
+ const result = [];
3751
+ for (let i = 0; i < length2; i++) {
3752
+ const nums = values.map(
3753
+ (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
3754
+ );
3755
+ result.push(Math.max(...nums));
3756
+ }
3757
+ return Series.fromArray(bars2, result);
3886
3758
  }
3887
-
3888
- // src/time/datestring.ts
3889
- var MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
3890
- var FULL_MONTHS = [
3891
- "january",
3892
- "february",
3893
- "march",
3894
- "april",
3895
- "may",
3896
- "june",
3897
- "july",
3898
- "august",
3899
- "september",
3900
- "october",
3901
- "november",
3902
- "december"
3903
- ];
3904
- var ZONE_NAMES = {
3905
- ut: 0,
3906
- utc: 0,
3907
- gmt: 0,
3908
- est: -5,
3909
- edt: -4,
3910
- cst: -6,
3911
- cdt: -5,
3912
- mst: -7,
3913
- mdt: -6,
3914
- pst: -8,
3915
- pdt: -7
3916
- };
3917
- var TIME = String.raw`(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3})\d*)?)?`;
3918
- var ZONE = String.raw`(ut|utc|gmt|[ecmp][sd]t|(?:utc|gmt)?\s*[+-]\d{1,2}(?::?\d{2})?)`;
3919
- var NUMERIC_ZONE = String.raw`([+-]\d{2}(?::?\d{2})?)`;
3920
- var DATE = String.raw`(\d{4})([-/])(\d{1,2})\2(\d{1,2})`;
3921
- var WEEKDAY = String.raw`(?:[a-z]{3,9},?\s+)?`;
3922
- var ISO_T = new RegExp(String.raw`^${DATE}t${TIME}\s*${NUMERIC_ZONE}?$`, "i");
3923
- var ISO = new RegExp(String.raw`^${DATE}(?:\s+${TIME})?\s*${ZONE}?$`, "i");
3924
- var DAY_FIRST = new RegExp(String.raw`^${WEEKDAY}(\d{1,2})(?:\s+|-)([a-z]+)\.?,?(?:\s+|-)(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
3925
- var MONTH_FIRST = new RegExp(String.raw`^${WEEKDAY}([a-z]+)\.?\s+(\d{1,2}),?\s+(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
3926
- function monthNumber(name) {
3927
- const lower2 = name.toLowerCase();
3928
- const i = FULL_MONTHS.findIndex((full, k) => lower2.startsWith(MONTHS[k]) && full.startsWith(lower2));
3929
- return i + 1;
3759
+ function min3(...values) {
3760
+ const hasSeries = values.some((v) => v instanceof Series);
3761
+ if (!hasSeries) {
3762
+ return Math.min(...values);
3763
+ }
3764
+ const firstSeries = values.find((v) => v instanceof Series);
3765
+ const bars2 = firstSeries.bars;
3766
+ const length2 = bars2.length;
3767
+ const valueArrays = values.map(
3768
+ (v) => v instanceof Series ? v.toArray() : null
3769
+ );
3770
+ const result = [];
3771
+ for (let i = 0; i < length2; i++) {
3772
+ const nums = values.map(
3773
+ (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
3774
+ );
3775
+ result.push(Math.min(...nums));
3776
+ }
3777
+ return Series.fromArray(bars2, result);
3930
3778
  }
3931
- function zoneMinutes(zone) {
3932
- if (!zone) return 0;
3933
- const lower2 = zone.toLowerCase().replace(/\s+/g, "");
3934
- const named = ZONE_NAMES[lower2];
3935
- if (named !== void 0) return named * 60;
3936
- const m = /^(?:utc|gmt)?([+-])(\d{1,2})(?::?(\d{2}))?$/.exec(lower2);
3937
- if (!m) return NaN;
3938
- const minutes = Number(m[2]) * 60 + (m[3] ? Number(m[3]) : 0);
3939
- return m[1] === "-" ? -minutes : minutes;
3779
+ function avg(...values) {
3780
+ const hasSeries = values.some((v) => v instanceof Series);
3781
+ if (!hasSeries) {
3782
+ return values.reduce((a, b2) => a + b2, 0) / values.length;
3783
+ }
3784
+ const firstSeries = values.find((v) => v instanceof Series);
3785
+ const bars2 = firstSeries.bars;
3786
+ const length2 = bars2.length;
3787
+ const valueArrays = values.map(
3788
+ (v) => v instanceof Series ? v.toArray() : null
3789
+ );
3790
+ const result = [];
3791
+ for (let i = 0; i < length2; i++) {
3792
+ const nums = values.map(
3793
+ (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
3794
+ );
3795
+ const sum2 = nums.reduce((a, b2) => a + b2, 0);
3796
+ result.push(sum2 / nums.length);
3797
+ }
3798
+ return Series.fromArray(bars2, result);
3940
3799
  }
3941
- function parseDateString(dateString) {
3942
- const text2 = dateString.trim();
3943
- let year3, month3, day;
3944
- let rest;
3945
- let m;
3946
- if (m = ISO_T.exec(text2) ?? ISO.exec(text2)) {
3947
- [year3, month3, day] = [m[1], Number(m[3]), m[4]];
3948
- rest = m.slice(5);
3949
- } else if (m = DAY_FIRST.exec(text2)) {
3950
- [day, month3, year3] = [m[1], monthNumber(m[2]), m[3]];
3951
- rest = m.slice(4);
3952
- } else if (m = MONTH_FIRST.exec(text2)) {
3953
- [month3, day, year3] = [monthNumber(m[1]), m[2], m[3]];
3954
- rest = m.slice(4);
3955
- } else {
3956
- throw new SyntaxError(`Invalid date string "${dateString}": use RFC 2822 or ISO 8601 ("20 Feb 2020", "2020-02-20T14:48:00")`);
3800
+ function sum(source, length2) {
3801
+ const sourceArray = source instanceof Series ? source.toArray() : source;
3802
+ const result = [];
3803
+ const len = Math.floor(length2);
3804
+ const window = [];
3805
+ for (let i = 0; i < sourceArray.length; i++) {
3806
+ const v = sourceArray[i];
3807
+ if (v !== void 0 && v !== null && !Number.isNaN(v)) {
3808
+ window.push(v);
3809
+ if (window.length > len) window.shift();
3810
+ }
3811
+ if (window.length < len) {
3812
+ result.push(NaN);
3813
+ } else {
3814
+ let total = 0;
3815
+ for (const x of window) total += x;
3816
+ result.push(total);
3817
+ }
3957
3818
  }
3958
- const [hour3 = "0", minute3 = "0", second3 = "0", millis = "0", zone] = rest;
3959
- const d = Number(day);
3960
- const h = Number(hour3);
3961
- const mi = Number(minute3);
3962
- const s = Number(second3);
3963
- const offset = zoneMinutes(zone);
3964
- const endOfDay = h === 24 && mi === 0 && s === 0 && Number(millis) === 0;
3965
- if (month3 < 1 || month3 > 12 || d < 1 || d > 31 || h > 23 && !endOfDay || mi > 59 || s > 59 || Number.isNaN(offset)) {
3966
- throw new SyntaxError(`Invalid date string "${dateString}": a field is out of range`);
3819
+ if (source instanceof Series) {
3820
+ return Series.fromArray(source.bars, result);
3967
3821
  }
3968
- const ms = Number(millis.padEnd(3, "0"));
3969
- return Date.UTC(Number(year3), month3 - 1, d, h, mi, s, ms) - offset * 6e4;
3822
+ return result;
3970
3823
  }
3971
-
3972
- // src/time/session.ts
3973
- var PERIOD = /^(\d{2})(\d{2})-(\d{2})(\d{2})$/;
3974
- var sessions = /* @__PURE__ */ new Map();
3975
- function parseSession(session2) {
3976
- let parsed = sessions.get(session2);
3977
- if (parsed) return parsed;
3978
- const invalid = () => new SyntaxError(`Invalid session "${session2}": expected "HHmm-HHmm[,HHmm-HHmm...][:days]"`);
3979
- const text2 = session2.trim() === "24x7" ? "0000-0000" : session2.replace(/\s+/g, "");
3980
- const [periodText = "", dayText, extra] = text2.split(":");
3981
- if (extra !== void 0 || periodText === "") throw invalid();
3982
- const periods = periodText.split(",").map((p) => {
3983
- const m = PERIOD.exec(p);
3984
- if (!m) throw invalid();
3985
- const [h1, m1, h2, m2] = [m[1], m[2], m[3], m[4]].map(Number);
3986
- if (h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) throw invalid();
3987
- return { start: h1 * 60 + m1, end: h2 * 60 + m2 };
3988
- });
3989
- const dayDigits = dayText ?? (periods.length > 1 ? "23456" : "1234567");
3990
- if (!/^[1-7]+$/.test(dayDigits)) throw invalid();
3991
- const days = new Array(8).fill(false);
3992
- for (const d of dayDigits) days[Number(d)] = true;
3993
- parsed = { periods, days };
3994
- sessions.set(session2, parsed);
3995
- return parsed;
3824
+ function sqrt(value) {
3825
+ if (value instanceof Series) {
3826
+ const bars2 = value.bars;
3827
+ const valueArray = value.toArray();
3828
+ const length2 = bars2.length;
3829
+ const result = [];
3830
+ for (let i = 0; i < length2; i++) {
3831
+ const v = valueArray[i] ?? NaN;
3832
+ result.push(Math.sqrt(v));
3833
+ }
3834
+ return Series.fromArray(bars2, result);
3835
+ }
3836
+ return Math.sqrt(value);
3996
3837
  }
3997
- function inSession(time2, session2, timezone) {
3998
- if (Number.isNaN(time2)) return false;
3999
- const { periods, days } = parseSession(session2);
4000
- const f = zonedFields(time2, timezone);
4001
- const minute3 = f.hour * 60 + f.minute;
4002
- const nextDay = f.dayOfWeek % 7 + 1;
4003
- for (const { start, end } of periods) {
4004
- if (start < end || start === 0 && end === 0) {
4005
- if (minute3 >= start && (minute3 < end || end === 0) && days[f.dayOfWeek]) return true;
4006
- } else {
4007
- if (minute3 >= start && days[nextDay]) return true;
4008
- if (minute3 < end && days[f.dayOfWeek]) return true;
3838
+ function pow(base, exponent) {
3839
+ const baseIsSeries = base instanceof Series;
3840
+ const expIsSeries = exponent instanceof Series;
3841
+ if (!baseIsSeries && !expIsSeries) {
3842
+ return Math.pow(base, exponent);
3843
+ }
3844
+ const bars2 = baseIsSeries ? base.bars : exponent.bars;
3845
+ const baseArray = baseIsSeries ? base.toArray() : null;
3846
+ const expArray = expIsSeries ? exponent.toArray() : null;
3847
+ const length2 = bars2.length;
3848
+ const result = [];
3849
+ for (let i = 0; i < length2; i++) {
3850
+ const b2 = baseArray ? baseArray[i] ?? NaN : base;
3851
+ const e2 = expArray ? expArray[i] ?? NaN : exponent;
3852
+ result.push(Math.pow(b2, e2));
3853
+ }
3854
+ return Series.fromArray(bars2, result);
3855
+ }
3856
+ function exp(value) {
3857
+ if (value instanceof Series) {
3858
+ const bars2 = value.bars;
3859
+ const valueArray = value.toArray();
3860
+ const length2 = bars2.length;
3861
+ const result = [];
3862
+ for (let i = 0; i < length2; i++) {
3863
+ const v = valueArray[i] ?? NaN;
3864
+ result.push(Math.exp(v));
4009
3865
  }
3866
+ return Series.fromArray(bars2, result);
4010
3867
  }
4011
- return false;
3868
+ return Math.exp(value);
4012
3869
  }
4013
-
4014
- // src/time/index.ts
4015
- function timestamp(first, ...rest) {
4016
- if (typeof first === "string") {
4017
- if (rest.length === 0) return parseDateString(first);
4018
- const [year3, month4, day2, hour4, minute4, second4] = rest;
4019
- return zonedToUnix(first, year3, month4, day2, hour4 ?? 0, minute4 ?? 0, second4 ?? 0);
3870
+ function log(value) {
3871
+ if (value instanceof Series) {
3872
+ const bars2 = value.bars;
3873
+ const valueArray = value.toArray();
3874
+ const length2 = bars2.length;
3875
+ const result = [];
3876
+ for (let i = 0; i < length2; i++) {
3877
+ const v = valueArray[i] ?? NaN;
3878
+ result.push(Math.log(v));
3879
+ }
3880
+ return Series.fromArray(bars2, result);
4020
3881
  }
4021
- const [month3, day, hour3, minute3, second3] = rest;
4022
- return new Date(first, month3 - 1, day, hour3 || 0, minute3 || 0, second3 || 0).getTime();
3882
+ return Math.log(value);
4023
3883
  }
4024
- function field(time2, timezone, pick) {
4025
- return Number.isNaN(time2) ? NaN : pick(zonedFields(time2, timezone));
3884
+ function log10(value) {
3885
+ if (value instanceof Series) {
3886
+ const bars2 = value.bars;
3887
+ const valueArray = value.toArray();
3888
+ const length2 = bars2.length;
3889
+ const result = [];
3890
+ for (let i = 0; i < length2; i++) {
3891
+ const v = valueArray[i] ?? NaN;
3892
+ result.push(Math.log10(v));
3893
+ }
3894
+ return Series.fromArray(bars2, result);
3895
+ }
3896
+ return Math.log10(value);
4026
3897
  }
4027
- function year(time2, timezone) {
4028
- return field(time2, timezone, (f) => f.year);
3898
+ function sin(value) {
3899
+ if (value instanceof Series) {
3900
+ const bars2 = value.bars;
3901
+ const valueArray = value.toArray();
3902
+ const length2 = bars2.length;
3903
+ const result = [];
3904
+ for (let i = 0; i < length2; i++) {
3905
+ const v = valueArray[i] ?? NaN;
3906
+ result.push(Math.sin(v));
3907
+ }
3908
+ return Series.fromArray(bars2, result);
3909
+ }
3910
+ return Math.sin(value);
4029
3911
  }
4030
- function month(time2, timezone) {
4031
- return field(time2, timezone, (f) => f.month);
3912
+ function cos(value) {
3913
+ if (value instanceof Series) {
3914
+ const bars2 = value.bars;
3915
+ const valueArray = value.toArray();
3916
+ const length2 = bars2.length;
3917
+ const result = [];
3918
+ for (let i = 0; i < length2; i++) {
3919
+ const v = valueArray[i] ?? NaN;
3920
+ result.push(Math.cos(v));
3921
+ }
3922
+ return Series.fromArray(bars2, result);
3923
+ }
3924
+ return Math.cos(value);
4032
3925
  }
4033
- function dayofmonth(time2, timezone) {
4034
- return field(time2, timezone, (f) => f.day);
3926
+ function tan(value) {
3927
+ if (value instanceof Series) {
3928
+ const bars2 = value.bars;
3929
+ const valueArray = value.toArray();
3930
+ const length2 = bars2.length;
3931
+ const result = [];
3932
+ for (let i = 0; i < length2; i++) {
3933
+ const v = valueArray[i] ?? NaN;
3934
+ result.push(Math.tan(v));
3935
+ }
3936
+ return Series.fromArray(bars2, result);
3937
+ }
3938
+ return Math.tan(value);
4035
3939
  }
4036
- function dayofweek(time2, timezone) {
4037
- return field(time2, timezone, (f) => f.dayOfWeek);
3940
+ function asin(value) {
3941
+ if (value instanceof Series) {
3942
+ const bars2 = value.bars;
3943
+ const valueArray = value.toArray();
3944
+ const length2 = bars2.length;
3945
+ const result = [];
3946
+ for (let i = 0; i < length2; i++) {
3947
+ const v = valueArray[i] ?? NaN;
3948
+ result.push(Math.asin(v));
3949
+ }
3950
+ return Series.fromArray(bars2, result);
3951
+ }
3952
+ return Math.asin(value);
4038
3953
  }
4039
- function weekofyear(time2, timezone) {
4040
- return field(time2, timezone, (f) => {
4041
- const date = Date.UTC(f.year, f.month - 1, f.day);
4042
- const mondayBased = (f.dayOfWeek + 5) % 7;
4043
- const thursday = new Date(date + (3 - mondayBased) * 864e5);
4044
- const yearStart = Date.UTC(thursday.getUTCFullYear(), 0, 1);
4045
- return 1 + Math.floor((thursday.getTime() - yearStart) / (7 * 864e5));
4046
- });
3954
+ function acos(value) {
3955
+ if (value instanceof Series) {
3956
+ const bars2 = value.bars;
3957
+ const valueArray = value.toArray();
3958
+ const length2 = bars2.length;
3959
+ const result = [];
3960
+ for (let i = 0; i < length2; i++) {
3961
+ const v = valueArray[i] ?? NaN;
3962
+ result.push(Math.acos(v));
3963
+ }
3964
+ return Series.fromArray(bars2, result);
3965
+ }
3966
+ return Math.acos(value);
4047
3967
  }
4048
- function hour(time2, timezone) {
4049
- return field(time2, timezone, (f) => f.hour);
3968
+ function atan(value) {
3969
+ if (value instanceof Series) {
3970
+ const bars2 = value.bars;
3971
+ const valueArray = value.toArray();
3972
+ const length2 = bars2.length;
3973
+ const result = [];
3974
+ for (let i = 0; i < length2; i++) {
3975
+ const v = valueArray[i] ?? NaN;
3976
+ result.push(Math.atan(v));
3977
+ }
3978
+ return Series.fromArray(bars2, result);
3979
+ }
3980
+ return Math.atan(value);
4050
3981
  }
4051
- function minute(time2, timezone) {
4052
- return field(time2, timezone, (f) => f.minute);
3982
+ function toradians(degrees) {
3983
+ if (degrees instanceof Series) {
3984
+ const bars2 = degrees.bars;
3985
+ const valueArray = degrees.toArray();
3986
+ const length2 = bars2.length;
3987
+ const result = [];
3988
+ for (let i = 0; i < length2; i++) {
3989
+ const v = valueArray[i] ?? NaN;
3990
+ result.push(v * (Math.PI / 180));
3991
+ }
3992
+ return Series.fromArray(bars2, result);
3993
+ }
3994
+ return degrees * (Math.PI / 180);
4053
3995
  }
4054
- function second(time2, timezone) {
4055
- return field(time2, timezone, (f) => f.second);
3996
+ function todegrees(radians) {
3997
+ if (radians instanceof Series) {
3998
+ const bars2 = radians.bars;
3999
+ const valueArray = radians.toArray();
4000
+ const length2 = bars2.length;
4001
+ const result = [];
4002
+ for (let i = 0; i < length2; i++) {
4003
+ const v = valueArray[i] ?? NaN;
4004
+ result.push(v * (180 / Math.PI));
4005
+ }
4006
+ return Series.fromArray(bars2, result);
4007
+ }
4008
+ return radians * (180 / Math.PI);
4056
4009
  }
4057
- function inSession2(time2, session2, timezone) {
4058
- return inSession(time2, session2, timezone);
4010
+ function random(min4, max4, _seed) {
4011
+ const rand = Math.random();
4012
+ if (min4 !== void 0 && max4 !== void 0) {
4013
+ return min4 + rand * (max4 - min4);
4014
+ }
4015
+ return rand;
4059
4016
  }
4060
-
4061
- // src/str/index.ts
4062
- var str_exports = {};
4063
- __export(str_exports, {
4064
- charAt: () => charAt,
4065
- concat: () => concat,
4066
- contains: () => contains,
4067
- endswith: () => endswith,
4068
- format: () => format,
4069
- format_time: () => format_time,
4070
- length: () => length,
4071
- lower: () => lower,
4072
- match: () => match,
4073
- pos: () => pos,
4074
- repeat: () => repeat,
4075
- replace: () => replace,
4076
- replace_all: () => replace_all,
4077
- split: () => split,
4078
- startswith: () => startswith,
4079
- substring: () => substring,
4080
- tonumber: () => tonumber,
4081
- tostring: () => tostring,
4082
- trim: () => trim,
4083
- trimLeft: () => trimLeft,
4084
- trimRight: () => trimRight,
4085
- upper: () => upper
4086
- });
4087
-
4088
- // src/str/numberformat.ts
4089
- var patterns = /* @__PURE__ */ new Map();
4090
- function parsePattern(pattern) {
4091
- const cached = patterns.get(pattern);
4092
- if (cached) return cached;
4093
- let prefix = "";
4094
- let suffix = "";
4095
- let number2 = "";
4096
- let percent = false;
4097
- let phase = "prefix";
4098
- for (let i = 0; i < pattern.length; i++) {
4099
- const ch = pattern[i];
4100
- let literal;
4101
- if (ch === "'") {
4102
- const end = pattern.indexOf("'", i + 1);
4103
- if (end === i + 1) {
4104
- literal = "'";
4105
- i = end;
4106
- } else {
4107
- literal = pattern.slice(i + 1, end === -1 ? void 0 : end);
4108
- i = end === -1 ? pattern.length : end;
4017
+ function sign(value) {
4018
+ if (value instanceof Series) {
4019
+ const bars2 = value.bars;
4020
+ const valueArray = value.toArray();
4021
+ const length2 = bars2.length;
4022
+ const result = [];
4023
+ for (let i = 0; i < length2; i++) {
4024
+ const v = valueArray[i] ?? NaN;
4025
+ result.push(v > 0 ? 1 : v < 0 ? -1 : 0);
4026
+ }
4027
+ return Series.fromArray(bars2, result);
4028
+ }
4029
+ return value > 0 ? 1 : value < 0 ? -1 : 0;
4030
+ }
4031
+ function round_to_mintick(number2, mintick) {
4032
+ if (number2 instanceof Series) {
4033
+ const bars2 = number2.bars;
4034
+ const valueArray = number2.toArray();
4035
+ const length2 = bars2.length;
4036
+ if (mintick === void 0) {
4037
+ throw new Error(
4038
+ "math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
4039
+ );
4040
+ }
4041
+ const result = [];
4042
+ if (mintick === 0) {
4043
+ for (let i = 0; i < length2; i++) {
4044
+ result.push(valueArray[i] ?? NaN);
4109
4045
  }
4110
- } else if (phase !== "suffix" && ("#0,.".includes(ch) || phase === "number" && ch === "E")) {
4111
- phase = "number";
4112
- number2 += ch;
4113
- continue;
4114
4046
  } else {
4115
- if (ch === "%") percent = true;
4116
- literal = ch;
4047
+ for (let i = 0; i < length2; i++) {
4048
+ const v = valueArray[i];
4049
+ if (v === void 0 || isNaN(v)) {
4050
+ result.push(NaN);
4051
+ } else {
4052
+ result.push(Math.round(v / mintick) * mintick);
4053
+ }
4054
+ }
4117
4055
  }
4118
- if (phase === "number") phase = "suffix";
4119
- if (phase === "prefix") prefix += literal;
4120
- else suffix += literal;
4056
+ return Series.fromArray(bars2, result);
4121
4057
  }
4122
- const [mantissa = "", exp2] = number2.split("E");
4123
- const [intPart = "", fracPart = ""] = mantissa.split(".");
4124
- const lastComma = intPart.lastIndexOf(",");
4125
- const parsed = {
4126
- prefix,
4127
- suffix,
4128
- // as in Java, a pattern with a decimal point and no '0' at all ("#.##") shows one integer digit
4129
- minInt: !mantissa.includes("0") && mantissa.includes(".") ? 1 : [...intPart].filter((c) => c === "0").length,
4130
- minFrac: [...fracPart].filter((c) => c === "0").length,
4131
- maxFrac: [...fracPart].filter((c) => c === "0" || c === "#").length,
4132
- grouping: lastComma === -1 ? 0 : intPart.length - lastComma - 1,
4133
- percent,
4134
- exponent: exp2 === void 0 ? null : exp2.length,
4135
- hasDigits: /[0#]/.test(mantissa)
4136
- };
4137
- patterns.set(pattern, parsed);
4138
- return parsed;
4139
- }
4140
- function shortestDigits(x) {
4141
- const m = /^(\d+)(?:\.(\d+))?(?:e([+-]\d+))?$/.exec(String(Math.abs(x)));
4142
- const all7 = m[1] + (m[2] ?? "");
4143
- let intLen = m[1].length + Number(m[3] ?? 0);
4144
- const lead = all7.length - all7.replace(/^0+/, "").length;
4145
- intLen -= lead;
4146
- const digits = all7.slice(lead).replace(/0+$/, "");
4147
- return { digits, intLen: digits ? intLen : 0 };
4058
+ if (isNaN(number2)) {
4059
+ return NaN;
4060
+ }
4061
+ if (mintick === void 0) {
4062
+ throw new Error(
4063
+ "math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
4064
+ );
4065
+ }
4066
+ if (mintick === 0) {
4067
+ return number2;
4068
+ }
4069
+ return Math.round(number2 / mintick) * mintick;
4148
4070
  }
4149
- function exactDigits(x) {
4150
- const abs2 = Math.abs(x);
4151
- if (abs2 === 0) return { digits: "", intLen: 0 };
4152
- const view = new DataView(new ArrayBuffer(8));
4153
- view.setFloat64(0, abs2);
4154
- const bits = view.getBigUint64(0);
4155
- const expBits = Number(bits >> 52n & 0x7ffn);
4156
- let mant = bits & 0xfffffffffffffn;
4157
- const e2 = expBits === 0 ? -1074 : expBits - 1075;
4158
- if (expBits !== 0) mant |= 1n << 52n;
4159
- let str2;
4160
- let fracDigits;
4161
- if (e2 >= 0) {
4162
- str2 = (mant << BigInt(e2)).toString();
4163
- fracDigits = 0;
4164
- } else {
4165
- str2 = (mant * 5n ** BigInt(-e2)).toString();
4166
- fracDigits = -e2;
4071
+ var pi = Math.PI;
4072
+ var e = Math.E;
4073
+ var phi = 1.618033988749895;
4074
+ var rphi = 0.618033988749895;
4075
+
4076
+ // src/timeframe/index.ts
4077
+ var timeframe_exports = {};
4078
+ __export(timeframe_exports, {
4079
+ from_seconds: () => from_seconds,
4080
+ in_seconds: () => in_seconds,
4081
+ info: () => info
4082
+ });
4083
+ var SECONDS_PER_MINUTE = 60;
4084
+ var SECONDS_PER_DAY = 86400;
4085
+ var SECONDS_PER_WEEK = 604800;
4086
+ var SECONDS_PER_MONTH = 2628003;
4087
+ var SECONDS_365_DAYS = 365 * SECONDS_PER_DAY;
4088
+ var UNIT_SECONDS = {
4089
+ S: 1,
4090
+ "": SECONDS_PER_MINUTE,
4091
+ D: SECONDS_PER_DAY,
4092
+ W: SECONDS_PER_WEEK,
4093
+ M: SECONDS_PER_MONTH
4094
+ };
4095
+ var SECOND_TIMEFRAMES = [1, 5, 10, 15, 30];
4096
+ function in_seconds(timeframe2) {
4097
+ const text2 = timeframe2.trim();
4098
+ if (text2 === "") {
4099
+ throw new RangeError("timeframe.in_seconds() needs a timeframe: the chart timeframe is not known here");
4167
4100
  }
4168
- const intLen = str2.length - fracDigits;
4169
- const lead = str2.length - str2.replace(/^0+/, "").length;
4170
- const digits = str2.slice(lead).replace(/0+$/, "");
4171
- return { digits, intLen: intLen - lead };
4101
+ const before = /^(\d*)([SDWMT]?)$/.exec(text2);
4102
+ const after = before ? null : /^([SDWMT])(\d+)$/.exec(text2);
4103
+ if (!before && !after) throw new RangeError(`Invalid timeframe "${timeframe2}"`);
4104
+ const [digits, unit] = before ? [before[1], before[2]] : [after[2], after[1]];
4105
+ if (unit === "T") return NaN;
4106
+ const multiplier = digits === "" ? 1 : Number(digits);
4107
+ return multiplier * UNIT_SECONDS[unit];
4172
4108
  }
4173
- function roundDigits(d, frac, mode3) {
4174
- const keep = d.intLen + frac;
4175
- if (keep >= d.digits.length) return d;
4176
- if (keep < 0) return { digits: "", intLen: 0 };
4177
- const first = d.digits.charCodeAt(keep) - 48;
4178
- let up;
4179
- if (mode3 === "halfUpShortest") up = first >= 5;
4180
- else if (first !== 5) up = first > 5;
4181
- else if (d.digits.length > keep + 1) up = true;
4182
- else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
4183
- let kept = d.digits.slice(0, keep);
4184
- let intLen = d.intLen;
4185
- if (up) {
4186
- const n = (BigInt(kept || "0") + 1n).toString();
4187
- if (n.length > kept.length) intLen += 1;
4188
- kept = n;
4109
+ function from_seconds(seconds) {
4110
+ if (Number.isNaN(seconds)) throw new RangeError("timeframe.from_seconds() needs a number of seconds");
4111
+ if (seconds >= SECONDS_365_DAYS) return "12M";
4112
+ if (seconds > 0) {
4113
+ if (seconds % SECONDS_PER_MONTH === 0) return `${seconds / SECONDS_PER_MONTH}M`;
4114
+ if (seconds % SECONDS_PER_WEEK === 0) return `${seconds / SECONDS_PER_WEEK}W`;
4115
+ if (seconds % SECONDS_PER_DAY === 0) return `${seconds / SECONDS_PER_DAY}D`;
4116
+ if (seconds % SECONDS_PER_MINUTE === 0 && seconds < SECONDS_PER_DAY) return `${seconds / SECONDS_PER_MINUTE}`;
4189
4117
  }
4190
- kept = kept.replace(/0+$/, "");
4191
- return { digits: kept, intLen: kept ? intLen : 0 };
4118
+ const second3 = SECOND_TIMEFRAMES.find((m) => seconds <= m);
4119
+ if (second3 !== void 0) return `${second3}S`;
4120
+ if (seconds < SECONDS_PER_DAY) return `${Math.ceil(seconds / SECONDS_PER_MINUTE)}`;
4121
+ return `${Math.ceil(seconds / SECONDS_PER_DAY)}D`;
4192
4122
  }
4193
- function group(int, size) {
4194
- if (!size || int.length <= size) return int;
4195
- const parts = [];
4196
- for (let end = int.length; end > 0; end -= size) parts.unshift(int.slice(Math.max(0, end - size), end));
4197
- return parts.join(",");
4123
+ function info(timeframe2) {
4124
+ const text2 = timeframe2.trim();
4125
+ const m = /^(\d*)([SDWMT]?)$/.exec(text2);
4126
+ if (!m || text2 === "") throw new RangeError(`Invalid timeframe "${timeframe2}"`);
4127
+ const unit = m[2];
4128
+ const multiplier = m[1] === "" ? 1 : Number(m[1]);
4129
+ return {
4130
+ period: unit === "" ? String(multiplier) : `${multiplier}${unit}`,
4131
+ multiplier,
4132
+ isintraday: unit === "" || unit === "S" || unit === "T",
4133
+ isdaily: unit === "D",
4134
+ isweekly: unit === "W",
4135
+ ismonthly: unit === "M",
4136
+ isminutes: unit === "",
4137
+ isseconds: unit === "S",
4138
+ isticks: unit === "T",
4139
+ isdwm: unit === "D" || unit === "W" || unit === "M"
4140
+ };
4198
4141
  }
4199
- function layout(d, p) {
4200
- let int = d.intLen > 0 ? d.digits.slice(0, d.intLen).padEnd(d.intLen, "0") : "";
4201
- int = int.replace(/^0+/, "").padStart(p.minInt, "0");
4202
- let frac = d.intLen < 0 ? "0".repeat(-d.intLen) + d.digits : d.digits.slice(Math.max(0, d.intLen));
4203
- frac = frac.slice(0, p.maxFrac).replace(/0+$/, "").padEnd(p.minFrac, "0");
4204
- if (!int && !frac) int = "0";
4205
- return group(int, p.grouping) + (frac ? "." + frac : "");
4142
+
4143
+ // src/time/timezone.ts
4144
+ var MS_PER_SECOND = 1e3;
4145
+ var MS_PER_DAY = 864e5;
4146
+ var MAX_OFFSET_SECONDS = 18 * 3600;
4147
+ var OFFSET_NOTATION = /^(?:UTC|GMT)(?:([+-])(\d{1,2})(?::?(\d{2})(?::?(\d{2}))?)?)?$/;
4148
+ var REJECTED_NAMES = /* @__PURE__ */ new Set(["EST", "MST", "HST"]);
4149
+ var canonicalNames;
4150
+ function hasTimeZoneCase(name) {
4151
+ if (name.split("/").some((segment) => !/^[A-Z]/.test(segment))) return false;
4152
+ canonicalNames ??= new Map(Intl.supportedValuesOf("timeZone").map((n) => [n.toLowerCase(), n]));
4153
+ const canonical = canonicalNames.get(name.toLowerCase());
4154
+ return canonical === void 0 || canonical === name;
4206
4155
  }
4207
- function formatNumber(value, pattern, mode3) {
4208
- if (Number.isNaN(value)) return "NaN";
4209
- const p = parsePattern(pattern);
4210
- if (!Number.isFinite(value)) return (value < 0 ? "-" : "") + p.prefix + "\u221E" + p.suffix;
4211
- const x = p.percent ? value * 100 : value;
4212
- const toDigits = mode3 === "halfUpShortest" ? shortestDigits : exactDigits;
4213
- let body;
4214
- let isZero;
4215
- if (p.exponent !== null) {
4216
- const rounded = roundDigits(toDigits(x), p.maxFrac, mode3);
4217
- const intDigits = Math.max(p.minInt, 1);
4218
- isZero = rounded.digits === "";
4219
- let exponent = isZero ? 0 : rounded.intLen - intDigits;
4220
- let mant = roundDigits({ digits: rounded.digits, intLen: isZero ? 0 : intDigits }, p.maxFrac, mode3);
4221
- if (mant.intLen > intDigits) {
4222
- exponent += 1;
4223
- mant = { digits: mant.digits, intLen: intDigits };
4224
- }
4225
- const e2 = String(Math.abs(exponent)).padStart(p.exponent, "0");
4226
- body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e2;
4156
+ var zones = /* @__PURE__ */ new Map();
4157
+ function resolveZone(timezone) {
4158
+ let zone = zones.get(timezone);
4159
+ if (zone) return zone;
4160
+ const invalid = () => new RangeError(
4161
+ `Invalid time zone "${timezone}": use UTC/GMT offset notation ("UTC-5", "GMT+0530") or an IANA name ("America/New_York")`
4162
+ );
4163
+ const m = OFFSET_NOTATION.exec(timezone);
4164
+ if (m) {
4165
+ const [, sign2, h = "0", mi = "0", s] = m;
4166
+ const minutes = Number(mi);
4167
+ const seconds = s === void 0 ? 0 : Number(s);
4168
+ if (minutes > 59 || seconds > 59 || s !== void 0 && h.length !== 2) throw invalid();
4169
+ const offsetSeconds = Number(h) * 3600 + minutes * 60 + seconds;
4170
+ if (offsetSeconds > MAX_OFFSET_SECONDS) throw invalid();
4171
+ zone = { kind: "fixed", offsetMs: (sign2 === "-" ? -offsetSeconds : offsetSeconds) * MS_PER_SECOND };
4227
4172
  } else {
4228
- const rounded = roundDigits(toDigits(x), p.hasDigits ? p.maxFrac : 0, mode3);
4229
- isZero = rounded.digits === "";
4230
- body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });
4173
+ if (REJECTED_NAMES.has(timezone) || !hasTimeZoneCase(timezone)) throw invalid();
4174
+ let format2;
4175
+ try {
4176
+ format2 = new Intl.DateTimeFormat("en-US", {
4177
+ timeZone: timezone,
4178
+ hourCycle: "h23",
4179
+ year: "numeric",
4180
+ month: "numeric",
4181
+ day: "numeric",
4182
+ hour: "numeric",
4183
+ minute: "numeric",
4184
+ second: "numeric"
4185
+ });
4186
+ } catch {
4187
+ throw invalid();
4188
+ }
4189
+ zone = { kind: "iana", format: format2 };
4231
4190
  }
4232
- const negative = x < 0 && (!isZero || !p.hasDigits);
4233
- return (negative ? "-" : "") + p.prefix + body + p.suffix;
4191
+ zones.set(timezone, zone);
4192
+ return zone;
4234
4193
  }
4235
-
4236
- // src/str/dateformat.ts
4237
- var MONTHS2 = [
4238
- "January",
4239
- "February",
4240
- "March",
4241
- "April",
4242
- "May",
4243
- "June",
4244
- "July",
4245
- "August",
4246
- "September",
4247
- "October",
4248
- "November",
4249
- "December"
4250
- ];
4251
- var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
4252
- var pad = (n, width) => String(n).padStart(width, "0");
4253
- function zoneName(time2, timezone, offsetMs) {
4254
- if (timezone === "Etc/UTC") return "UTC";
4255
- if (/^(UTC|GMT)/.test(timezone)) {
4256
- if (offsetMs === 0) return "GMT";
4257
- const minutes = Math.abs(offsetMs) / 6e4;
4258
- return `GMT${offsetMs < 0 ? "-" : "+"}${pad(Math.floor(minutes / 60), 2)}:${pad(minutes % 60, 2)}`;
4194
+ function offsetAt(zone, time2) {
4195
+ if (zone.kind === "fixed") return zone.offsetMs;
4196
+ const wholeSecond = Math.floor(time2 / 1e3) * 1e3;
4197
+ const f = {};
4198
+ for (const part of zone.format.formatToParts(wholeSecond)) {
4199
+ if (part.type !== "literal") f[part.type] = Number(part.value);
4259
4200
  }
4260
- const part = new Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(time2).find((p) => p.type === "timeZoneName");
4261
- return part?.value ?? timezone;
4201
+ const local = Date.UTC(f.year, f.month - 1, f.day, f.hour, f.minute, f.second);
4202
+ return local - wholeSecond;
4262
4203
  }
4263
- function weekOfYear(year3, month3, day) {
4264
- const date = Date.UTC(year3, month3 - 1, day);
4265
- const dow = new Date(date).getUTCDay();
4266
- const weekStart2 = date - dow * 864e5;
4267
- if (weekStart2 + 6 * 864e5 >= Date.UTC(year3 + 1, 0, 1)) return 1;
4268
- const jan1 = Date.UTC(year3, 0, 1);
4269
- const jan1Dow = new Date(jan1).getUTCDay();
4270
- return Math.floor((date - jan1 + jan1Dow * 864e5) / (7 * 864e5)) + 1;
4204
+ function zoneOffset(time2, timezone) {
4205
+ return offsetAt(resolveZone(timezone), time2);
4271
4206
  }
4272
- function formatDate(time2, pattern, timezone) {
4273
- if (Number.isNaN(time2)) return "NaN";
4274
- const f = zonedFields(time2, timezone);
4275
- const offset = zoneOffset(time2, timezone);
4276
- const ms = (time2 % 1e3 + 1e3) % 1e3;
4277
- const dayOfYear = (Date.UTC(f.year, f.month - 1, f.day) - Date.UTC(f.year, 0, 1)) / 864e5 + 1;
4278
- let out = "";
4279
- for (let i = 0; i < pattern.length; ) {
4280
- const ch = pattern[i];
4281
- if (ch === "'") {
4282
- const end = pattern.indexOf("'", i + 1);
4283
- if (end === i + 1) {
4284
- out += "'";
4285
- i += 2;
4286
- } else {
4287
- out += pattern.slice(i + 1, end === -1 ? void 0 : end);
4288
- i = end === -1 ? pattern.length : end + 1;
4289
- }
4290
- continue;
4291
- }
4292
- if (!/[A-Za-z]/.test(ch)) {
4293
- out += ch;
4294
- i++;
4295
- continue;
4296
- }
4297
- let n = 1;
4298
- while (pattern[i + n] === ch) n++;
4299
- i += n;
4300
- const hour12 = f.hour % 12;
4301
- switch (ch) {
4302
- case "y":
4303
- out += n === 2 ? pad(f.year % 100, 2) : pad(f.year, n);
4304
- break;
4305
- case "M":
4306
- out += n >= 4 ? MONTHS2[f.month - 1] : n === 3 ? MONTHS2[f.month - 1].slice(0, 3) : pad(f.month, n);
4307
- break;
4308
- case "d":
4309
- out += pad(f.day, n);
4310
- break;
4311
- case "E":
4312
- out += n >= 4 ? DAYS[f.dayOfWeek - 1] : DAYS[f.dayOfWeek - 1].slice(0, 3);
4313
- break;
4314
- case "u":
4315
- out += pad(f.dayOfWeek === 1 ? 7 : f.dayOfWeek - 1, n);
4316
- break;
4317
- case "D":
4318
- out += pad(dayOfYear, n);
4319
- break;
4320
- case "w":
4321
- out += pad(weekOfYear(f.year, f.month, f.day), n);
4322
- break;
4323
- case "H":
4324
- out += pad(f.hour, n);
4325
- break;
4326
- case "k":
4327
- out += pad(f.hour === 0 ? 24 : f.hour, n);
4328
- break;
4329
- case "K":
4330
- out += pad(hour12, n);
4331
- break;
4332
- case "h":
4333
- out += pad(hour12 === 0 ? 12 : hour12, n);
4334
- break;
4335
- case "a":
4336
- out += f.hour < 12 ? "AM" : "PM";
4337
- break;
4338
- case "m":
4339
- out += pad(f.minute, n);
4340
- break;
4341
- case "s":
4342
- out += pad(f.second, n);
4343
- break;
4344
- case "S":
4345
- out += pad(ms, n);
4346
- break;
4347
- case "Z": {
4348
- const minutes = Math.abs(offset) / 6e4;
4349
- out += (offset < 0 ? "-" : "+") + pad(Math.floor(minutes / 60), 2) + pad(minutes % 60, 2);
4350
- break;
4351
- }
4352
- case "z":
4353
- out += zoneName(time2, timezone, offset);
4354
- break;
4355
- default:
4356
- throw new SyntaxError(`Unsupported date pattern letter "${ch}" in "${pattern}"`);
4357
- }
4358
- }
4359
- return out;
4207
+ function zonedFields(time2, timezone) {
4208
+ const local = new Date(time2 + offsetAt(resolveZone(timezone), time2));
4209
+ return {
4210
+ year: local.getUTCFullYear(),
4211
+ month: local.getUTCMonth() + 1,
4212
+ day: local.getUTCDate(),
4213
+ hour: local.getUTCHours(),
4214
+ minute: local.getUTCMinutes(),
4215
+ second: local.getUTCSeconds(),
4216
+ dayOfWeek: local.getUTCDay() + 1
4217
+ };
4218
+ }
4219
+ function zonedToUnix(timezone, year3, month3, day, hour3 = 0, minute3 = 0, second3 = 0) {
4220
+ const zone = resolveZone(timezone);
4221
+ const local = Date.UTC(year3, month3 - 1, day, hour3, minute3, second3);
4222
+ if (zone.kind === "fixed") return local - zone.offsetMs;
4223
+ const before = offsetAt(zone, local - MS_PER_DAY);
4224
+ const after = offsetAt(zone, local + MS_PER_DAY);
4225
+ const candidates = [local - before, local - after].filter((t2) => local - offsetAt(zone, t2) === t2);
4226
+ if (candidates.length) return Math.max(...candidates);
4227
+ return local - before;
4360
4228
  }
4361
4229
 
4362
- // src/str/messageformat.ts
4363
- var DATE_STYLES = {
4364
- short: "M/d/yy",
4365
- medium: "MMM d, yyyy",
4366
- long: "MMMM d, yyyy",
4367
- full: "EEEE, MMMM d, yyyy"
4368
- };
4369
- var TIME_STYLES = {
4370
- short: "h:mm\u202Fa",
4371
- medium: "h:mm:ss\u202Fa",
4372
- long: "h:mm:ss\u202Fa z",
4373
- full: "h:mm:ss\u202Fa z"
4230
+ // src/time/datestring.ts
4231
+ var MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
4232
+ var FULL_MONTHS = [
4233
+ "january",
4234
+ "february",
4235
+ "march",
4236
+ "april",
4237
+ "may",
4238
+ "june",
4239
+ "july",
4240
+ "august",
4241
+ "september",
4242
+ "october",
4243
+ "november",
4244
+ "december"
4245
+ ];
4246
+ var ZONE_NAMES = {
4247
+ ut: 0,
4248
+ utc: 0,
4249
+ gmt: 0,
4250
+ est: -5,
4251
+ edt: -4,
4252
+ cst: -6,
4253
+ cdt: -5,
4254
+ mst: -7,
4255
+ mdt: -6,
4256
+ pst: -8,
4257
+ pdt: -7
4374
4258
  };
4375
- function number(value, pattern) {
4376
- return typeof value === "number" ? formatNumber(value, pattern, "halfEvenExact") : String(value);
4377
- }
4378
- function choice(value, spec) {
4379
- const options = spec.split("|").map((part) => {
4380
- const m = /^\s*(-?[\d.]+|-?∞)\s*([#<≤])(.*)$/.exec(part);
4381
- if (!m) throw new SyntaxError(`Invalid choice format "${spec}"`);
4382
- const limit = m[1].includes("\u221E") ? m[1].startsWith("-") ? -Infinity : Infinity : Number(m[1]);
4383
- return { limit: m[2] === "<" ? limit + Number.EPSILON * Math.max(1, Math.abs(limit)) : limit, text: m[3] };
4384
- });
4385
- let chosen = options[0];
4386
- for (const o of options) if (value >= o.limit) chosen = o;
4387
- return chosen.text;
4259
+ var TIME = String.raw`(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3})\d*)?)?`;
4260
+ var ZONE = String.raw`(ut|utc|gmt|[ecmp][sd]t|(?:utc|gmt)?\s*[+-]\d{1,2}(?::?\d{2})?)`;
4261
+ var NUMERIC_ZONE = String.raw`([+-]\d{2}(?::?\d{2})?)`;
4262
+ var DATE = String.raw`(\d{4})([-/])(\d{1,2})\2(\d{1,2})`;
4263
+ var WEEKDAY = String.raw`(?:[a-z]{3,9},?\s+)?`;
4264
+ var ISO_T = new RegExp(String.raw`^${DATE}t${TIME}\s*${NUMERIC_ZONE}?$`, "i");
4265
+ var ISO = new RegExp(String.raw`^${DATE}(?:\s+${TIME})?\s*${ZONE}?$`, "i");
4266
+ var DAY_FIRST = new RegExp(String.raw`^${WEEKDAY}(\d{1,2})(?:\s+|-)([a-z]+)\.?,?(?:\s+|-)(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
4267
+ var MONTH_FIRST = new RegExp(String.raw`^${WEEKDAY}([a-z]+)\.?\s+(\d{1,2}),?\s+(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
4268
+ function monthNumber(name) {
4269
+ const lower2 = name.toLowerCase();
4270
+ const i = FULL_MONTHS.findIndex((full, k) => lower2.startsWith(MONTHS[k]) && full.startsWith(lower2));
4271
+ return i + 1;
4388
4272
  }
4389
- function formatArgument(value, type, style, timezone) {
4390
- if (value === void 0) return "";
4391
- if (!type) return typeof value === "number" ? number(value, "#,##0.###") : String(value);
4392
- switch (type.trim()) {
4393
- case "number": {
4394
- const s = style?.trim();
4395
- if (!s) return number(value, "#,##0.###");
4396
- if (s === "integer") return number(value, "#,##0");
4397
- if (s === "percent") return number(value, "#,##0%");
4398
- if (s === "currency") return number(value, "'$'#,##0.00");
4399
- return number(value, style);
4400
- }
4401
- case "date":
4402
- case "time": {
4403
- const styles = type.trim() === "date" ? DATE_STYLES : TIME_STYLES;
4404
- const s = style?.trim() ?? "medium";
4405
- return formatDate(Number(value), styles[s] ?? style, typeof timezone === "function" ? timezone() : timezone);
4406
- }
4407
- case "choice":
4408
- return choice(Number(value), style ?? "");
4409
- default:
4410
- throw new SyntaxError(`Unknown format type "${type}"`);
4411
- }
4273
+ function zoneMinutes(zone) {
4274
+ if (!zone) return 0;
4275
+ const lower2 = zone.toLowerCase().replace(/\s+/g, "");
4276
+ const named = ZONE_NAMES[lower2];
4277
+ if (named !== void 0) return named * 60;
4278
+ const m = /^(?:utc|gmt)?([+-])(\d{1,2})(?::?(\d{2}))?$/.exec(lower2);
4279
+ if (!m) return NaN;
4280
+ const minutes = Number(m[2]) * 60 + (m[3] ? Number(m[3]) : 0);
4281
+ return m[1] === "-" ? -minutes : minutes;
4412
4282
  }
4413
- function formatMessage(pattern, args, timezone) {
4414
- let out = "";
4415
- for (let i = 0; i < pattern.length; ) {
4416
- const ch = pattern[i];
4417
- if (ch === "'") {
4418
- if (pattern[i + 1] === "'") {
4419
- out += "'";
4420
- i += 2;
4421
- continue;
4422
- }
4423
- const end = pattern.indexOf("'", i + 1);
4424
- out += pattern.slice(i + 1, end === -1 ? void 0 : end);
4425
- i = end === -1 ? pattern.length : end + 1;
4426
- continue;
4427
- }
4428
- if (ch !== "{") {
4429
- out += ch;
4430
- i++;
4431
- continue;
4432
- }
4433
- let depth = 1;
4434
- let j = i + 1;
4435
- let inQuote = false;
4436
- for (; j < pattern.length && depth > 0; j++) {
4437
- const c = pattern[j];
4438
- if (c === "'") inQuote = !inQuote;
4439
- else if (!inQuote && c === "{") depth++;
4440
- else if (!inQuote && c === "}") depth--;
4441
- }
4442
- const inner = pattern.slice(i + 1, j - 1);
4443
- const [index = "", type, ...rest] = inner.split(",");
4444
- const style = rest.length ? rest.join(",") : void 0;
4445
- const n = Number(index.trim());
4446
- out += n < args.length ? formatArgument(args[n], type, style, timezone) : `{${inner}}`;
4447
- i = j;
4283
+ function parseDateString(dateString) {
4284
+ const text2 = dateString.trim();
4285
+ let year3, month3, day;
4286
+ let rest;
4287
+ let m;
4288
+ if (m = ISO_T.exec(text2) ?? ISO.exec(text2)) {
4289
+ [year3, month3, day] = [m[1], Number(m[3]), m[4]];
4290
+ rest = m.slice(5);
4291
+ } else if (m = DAY_FIRST.exec(text2)) {
4292
+ [day, month3, year3] = [m[1], monthNumber(m[2]), m[3]];
4293
+ rest = m.slice(4);
4294
+ } else if (m = MONTH_FIRST.exec(text2)) {
4295
+ [month3, day, year3] = [monthNumber(m[1]), m[2], m[3]];
4296
+ rest = m.slice(4);
4297
+ } else {
4298
+ throw new SyntaxError(`Invalid date string "${dateString}": use RFC 2822 or ISO 8601 ("20 Feb 2020", "2020-02-20T14:48:00")`);
4448
4299
  }
4449
- return out;
4300
+ const [hour3 = "0", minute3 = "0", second3 = "0", millis = "0", zone] = rest;
4301
+ const d = Number(day);
4302
+ const h = Number(hour3);
4303
+ const mi = Number(minute3);
4304
+ const s = Number(second3);
4305
+ const offset = zoneMinutes(zone);
4306
+ const endOfDay = h === 24 && mi === 0 && s === 0 && Number(millis) === 0;
4307
+ if (month3 < 1 || month3 > 12 || d < 1 || d > 31 || h > 23 && !endOfDay || mi > 59 || s > 59 || Number.isNaN(offset)) {
4308
+ throw new SyntaxError(`Invalid date string "${dateString}": a field is out of range`);
4309
+ }
4310
+ const ms = Number(millis.padEnd(3, "0"));
4311
+ return Date.UTC(Number(year3), month3 - 1, d, h, mi, s, ms) - offset * 6e4;
4450
4312
  }
4451
4313
 
4452
- // src/str/index.ts
4453
- function length(str2) {
4454
- return str2.length;
4314
+ // src/time/session.ts
4315
+ var PERIOD = /^(\d{2})(\d{2})-(\d{2})(\d{2})$/;
4316
+ var sessions = /* @__PURE__ */ new Map();
4317
+ function parseSession(session2) {
4318
+ let parsed = sessions.get(session2);
4319
+ if (parsed) return parsed;
4320
+ const invalid = () => new SyntaxError(`Invalid session "${session2}": expected "HHmm-HHmm[,HHmm-HHmm...][:days]"`);
4321
+ const text2 = session2.trim() === "24x7" ? "0000-0000" : session2.replace(/\s+/g, "");
4322
+ const [periodText = "", dayText, extra] = text2.split(":");
4323
+ if (extra !== void 0 || periodText === "") throw invalid();
4324
+ const periods = periodText.split(",").map((p) => {
4325
+ const m = PERIOD.exec(p);
4326
+ if (!m) throw invalid();
4327
+ const [h1, m1, h2, m2] = [m[1], m[2], m[3], m[4]].map(Number);
4328
+ if (h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) throw invalid();
4329
+ return { start: h1 * 60 + m1, end: h2 * 60 + m2 };
4330
+ });
4331
+ const dayDigits = dayText ?? (periods.length > 1 ? "23456" : "1234567");
4332
+ if (!/^[1-7]+$/.test(dayDigits)) throw invalid();
4333
+ const days = new Array(8).fill(false);
4334
+ for (const d of dayDigits) days[Number(d)] = true;
4335
+ parsed = { periods, days };
4336
+ sessions.set(session2, parsed);
4337
+ return parsed;
4455
4338
  }
4456
- function tostring(value, format2) {
4457
- if (typeof value !== "number") return String(value);
4458
- if (!Number.isFinite(value)) return String(value);
4459
- if (format2 === void 0 || format2 === "") {
4460
- if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
4461
- return String(value).replace("e+", "E").replace(/\.0+E/, "E");
4462
- }
4463
- return formatNumber(value, "#.##########", "halfUpShortest");
4464
- }
4465
- switch (format2) {
4466
- case "percent":
4467
- return formatNumber(value, "0.00", "halfUpShortest") + "%";
4468
- case "volume": {
4469
- const abs2 = Math.abs(value);
4470
- for (const [size, unit] of [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]]) {
4471
- if (abs2 >= size) return formatNumber(value / size, "#.###", "halfUpShortest") + unit;
4472
- }
4473
- return formatNumber(value, "#", "halfUpShortest");
4339
+ function inSession(time2, session2, timezone) {
4340
+ if (Number.isNaN(time2)) return false;
4341
+ const { periods, days } = parseSession(session2);
4342
+ const f = zonedFields(time2, timezone);
4343
+ const minute3 = f.hour * 60 + f.minute;
4344
+ const nextDay = f.dayOfWeek % 7 + 1;
4345
+ for (const { start, end } of periods) {
4346
+ if (start < end || start === 0 && end === 0) {
4347
+ if (minute3 >= start && (minute3 < end || end === 0) && days[f.dayOfWeek]) return true;
4348
+ } else {
4349
+ if (minute3 >= start && days[nextDay]) return true;
4350
+ if (minute3 < end && days[f.dayOfWeek]) return true;
4474
4351
  }
4475
- case "mintick":
4476
- throw new Error("str.tostring(x, format.mintick) needs the symbol tick size (syminfo.mintick), which is not known here");
4477
- default:
4478
- return formatNumber(value, format2, "halfUpShortest");
4479
4352
  }
4353
+ return false;
4480
4354
  }
4481
- function tonumber(str2) {
4482
- const num = parseFloat(str2);
4483
- return isNaN(num) ? null : num;
4484
- }
4485
- function substring(str2, begin, end) {
4486
- return str2.substring(begin, end);
4487
- }
4488
- function upper(str2) {
4489
- return str2.toUpperCase();
4490
- }
4491
- function lower(str2) {
4492
- return str2.toLowerCase();
4493
- }
4494
- function contains(source, str2) {
4495
- return source.includes(str2);
4496
- }
4497
- function pos(source, str2) {
4498
- return source.indexOf(str2);
4499
- }
4500
- function replace(source, target, replacement, occurrence) {
4501
- if (occurrence === 0) {
4502
- return source.replace(target, replacement);
4503
- } else {
4504
- return source.replaceAll(target, replacement);
4355
+
4356
+ // src/time/index.ts
4357
+ function timestamp(first, ...rest) {
4358
+ if (typeof first === "string") {
4359
+ if (rest.length === 0) return parseDateString(first);
4360
+ const [year3, month4, day2, hour4, minute4, second4] = rest;
4361
+ return zonedToUnix(first, year3, month4, day2, hour4 ?? 0, minute4 ?? 0, second4 ?? 0);
4505
4362
  }
4363
+ const [month3, day, hour3, minute3, second3] = rest;
4364
+ return new Date(first, month3 - 1, day, hour3 || 0, minute3 || 0, second3 || 0).getTime();
4506
4365
  }
4507
- function replace_all(source, target, replacement) {
4508
- return source.replaceAll(target, replacement);
4366
+ function field(time2, timezone, pick) {
4367
+ return Number.isNaN(time2) ? NaN : pick(zonedFields(time2, timezone));
4509
4368
  }
4510
- function split(str2, separator) {
4511
- return str2.split(separator);
4369
+ function year(time2, timezone) {
4370
+ return field(time2, timezone, (f) => f.year);
4512
4371
  }
4513
- function concat(...strings) {
4514
- return strings.join("");
4372
+ function month(time2, timezone) {
4373
+ return field(time2, timezone, (f) => f.month);
4515
4374
  }
4516
- function format(formatStr, ...args) {
4517
- return formatMessage(formatStr, args, "Etc/UTC");
4375
+ function dayofmonth(time2, timezone) {
4376
+ return field(time2, timezone, (f) => f.day);
4518
4377
  }
4519
- function startswith(source, str2) {
4520
- return source.startsWith(str2);
4378
+ function dayofweek(time2, timezone) {
4379
+ return field(time2, timezone, (f) => f.dayOfWeek);
4521
4380
  }
4522
- function endswith(source, str2) {
4523
- return source.endsWith(str2);
4381
+ function weekofyear(time2, timezone) {
4382
+ return field(time2, timezone, (f) => {
4383
+ const date = Date.UTC(f.year, f.month - 1, f.day);
4384
+ const mondayBased = (f.dayOfWeek + 5) % 7;
4385
+ const thursday = new Date(date + (3 - mondayBased) * 864e5);
4386
+ const yearStart = Date.UTC(thursday.getUTCFullYear(), 0, 1);
4387
+ return 1 + Math.floor((thursday.getTime() - yearStart) / (7 * 864e5));
4388
+ });
4524
4389
  }
4525
- function charAt(str2, pos2) {
4526
- return str2.charAt(pos2);
4390
+ function hour(time2, timezone) {
4391
+ return field(time2, timezone, (f) => f.hour);
4527
4392
  }
4528
- function trim(str2) {
4529
- return str2.trim();
4393
+ function minute(time2, timezone) {
4394
+ return field(time2, timezone, (f) => f.minute);
4530
4395
  }
4531
- function trimLeft(str2) {
4532
- return str2.trimStart();
4396
+ function second(time2, timezone) {
4397
+ return field(time2, timezone, (f) => f.second);
4533
4398
  }
4534
- function trimRight(str2) {
4535
- return str2.trimEnd();
4399
+ function inSession2(time2, session2, timezone) {
4400
+ return inSession(time2, session2, timezone);
4536
4401
  }
4537
- function match(source, regex) {
4538
- return new RegExp(regex).test(source);
4402
+
4403
+ // src/str/index.ts
4404
+ var str_exports = {};
4405
+ __export(str_exports, {
4406
+ charAt: () => charAt,
4407
+ concat: () => concat,
4408
+ contains: () => contains,
4409
+ endswith: () => endswith,
4410
+ format: () => format,
4411
+ format_time: () => format_time,
4412
+ length: () => length,
4413
+ lower: () => lower,
4414
+ match: () => match,
4415
+ pos: () => pos,
4416
+ repeat: () => repeat,
4417
+ replace: () => replace,
4418
+ replace_all: () => replace_all,
4419
+ split: () => split,
4420
+ startswith: () => startswith,
4421
+ substring: () => substring,
4422
+ tonumber: () => tonumber,
4423
+ tostring: () => tostring,
4424
+ trim: () => trim,
4425
+ trimLeft: () => trimLeft,
4426
+ trimRight: () => trimRight,
4427
+ upper: () => upper
4428
+ });
4429
+
4430
+ // src/str/numberformat.ts
4431
+ var patterns = /* @__PURE__ */ new Map();
4432
+ function parsePattern(pattern) {
4433
+ const cached = patterns.get(pattern);
4434
+ if (cached) return cached;
4435
+ let prefix = "";
4436
+ let suffix = "";
4437
+ let number2 = "";
4438
+ let percent = false;
4439
+ let phase = "prefix";
4440
+ for (let i = 0; i < pattern.length; i++) {
4441
+ const ch = pattern[i];
4442
+ let literal;
4443
+ if (ch === "'") {
4444
+ const end = pattern.indexOf("'", i + 1);
4445
+ if (end === i + 1) {
4446
+ literal = "'";
4447
+ i = end;
4448
+ } else {
4449
+ literal = pattern.slice(i + 1, end === -1 ? void 0 : end);
4450
+ i = end === -1 ? pattern.length : end;
4451
+ }
4452
+ } else if (phase !== "suffix" && ("#0,.".includes(ch) || phase === "number" && ch === "E")) {
4453
+ phase = "number";
4454
+ number2 += ch;
4455
+ continue;
4456
+ } else {
4457
+ if (ch === "%") percent = true;
4458
+ literal = ch;
4459
+ }
4460
+ if (phase === "number") phase = "suffix";
4461
+ if (phase === "prefix") prefix += literal;
4462
+ else suffix += literal;
4463
+ }
4464
+ const [mantissa = "", exp2] = number2.split("E");
4465
+ const [intPart = "", fracPart = ""] = mantissa.split(".");
4466
+ const lastComma = intPart.lastIndexOf(",");
4467
+ const parsed = {
4468
+ prefix,
4469
+ suffix,
4470
+ // as in Java, a pattern with a decimal point and no '0' at all ("#.##") shows one integer digit
4471
+ minInt: !mantissa.includes("0") && mantissa.includes(".") ? 1 : [...intPart].filter((c) => c === "0").length,
4472
+ minFrac: [...fracPart].filter((c) => c === "0").length,
4473
+ maxFrac: [...fracPart].filter((c) => c === "0" || c === "#").length,
4474
+ grouping: lastComma === -1 ? 0 : intPart.length - lastComma - 1,
4475
+ percent,
4476
+ exponent: exp2 === void 0 ? null : exp2.length,
4477
+ hasDigits: /[0#]/.test(mantissa)
4478
+ };
4479
+ patterns.set(pattern, parsed);
4480
+ return parsed;
4539
4481
  }
4540
- function repeat(source, count, separator = "") {
4541
- if (source == null) return null;
4542
- if (count <= 0) return "";
4543
- return Array(count).fill(source).join(separator);
4482
+ function shortestDigits(x) {
4483
+ const m = /^(\d+)(?:\.(\d+))?(?:e([+-]\d+))?$/.exec(String(Math.abs(x)));
4484
+ const all7 = m[1] + (m[2] ?? "");
4485
+ let intLen = m[1].length + Number(m[3] ?? 0);
4486
+ const lead = all7.length - all7.replace(/^0+/, "").length;
4487
+ intLen -= lead;
4488
+ const digits = all7.slice(lead).replace(/0+$/, "");
4489
+ return { digits, intLen: digits ? intLen : 0 };
4544
4490
  }
4545
- function format_time(time2, format2 = "yyyy-MM-dd'T'HH:mm:ssZ", timezone = "Etc/UTC") {
4546
- return formatDate(time2, format2, timezone);
4491
+ function exactDigits(x) {
4492
+ const abs2 = Math.abs(x);
4493
+ if (abs2 === 0) return { digits: "", intLen: 0 };
4494
+ const view = new DataView(new ArrayBuffer(8));
4495
+ view.setFloat64(0, abs2);
4496
+ const bits = view.getBigUint64(0);
4497
+ const expBits = Number(bits >> 52n & 0x7ffn);
4498
+ let mant = bits & 0xfffffffffffffn;
4499
+ const e2 = expBits === 0 ? -1074 : expBits - 1075;
4500
+ if (expBits !== 0) mant |= 1n << 52n;
4501
+ let str2;
4502
+ let fracDigits;
4503
+ if (e2 >= 0) {
4504
+ str2 = (mant << BigInt(e2)).toString();
4505
+ fracDigits = 0;
4506
+ } else {
4507
+ str2 = (mant * 5n ** BigInt(-e2)).toString();
4508
+ fracDigits = -e2;
4509
+ }
4510
+ const intLen = str2.length - fracDigits;
4511
+ const lead = str2.length - str2.replace(/^0+/, "").length;
4512
+ const digits = str2.slice(lead).replace(/0+$/, "");
4513
+ return { digits, intLen: intLen - lead };
4547
4514
  }
4548
-
4549
- // src/session/calendar.ts
4550
- var toDateNum = (y, m, d) => y * 1e4 + m * 100 + d;
4551
- var utcOf = (date) => Date.UTC(Math.floor(date / 1e4), Math.floor(date / 100) % 100 - 1, date % 100);
4552
- var fromUtc = (ms) => {
4553
- const d = new Date(ms);
4554
- return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
4555
- };
4556
- var addDays = (date, n) => fromUtc(utcOf(date) + n * 864e5);
4557
- var weekdayOf = (date) => new Date(utcOf(date)).getUTCDay() + 1;
4558
- var dateUtcMs = utcOf;
4559
- function parseTime(token, spec) {
4560
- const m = /^(\d{2})(\d{2})(?:F(\d+))?$/.exec(token.trim());
4561
- if (!m) throw new SyntaxError(`Invalid session "${spec}"`);
4562
- return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === void 0 ? void 0 : Number(m[3]) };
4515
+ function roundDigits(d, frac, mode3) {
4516
+ const keep = d.intLen + frac;
4517
+ if (keep >= d.digits.length) return d;
4518
+ if (keep < 0) return { digits: "", intLen: 0 };
4519
+ const first = d.digits.charCodeAt(keep) - 48;
4520
+ let up;
4521
+ if (mode3 === "halfUpShortest") up = first >= 5;
4522
+ else if (first !== 5) up = first > 5;
4523
+ else if (d.digits.length > keep + 1) up = true;
4524
+ else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
4525
+ let kept = d.digits.slice(0, keep);
4526
+ let intLen = d.intLen;
4527
+ if (up) {
4528
+ const n = (BigInt(kept || "0") + 1n).toString();
4529
+ if (n.length > kept.length) intLen += 1;
4530
+ kept = n;
4531
+ }
4532
+ kept = kept.replace(/0+$/, "");
4533
+ return { digits: kept, intLen: kept ? intLen : 0 };
4563
4534
  }
4564
- function parsePeriods(text2, spec) {
4565
- if (text2.trim() === "24x7") return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];
4566
- return text2.split(",").map((p) => {
4567
- const [a = "", b2 = ""] = p.split("-");
4568
- const s = parseTime(a, spec);
4569
- const e2 = parseTime(b2, spec);
4570
- if (s.minutes === 0 && e2.minutes === 0 && s.offset === void 0 && e2.offset === void 0) {
4571
- return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };
4572
- }
4573
- return {
4574
- start: s.minutes,
4575
- end: e2.minutes,
4576
- startOffset: s.offset ?? (e2.minutes <= s.minutes ? 1 : 0),
4577
- endOffset: e2.offset ?? 0
4578
- };
4579
- });
4535
+ function group(int, size) {
4536
+ if (!size || int.length <= size) return int;
4537
+ const parts = [];
4538
+ for (let end = int.length; end > 0; end -= size) parts.unshift(int.slice(Math.max(0, end - size), end));
4539
+ return parts.join(",");
4580
4540
  }
4581
- function parseVersion(text2, from, spec, rule) {
4582
- const [periodText = "", dayText] = text2.trim().split(":");
4583
- const periods = parsePeriods(periodText, spec);
4584
- const allDays = periodText.trim() === "24x7" || rule === "pine" && periods.length === 1;
4585
- const digits = dayText ?? (allDays ? "1234567" : "23456");
4586
- if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in "${spec}"`);
4587
- const days = new Array(8).fill(false);
4588
- for (const d of digits) days[Number(d)] = true;
4589
- return { from, periods, days };
4541
+ function layout(d, p) {
4542
+ let int = d.intLen > 0 ? d.digits.slice(0, d.intLen).padEnd(d.intLen, "0") : "";
4543
+ int = int.replace(/^0+/, "").padStart(p.minInt, "0");
4544
+ let frac = d.intLen < 0 ? "0".repeat(-d.intLen) + d.digits : d.digits.slice(Math.max(0, d.intLen));
4545
+ frac = frac.slice(0, p.maxFrac).replace(/0+$/, "").padEnd(p.minFrac, "0");
4546
+ if (!int && !frac) int = "0";
4547
+ return group(int, p.grouping) + (frac ? "." + frac : "");
4590
4548
  }
4591
- var TradingCalendar = class {
4592
- /**
4593
- * @param timezone - Time zone of the session hours (the exchange time zone)
4594
- * @param spec - Session information (hours, corrections, holidays)
4595
- * @param rule - Days without ":days": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)
4596
- */
4597
- constructor(timezone, spec, rule = "symbol") {
4598
- this.timezone = timezone;
4599
- const [first = "", ...changes] = spec.session.split("#");
4600
- this.versions = [parseVersion(first, 0, spec.session, rule)];
4601
- for (const change3 of changes) {
4602
- const m = /^(\d{8})\/(.+)$/.exec(change3.trim());
4603
- if (!m) throw new SyntaxError(`Invalid session change in "${spec.session}"`);
4604
- this.versions.push(parseVersion(m[2], Number(m[1]), spec.session, rule));
4605
- }
4606
- for (const part of (spec.corrections ?? "").split(";").filter((p) => p.trim())) {
4607
- const i = part.lastIndexOf(":");
4608
- const text2 = part.slice(0, i).trim();
4609
- const periods = text2 === "dayoff" ? null : parsePeriods(text2, part);
4610
- for (const date of part.slice(i + 1).split(",")) this.corrections.set(Number(date), periods);
4549
+ function formatNumber(value, pattern, mode3) {
4550
+ if (Number.isNaN(value)) return "NaN";
4551
+ const p = parsePattern(pattern);
4552
+ if (!Number.isFinite(value)) return (value < 0 ? "-" : "") + p.prefix + "\u221E" + p.suffix;
4553
+ const x = p.percent ? value * 100 : value;
4554
+ const toDigits = mode3 === "halfUpShortest" ? shortestDigits : exactDigits;
4555
+ let body;
4556
+ let isZero;
4557
+ if (p.exponent !== null) {
4558
+ const rounded = roundDigits(toDigits(x), p.maxFrac, mode3);
4559
+ const intDigits = Math.max(p.minInt, 1);
4560
+ isZero = rounded.digits === "";
4561
+ let exponent = isZero ? 0 : rounded.intLen - intDigits;
4562
+ let mant = roundDigits({ digits: rounded.digits, intLen: isZero ? 0 : intDigits }, p.maxFrac, mode3);
4563
+ if (mant.intLen > intDigits) {
4564
+ exponent += 1;
4565
+ mant = { digits: mant.digits, intLen: intDigits };
4611
4566
  }
4612
- const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? "").split(",");
4613
- this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));
4567
+ const e2 = String(Math.abs(exponent)).padStart(p.exponent, "0");
4568
+ body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e2;
4569
+ } else {
4570
+ const rounded = roundDigits(toDigits(x), p.hasDigits ? p.maxFrac : 0, mode3);
4571
+ isZero = rounded.digits === "";
4572
+ body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });
4614
4573
  }
4615
- timezone;
4616
- versions;
4617
- corrections = /* @__PURE__ */ new Map();
4618
- holidays;
4619
- cache = /* @__PURE__ */ new Map();
4620
- /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */
4621
- periods(date) {
4622
- let out = this.cache.get(date);
4623
- if (out) return out;
4624
- let periods = [];
4625
- const correction = this.corrections.get(date);
4626
- if (correction !== void 0) {
4627
- periods = correction ?? [];
4628
- } else if (!this.holidays.has(date)) {
4629
- const version = [...this.versions].reverse().find((v) => v.from <= date);
4630
- if (version.days[weekdayOf(date)]) periods = version.periods;
4631
- }
4632
- const y = Math.floor(date / 1e4);
4633
- const m = Math.floor(date / 100) % 100;
4634
- const d = date % 100;
4635
- out = periods.map((p) => [
4636
- zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),
4637
- zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)
4638
- ]);
4639
- this.cache.set(date, out);
4640
- return out;
4641
- }
4642
- isTradingDay(date) {
4643
- return this.periods(date).length > 0;
4644
- }
4645
- /**
4646
- * Trading day and period of a time: the period that contains it, else the last period that started
4647
- * before it. Null when no trading day is found near the time.
4648
- */
4649
- locate(time2) {
4650
- const f = zonedFields(time2, this.timezone);
4651
- const local = toDateNum(f.year, f.month, f.day);
4652
- for (let k = 0; k <= 5; k++) {
4653
- const date = addDays(local, k);
4654
- const index = this.periods(date).findIndex(([s, e2]) => s <= time2 && time2 < e2);
4655
- if (index !== -1) return { date, index, inside: true };
4656
- }
4657
- for (let k = 5; k >= -7; k--) {
4658
- const date = addDays(local, k);
4659
- const periods = this.periods(date);
4660
- let index = -1;
4661
- periods.forEach(([s], i) => {
4662
- if (s <= time2) index = i;
4663
- });
4664
- if (index !== -1) return { date, index, inside: false };
4665
- }
4666
- return null;
4667
- }
4668
- /** First trading day in [from, to] (dates), or null. */
4669
- firstTradingDay(from, to) {
4670
- for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;
4671
- return null;
4672
- }
4673
- /** Last trading day in [from, to] (dates), or null. */
4674
- lastTradingDay(from, to) {
4675
- for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;
4676
- return null;
4677
- }
4678
- };
4679
- var weekStart = (date) => addDays(date, -((weekdayOf(date) + 5) % 7));
4680
- var monthStart = (date) => Math.floor(date / 100) * 100 + 1;
4681
- var monthEnd = (date) => addDays(monthStart(addDays(monthStart(date), 32)), -1);
4682
-
4683
- // src/session/bars.ts
4684
- function unitOf(tf) {
4685
- return { unit: tf.isdaily ? "D" : tf.isweekly ? "W" : "M", n: tf.multiplier };
4574
+ const negative = x < 0 && (!isZero || !p.hasDigits);
4575
+ return (negative ? "-" : "") + p.prefix + body + p.suffix;
4686
4576
  }
4687
- var yearOf = (date) => Math.floor(date / 1e4);
4688
- var firstMonday = (year3) => {
4689
- const jan1 = year3 * 1e4 + 101;
4690
- const monday = weekStart(jan1);
4691
- return monday === jan1 ? jan1 : addDays(monday, 7);
4692
- };
4693
- var SessionBars = class {
4694
- constructor(calendar2, chartTimeframe2) {
4695
- this.calendar = calendar2;
4696
- this.chart = info(chartTimeframe2);
4577
+
4578
+ // src/str/dateformat.ts
4579
+ var MONTHS2 = [
4580
+ "January",
4581
+ "February",
4582
+ "March",
4583
+ "April",
4584
+ "May",
4585
+ "June",
4586
+ "July",
4587
+ "August",
4588
+ "September",
4589
+ "October",
4590
+ "November",
4591
+ "December"
4592
+ ];
4593
+ var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
4594
+ var pad = (n, width) => String(n).padStart(width, "0");
4595
+ function zoneName(time2, timezone, offsetMs) {
4596
+ if (timezone === "Etc/UTC") return "UTC";
4597
+ if (/^(UTC|GMT)/.test(timezone)) {
4598
+ if (offsetMs === 0) return "GMT";
4599
+ const minutes = Math.abs(offsetMs) / 6e4;
4600
+ return `GMT${offsetMs < 0 ? "-" : "+"}${pad(Math.floor(minutes / 60), 2)}:${pad(minutes % 60, 2)}`;
4697
4601
  }
4698
- calendar;
4699
- chart;
4700
- yearDays = /* @__PURE__ */ new Map();
4701
- /** Trading days of a calendar year, in order. */
4702
- tradingDaysOf(year3) {
4703
- let days = this.yearDays.get(year3);
4704
- if (!days) {
4705
- days = [];
4706
- for (let date = year3 * 1e4 + 101; yearOf(date) === year3; date = addDays(date, 1)) {
4707
- if (this.calendar.isTradingDay(date)) days.push(date);
4602
+ const part = new Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(time2).find((p) => p.type === "timeZoneName");
4603
+ return part?.value ?? timezone;
4604
+ }
4605
+ function weekOfYear(year3, month3, day) {
4606
+ const date = Date.UTC(year3, month3 - 1, day);
4607
+ const dow = new Date(date).getUTCDay();
4608
+ const weekStart2 = date - dow * 864e5;
4609
+ if (weekStart2 + 6 * 864e5 >= Date.UTC(year3 + 1, 0, 1)) return 1;
4610
+ const jan1 = Date.UTC(year3, 0, 1);
4611
+ const jan1Dow = new Date(jan1).getUTCDay();
4612
+ return Math.floor((date - jan1 + jan1Dow * 864e5) / (7 * 864e5)) + 1;
4613
+ }
4614
+ function formatDate(time2, pattern, timezone) {
4615
+ if (Number.isNaN(time2)) return "NaN";
4616
+ const f = zonedFields(time2, timezone);
4617
+ const offset = zoneOffset(time2, timezone);
4618
+ const ms = (time2 % 1e3 + 1e3) % 1e3;
4619
+ const dayOfYear = (Date.UTC(f.year, f.month - 1, f.day) - Date.UTC(f.year, 0, 1)) / 864e5 + 1;
4620
+ let out = "";
4621
+ for (let i = 0; i < pattern.length; ) {
4622
+ const ch = pattern[i];
4623
+ if (ch === "'") {
4624
+ const end = pattern.indexOf("'", i + 1);
4625
+ if (end === i + 1) {
4626
+ out += "'";
4627
+ i += 2;
4628
+ } else {
4629
+ out += pattern.slice(i + 1, end === -1 ? void 0 : end);
4630
+ i = end === -1 ? pattern.length : end + 1;
4708
4631
  }
4709
- this.yearDays.set(year3, days);
4710
- }
4711
- return days;
4712
- }
4713
- /**
4714
- * Dates [from, to] of the `n`-`unit` period that contains `date` (for "D": its first and last trading days).
4715
- * Periods restart each calendar year.
4716
- */
4717
- periodRange(date, unit, n) {
4718
- if (unit === "D") {
4719
- if (n === 1) return [date, date];
4720
- const days = this.tradingDaysOf(yearOf(date));
4721
- let k = days.findIndex((d) => d >= date);
4722
- if (k === -1) return this.periodRange(yearOf(date) * 1e4 + 1e4 + 101, unit, n);
4723
- k -= k % n;
4724
- return [days[k], days[Math.min(k + n, days.length) - 1]];
4725
- }
4726
- if (unit === "W") {
4727
- const monday = weekStart(date);
4728
- const year4 = yearOf(monday);
4729
- const first = firstMonday(year4);
4730
- const next = firstMonday(year4 + 1);
4731
- const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 864e5));
4732
- const from = addDays(first, (k - k % n) * 7);
4733
- const to = addDays(from, n * 7 - 1);
4734
- return [from, to < next ? to : addDays(next, -1)];
4735
- }
4736
- const year3 = yearOf(date);
4737
- const month3 = Math.floor(date / 100) % 100;
4738
- const firstMonth = month3 - (month3 - 1) % n;
4739
- const lastMonth = Math.min(firstMonth + n - 1, 12);
4740
- return [monthStart(year3 * 1e4 + firstMonth * 100 + 1), monthEnd(year3 * 1e4 + lastMonth * 100 + 1)];
4741
- }
4742
- dayStart(date) {
4743
- return this.calendar.periods(date)[0][0];
4744
- }
4745
- dayEnd(date) {
4746
- const p = this.calendar.periods(date);
4747
- return p[p.length - 1][1];
4748
- }
4749
- /** PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. */
4750
- timeOf(time2, timeframe2) {
4751
- const tf = info(timeframe2);
4752
- const at = this.calendar.locate(time2);
4753
- if (!at) return NaN;
4754
- if (tf.isintraday) {
4755
- if (this.chart.isdwm) return time2;
4756
- const [start] = this.calendar.periods(at.date)[at.index];
4757
- const ms = in_seconds(tf.period) * 1e3;
4758
- return start + Math.floor((time2 - start) / ms) * ms;
4759
- }
4760
- const { unit, n } = unitOf(tf);
4761
- const [from, to] = this.periodRange(at.date, unit, n);
4762
- const first = this.calendar.firstTradingDay(from, to);
4763
- return first === null ? NaN : this.dayStart(first);
4764
- }
4765
- /** PineScript `time_close(tf)` for a bar that opens at `time`. */
4766
- closeOf(time2, timeframe2) {
4767
- const tf = info(timeframe2);
4768
- const at = this.calendar.locate(time2);
4769
- if (!at) return NaN;
4770
- if (tf.isintraday) {
4771
- const [, end] = this.calendar.periods(at.date)[at.index];
4772
- if (this.chart.isdwm) return end;
4773
- return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
4774
- }
4775
- const { unit, n } = unitOf(tf);
4776
- const [from, to] = this.periodRange(at.date, unit, n);
4777
- if (this.chart.isintraday && (unit !== "D" || n > 1)) {
4778
- const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);
4779
- const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));
4780
- return first === null ? NaN : this.dayStart(first);
4632
+ continue;
4781
4633
  }
4782
- const last = this.calendar.lastTradingDay(from, to);
4783
- return last === null ? NaN : this.dayEnd(last);
4784
- }
4785
- /** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */
4786
- tradingDay(time2) {
4787
- const at = this.calendar.locate(time2);
4788
- if (!at) return null;
4789
- if (this.chart.isdwm) {
4790
- const { unit, n } = unitOf(this.chart);
4791
- const [from, to] = this.periodRange(at.date, unit, n);
4792
- return this.calendar.lastTradingDay(from, to);
4634
+ if (!/[A-Za-z]/.test(ch)) {
4635
+ out += ch;
4636
+ i++;
4637
+ continue;
4793
4638
  }
4794
- return at.date;
4795
- }
4796
- /**
4797
- * End of the `timeframe` period that contains `time`: the intraday close, the session end of the day,
4798
- * or the session end of the last trading day of the period. Used to know whether the last loaded
4799
- * bar completes a higher-timeframe period (request.security).
4800
- */
4801
- periodEnd(time2, timeframe2) {
4802
- const tf = info(timeframe2);
4803
- const at = this.calendar.locate(time2);
4804
- if (!at) return NaN;
4805
- if (tf.isintraday) {
4806
- const [, end] = this.calendar.periods(at.date)[at.index];
4807
- return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
4639
+ let n = 1;
4640
+ while (pattern[i + n] === ch) n++;
4641
+ i += n;
4642
+ const hour12 = f.hour % 12;
4643
+ switch (ch) {
4644
+ case "y":
4645
+ out += n === 2 ? pad(f.year % 100, 2) : pad(f.year, n);
4646
+ break;
4647
+ case "M":
4648
+ out += n >= 4 ? MONTHS2[f.month - 1] : n === 3 ? MONTHS2[f.month - 1].slice(0, 3) : pad(f.month, n);
4649
+ break;
4650
+ case "d":
4651
+ out += pad(f.day, n);
4652
+ break;
4653
+ case "E":
4654
+ out += n >= 4 ? DAYS[f.dayOfWeek - 1] : DAYS[f.dayOfWeek - 1].slice(0, 3);
4655
+ break;
4656
+ case "u":
4657
+ out += pad(f.dayOfWeek === 1 ? 7 : f.dayOfWeek - 1, n);
4658
+ break;
4659
+ case "D":
4660
+ out += pad(dayOfYear, n);
4661
+ break;
4662
+ case "w":
4663
+ out += pad(weekOfYear(f.year, f.month, f.day), n);
4664
+ break;
4665
+ case "H":
4666
+ out += pad(f.hour, n);
4667
+ break;
4668
+ case "k":
4669
+ out += pad(f.hour === 0 ? 24 : f.hour, n);
4670
+ break;
4671
+ case "K":
4672
+ out += pad(hour12, n);
4673
+ break;
4674
+ case "h":
4675
+ out += pad(hour12 === 0 ? 12 : hour12, n);
4676
+ break;
4677
+ case "a":
4678
+ out += f.hour < 12 ? "AM" : "PM";
4679
+ break;
4680
+ case "m":
4681
+ out += pad(f.minute, n);
4682
+ break;
4683
+ case "s":
4684
+ out += pad(f.second, n);
4685
+ break;
4686
+ case "S":
4687
+ out += pad(ms, n);
4688
+ break;
4689
+ case "Z": {
4690
+ const minutes = Math.abs(offset) / 6e4;
4691
+ out += (offset < 0 ? "-" : "+") + pad(Math.floor(minutes / 60), 2) + pad(minutes % 60, 2);
4692
+ break;
4693
+ }
4694
+ case "z":
4695
+ out += zoneName(time2, timezone, offset);
4696
+ break;
4697
+ default:
4698
+ throw new SyntaxError(`Unsupported date pattern letter "${ch}" in "${pattern}"`);
4808
4699
  }
4809
- const { unit, n } = unitOf(tf);
4810
- const [from, to] = this.periodRange(at.date, unit, n);
4811
- const last = this.calendar.lastTradingDay(from, to);
4812
- return last === null ? NaN : this.dayEnd(last);
4813
- }
4814
- /** PineScript `time_tradingday`: 00:00 UTC of the trading day. */
4815
- tradingDayTime(time2) {
4816
- const day = this.tradingDay(time2);
4817
- return day === null ? NaN : dateUtcMs(day);
4818
- }
4819
- /** True when the bar at `time` is inside the session. */
4820
- inside(time2) {
4821
- return this.calendar.locate(time2)?.inside ?? false;
4822
- }
4823
- /** Session start and end of the trading day of `time`, or null. */
4824
- dayBounds(time2) {
4825
- const at = this.calendar.locate(time2);
4826
- return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;
4827
4700
  }
4701
+ return out;
4702
+ }
4703
+
4704
+ // src/str/messageformat.ts
4705
+ var DATE_STYLES = {
4706
+ short: "M/d/yy",
4707
+ medium: "MMM d, yyyy",
4708
+ long: "MMMM d, yyyy",
4709
+ full: "EEEE, MMMM d, yyyy"
4828
4710
  };
4829
- function sessionFlags(times, bars2, regular) {
4830
- const n = times.length;
4831
- const flag2 = () => new Array(n).fill(false);
4832
- const out = {
4833
- isfirstbar: flag2(),
4834
- islastbar: flag2(),
4835
- ismarket: flag2(),
4836
- ispremarket: flag2(),
4837
- ispostmarket: flag2(),
4838
- isfirstbar_regular: flag2(),
4839
- islastbar_regular: flag2()
4840
- };
4841
- const chart2 = bars2.chart;
4842
- let previousPeriod = "";
4843
- let previousRegular = "";
4844
- for (let i = 0; i < n; i++) {
4845
- const t2 = times[i];
4846
- if (chart2.isdwm) {
4847
- for (const k of ["isfirstbar", "islastbar", "ismarket", "isfirstbar_regular", "islastbar_regular"]) out[k][i] = true;
4711
+ var TIME_STYLES = {
4712
+ short: "h:mm\u202Fa",
4713
+ medium: "h:mm:ss\u202Fa",
4714
+ long: "h:mm:ss\u202Fa z",
4715
+ full: "h:mm:ss\u202Fa z"
4716
+ };
4717
+ function number(value, pattern) {
4718
+ return typeof value === "number" ? formatNumber(value, pattern, "halfEvenExact") : String(value);
4719
+ }
4720
+ function choice(value, spec) {
4721
+ const options = spec.split("|").map((part) => {
4722
+ const m = /^\s*(-?[\d.]+|-?∞)\s*([#<≤])(.*)$/.exec(part);
4723
+ if (!m) throw new SyntaxError(`Invalid choice format "${spec}"`);
4724
+ const limit = m[1].includes("\u221E") ? m[1].startsWith("-") ? -Infinity : Infinity : Number(m[1]);
4725
+ return { limit: m[2] === "<" ? limit + Number.EPSILON * Math.max(1, Math.abs(limit)) : limit, text: m[3] };
4726
+ });
4727
+ let chosen = options[0];
4728
+ for (const o of options) if (value >= o.limit) chosen = o;
4729
+ return chosen.text;
4730
+ }
4731
+ function formatArgument(value, type, style, timezone) {
4732
+ if (value === void 0) return "";
4733
+ if (!type) return typeof value === "number" ? number(value, "#,##0.###") : String(value);
4734
+ switch (type.trim()) {
4735
+ case "number": {
4736
+ const s = style?.trim();
4737
+ if (!s) return number(value, "#,##0.###");
4738
+ if (s === "integer") return number(value, "#,##0");
4739
+ if (s === "percent") return number(value, "#,##0%");
4740
+ if (s === "currency") return number(value, "'$'#,##0.00");
4741
+ return number(value, style);
4742
+ }
4743
+ case "date":
4744
+ case "time": {
4745
+ const styles = type.trim() === "date" ? DATE_STYLES : TIME_STYLES;
4746
+ const s = style?.trim() ?? "medium";
4747
+ return formatDate(Number(value), styles[s] ?? style, typeof timezone === "function" ? timezone() : timezone);
4748
+ }
4749
+ case "choice":
4750
+ return choice(Number(value), style ?? "");
4751
+ default:
4752
+ throw new SyntaxError(`Unknown format type "${type}"`);
4753
+ }
4754
+ }
4755
+ function formatMessage(pattern, args, timezone) {
4756
+ let out = "";
4757
+ for (let i = 0; i < pattern.length; ) {
4758
+ const ch = pattern[i];
4759
+ if (ch === "'") {
4760
+ if (pattern[i + 1] === "'") {
4761
+ out += "'";
4762
+ i += 2;
4763
+ continue;
4764
+ }
4765
+ const end = pattern.indexOf("'", i + 1);
4766
+ out += pattern.slice(i + 1, end === -1 ? void 0 : end);
4767
+ i = end === -1 ? pattern.length : end + 1;
4848
4768
  continue;
4849
4769
  }
4850
- const at = bars2.calendar.locate(t2);
4851
- if (!at) {
4852
- previousPeriod = previousRegular = "";
4770
+ if (ch !== "{") {
4771
+ out += ch;
4772
+ i++;
4853
4773
  continue;
4854
4774
  }
4855
- const close2 = bars2.closeOf(t2, chart2.period);
4856
- const [, end] = bars2.calendar.periods(at.date)[at.index];
4857
- const period = `${at.date}/${at.index}`;
4858
- out.isfirstbar[i] = period !== previousPeriod;
4859
- out.islastbar[i] = close2 >= end;
4860
- const regularPeriods = regular.calendar.periods(at.date);
4861
- const r2 = regularPeriods.findIndex(([s, e2]) => s <= t2 && t2 < e2);
4862
- const market = r2 !== -1;
4863
- out.ismarket[i] = market;
4864
- if (!market && regularPeriods.length) {
4865
- out.ispremarket[i] = t2 < regularPeriods[0][0];
4866
- out.ispostmarket[i] = t2 >= regularPeriods[regularPeriods.length - 1][1];
4775
+ let depth = 1;
4776
+ let j = i + 1;
4777
+ let inQuote = false;
4778
+ for (; j < pattern.length && depth > 0; j++) {
4779
+ const c = pattern[j];
4780
+ if (c === "'") inQuote = !inQuote;
4781
+ else if (!inQuote && c === "{") depth++;
4782
+ else if (!inQuote && c === "}") depth--;
4867
4783
  }
4868
- const regularPeriod = market ? `${at.date}/${r2}` : "";
4869
- out.isfirstbar_regular[i] = market && regularPeriod !== previousRegular;
4870
- out.islastbar_regular[i] = market && close2 >= regularPeriods[r2][1];
4871
- previousPeriod = period;
4872
- previousRegular = regularPeriod;
4784
+ const inner = pattern.slice(i + 1, j - 1);
4785
+ const [index = "", type, ...rest] = inner.split(",");
4786
+ const style = rest.length ? rest.join(",") : void 0;
4787
+ const n = Number(index.trim());
4788
+ out += n < args.length ? formatArgument(args[n], type, style, timezone) : `{${inner}}`;
4789
+ i = j;
4873
4790
  }
4874
4791
  return out;
4875
4792
  }
4876
4793
 
4877
- // src/security/resample.ts
4878
- function resample(bars2, periodStart) {
4879
- const out = [];
4880
- const group2 = [];
4881
- bars2.forEach((b2, i) => {
4882
- const start = periodStart[i];
4883
- const last = out[out.length - 1];
4884
- if (!last || last.time !== start) {
4885
- out.push({ time: start, open: b2.open, high: b2.high, low: b2.low, close: b2.close, volume: b2.volume ?? NaN });
4886
- } else {
4887
- last.high = Math.max(last.high, b2.high);
4888
- last.low = Math.min(last.low, b2.low);
4889
- last.close = b2.close;
4890
- last.volume = (last.volume ?? 0) + (b2.volume ?? NaN);
4794
+ // src/str/index.ts
4795
+ function length(str2) {
4796
+ return str2.length;
4797
+ }
4798
+ function tostring(value, format2) {
4799
+ if (typeof value !== "number") return String(value);
4800
+ if (!Number.isFinite(value)) return String(value);
4801
+ if (format2 === void 0 || format2 === "") {
4802
+ if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
4803
+ return String(value).replace("e+", "E").replace(/\.0+E/, "E");
4891
4804
  }
4892
- group2.push(out.length - 1);
4893
- });
4894
- return { bars: out, group: group2 };
4805
+ return formatNumber(value, "#.##########", "halfUpShortest");
4806
+ }
4807
+ switch (format2) {
4808
+ case "percent":
4809
+ return formatNumber(value, "0.00", "halfUpShortest") + "%";
4810
+ case "volume": {
4811
+ const abs2 = Math.abs(value);
4812
+ for (const [size, unit] of [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]]) {
4813
+ if (abs2 >= size) return formatNumber(value / size, "#.###", "halfUpShortest") + unit;
4814
+ }
4815
+ return formatNumber(value, "#", "halfUpShortest");
4816
+ }
4817
+ case "mintick":
4818
+ throw new Error("str.tostring(x, format.mintick) needs the symbol tick size (syminfo.mintick), which is not known here");
4819
+ default:
4820
+ return formatNumber(value, format2, "halfUpShortest");
4821
+ }
4895
4822
  }
4896
- function periodsOf(starts, dayPeriods, reaches) {
4897
- const n = starts.length;
4898
- const group2 = [];
4899
- starts.forEach((s, i) => group2.push(i === 0 ? 0 : group2[i - 1] + (s !== starts[i - 1] ? 1 : 0)));
4900
- const latest = [];
4901
- starts.forEach((s, i) => {
4902
- let done = i === 0 ? -1 : latest[i - 1];
4903
- if (i > 0 && group2[i] !== group2[i - 1]) done = Math.max(done, group2[i] - 1);
4904
- const ends = i < n - 1 ? starts[i + 1] !== s || dayPeriods[i + 1] !== dayPeriods[i] : true;
4905
- if (ends && reaches[i]) done = Math.max(done, group2[i]);
4906
- latest.push(done);
4907
- });
4908
- return { group: group2, latest };
4823
+ function tonumber(str2) {
4824
+ const num = parseFloat(str2);
4825
+ return isNaN(num) ? null : num;
4909
4826
  }
4910
- function mapToChart(values, group2, latest, lookahead, gaps) {
4911
- return group2.map((g2, i) => {
4912
- if (lookahead) {
4913
- const first = i === 0 || group2[i - 1] !== g2;
4914
- return gaps && !first ? NaN : values[g2] ?? NaN;
4915
- }
4916
- const done = latest[i];
4917
- if (done < 0 || gaps && i > 0 && latest[i - 1] === done) return NaN;
4918
- return values[done] ?? NaN;
4919
- });
4827
+ function substring(str2, begin, end) {
4828
+ return str2.substring(begin, end);
4920
4829
  }
4921
- function heikinAshi(bars2) {
4922
- const out = [];
4923
- bars2.forEach((b2, i) => {
4924
- const close2 = (b2.open + b2.high + b2.low + b2.close) / 4;
4925
- const prev = out[i - 1];
4926
- const open2 = prev ? (prev.open + prev.close) / 2 : (b2.open + b2.close) / 2;
4927
- out.push({ time: b2.time, open: open2, high: Math.max(b2.high, open2, close2), low: Math.min(b2.low, open2, close2), close: close2, volume: b2.volume });
4928
- });
4929
- return out;
4830
+ function upper(str2) {
4831
+ return str2.toUpperCase();
4930
4832
  }
4931
-
4932
- // src/math/index.ts
4933
- var math_exports = {};
4934
- __export(math_exports, {
4935
- abs: () => abs,
4936
- acos: () => acos,
4937
- asin: () => asin,
4938
- atan: () => atan,
4939
- avg: () => avg,
4940
- ceil: () => ceil,
4941
- cos: () => cos,
4942
- e: () => e,
4943
- exp: () => exp,
4944
- floor: () => floor,
4945
- log: () => log,
4946
- log10: () => log10,
4947
- max: () => max3,
4948
- min: () => min3,
4949
- phi: () => phi,
4950
- pi: () => pi,
4951
- pow: () => pow,
4952
- random: () => random,
4953
- round: () => round,
4954
- round_to_mintick: () => round_to_mintick,
4955
- rphi: () => rphi,
4956
- sign: () => sign,
4957
- sin: () => sin,
4958
- sqrt: () => sqrt,
4959
- sum: () => sum,
4960
- tan: () => tan,
4961
- todegrees: () => todegrees,
4962
- toradians: () => toradians
4963
- });
4964
- function abs(value) {
4965
- if (value instanceof Series) {
4966
- const bars2 = value.bars;
4967
- const valueArray = value.toArray();
4968
- const length2 = bars2.length;
4969
- const result = [];
4970
- for (let i = 0; i < length2; i++) {
4971
- const v = valueArray[i] ?? NaN;
4972
- result.push(Math.abs(v));
4973
- }
4974
- return Series.fromArray(bars2, result);
4833
+ function lower(str2) {
4834
+ return str2.toLowerCase();
4835
+ }
4836
+ function contains(source, str2) {
4837
+ return source.includes(str2);
4838
+ }
4839
+ function pos(source, str2) {
4840
+ return source.indexOf(str2);
4841
+ }
4842
+ function replace(source, target, replacement, occurrence) {
4843
+ if (occurrence === 0) {
4844
+ return source.replace(target, replacement);
4845
+ } else {
4846
+ return source.replaceAll(target, replacement);
4975
4847
  }
4976
- return Math.abs(value);
4977
4848
  }
4978
- function ceil(value) {
4979
- if (value instanceof Series) {
4980
- const bars2 = value.bars;
4981
- const valueArray = value.toArray();
4982
- const length2 = bars2.length;
4983
- const result = [];
4984
- for (let i = 0; i < length2; i++) {
4985
- const v = valueArray[i] ?? NaN;
4986
- result.push(Math.ceil(v));
4849
+ function replace_all(source, target, replacement) {
4850
+ return source.replaceAll(target, replacement);
4851
+ }
4852
+ function split(str2, separator) {
4853
+ return str2.split(separator);
4854
+ }
4855
+ function concat(...strings) {
4856
+ return strings.join("");
4857
+ }
4858
+ function format(formatStr, ...args) {
4859
+ return formatMessage(formatStr, args, "Etc/UTC");
4860
+ }
4861
+ function startswith(source, str2) {
4862
+ return source.startsWith(str2);
4863
+ }
4864
+ function endswith(source, str2) {
4865
+ return source.endsWith(str2);
4866
+ }
4867
+ function charAt(str2, pos2) {
4868
+ return str2.charAt(pos2);
4869
+ }
4870
+ function trim(str2) {
4871
+ return str2.trim();
4872
+ }
4873
+ function trimLeft(str2) {
4874
+ return str2.trimStart();
4875
+ }
4876
+ function trimRight(str2) {
4877
+ return str2.trimEnd();
4878
+ }
4879
+ function match(source, regex) {
4880
+ return new RegExp(regex).test(source);
4881
+ }
4882
+ function repeat(source, count, separator = "") {
4883
+ if (source == null) return null;
4884
+ if (count <= 0) return "";
4885
+ return Array(count).fill(source).join(separator);
4886
+ }
4887
+ function format_time(time2, format2 = "yyyy-MM-dd'T'HH:mm:ssZ", timezone = "Etc/UTC") {
4888
+ return formatDate(time2, format2, timezone);
4889
+ }
4890
+
4891
+ // src/session/calendar.ts
4892
+ var toDateNum = (y, m, d) => y * 1e4 + m * 100 + d;
4893
+ var utcOf = (date) => Date.UTC(Math.floor(date / 1e4), Math.floor(date / 100) % 100 - 1, date % 100);
4894
+ var fromUtc = (ms) => {
4895
+ const d = new Date(ms);
4896
+ return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
4897
+ };
4898
+ var addDays = (date, n) => fromUtc(utcOf(date) + n * 864e5);
4899
+ var weekdayOf = (date) => new Date(utcOf(date)).getUTCDay() + 1;
4900
+ var dateUtcMs = utcOf;
4901
+ function parseTime(token, spec) {
4902
+ const m = /^(\d{2})(\d{2})(?:F(\d+))?$/.exec(token.trim());
4903
+ if (!m) throw new SyntaxError(`Invalid session "${spec}"`);
4904
+ return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === void 0 ? void 0 : Number(m[3]) };
4905
+ }
4906
+ function parsePeriods(text2, spec) {
4907
+ if (text2.trim() === "24x7") return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];
4908
+ return text2.split(",").map((p) => {
4909
+ const [a = "", b2 = ""] = p.split("-");
4910
+ const s = parseTime(a, spec);
4911
+ const e2 = parseTime(b2, spec);
4912
+ if (s.minutes === 0 && e2.minutes === 0 && s.offset === void 0 && e2.offset === void 0) {
4913
+ return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };
4987
4914
  }
4988
- return Series.fromArray(bars2, result);
4989
- }
4990
- return Math.ceil(value);
4915
+ return {
4916
+ start: s.minutes,
4917
+ end: e2.minutes,
4918
+ startOffset: s.offset ?? (e2.minutes <= s.minutes ? 1 : 0),
4919
+ endOffset: e2.offset ?? 0
4920
+ };
4921
+ });
4991
4922
  }
4992
- function floor(value) {
4993
- if (value instanceof Series) {
4994
- const bars2 = value.bars;
4995
- const valueArray = value.toArray();
4996
- const length2 = bars2.length;
4997
- const result = [];
4998
- for (let i = 0; i < length2; i++) {
4999
- const v = valueArray[i] ?? NaN;
5000
- result.push(Math.floor(v));
5001
- }
5002
- return Series.fromArray(bars2, result);
5003
- }
5004
- return Math.floor(value);
4923
+ function parseVersion(text2, from, spec, rule) {
4924
+ const [periodText = "", dayText] = text2.trim().split(":");
4925
+ const periods = parsePeriods(periodText, spec);
4926
+ const allDays = periodText.trim() === "24x7" || rule === "pine" && periods.length === 1;
4927
+ const digits = dayText ?? (allDays ? "1234567" : "23456");
4928
+ if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in "${spec}"`);
4929
+ const days = new Array(8).fill(false);
4930
+ for (const d of digits) days[Number(d)] = true;
4931
+ return { from, periods, days };
5005
4932
  }
5006
- function round(value, precision) {
5007
- if (value instanceof Series) {
5008
- const bars2 = value.bars;
5009
- const valueArray = value.toArray();
5010
- const length2 = bars2.length;
5011
- const result = [];
5012
- if (precision === void 0) {
5013
- for (let i = 0; i < length2; i++) {
5014
- const v = valueArray[i] ?? NaN;
5015
- result.push(Math.round(v));
5016
- }
5017
- } else {
5018
- const multiplier2 = Math.pow(10, precision);
5019
- for (let i = 0; i < length2; i++) {
5020
- const v = valueArray[i] ?? NaN;
5021
- result.push(Math.round(v * multiplier2) / multiplier2);
5022
- }
4933
+ var TradingCalendar = class {
4934
+ /**
4935
+ * @param timezone - Time zone of the session hours (the exchange time zone)
4936
+ * @param spec - Session information (hours, corrections, holidays)
4937
+ * @param rule - Days without ":days": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)
4938
+ */
4939
+ constructor(timezone, spec, rule = "symbol") {
4940
+ this.timezone = timezone;
4941
+ const [first = "", ...changes] = spec.session.split("#");
4942
+ this.versions = [parseVersion(first, 0, spec.session, rule)];
4943
+ for (const change3 of changes) {
4944
+ const m = /^(\d{8})\/(.+)$/.exec(change3.trim());
4945
+ if (!m) throw new SyntaxError(`Invalid session change in "${spec.session}"`);
4946
+ this.versions.push(parseVersion(m[2], Number(m[1]), spec.session, rule));
5023
4947
  }
5024
- return Series.fromArray(bars2, result);
4948
+ for (const part of (spec.corrections ?? "").split(";").filter((p) => p.trim())) {
4949
+ const i = part.lastIndexOf(":");
4950
+ const text2 = part.slice(0, i).trim();
4951
+ const periods = text2 === "dayoff" ? null : parsePeriods(text2, part);
4952
+ for (const date of part.slice(i + 1).split(",")) this.corrections.set(Number(date), periods);
4953
+ }
4954
+ const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? "").split(",");
4955
+ this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));
5025
4956
  }
5026
- if (precision === void 0) {
5027
- return Math.round(value);
4957
+ timezone;
4958
+ versions;
4959
+ corrections = /* @__PURE__ */ new Map();
4960
+ holidays;
4961
+ cache = /* @__PURE__ */ new Map();
4962
+ /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */
4963
+ periods(date) {
4964
+ let out = this.cache.get(date);
4965
+ if (out) return out;
4966
+ let periods = [];
4967
+ const correction = this.corrections.get(date);
4968
+ if (correction !== void 0) {
4969
+ periods = correction ?? [];
4970
+ } else if (!this.holidays.has(date)) {
4971
+ const version = [...this.versions].reverse().find((v) => v.from <= date);
4972
+ if (version.days[weekdayOf(date)]) periods = version.periods;
4973
+ }
4974
+ const y = Math.floor(date / 1e4);
4975
+ const m = Math.floor(date / 100) % 100;
4976
+ const d = date % 100;
4977
+ out = periods.map((p) => [
4978
+ zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),
4979
+ zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)
4980
+ ]);
4981
+ this.cache.set(date, out);
4982
+ return out;
5028
4983
  }
5029
- const multiplier = Math.pow(10, precision);
5030
- return Math.round(value * multiplier) / multiplier;
5031
- }
5032
- function max3(...values) {
5033
- const hasSeries = values.some((v) => v instanceof Series);
5034
- if (!hasSeries) {
5035
- return Math.max(...values);
4984
+ isTradingDay(date) {
4985
+ return this.periods(date).length > 0;
5036
4986
  }
5037
- const firstSeries = values.find((v) => v instanceof Series);
5038
- const bars2 = firstSeries.bars;
5039
- const length2 = bars2.length;
5040
- const valueArrays = values.map(
5041
- (v) => v instanceof Series ? v.toArray() : null
5042
- );
5043
- const result = [];
5044
- for (let i = 0; i < length2; i++) {
5045
- const nums = values.map(
5046
- (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
5047
- );
5048
- result.push(Math.max(...nums));
4987
+ /**
4988
+ * Trading day and period of a time: the period that contains it, else the last period that started
4989
+ * before it. Null when no trading day is found near the time.
4990
+ */
4991
+ locate(time2) {
4992
+ const f = zonedFields(time2, this.timezone);
4993
+ const local = toDateNum(f.year, f.month, f.day);
4994
+ for (let k = 0; k <= 5; k++) {
4995
+ const date = addDays(local, k);
4996
+ const index = this.periods(date).findIndex(([s, e2]) => s <= time2 && time2 < e2);
4997
+ if (index !== -1) return { date, index, inside: true };
4998
+ }
4999
+ for (let k = 5; k >= -7; k--) {
5000
+ const date = addDays(local, k);
5001
+ const periods = this.periods(date);
5002
+ let index = -1;
5003
+ periods.forEach(([s], i) => {
5004
+ if (s <= time2) index = i;
5005
+ });
5006
+ if (index !== -1) return { date, index, inside: false };
5007
+ }
5008
+ return null;
5049
5009
  }
5050
- return Series.fromArray(bars2, result);
5051
- }
5052
- function min3(...values) {
5053
- const hasSeries = values.some((v) => v instanceof Series);
5054
- if (!hasSeries) {
5055
- return Math.min(...values);
5010
+ /** First trading day in [from, to] (dates), or null. */
5011
+ firstTradingDay(from, to) {
5012
+ for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;
5013
+ return null;
5056
5014
  }
5057
- const firstSeries = values.find((v) => v instanceof Series);
5058
- const bars2 = firstSeries.bars;
5059
- const length2 = bars2.length;
5060
- const valueArrays = values.map(
5061
- (v) => v instanceof Series ? v.toArray() : null
5062
- );
5063
- const result = [];
5064
- for (let i = 0; i < length2; i++) {
5065
- const nums = values.map(
5066
- (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
5067
- );
5068
- result.push(Math.min(...nums));
5015
+ /** Last trading day in [from, to] (dates), or null. */
5016
+ lastTradingDay(from, to) {
5017
+ for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;
5018
+ return null;
5069
5019
  }
5070
- return Series.fromArray(bars2, result);
5020
+ };
5021
+ var weekStart = (date) => addDays(date, -((weekdayOf(date) + 5) % 7));
5022
+ var monthStart = (date) => Math.floor(date / 100) * 100 + 1;
5023
+ var monthEnd = (date) => addDays(monthStart(addDays(monthStart(date), 32)), -1);
5024
+
5025
+ // src/session/bars.ts
5026
+ function unitOf(tf) {
5027
+ return { unit: tf.isdaily ? "D" : tf.isweekly ? "W" : "M", n: tf.multiplier };
5071
5028
  }
5072
- function avg(...values) {
5073
- const hasSeries = values.some((v) => v instanceof Series);
5074
- if (!hasSeries) {
5075
- return values.reduce((a, b2) => a + b2, 0) / values.length;
5076
- }
5077
- const firstSeries = values.find((v) => v instanceof Series);
5078
- const bars2 = firstSeries.bars;
5079
- const length2 = bars2.length;
5080
- const valueArrays = values.map(
5081
- (v) => v instanceof Series ? v.toArray() : null
5082
- );
5083
- const result = [];
5084
- for (let i = 0; i < length2; i++) {
5085
- const nums = values.map(
5086
- (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
5087
- );
5088
- const sum2 = nums.reduce((a, b2) => a + b2, 0);
5089
- result.push(sum2 / nums.length);
5029
+ var yearOf = (date) => Math.floor(date / 1e4);
5030
+ var firstMonday = (year3) => {
5031
+ const jan1 = year3 * 1e4 + 101;
5032
+ const monday = weekStart(jan1);
5033
+ return monday === jan1 ? jan1 : addDays(monday, 7);
5034
+ };
5035
+ var SessionBars = class {
5036
+ constructor(calendar2, chartTimeframe2) {
5037
+ this.calendar = calendar2;
5038
+ this.chart = info(chartTimeframe2);
5090
5039
  }
5091
- return Series.fromArray(bars2, result);
5092
- }
5093
- function sum(source, length2) {
5094
- const sourceArray = source instanceof Series ? source.toArray() : source;
5095
- const result = [];
5096
- const len = Math.floor(length2);
5097
- const window = [];
5098
- for (let i = 0; i < sourceArray.length; i++) {
5099
- const v = sourceArray[i];
5100
- if (v !== void 0 && v !== null && !Number.isNaN(v)) {
5101
- window.push(v);
5102
- if (window.length > len) window.shift();
5103
- }
5104
- if (window.length < len) {
5105
- result.push(NaN);
5106
- } else {
5107
- let total = 0;
5108
- for (const x of window) total += x;
5109
- result.push(total);
5040
+ calendar;
5041
+ chart;
5042
+ yearDays = /* @__PURE__ */ new Map();
5043
+ /** Trading days of a calendar year, in order. */
5044
+ tradingDaysOf(year3) {
5045
+ let days = this.yearDays.get(year3);
5046
+ if (!days) {
5047
+ days = [];
5048
+ for (let date = year3 * 1e4 + 101; yearOf(date) === year3; date = addDays(date, 1)) {
5049
+ if (this.calendar.isTradingDay(date)) days.push(date);
5050
+ }
5051
+ this.yearDays.set(year3, days);
5110
5052
  }
5053
+ return days;
5111
5054
  }
5112
- if (source instanceof Series) {
5113
- return Series.fromArray(source.bars, result);
5114
- }
5115
- return result;
5116
- }
5117
- function sqrt(value) {
5118
- if (value instanceof Series) {
5119
- const bars2 = value.bars;
5120
- const valueArray = value.toArray();
5121
- const length2 = bars2.length;
5122
- const result = [];
5123
- for (let i = 0; i < length2; i++) {
5124
- const v = valueArray[i] ?? NaN;
5125
- result.push(Math.sqrt(v));
5055
+ /**
5056
+ * Dates [from, to] of the `n`-`unit` period that contains `date` (for "D": its first and last trading days).
5057
+ * Periods restart each calendar year.
5058
+ */
5059
+ periodRange(date, unit, n) {
5060
+ if (unit === "D") {
5061
+ if (n === 1) return [date, date];
5062
+ const days = this.tradingDaysOf(yearOf(date));
5063
+ let k = days.findIndex((d) => d >= date);
5064
+ if (k === -1) return this.periodRange(yearOf(date) * 1e4 + 1e4 + 101, unit, n);
5065
+ k -= k % n;
5066
+ return [days[k], days[Math.min(k + n, days.length) - 1]];
5126
5067
  }
5127
- return Series.fromArray(bars2, result);
5068
+ if (unit === "W") {
5069
+ const monday = weekStart(date);
5070
+ const year4 = yearOf(monday);
5071
+ const first = firstMonday(year4);
5072
+ const next = firstMonday(year4 + 1);
5073
+ const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 864e5));
5074
+ const from = addDays(first, (k - k % n) * 7);
5075
+ const to = addDays(from, n * 7 - 1);
5076
+ return [from, to < next ? to : addDays(next, -1)];
5077
+ }
5078
+ const year3 = yearOf(date);
5079
+ const month3 = Math.floor(date / 100) % 100;
5080
+ const firstMonth = month3 - (month3 - 1) % n;
5081
+ const lastMonth = Math.min(firstMonth + n - 1, 12);
5082
+ return [monthStart(year3 * 1e4 + firstMonth * 100 + 1), monthEnd(year3 * 1e4 + lastMonth * 100 + 1)];
5128
5083
  }
5129
- return Math.sqrt(value);
5130
- }
5131
- function pow(base, exponent) {
5132
- const baseIsSeries = base instanceof Series;
5133
- const expIsSeries = exponent instanceof Series;
5134
- if (!baseIsSeries && !expIsSeries) {
5135
- return Math.pow(base, exponent);
5084
+ dayStart(date) {
5085
+ return this.calendar.periods(date)[0][0];
5136
5086
  }
5137
- const bars2 = baseIsSeries ? base.bars : exponent.bars;
5138
- const baseArray = baseIsSeries ? base.toArray() : null;
5139
- const expArray = expIsSeries ? exponent.toArray() : null;
5140
- const length2 = bars2.length;
5141
- const result = [];
5142
- for (let i = 0; i < length2; i++) {
5143
- const b2 = baseArray ? baseArray[i] ?? NaN : base;
5144
- const e2 = expArray ? expArray[i] ?? NaN : exponent;
5145
- result.push(Math.pow(b2, e2));
5087
+ dayEnd(date) {
5088
+ const p = this.calendar.periods(date);
5089
+ return p[p.length - 1][1];
5146
5090
  }
5147
- return Series.fromArray(bars2, result);
5148
- }
5149
- function exp(value) {
5150
- if (value instanceof Series) {
5151
- const bars2 = value.bars;
5152
- const valueArray = value.toArray();
5153
- const length2 = bars2.length;
5154
- const result = [];
5155
- for (let i = 0; i < length2; i++) {
5156
- const v = valueArray[i] ?? NaN;
5157
- result.push(Math.exp(v));
5091
+ /** PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. */
5092
+ timeOf(time2, timeframe2) {
5093
+ const tf = info(timeframe2);
5094
+ const at = this.calendar.locate(time2);
5095
+ if (!at) return NaN;
5096
+ if (tf.isintraday) {
5097
+ if (this.chart.isdwm) return time2;
5098
+ const [start] = this.calendar.periods(at.date)[at.index];
5099
+ const ms = in_seconds(tf.period) * 1e3;
5100
+ return start + Math.floor((time2 - start) / ms) * ms;
5158
5101
  }
5159
- return Series.fromArray(bars2, result);
5102
+ const { unit, n } = unitOf(tf);
5103
+ const [from, to] = this.periodRange(at.date, unit, n);
5104
+ const first = this.calendar.firstTradingDay(from, to);
5105
+ return first === null ? NaN : this.dayStart(first);
5160
5106
  }
5161
- return Math.exp(value);
5162
- }
5163
- function log(value) {
5164
- if (value instanceof Series) {
5165
- const bars2 = value.bars;
5166
- const valueArray = value.toArray();
5167
- const length2 = bars2.length;
5168
- const result = [];
5169
- for (let i = 0; i < length2; i++) {
5170
- const v = valueArray[i] ?? NaN;
5171
- result.push(Math.log(v));
5107
+ /** PineScript `time_close(tf)` for a bar that opens at `time`. */
5108
+ closeOf(time2, timeframe2) {
5109
+ const tf = info(timeframe2);
5110
+ const at = this.calendar.locate(time2);
5111
+ if (!at) return NaN;
5112
+ if (tf.isintraday) {
5113
+ const [, end] = this.calendar.periods(at.date)[at.index];
5114
+ if (this.chart.isdwm) return end;
5115
+ return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
5172
5116
  }
5173
- return Series.fromArray(bars2, result);
5174
- }
5175
- return Math.log(value);
5176
- }
5177
- function log10(value) {
5178
- if (value instanceof Series) {
5179
- const bars2 = value.bars;
5180
- const valueArray = value.toArray();
5181
- const length2 = bars2.length;
5182
- const result = [];
5183
- for (let i = 0; i < length2; i++) {
5184
- const v = valueArray[i] ?? NaN;
5185
- result.push(Math.log10(v));
5117
+ const { unit, n } = unitOf(tf);
5118
+ const [from, to] = this.periodRange(at.date, unit, n);
5119
+ if (this.chart.isintraday && (unit !== "D" || n > 1)) {
5120
+ const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);
5121
+ const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));
5122
+ return first === null ? NaN : this.dayStart(first);
5186
5123
  }
5187
- return Series.fromArray(bars2, result);
5124
+ const last = this.calendar.lastTradingDay(from, to);
5125
+ return last === null ? NaN : this.dayEnd(last);
5188
5126
  }
5189
- return Math.log10(value);
5190
- }
5191
- function sin(value) {
5192
- if (value instanceof Series) {
5193
- const bars2 = value.bars;
5194
- const valueArray = value.toArray();
5195
- const length2 = bars2.length;
5196
- const result = [];
5197
- for (let i = 0; i < length2; i++) {
5198
- const v = valueArray[i] ?? NaN;
5199
- result.push(Math.sin(v));
5127
+ /** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */
5128
+ tradingDay(time2) {
5129
+ const at = this.calendar.locate(time2);
5130
+ if (!at) return null;
5131
+ if (this.chart.isdwm) {
5132
+ const { unit, n } = unitOf(this.chart);
5133
+ const [from, to] = this.periodRange(at.date, unit, n);
5134
+ return this.calendar.lastTradingDay(from, to);
5200
5135
  }
5201
- return Series.fromArray(bars2, result);
5136
+ return at.date;
5202
5137
  }
5203
- return Math.sin(value);
5204
- }
5205
- function cos(value) {
5206
- if (value instanceof Series) {
5207
- const bars2 = value.bars;
5208
- const valueArray = value.toArray();
5209
- const length2 = bars2.length;
5210
- const result = [];
5211
- for (let i = 0; i < length2; i++) {
5212
- const v = valueArray[i] ?? NaN;
5213
- result.push(Math.cos(v));
5138
+ /**
5139
+ * End of the `timeframe` period that contains `time`: the intraday close, the session end of the day,
5140
+ * or the session end of the last trading day of the period. Used to know whether the last loaded
5141
+ * bar completes a higher-timeframe period (request.security).
5142
+ */
5143
+ periodEnd(time2, timeframe2) {
5144
+ const tf = info(timeframe2);
5145
+ const at = this.calendar.locate(time2);
5146
+ if (!at) return NaN;
5147
+ if (tf.isintraday) {
5148
+ const [, end] = this.calendar.periods(at.date)[at.index];
5149
+ return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
5214
5150
  }
5215
- return Series.fromArray(bars2, result);
5151
+ const { unit, n } = unitOf(tf);
5152
+ const [from, to] = this.periodRange(at.date, unit, n);
5153
+ const last = this.calendar.lastTradingDay(from, to);
5154
+ return last === null ? NaN : this.dayEnd(last);
5216
5155
  }
5217
- return Math.cos(value);
5218
- }
5219
- function tan(value) {
5220
- if (value instanceof Series) {
5221
- const bars2 = value.bars;
5222
- const valueArray = value.toArray();
5223
- const length2 = bars2.length;
5224
- const result = [];
5225
- for (let i = 0; i < length2; i++) {
5226
- const v = valueArray[i] ?? NaN;
5227
- result.push(Math.tan(v));
5228
- }
5229
- return Series.fromArray(bars2, result);
5156
+ /** PineScript `time_tradingday`: 00:00 UTC of the trading day. */
5157
+ tradingDayTime(time2) {
5158
+ const day = this.tradingDay(time2);
5159
+ return day === null ? NaN : dateUtcMs(day);
5230
5160
  }
5231
- return Math.tan(value);
5232
- }
5233
- function asin(value) {
5234
- if (value instanceof Series) {
5235
- const bars2 = value.bars;
5236
- const valueArray = value.toArray();
5237
- const length2 = bars2.length;
5238
- const result = [];
5239
- for (let i = 0; i < length2; i++) {
5240
- const v = valueArray[i] ?? NaN;
5241
- result.push(Math.asin(v));
5242
- }
5243
- return Series.fromArray(bars2, result);
5161
+ /** True when the bar at `time` is inside the session. */
5162
+ inside(time2) {
5163
+ return this.calendar.locate(time2)?.inside ?? false;
5244
5164
  }
5245
- return Math.asin(value);
5246
- }
5247
- function acos(value) {
5248
- if (value instanceof Series) {
5249
- const bars2 = value.bars;
5250
- const valueArray = value.toArray();
5251
- const length2 = bars2.length;
5252
- const result = [];
5253
- for (let i = 0; i < length2; i++) {
5254
- const v = valueArray[i] ?? NaN;
5255
- result.push(Math.acos(v));
5256
- }
5257
- return Series.fromArray(bars2, result);
5165
+ /** Session start and end of the trading day of `time`, or null. */
5166
+ dayBounds(time2) {
5167
+ const at = this.calendar.locate(time2);
5168
+ return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;
5258
5169
  }
5259
- return Math.acos(value);
5260
- }
5261
- function atan(value) {
5262
- if (value instanceof Series) {
5263
- const bars2 = value.bars;
5264
- const valueArray = value.toArray();
5265
- const length2 = bars2.length;
5266
- const result = [];
5267
- for (let i = 0; i < length2; i++) {
5268
- const v = valueArray[i] ?? NaN;
5269
- result.push(Math.atan(v));
5170
+ };
5171
+ function sessionFlags(times, bars2, regular) {
5172
+ const n = times.length;
5173
+ const flag2 = () => new Array(n).fill(false);
5174
+ const out = {
5175
+ isfirstbar: flag2(),
5176
+ islastbar: flag2(),
5177
+ ismarket: flag2(),
5178
+ ispremarket: flag2(),
5179
+ ispostmarket: flag2(),
5180
+ isfirstbar_regular: flag2(),
5181
+ islastbar_regular: flag2()
5182
+ };
5183
+ const chart2 = bars2.chart;
5184
+ let previousPeriod = "";
5185
+ let previousRegular = "";
5186
+ for (let i = 0; i < n; i++) {
5187
+ const t2 = times[i];
5188
+ if (chart2.isdwm) {
5189
+ for (const k of ["isfirstbar", "islastbar", "ismarket", "isfirstbar_regular", "islastbar_regular"]) out[k][i] = true;
5190
+ continue;
5270
5191
  }
5271
- return Series.fromArray(bars2, result);
5272
- }
5273
- return Math.atan(value);
5274
- }
5275
- function toradians(degrees) {
5276
- if (degrees instanceof Series) {
5277
- const bars2 = degrees.bars;
5278
- const valueArray = degrees.toArray();
5279
- const length2 = bars2.length;
5280
- const result = [];
5281
- for (let i = 0; i < length2; i++) {
5282
- const v = valueArray[i] ?? NaN;
5283
- result.push(v * (Math.PI / 180));
5192
+ const at = bars2.calendar.locate(t2);
5193
+ if (!at) {
5194
+ previousPeriod = previousRegular = "";
5195
+ continue;
5284
5196
  }
5285
- return Series.fromArray(bars2, result);
5197
+ const close2 = bars2.closeOf(t2, chart2.period);
5198
+ const [, end] = bars2.calendar.periods(at.date)[at.index];
5199
+ const period = `${at.date}/${at.index}`;
5200
+ out.isfirstbar[i] = period !== previousPeriod;
5201
+ out.islastbar[i] = close2 >= end;
5202
+ const regularPeriods = regular.calendar.periods(at.date);
5203
+ const r2 = regularPeriods.findIndex(([s, e2]) => s <= t2 && t2 < e2);
5204
+ const market = r2 !== -1;
5205
+ out.ismarket[i] = market;
5206
+ if (!market && regularPeriods.length) {
5207
+ out.ispremarket[i] = t2 < regularPeriods[0][0];
5208
+ out.ispostmarket[i] = t2 >= regularPeriods[regularPeriods.length - 1][1];
5209
+ }
5210
+ const regularPeriod = market ? `${at.date}/${r2}` : "";
5211
+ out.isfirstbar_regular[i] = market && regularPeriod !== previousRegular;
5212
+ out.islastbar_regular[i] = market && close2 >= regularPeriods[r2][1];
5213
+ previousPeriod = period;
5214
+ previousRegular = regularPeriod;
5286
5215
  }
5287
- return degrees * (Math.PI / 180);
5216
+ return out;
5288
5217
  }
5289
- function todegrees(radians) {
5290
- if (radians instanceof Series) {
5291
- const bars2 = radians.bars;
5292
- const valueArray = radians.toArray();
5293
- const length2 = bars2.length;
5294
- const result = [];
5295
- for (let i = 0; i < length2; i++) {
5296
- const v = valueArray[i] ?? NaN;
5297
- result.push(v * (180 / Math.PI));
5218
+
5219
+ // src/security/resample.ts
5220
+ function resample(bars2, periodStart) {
5221
+ const out = [];
5222
+ const group2 = [];
5223
+ bars2.forEach((b2, i) => {
5224
+ const start = periodStart[i];
5225
+ const last = out[out.length - 1];
5226
+ if (!last || last.time !== start) {
5227
+ out.push({ time: start, open: b2.open, high: b2.high, low: b2.low, close: b2.close, volume: b2.volume ?? NaN });
5228
+ } else {
5229
+ last.high = Math.max(last.high, b2.high);
5230
+ last.low = Math.min(last.low, b2.low);
5231
+ last.close = b2.close;
5232
+ last.volume = (last.volume ?? 0) + (b2.volume ?? NaN);
5298
5233
  }
5299
- return Series.fromArray(bars2, result);
5300
- }
5301
- return radians * (180 / Math.PI);
5234
+ group2.push(out.length - 1);
5235
+ });
5236
+ return { bars: out, group: group2 };
5302
5237
  }
5303
- function random(min4, max4, _seed) {
5304
- const rand = Math.random();
5305
- if (min4 !== void 0 && max4 !== void 0) {
5306
- return min4 + rand * (max4 - min4);
5307
- }
5308
- return rand;
5238
+ function periodsOf(starts, dayPeriods, reaches) {
5239
+ const n = starts.length;
5240
+ const group2 = [];
5241
+ starts.forEach((s, i) => group2.push(i === 0 ? 0 : group2[i - 1] + (s !== starts[i - 1] ? 1 : 0)));
5242
+ const latest = [];
5243
+ starts.forEach((s, i) => {
5244
+ let done = i === 0 ? -1 : latest[i - 1];
5245
+ if (i > 0 && group2[i] !== group2[i - 1]) done = Math.max(done, group2[i] - 1);
5246
+ const ends = i < n - 1 ? starts[i + 1] !== s || dayPeriods[i + 1] !== dayPeriods[i] : true;
5247
+ if (ends && reaches[i]) done = Math.max(done, group2[i]);
5248
+ latest.push(done);
5249
+ });
5250
+ return { group: group2, latest };
5309
5251
  }
5310
- function sign(value) {
5311
- if (value instanceof Series) {
5312
- const bars2 = value.bars;
5313
- const valueArray = value.toArray();
5314
- const length2 = bars2.length;
5315
- const result = [];
5316
- for (let i = 0; i < length2; i++) {
5317
- const v = valueArray[i] ?? NaN;
5318
- result.push(v > 0 ? 1 : v < 0 ? -1 : 0);
5252
+ function mapToChart(values, group2, latest, lookahead, gaps) {
5253
+ return group2.map((g2, i) => {
5254
+ if (lookahead) {
5255
+ const first = i === 0 || group2[i - 1] !== g2;
5256
+ return gaps && !first ? NaN : values[g2] ?? NaN;
5319
5257
  }
5320
- return Series.fromArray(bars2, result);
5321
- }
5322
- return value > 0 ? 1 : value < 0 ? -1 : 0;
5258
+ const done = latest[i];
5259
+ if (done < 0 || gaps && i > 0 && latest[i - 1] === done) return NaN;
5260
+ return values[done] ?? NaN;
5261
+ });
5323
5262
  }
5324
- function round_to_mintick(number2, mintick) {
5325
- if (number2 instanceof Series) {
5326
- const bars2 = number2.bars;
5327
- const valueArray = number2.toArray();
5328
- const length2 = bars2.length;
5329
- if (mintick === void 0) {
5330
- throw new Error(
5331
- "math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
5332
- );
5333
- }
5334
- const result = [];
5335
- if (mintick === 0) {
5336
- for (let i = 0; i < length2; i++) {
5337
- result.push(valueArray[i] ?? NaN);
5338
- }
5339
- } else {
5340
- for (let i = 0; i < length2; i++) {
5341
- const v = valueArray[i];
5342
- if (v === void 0 || isNaN(v)) {
5343
- result.push(NaN);
5344
- } else {
5345
- result.push(Math.round(v / mintick) * mintick);
5346
- }
5347
- }
5348
- }
5349
- return Series.fromArray(bars2, result);
5350
- }
5351
- if (isNaN(number2)) {
5352
- return NaN;
5353
- }
5354
- if (mintick === void 0) {
5355
- throw new Error(
5356
- "math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
5357
- );
5358
- }
5359
- if (mintick === 0) {
5360
- return number2;
5361
- }
5362
- return Math.round(number2 / mintick) * mintick;
5263
+ function heikinAshi(bars2) {
5264
+ const out = [];
5265
+ bars2.forEach((b2, i) => {
5266
+ const close2 = (b2.open + b2.high + b2.low + b2.close) / 4;
5267
+ const prev = out[i - 1];
5268
+ const open2 = prev ? (prev.open + prev.close) / 2 : (b2.open + b2.close) / 2;
5269
+ out.push({ time: b2.time, open: open2, high: Math.max(b2.high, open2, close2), low: Math.min(b2.low, open2, close2), close: close2, volume: b2.volume });
5270
+ });
5271
+ return out;
5363
5272
  }
5364
- var pi = Math.PI;
5365
- var e = Math.E;
5366
- var phi = 1.618033988749895;
5367
- var rphi = 0.618033988749895;
5273
+
5274
+ // src/strategy/index.ts
5275
+ var STRATEGY_CONSTANTS = {
5276
+ long: "long",
5277
+ short: "short",
5278
+ fixed: "fixed",
5279
+ cash: "cash",
5280
+ percent_of_equity: "percent_of_equity",
5281
+ commission: { percent: "percent", cash_per_contract: "cash_per_contract", cash_per_order: "cash_per_order" },
5282
+ direction: { all: "all", long: "long", short: "short" },
5283
+ oca: { none: "none", cancel: "cancel", reduce: "reduce" }
5284
+ };
5285
+ var STRATEGY_DEFAULTS = {
5286
+ pyramiding: 0,
5287
+ calc_on_order_fills: false,
5288
+ calc_on_every_tick: false,
5289
+ backtest_fill_limits_assumption: 0,
5290
+ // v6 values; PineScript v5: fixed / 1 / 1000000 and margins 0
5291
+ default_qty_type: "percent_of_equity",
5292
+ default_qty_value: 100,
5293
+ initial_capital: 1e5,
5294
+ slippage: 0,
5295
+ commission_type: "percent",
5296
+ commission_value: 0,
5297
+ process_orders_on_close: false,
5298
+ close_entries_rule: "FIFO",
5299
+ margin_long: 100,
5300
+ margin_short: 100,
5301
+ calc_bars_count: 0,
5302
+ risk_free_rate: 2,
5303
+ use_bar_magnifier: false,
5304
+ fill_orders_on_standard_ohlc: false
5305
+ };
5306
+ var STRATEGY_NUMBER_VARIABLES = [
5307
+ "position_size",
5308
+ "position_avg_price",
5309
+ "equity",
5310
+ "initial_capital",
5311
+ "netprofit",
5312
+ "netprofit_percent",
5313
+ "openprofit",
5314
+ "openprofit_percent",
5315
+ "grossprofit",
5316
+ "grossprofit_percent",
5317
+ "grossloss",
5318
+ "grossloss_percent",
5319
+ "opentrades",
5320
+ "closedtrades",
5321
+ "wintrades",
5322
+ "losstrades",
5323
+ "eventrades",
5324
+ "avg_trade",
5325
+ "avg_trade_percent",
5326
+ "avg_winning_trade",
5327
+ "avg_winning_trade_percent",
5328
+ "avg_losing_trade",
5329
+ "avg_losing_trade_percent",
5330
+ "max_drawdown",
5331
+ "max_drawdown_percent",
5332
+ "max_runup",
5333
+ "max_runup_percent",
5334
+ "max_contracts_held_all",
5335
+ "max_contracts_held_long",
5336
+ "max_contracts_held_short",
5337
+ "margin_liquidation_price"
5338
+ ];
5339
+ var STRATEGY_TEXT_VARIABLES = ["position_entry_name", "account_currency"];
5368
5340
 
5369
5341
  // src/script/index.ts
5370
5342
  var ctxBars = new BarData([]);
@@ -5392,6 +5364,8 @@ function freshCollector(inputValues) {
5392
5364
  fillConfig: [],
5393
5365
  shapeConfig: [],
5394
5366
  markers: [],
5367
+ arrowConfig: [],
5368
+ arrows: [],
5395
5369
  barColorConfig: [],
5396
5370
  bgcolors: [],
5397
5371
  barcolors: [],
@@ -5406,9 +5380,11 @@ function slugId(title, fallback, taken) {
5406
5380
  taken.add(id);
5407
5381
  return id;
5408
5382
  }
5409
- function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
5383
+ var scriptOptions = {};
5384
+ function executeScript(body, barsInput, inputs = {}, chart2 = {}, options = {}) {
5410
5385
  ctxBars.setAll(barsInput);
5411
5386
  chartCtx = { ...chart2 };
5387
+ scriptOptions = options;
5412
5388
  ctx = freshCollector(inputs);
5413
5389
  resetDrawings();
5414
5390
  try {
@@ -5427,14 +5403,17 @@ function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
5427
5403
  hlineConfig: c.hlineConfig,
5428
5404
  fillConfig: c.fillConfig,
5429
5405
  shapeConfig: c.shapeConfig,
5406
+ arrowConfig: c.arrowConfig,
5430
5407
  barColorConfig: c.barColorConfig,
5431
5408
  defaultInputs: c.defaultInputs,
5432
5409
  alertConfig: c.alertConfig,
5410
+ strategyConfig: c.strategy?.properties,
5433
5411
  result: {
5434
5412
  metadata: { title: c.title, shorttitle: c.shortTitle, overlay: c.overlay, precision: c.precision },
5435
5413
  plots: c.plots,
5436
5414
  fills: c.fills.length ? c.fills : void 0,
5437
5415
  markers: c.markers.length ? c.markers : void 0,
5416
+ arrows: c.arrows.length ? c.arrows : void 0,
5438
5417
  bgcolors: c.bgcolors.length ? c.bgcolors : void 0,
5439
5418
  barcolors: c.barcolors.length ? c.barcolors : void 0,
5440
5419
  alerts: c.alerts.length ? c.alerts : void 0
@@ -5446,6 +5425,7 @@ function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
5446
5425
  }
5447
5426
  function indicator(title, options = {}) {
5448
5427
  const c = collector();
5428
+ declare(c, "indicator");
5449
5429
  c.title = title;
5450
5430
  c.shortTitle = options.shorttitle;
5451
5431
  c.overlay = options.overlay ?? true;
@@ -5458,6 +5438,21 @@ function indicator(title, options = {}) {
5458
5438
  polyline: options.max_polylines_count
5459
5439
  });
5460
5440
  }
5441
+ function declare(c, kind) {
5442
+ if (c.declaration) {
5443
+ throw new Error(`${kind}(): the script already declared ${c.declaration}(); a script has one declaration.`);
5444
+ }
5445
+ c.declaration = kind;
5446
+ }
5447
+ function common(opts) {
5448
+ const out = {};
5449
+ if (opts.group !== void 0) out.group = opts.group;
5450
+ if (opts.inline !== void 0) out.inline = opts.inline;
5451
+ if (opts.tooltip !== void 0) out.tooltip = opts.tooltip;
5452
+ if (opts.confirm !== void 0) out.confirm = opts.confirm;
5453
+ if (opts.display !== void 0) out.display = opts.display;
5454
+ return out;
5455
+ }
5461
5456
  function registerInput(config, fallbackId) {
5462
5457
  const c = collector();
5463
5458
  const id = slugId(config.title, fallbackId, c.inputIds);
@@ -5468,7 +5463,7 @@ function registerInput(config, fallbackId) {
5468
5463
  }
5469
5464
  function registerNumeric(type, defval, title, opts = {}) {
5470
5465
  const v = registerInput(
5471
- { type, defval, title, min: opts.minval, max: opts.maxval, step: opts.step },
5466
+ { type, defval, title, min: opts.minval, max: opts.maxval, step: opts.step, ...common(opts) },
5472
5467
  `input_${type}`
5473
5468
  );
5474
5469
  return typeof v === "number" ? v : defval;
@@ -5480,31 +5475,37 @@ var input = {
5480
5475
  float(defval, title, opts = {}) {
5481
5476
  return registerNumeric("float", defval, title, opts);
5482
5477
  },
5483
- bool(defval, title) {
5484
- return registerInput({ type: "bool", defval, title }, "input_bool");
5478
+ bool(defval, title, opts = {}) {
5479
+ return registerInput({ type: "bool", defval, title, ...common(opts) }, "input_bool");
5485
5480
  },
5486
5481
  string(defval, title, opts = {}) {
5487
- return registerInput({ type: "string", defval, title, options: opts.options }, "input_string");
5482
+ return registerInput({ type: "string", defval, title, options: opts.options, ...common(opts) }, "input_string");
5488
5483
  },
5489
- color(defval, title) {
5490
- return registerInput({ type: "color", defval, title }, "input_color");
5484
+ color(defval, title, opts = {}) {
5485
+ return registerInput({ type: "color", defval, title, ...common(opts) }, "input_color");
5491
5486
  },
5492
5487
  /** Declares a source input and returns it as a Series. */
5493
- source(defval = "close", title) {
5494
- const name = registerInput({ type: "source", defval, title }, "input_source");
5488
+ source(defval = "close", title, opts = {}) {
5489
+ const name = registerInput({ type: "source", defval, title, ...common(opts) }, "input_source");
5495
5490
  return sourceSeries(name);
5496
5491
  },
5497
5492
  /** Timeframe string ("60", "1D", "W"). As in PineScript, "" means the chart timeframe. */
5498
5493
  timeframe(defval, title, opts = {}) {
5499
- return registerInput({ type: "timeframe", defval, title, options: opts.options }, "input_timeframe");
5494
+ return registerInput(
5495
+ { type: "timeframe", defval, title, options: opts.options, ...common(opts) },
5496
+ "input_timeframe"
5497
+ );
5500
5498
  },
5501
5499
  /** Session string ("0930-1600", "0930-1600:23456"). */
5502
5500
  session(defval, title, opts = {}) {
5503
- return registerInput({ type: "session", defval, title, options: opts.options }, "input_session");
5501
+ return registerInput(
5502
+ { type: "session", defval, title, options: opts.options, ...common(opts) },
5503
+ "input_session"
5504
+ );
5504
5505
  },
5505
5506
  /** Date and time as a UNIX time in milliseconds, e.g. `input.time(timestamp('2024-01-01'), 'Start')`. */
5506
- time(defval, title) {
5507
- const v = registerInput({ type: "time", defval, title }, "input_time");
5507
+ time(defval, title, opts = {}) {
5508
+ const v = registerInput({ type: "time", defval, title, ...common(opts) }, "input_time");
5508
5509
  return typeof v === "number" ? v : defval;
5509
5510
  }
5510
5511
  };
@@ -5558,6 +5559,8 @@ var ta2 = {
5558
5559
  const a = anchor ?? changeOf("1D");
5559
5560
  return stdev_mult === void 0 ? vwap2(source, volume, a) : vwapBands(source, volume, a, stdev_mult);
5560
5561
  }),
5562
+ /** `ta.mfi(source, length)` with the chart volume (PineScript takes 2 arguments). */
5563
+ mfi: (source, length2) => mfi2(source, length2, volume),
5561
5564
  /** `ta.pivothigh(source, leftbars, rightbars)` or `ta.pivothigh(leftbars, rightbars)` (source = high).
5562
5565
  * The value appears `rightbars` bars after the pivot bar, as in PineScript. */
5563
5566
  pivothigh: (sourceOrLeft, leftOrRight, rightbars) => typeof sourceOrLeft === "number" ? pivothigh2(high, sourceOrLeft, leftOrRight) : pivothigh2(sourceOrLeft, leftOrRight, rightbars),
@@ -5584,7 +5587,7 @@ var color = {
5584
5587
  new: new_color,
5585
5588
  when: colorWhen
5586
5589
  };
5587
- function plot(series, title, options = {}) {
5590
+ function plotImpl(series, title, options = {}) {
5588
5591
  const c = collector();
5589
5592
  const id = `plot${c.plotConfig.length}`;
5590
5593
  const perBar2 = Array.isArray(options.color) ? options.color : void 0;
@@ -5595,6 +5598,7 @@ function plot(series, title, options = {}) {
5595
5598
  color: staticColor,
5596
5599
  lineWidth: options.linewidth,
5597
5600
  style: options.style,
5601
+ linestyle: options.linestyle,
5598
5602
  display: options.display,
5599
5603
  histbase: options.histbase
5600
5604
  });
@@ -5603,13 +5607,29 @@ function plot(series, title, options = {}) {
5603
5607
  const data = [];
5604
5608
  for (let i = 0; i < b2.length; i++) {
5605
5609
  const value = values[i];
5606
- if (value === void 0 || Number.isNaN(value)) continue;
5610
+ if (value === void 0 || !Number.isFinite(value)) continue;
5607
5611
  data.push(perBar2 ? { time: b2[i].time, value, color: perBar2[i] } : { time: b2[i].time, value });
5608
5612
  }
5609
5613
  c.plots[id] = data;
5610
5614
  return { id, kind: "plot" };
5611
5615
  }
5612
- function hline(price, title, options = {}) {
5616
+ var plot = Object.assign(plotImpl, {
5617
+ style_line: "line",
5618
+ style_linebr: "linebr",
5619
+ style_stepline: "stepline",
5620
+ style_steplinebr: "steplinebr",
5621
+ style_stepline_diamond: "stepline_diamond",
5622
+ style_histogram: "histogram",
5623
+ style_columns: "columns",
5624
+ style_area: "area",
5625
+ style_areabr: "areabr",
5626
+ style_circles: "circles",
5627
+ style_cross: "cross",
5628
+ linestyle_solid: "solid",
5629
+ linestyle_dotted: "dotted",
5630
+ linestyle_dashed: "dashed"
5631
+ });
5632
+ function hlineImpl(price, title, options = {}) {
5613
5633
  const c = collector();
5614
5634
  const id = `hline${c.hlineConfig.length}`;
5615
5635
  c.hlineConfig.push({
@@ -5617,11 +5637,16 @@ function hline(price, title, options = {}) {
5617
5637
  price,
5618
5638
  title,
5619
5639
  color: options.color,
5620
- linestyle: options.linestyle,
5640
+ linestyle: options.linestyle ?? "dashed",
5621
5641
  linewidth: options.linewidth
5622
5642
  });
5623
5643
  return { id, kind: "hline" };
5624
5644
  }
5645
+ var hline = Object.assign(hlineImpl, {
5646
+ style_solid: "solid",
5647
+ style_dotted: "dotted",
5648
+ style_dashed: "dashed"
5649
+ });
5625
5650
  function perBarValues(v, n) {
5626
5651
  return v instanceof Series ? v.toArray().slice(0, n) : new Array(n).fill(v);
5627
5652
  }
@@ -5724,6 +5749,35 @@ function plotshape(condition, title, options = {}) {
5724
5749
  function plotchar(condition, title, options = {}) {
5725
5750
  emitMarkers("char", condition, title, options);
5726
5751
  }
5752
+ function plotarrow(series, title, options = {}) {
5753
+ const c = collector();
5754
+ const id = `arrow${c.arrowConfig.length}`;
5755
+ const pick = (col2, fallback, i) => col2 === void 0 ? fallback : typeof col2 === "string" ? col2 : col2[i];
5756
+ const colorup = typeof options.colorup === "string" ? options.colorup : "#00FF00";
5757
+ const colordown = typeof options.colordown === "string" ? options.colordown : "#FF0000";
5758
+ c.arrowConfig.push({
5759
+ id,
5760
+ title,
5761
+ colorup,
5762
+ colordown,
5763
+ minheight: options.minheight ?? 5,
5764
+ maxheight: options.maxheight ?? 100,
5765
+ offset: options.offset,
5766
+ display: options.display
5767
+ });
5768
+ const values = series.toArray();
5769
+ const b2 = bars();
5770
+ const off = options.offset ?? 0;
5771
+ for (let i = 0; i < b2.length; i++) {
5772
+ const v = values[i];
5773
+ if (v === void 0 || Number.isNaN(v) || v === 0) continue;
5774
+ const j = i + off;
5775
+ if (j < 0 || j >= b2.length) continue;
5776
+ const color2 = v > 0 ? pick(options.colorup, "#00FF00", i) : pick(options.colordown, "#FF0000", i);
5777
+ if (color2 === void 0) continue;
5778
+ c.arrows.push({ time: b2[j].time, id, value: v, color: color2 });
5779
+ }
5780
+ }
5727
5781
  function emitBarColors(kind, colors, options) {
5728
5782
  const c = collector();
5729
5783
  const id = `${kind}${c.barColorConfig.filter((x) => x.kind === kind).length}`;
@@ -5800,6 +5854,139 @@ function eachBar(fn) {
5800
5854
  return Series.fromArray(ctxBars, out);
5801
5855
  }
5802
5856
  var currentBar = -1;
5857
+ function strategyRun(caller) {
5858
+ const run = collector().strategy;
5859
+ if (!run) throw new Error(`${caller}: declare the script with strategy() first.`);
5860
+ return run;
5861
+ }
5862
+ function strategyEngine(caller) {
5863
+ const run = strategyRun(caller);
5864
+ if (!run.engine) {
5865
+ throw new Error(
5866
+ `${caller}: no strategy engine; pass it as executeScript(body, bars, inputs, chart, { strategyEngine }).`
5867
+ );
5868
+ }
5869
+ return run.engine;
5870
+ }
5871
+ function orderEngine(caller) {
5872
+ const engine = strategyEngine(caller);
5873
+ if (!strategyRun(caller).inLoop) throw new Error(`${caller} must be called inside strategy.eachBar().`);
5874
+ return engine;
5875
+ }
5876
+ function given(options) {
5877
+ const out = {};
5878
+ for (const [key, value] of Object.entries(options)) {
5879
+ if (value === void 0 || typeof value === "number" && Number.isNaN(value)) continue;
5880
+ out[key] = value;
5881
+ }
5882
+ return out;
5883
+ }
5884
+ function strategyVariable(name) {
5885
+ const value = strategyEngine(`strategy.${name}`).get(name);
5886
+ if (value === void 0) throw new Error(`strategy.${name} is not provided by the strategy engine.`);
5887
+ return value;
5888
+ }
5889
+ function riskRule(rule) {
5890
+ const engine = strategyEngine(`strategy.risk.${rule.rule}()`);
5891
+ if (!engine.risk) throw new Error(`strategy.risk.${rule.rule}() is not provided by the strategy engine.`);
5892
+ engine.risk(rule);
5893
+ }
5894
+ function declareStrategy(title, options = {}) {
5895
+ const c = collector();
5896
+ declare(c, "strategy");
5897
+ c.title = title;
5898
+ c.shortTitle = options.shorttitle;
5899
+ c.overlay = options.overlay ?? false;
5900
+ c.precision = options.precision;
5901
+ c.format = options.format;
5902
+ setDrawingLimits({
5903
+ line: options.max_lines_count,
5904
+ label: options.max_labels_count,
5905
+ box: options.max_boxes_count,
5906
+ polyline: options.max_polylines_count
5907
+ });
5908
+ const properties = { ...STRATEGY_DEFAULTS };
5909
+ const target = properties;
5910
+ for (const key of [...Object.keys(STRATEGY_DEFAULTS), "currency"]) {
5911
+ const value = options[key];
5912
+ if (value !== void 0) target[key] = value;
5913
+ }
5914
+ const factory = scriptOptions.strategyEngine;
5915
+ c.strategy = {
5916
+ properties,
5917
+ engine: factory?.({ properties, bars: ctxBars.bars, chart: chartCtx }),
5918
+ looped: false,
5919
+ inLoop: false
5920
+ };
5921
+ }
5922
+ function strategyEachBar(fn) {
5923
+ const run = strategyRun("strategy.eachBar()");
5924
+ const engine = strategyEngine("strategy.eachBar()");
5925
+ if (run.looped) throw new Error("strategy.eachBar() runs once per script run.");
5926
+ run.looped = true;
5927
+ return eachBar((c) => {
5928
+ engine.processBar(c.i);
5929
+ run.inLoop = true;
5930
+ try {
5931
+ return fn(c);
5932
+ } finally {
5933
+ run.inLoop = false;
5934
+ engine.processClose(c.i);
5935
+ }
5936
+ });
5937
+ }
5938
+ var strategyMembers = {
5939
+ ...STRATEGY_CONSTANTS,
5940
+ eachBar: strategyEachBar,
5941
+ entry(id, direction, options = {}) {
5942
+ orderEngine("strategy.entry()").entry(id, direction, given(options));
5943
+ },
5944
+ order(id, direction, options = {}) {
5945
+ orderEngine("strategy.order()").order(id, direction, given(options));
5946
+ },
5947
+ exit(id, options = {}) {
5948
+ orderEngine("strategy.exit()").exit(id, given(options));
5949
+ },
5950
+ close(id, options = {}) {
5951
+ orderEngine("strategy.close()").close(id, given(options));
5952
+ },
5953
+ close_all(options = {}) {
5954
+ orderEngine("strategy.close_all()").close_all(given(options));
5955
+ },
5956
+ cancel(id) {
5957
+ orderEngine("strategy.cancel()").cancel(id);
5958
+ },
5959
+ cancel_all() {
5960
+ orderEngine("strategy.cancel_all()").cancel_all();
5961
+ },
5962
+ opentrade(index) {
5963
+ return strategyEngine("strategy.opentrade()").openTrade(index);
5964
+ },
5965
+ closedtrade(index) {
5966
+ return strategyEngine("strategy.closedtrade()").closedTrade(index);
5967
+ },
5968
+ default_entry_qty(fill_price) {
5969
+ const engine = strategyEngine("strategy.default_entry_qty()");
5970
+ if (!engine.defaultEntryQty) throw new Error("strategy.default_entry_qty() is not provided by the strategy engine.");
5971
+ return engine.defaultEntryQty(fill_price);
5972
+ },
5973
+ risk: {
5974
+ allow_entry_in: (direction) => riskRule({ rule: "allow_entry_in", direction }),
5975
+ max_cons_loss_days: (count, alert_message) => riskRule(given({ rule: "max_cons_loss_days", count, alert_message })),
5976
+ max_drawdown: (value, type, alert_message) => riskRule(given({ rule: "max_drawdown", value, type, alert_message })),
5977
+ max_intraday_filled_orders: (count, alert_message) => riskRule(given({ rule: "max_intraday_filled_orders", count, alert_message })),
5978
+ max_intraday_loss: (value, type, alert_message) => riskRule(given({ rule: "max_intraday_loss", value, type, alert_message })),
5979
+ max_position_size: (contracts) => riskRule({ rule: "max_position_size", contracts })
5980
+ }
5981
+ };
5982
+ var variableGetters = {};
5983
+ for (const name of [...STRATEGY_NUMBER_VARIABLES, ...STRATEGY_TEXT_VARIABLES]) {
5984
+ variableGetters[name] = { get: () => strategyVariable(name), enumerable: true };
5985
+ }
5986
+ var strategy = Object.defineProperties(
5987
+ Object.assign((title, options) => declareStrategy(title, options), strategyMembers),
5988
+ variableGetters
5989
+ );
5803
5990
  var chartCtx = {};
5804
5991
  function chartTimeframe() {
5805
5992
  collector();
@@ -5859,7 +6046,24 @@ var timeframe = {
5859
6046
  return in_seconds(tf === void 0 || tf === "" ? chartTimeframe().period : tf);
5860
6047
  }
5861
6048
  };
6049
+ function symbolProperty(key) {
6050
+ collector();
6051
+ const value = chartCtx[key];
6052
+ if (value === void 0) {
6053
+ throw new Error(`syminfo.${key} is not known: pass it as executeScript(body, bars, inputs, { ${key} }).`);
6054
+ }
6055
+ return value;
6056
+ }
5862
6057
  var syminfo = {
6058
+ get mintick() {
6059
+ return symbolProperty("mintick");
6060
+ },
6061
+ get pointvalue() {
6062
+ return symbolProperty("pointvalue");
6063
+ },
6064
+ get mincontract() {
6065
+ return symbolProperty("mincontract");
6066
+ },
5863
6067
  get timezone() {
5864
6068
  return exchangeTimezone();
5865
6069
  },
@@ -5975,6 +6179,37 @@ function flag(key) {
5975
6179
  const values = sessionFlags(ctxBars.bars.map(barTime), bars2, regular)[key];
5976
6180
  return Series.fromArray(ctxBars, values.map((v) => v ? 1 : 0));
5977
6181
  }
6182
+ function barFlag(test) {
6183
+ const n = ctxBars.bars.length;
6184
+ return Series.fromArray(ctxBars, Array.from({ length: n }, (_, i) => test(i, n) ? 1 : 0));
6185
+ }
6186
+ var lastRealtime = (i, n) => i === n - 1 && chartCtx.realtime === true;
6187
+ var barstate = {
6188
+ get isfirst() {
6189
+ return barFlag((i) => i === 0);
6190
+ },
6191
+ get islast() {
6192
+ return barFlag((i, n) => i === n - 1);
6193
+ },
6194
+ get isconfirmed() {
6195
+ return barFlag((i, n) => i < n - 1 || chartCtx.lastBarConfirmed === true);
6196
+ },
6197
+ get isrealtime() {
6198
+ return barFlag(lastRealtime);
6199
+ },
6200
+ get ishistory() {
6201
+ return barFlag((i, n) => !lastRealtime(i, n));
6202
+ },
6203
+ get isnew() {
6204
+ return barFlag((i, n) => !lastRealtime(i, n) || chartCtx.lastBarNew !== false);
6205
+ },
6206
+ get islastconfirmedhistory() {
6207
+ return barFlag((i, n) => {
6208
+ const lastConfirmed = chartCtx.lastBarConfirmed === true && chartCtx.realtime !== true;
6209
+ return i === (lastConfirmed ? n - 1 : n - 2);
6210
+ });
6211
+ }
6212
+ };
5978
6213
  var session = {
5979
6214
  regular: "regular",
5980
6215
  extended: "extended",
@@ -6128,3 +6363,10 @@ function alertcondition(condition, title, message) {
6128
6363
  const last = condition.length() ? condition.last() : NaN;
6129
6364
  c.alerts.push({ id, title, message, triggered: !isNA(last) && last !== 0 });
6130
6365
  }
6366
+ var math = {
6367
+ ...math_exports,
6368
+ round_to_mintick: ((number2, mintick) => {
6369
+ const tick = mintick ?? syminfo.mintick;
6370
+ return number2 instanceof Series ? round_to_mintick(number2, tick) : round_to_mintick(number2, tick);
6371
+ })
6372
+ };