oakscriptjs 0.7.2 → 0.8.0

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,6 +21,7 @@ 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,
@@ -50,7 +51,7 @@ __export(index_exports, {
50
51
  line: () => line,
51
52
  linefill: () => linefill,
52
53
  low: () => low,
53
- math: () => math_exports,
54
+ math: () => math,
54
55
  minute: () => minute2,
55
56
  month: () => month2,
56
57
  na: () => isNA,
@@ -66,6 +67,7 @@ __export(index_exports, {
66
67
  seriesOf: () => seriesOf,
67
68
  session: () => session,
68
69
  str: () => str,
70
+ strategy: () => strategy,
69
71
  syminfo: () => syminfo,
70
72
  ta: () => ta2,
71
73
  text: () => text,
@@ -894,7 +896,7 @@ function tr(handle_na = false, high2, low2, close2) {
894
896
  const result = [];
895
897
  for (let i = 0; i < high2.length; i++) {
896
898
  if (i === 0) {
897
- result.push(high2[i] - low2[i]);
899
+ result.push(handle_na ? high2[i] - low2[i] : NaN);
898
900
  } else {
899
901
  const prevClose = close2[i - 1];
900
902
  if (isNaN(prevClose)) {
@@ -921,7 +923,7 @@ function atr(length2, high2, low2, close2) {
921
923
  "ta.atr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
922
924
  );
923
925
  }
924
- const trueRange = tr(false, high2, low2, close2);
926
+ const trueRange = tr(true, high2, low2, close2);
925
927
  return rma(trueRange, length2);
926
928
  }
927
929
  function supertrend(factor, atrPeriod, high2, low2, close2, wicks = false) {
@@ -1398,48 +1400,26 @@ function mfi(source, length2, volume2) {
1398
1400
  "ta.mfi() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
1399
1401
  );
1400
1402
  }
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 = [];
1408
- 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
- }
1403
+ const upperFlow = [];
1404
+ const lowerFlow = [];
1423
1405
  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;
1406
+ const change3 = i === 0 ? NaN : source[i] - source[i - 1];
1407
+ const flow = volume2[i] * source[i];
1408
+ upperFlow.push(le(change3, 0) ? 0 : flow);
1409
+ lowerFlow.push(ge(change3, 0) ? 0 : flow);
1410
+ }
1411
+ const sumAt = (flow, i) => {
1412
+ const values = lastValues(flow, i, Math.floor(length2));
1413
+ return values ? values.reduce((a, b2) => a + b2, 0) : NaN;
1414
+ };
1415
+ const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
1416
+ const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
1417
+ return upper2.map((u, i) => {
1418
+ const l = lower2[i];
1419
+ if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
1420
+ if (l === 0) return 100;
1421
+ return 100 - 100 / (1 + u / l);
1422
+ });
1443
1423
  }
1444
1424
  function hma(source, length2) {
1445
1425
  const halfLength = Math.floor(length2 / 2);
@@ -1603,50 +1583,32 @@ function dmi(diLength, adxSmoothing, high2, low2, close2) {
1603
1583
  const len = Math.max(high2.length, low2.length, close2.length);
1604
1584
  const plusDM = [];
1605
1585
  const minusDM = [];
1606
- const trueRangeValues = tr(false, high2, low2, close2);
1607
- 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
1586
  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);
1587
+ const up = i === 0 ? NaN : high2[i] - high2[i - 1];
1588
+ const down = i === 0 ? NaN : low2[i - 1] - low2[i];
1589
+ plusDM.push(Number.isNaN(up) ? NaN : gt(up, down) && gt(up, 0) ? up : 0);
1590
+ minusDM.push(Number.isNaN(down) ? NaN : gt(down, up) && gt(down, 0) ? down : 0);
1591
+ }
1592
+ const trur = rma(tr(false, high2, low2, close2), diLength);
1593
+ const smoothedPlus = rma(plusDM, diLength);
1594
+ const smoothedMinus = rma(minusDM, diLength);
1595
+ const directional = (smoothed) => {
1596
+ let last = NaN;
1597
+ return smoothed.map((v, i) => {
1598
+ const t2 = trur[i];
1599
+ const value = t2 === 0 ? NaN : 100 * v / t2;
1600
+ if (!Number.isNaN(value)) last = value;
1601
+ return last;
1602
+ });
1603
+ };
1604
+ const plusDI = directional(smoothedPlus);
1605
+ const minusDI = directional(smoothedMinus);
1606
+ const dx = plusDI.map((plus, i) => {
1607
+ const minus = minusDI[i];
1608
+ const sum2 = plus + minus;
1609
+ return Math.abs(plus - minus) / (eq(sum2, 0) ? 1 : sum2);
1610
+ });
1611
+ const adx = rma(dx, adxSmoothing).map((v) => 100 * v);
1650
1612
  return [plusDI, minusDI, adx];
1651
1613
  }
1652
1614
  function tsi(source, shortLength, longLength) {
@@ -3731,1640 +3693,1706 @@ var format_none = 0;
3731
3693
  var format_bold = 1;
3732
3694
  var format_italic = 2;
3733
3695
 
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
3696
+ // src/math/index.ts
3697
+ var math_exports = {};
3698
+ __export(math_exports, {
3699
+ abs: () => abs,
3700
+ acos: () => acos,
3701
+ asin: () => asin,
3702
+ atan: () => atan,
3703
+ avg: () => avg,
3704
+ ceil: () => ceil,
3705
+ cos: () => cos,
3706
+ e: () => e,
3707
+ exp: () => exp,
3708
+ floor: () => floor,
3709
+ log: () => log,
3710
+ log10: () => log10,
3711
+ max: () => max3,
3712
+ min: () => min3,
3713
+ phi: () => phi,
3714
+ pi: () => pi,
3715
+ pow: () => pow,
3716
+ random: () => random,
3717
+ round: () => round,
3718
+ round_to_mintick: () => round_to_mintick,
3719
+ rphi: () => rphi,
3720
+ sign: () => sign,
3721
+ sin: () => sin,
3722
+ sqrt: () => sqrt,
3723
+ sum: () => sum,
3724
+ tan: () => tan,
3725
+ todegrees: () => todegrees,
3726
+ toradians: () => toradians
3740
3727
  });
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");
3728
+ function abs(value) {
3729
+ if (value instanceof Series) {
3730
+ const bars2 = value.bars;
3731
+ const valueArray = value.toArray();
3732
+ const length2 = bars2.length;
3733
+ const result = [];
3734
+ for (let i = 0; i < length2; i++) {
3735
+ const v = valueArray[i] ?? NaN;
3736
+ result.push(Math.abs(v));
3737
+ }
3738
+ return Series.fromArray(bars2, result);
3758
3739
  }
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];
3740
+ return Math.abs(value);
3766
3741
  }
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}`;
3742
+ function ceil(value) {
3743
+ if (value instanceof Series) {
3744
+ const bars2 = value.bars;
3745
+ const valueArray = value.toArray();
3746
+ const length2 = bars2.length;
3747
+ const result = [];
3748
+ for (let i = 0; i < length2; i++) {
3749
+ const v = valueArray[i] ?? NaN;
3750
+ result.push(Math.ceil(v));
3751
+ }
3752
+ return Series.fromArray(bars2, result);
3775
3753
  }
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`;
3780
- }
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
- };
3799
- }
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;
3754
+ return Math.ceil(value);
3813
3755
  }
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();
3756
+ function floor(value) {
3757
+ if (value instanceof Series) {
3758
+ const bars2 = value.bars;
3759
+ const valueArray = value.toArray();
3760
+ const length2 = bars2.length;
3761
+ const result = [];
3762
+ for (let i = 0; i < length2; i++) {
3763
+ const v = valueArray[i] ?? NaN;
3764
+ result.push(Math.floor(v));
3846
3765
  }
3847
- zone = { kind: "iana", format: format2 };
3766
+ return Series.fromArray(bars2, result);
3848
3767
  }
3849
- zones.set(timezone, zone);
3850
- return zone;
3768
+ return Math.floor(value);
3851
3769
  }
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);
3770
+ function round(value, precision) {
3771
+ if (value instanceof Series) {
3772
+ const bars2 = value.bars;
3773
+ const valueArray = value.toArray();
3774
+ const length2 = bars2.length;
3775
+ const result = [];
3776
+ if (precision === void 0) {
3777
+ for (let i = 0; i < length2; i++) {
3778
+ const v = valueArray[i] ?? NaN;
3779
+ result.push(Math.round(v));
3780
+ }
3781
+ } else {
3782
+ const multiplier2 = Math.pow(10, precision);
3783
+ for (let i = 0; i < length2; i++) {
3784
+ const v = valueArray[i] ?? NaN;
3785
+ result.push(Math.round(v * multiplier2) / multiplier2);
3786
+ }
3787
+ }
3788
+ return Series.fromArray(bars2, result);
3858
3789
  }
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);
3790
+ if (precision === void 0) {
3791
+ return Math.round(value);
3792
+ }
3793
+ const multiplier = Math.pow(10, precision);
3794
+ return Math.round(value * multiplier) / multiplier;
3864
3795
  }
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
- };
3876
- }
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;
3886
- }
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;
3930
- }
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;
3796
+ function max3(...values) {
3797
+ const hasSeries = values.some((v) => v instanceof Series);
3798
+ if (!hasSeries) {
3799
+ return Math.max(...values);
3800
+ }
3801
+ const firstSeries = values.find((v) => v instanceof Series);
3802
+ const bars2 = firstSeries.bars;
3803
+ const length2 = bars2.length;
3804
+ const valueArrays = values.map(
3805
+ (v) => v instanceof Series ? v.toArray() : null
3806
+ );
3807
+ const result = [];
3808
+ for (let i = 0; i < length2; i++) {
3809
+ const nums = values.map(
3810
+ (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
3811
+ );
3812
+ result.push(Math.max(...nums));
3813
+ }
3814
+ return Series.fromArray(bars2, result);
3940
3815
  }
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")`);
3816
+ function min3(...values) {
3817
+ const hasSeries = values.some((v) => v instanceof Series);
3818
+ if (!hasSeries) {
3819
+ return Math.min(...values);
3957
3820
  }
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`);
3821
+ const firstSeries = values.find((v) => v instanceof Series);
3822
+ const bars2 = firstSeries.bars;
3823
+ const length2 = bars2.length;
3824
+ const valueArrays = values.map(
3825
+ (v) => v instanceof Series ? v.toArray() : null
3826
+ );
3827
+ const result = [];
3828
+ for (let i = 0; i < length2; i++) {
3829
+ const nums = values.map(
3830
+ (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
3831
+ );
3832
+ result.push(Math.min(...nums));
3967
3833
  }
3968
- const ms = Number(millis.padEnd(3, "0"));
3969
- return Date.UTC(Number(year3), month3 - 1, d, h, mi, s, ms) - offset * 6e4;
3834
+ return Series.fromArray(bars2, result);
3970
3835
  }
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;
3836
+ function avg(...values) {
3837
+ const hasSeries = values.some((v) => v instanceof Series);
3838
+ if (!hasSeries) {
3839
+ return values.reduce((a, b2) => a + b2, 0) / values.length;
3840
+ }
3841
+ const firstSeries = values.find((v) => v instanceof Series);
3842
+ const bars2 = firstSeries.bars;
3843
+ const length2 = bars2.length;
3844
+ const valueArrays = values.map(
3845
+ (v) => v instanceof Series ? v.toArray() : null
3846
+ );
3847
+ const result = [];
3848
+ for (let i = 0; i < length2; i++) {
3849
+ const nums = values.map(
3850
+ (v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
3851
+ );
3852
+ const sum2 = nums.reduce((a, b2) => a + b2, 0);
3853
+ result.push(sum2 / nums.length);
3854
+ }
3855
+ return Series.fromArray(bars2, result);
3996
3856
  }
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;
3857
+ function sum(source, length2) {
3858
+ const sourceArray = source instanceof Series ? source.toArray() : source;
3859
+ const result = [];
3860
+ const len = Math.floor(length2);
3861
+ const window = [];
3862
+ for (let i = 0; i < sourceArray.length; i++) {
3863
+ const v = sourceArray[i];
3864
+ if (v !== void 0 && v !== null && !Number.isNaN(v)) {
3865
+ window.push(v);
3866
+ if (window.length > len) window.shift();
3867
+ }
3868
+ if (window.length < len) {
3869
+ result.push(NaN);
4006
3870
  } else {
4007
- if (minute3 >= start && days[nextDay]) return true;
4008
- if (minute3 < end && days[f.dayOfWeek]) return true;
3871
+ let total = 0;
3872
+ for (const x of window) total += x;
3873
+ result.push(total);
4009
3874
  }
4010
3875
  }
4011
- return false;
4012
- }
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);
3876
+ if (source instanceof Series) {
3877
+ return Series.fromArray(source.bars, result);
4020
3878
  }
4021
- const [month3, day, hour3, minute3, second3] = rest;
4022
- return new Date(first, month3 - 1, day, hour3 || 0, minute3 || 0, second3 || 0).getTime();
4023
- }
4024
- function field(time2, timezone, pick) {
4025
- return Number.isNaN(time2) ? NaN : pick(zonedFields(time2, timezone));
4026
- }
4027
- function year(time2, timezone) {
4028
- return field(time2, timezone, (f) => f.year);
3879
+ return result;
4029
3880
  }
4030
- function month(time2, timezone) {
4031
- return field(time2, timezone, (f) => f.month);
3881
+ function sqrt(value) {
3882
+ if (value instanceof Series) {
3883
+ const bars2 = value.bars;
3884
+ const valueArray = value.toArray();
3885
+ const length2 = bars2.length;
3886
+ const result = [];
3887
+ for (let i = 0; i < length2; i++) {
3888
+ const v = valueArray[i] ?? NaN;
3889
+ result.push(Math.sqrt(v));
3890
+ }
3891
+ return Series.fromArray(bars2, result);
3892
+ }
3893
+ return Math.sqrt(value);
4032
3894
  }
4033
- function dayofmonth(time2, timezone) {
4034
- return field(time2, timezone, (f) => f.day);
3895
+ function pow(base, exponent) {
3896
+ const baseIsSeries = base instanceof Series;
3897
+ const expIsSeries = exponent instanceof Series;
3898
+ if (!baseIsSeries && !expIsSeries) {
3899
+ return Math.pow(base, exponent);
3900
+ }
3901
+ const bars2 = baseIsSeries ? base.bars : exponent.bars;
3902
+ const baseArray = baseIsSeries ? base.toArray() : null;
3903
+ const expArray = expIsSeries ? exponent.toArray() : null;
3904
+ const length2 = bars2.length;
3905
+ const result = [];
3906
+ for (let i = 0; i < length2; i++) {
3907
+ const b2 = baseArray ? baseArray[i] ?? NaN : base;
3908
+ const e2 = expArray ? expArray[i] ?? NaN : exponent;
3909
+ result.push(Math.pow(b2, e2));
3910
+ }
3911
+ return Series.fromArray(bars2, result);
4035
3912
  }
4036
- function dayofweek(time2, timezone) {
4037
- return field(time2, timezone, (f) => f.dayOfWeek);
4038
- }
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
- });
4047
- }
4048
- function hour(time2, timezone) {
4049
- return field(time2, timezone, (f) => f.hour);
4050
- }
4051
- function minute(time2, timezone) {
4052
- return field(time2, timezone, (f) => f.minute);
3913
+ function exp(value) {
3914
+ if (value instanceof Series) {
3915
+ const bars2 = value.bars;
3916
+ const valueArray = value.toArray();
3917
+ const length2 = bars2.length;
3918
+ const result = [];
3919
+ for (let i = 0; i < length2; i++) {
3920
+ const v = valueArray[i] ?? NaN;
3921
+ result.push(Math.exp(v));
3922
+ }
3923
+ return Series.fromArray(bars2, result);
3924
+ }
3925
+ return Math.exp(value);
4053
3926
  }
4054
- function second(time2, timezone) {
4055
- return field(time2, timezone, (f) => f.second);
3927
+ function log(value) {
3928
+ if (value instanceof Series) {
3929
+ const bars2 = value.bars;
3930
+ const valueArray = value.toArray();
3931
+ const length2 = bars2.length;
3932
+ const result = [];
3933
+ for (let i = 0; i < length2; i++) {
3934
+ const v = valueArray[i] ?? NaN;
3935
+ result.push(Math.log(v));
3936
+ }
3937
+ return Series.fromArray(bars2, result);
3938
+ }
3939
+ return Math.log(value);
4056
3940
  }
4057
- function inSession2(time2, session2, timezone) {
4058
- return inSession(time2, session2, timezone);
3941
+ function log10(value) {
3942
+ if (value instanceof Series) {
3943
+ const bars2 = value.bars;
3944
+ const valueArray = value.toArray();
3945
+ const length2 = bars2.length;
3946
+ const result = [];
3947
+ for (let i = 0; i < length2; i++) {
3948
+ const v = valueArray[i] ?? NaN;
3949
+ result.push(Math.log10(v));
3950
+ }
3951
+ return Series.fromArray(bars2, result);
3952
+ }
3953
+ return Math.log10(value);
4059
3954
  }
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;
4109
- }
4110
- } else if (phase !== "suffix" && ("#0,.".includes(ch) || phase === "number" && ch === "E")) {
4111
- phase = "number";
4112
- number2 += ch;
4113
- continue;
4114
- } else {
4115
- if (ch === "%") percent = true;
4116
- literal = ch;
3955
+ function sin(value) {
3956
+ if (value instanceof Series) {
3957
+ const bars2 = value.bars;
3958
+ const valueArray = value.toArray();
3959
+ const length2 = bars2.length;
3960
+ const result = [];
3961
+ for (let i = 0; i < length2; i++) {
3962
+ const v = valueArray[i] ?? NaN;
3963
+ result.push(Math.sin(v));
4117
3964
  }
4118
- if (phase === "number") phase = "suffix";
4119
- if (phase === "prefix") prefix += literal;
4120
- else suffix += literal;
3965
+ return Series.fromArray(bars2, result);
4121
3966
  }
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;
3967
+ return Math.sin(value);
4139
3968
  }
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 };
3969
+ function cos(value) {
3970
+ if (value instanceof Series) {
3971
+ const bars2 = value.bars;
3972
+ const valueArray = value.toArray();
3973
+ const length2 = bars2.length;
3974
+ const result = [];
3975
+ for (let i = 0; i < length2; i++) {
3976
+ const v = valueArray[i] ?? NaN;
3977
+ result.push(Math.cos(v));
3978
+ }
3979
+ return Series.fromArray(bars2, result);
3980
+ }
3981
+ return Math.cos(value);
4148
3982
  }
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;
3983
+ function tan(value) {
3984
+ if (value instanceof Series) {
3985
+ const bars2 = value.bars;
3986
+ const valueArray = value.toArray();
3987
+ const length2 = bars2.length;
3988
+ const result = [];
3989
+ for (let i = 0; i < length2; i++) {
3990
+ const v = valueArray[i] ?? NaN;
3991
+ result.push(Math.tan(v));
3992
+ }
3993
+ return Series.fromArray(bars2, result);
4167
3994
  }
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 };
3995
+ return Math.tan(value);
4172
3996
  }
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;
3997
+ function asin(value) {
3998
+ if (value instanceof Series) {
3999
+ const bars2 = value.bars;
4000
+ const valueArray = value.toArray();
4001
+ const length2 = bars2.length;
4002
+ const result = [];
4003
+ for (let i = 0; i < length2; i++) {
4004
+ const v = valueArray[i] ?? NaN;
4005
+ result.push(Math.asin(v));
4006
+ }
4007
+ return Series.fromArray(bars2, result);
4189
4008
  }
4190
- kept = kept.replace(/0+$/, "");
4191
- return { digits: kept, intLen: kept ? intLen : 0 };
4009
+ return Math.asin(value);
4192
4010
  }
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(",");
4011
+ function acos(value) {
4012
+ if (value instanceof Series) {
4013
+ const bars2 = value.bars;
4014
+ const valueArray = value.toArray();
4015
+ const length2 = bars2.length;
4016
+ const result = [];
4017
+ for (let i = 0; i < length2; i++) {
4018
+ const v = valueArray[i] ?? NaN;
4019
+ result.push(Math.acos(v));
4020
+ }
4021
+ return Series.fromArray(bars2, result);
4022
+ }
4023
+ return Math.acos(value);
4198
4024
  }
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 : "");
4025
+ function atan(value) {
4026
+ if (value instanceof Series) {
4027
+ const bars2 = value.bars;
4028
+ const valueArray = value.toArray();
4029
+ const length2 = bars2.length;
4030
+ const result = [];
4031
+ for (let i = 0; i < length2; i++) {
4032
+ const v = valueArray[i] ?? NaN;
4033
+ result.push(Math.atan(v));
4034
+ }
4035
+ return Series.fromArray(bars2, result);
4036
+ }
4037
+ return Math.atan(value);
4206
4038
  }
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 };
4039
+ function toradians(degrees) {
4040
+ if (degrees instanceof Series) {
4041
+ const bars2 = degrees.bars;
4042
+ const valueArray = degrees.toArray();
4043
+ const length2 = bars2.length;
4044
+ const result = [];
4045
+ for (let i = 0; i < length2; i++) {
4046
+ const v = valueArray[i] ?? NaN;
4047
+ result.push(v * (Math.PI / 180));
4224
4048
  }
4225
- const e2 = String(Math.abs(exponent)).padStart(p.exponent, "0");
4226
- body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e2;
4227
- } 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 });
4049
+ return Series.fromArray(bars2, result);
4231
4050
  }
4232
- const negative = x < 0 && (!isZero || !p.hasDigits);
4233
- return (negative ? "-" : "") + p.prefix + body + p.suffix;
4051
+ return degrees * (Math.PI / 180);
4234
4052
  }
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)}`;
4053
+ function todegrees(radians) {
4054
+ if (radians instanceof Series) {
4055
+ const bars2 = radians.bars;
4056
+ const valueArray = radians.toArray();
4057
+ const length2 = bars2.length;
4058
+ const result = [];
4059
+ for (let i = 0; i < length2; i++) {
4060
+ const v = valueArray[i] ?? NaN;
4061
+ result.push(v * (180 / Math.PI));
4062
+ }
4063
+ return Series.fromArray(bars2, result);
4259
4064
  }
4260
- const part = new Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(time2).find((p) => p.type === "timeZoneName");
4261
- return part?.value ?? timezone;
4065
+ return radians * (180 / Math.PI);
4262
4066
  }
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;
4067
+ function random(min4, max4, _seed) {
4068
+ const rand = Math.random();
4069
+ if (min4 !== void 0 && max4 !== void 0) {
4070
+ return min4 + rand * (max4 - min4);
4071
+ }
4072
+ return rand;
4271
4073
  }
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;
4074
+ function sign(value) {
4075
+ if (value instanceof Series) {
4076
+ const bars2 = value.bars;
4077
+ const valueArray = value.toArray();
4078
+ const length2 = bars2.length;
4079
+ const result = [];
4080
+ for (let i = 0; i < length2; i++) {
4081
+ const v = valueArray[i] ?? NaN;
4082
+ result.push(v > 0 ? 1 : v < 0 ? -1 : 0);
4291
4083
  }
4292
- if (!/[A-Za-z]/.test(ch)) {
4293
- out += ch;
4294
- i++;
4295
- continue;
4084
+ return Series.fromArray(bars2, result);
4085
+ }
4086
+ return value > 0 ? 1 : value < 0 ? -1 : 0;
4087
+ }
4088
+ function round_to_mintick(number2, mintick) {
4089
+ if (number2 instanceof Series) {
4090
+ const bars2 = number2.bars;
4091
+ const valueArray = number2.toArray();
4092
+ const length2 = bars2.length;
4093
+ if (mintick === void 0) {
4094
+ throw new Error(
4095
+ "math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
4096
+ );
4296
4097
  }
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;
4098
+ const result = [];
4099
+ if (mintick === 0) {
4100
+ for (let i = 0; i < length2; i++) {
4101
+ result.push(valueArray[i] ?? NaN);
4102
+ }
4103
+ } else {
4104
+ for (let i = 0; i < length2; i++) {
4105
+ const v = valueArray[i];
4106
+ if (v === void 0 || isNaN(v)) {
4107
+ result.push(NaN);
4108
+ } else {
4109
+ result.push(Math.round(v / mintick) * mintick);
4110
+ }
4351
4111
  }
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
4112
  }
4113
+ return Series.fromArray(bars2, result);
4358
4114
  }
4359
- return out;
4115
+ if (isNaN(number2)) {
4116
+ return NaN;
4117
+ }
4118
+ if (mintick === void 0) {
4119
+ throw new Error(
4120
+ "math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
4121
+ );
4122
+ }
4123
+ if (mintick === 0) {
4124
+ return number2;
4125
+ }
4126
+ return Math.round(number2 / mintick) * mintick;
4360
4127
  }
4128
+ var pi = Math.PI;
4129
+ var e = Math.E;
4130
+ var phi = 1.618033988749895;
4131
+ var rphi = 0.618033988749895;
4361
4132
 
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"
4374
- };
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;
4388
- }
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}"`);
4133
+ // src/timeframe/index.ts
4134
+ var timeframe_exports = {};
4135
+ __export(timeframe_exports, {
4136
+ from_seconds: () => from_seconds,
4137
+ in_seconds: () => in_seconds,
4138
+ info: () => info
4139
+ });
4140
+ var SECONDS_PER_MINUTE = 60;
4141
+ var SECONDS_PER_DAY = 86400;
4142
+ var SECONDS_PER_WEEK = 604800;
4143
+ var SECONDS_PER_MONTH = 2628003;
4144
+ var SECONDS_365_DAYS = 365 * SECONDS_PER_DAY;
4145
+ var UNIT_SECONDS = {
4146
+ S: 1,
4147
+ "": SECONDS_PER_MINUTE,
4148
+ D: SECONDS_PER_DAY,
4149
+ W: SECONDS_PER_WEEK,
4150
+ M: SECONDS_PER_MONTH
4151
+ };
4152
+ var SECOND_TIMEFRAMES = [1, 5, 10, 15, 30];
4153
+ function in_seconds(timeframe2) {
4154
+ const text2 = timeframe2.trim();
4155
+ if (text2 === "") {
4156
+ throw new RangeError("timeframe.in_seconds() needs a timeframe: the chart timeframe is not known here");
4411
4157
  }
4158
+ const before = /^(\d*)([SDWMT]?)$/.exec(text2);
4159
+ const after = before ? null : /^([SDWMT])(\d+)$/.exec(text2);
4160
+ if (!before && !after) throw new RangeError(`Invalid timeframe "${timeframe2}"`);
4161
+ const [digits, unit] = before ? [before[1], before[2]] : [after[2], after[1]];
4162
+ if (unit === "T") return NaN;
4163
+ const multiplier = digits === "" ? 1 : Number(digits);
4164
+ return multiplier * UNIT_SECONDS[unit];
4412
4165
  }
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;
4166
+ function from_seconds(seconds) {
4167
+ if (Number.isNaN(seconds)) throw new RangeError("timeframe.from_seconds() needs a number of seconds");
4168
+ if (seconds >= SECONDS_365_DAYS) return "12M";
4169
+ if (seconds > 0) {
4170
+ if (seconds % SECONDS_PER_MONTH === 0) return `${seconds / SECONDS_PER_MONTH}M`;
4171
+ if (seconds % SECONDS_PER_WEEK === 0) return `${seconds / SECONDS_PER_WEEK}W`;
4172
+ if (seconds % SECONDS_PER_DAY === 0) return `${seconds / SECONDS_PER_DAY}D`;
4173
+ if (seconds % SECONDS_PER_MINUTE === 0 && seconds < SECONDS_PER_DAY) return `${seconds / SECONDS_PER_MINUTE}`;
4448
4174
  }
4449
- return out;
4175
+ const second3 = SECOND_TIMEFRAMES.find((m) => seconds <= m);
4176
+ if (second3 !== void 0) return `${second3}S`;
4177
+ if (seconds < SECONDS_PER_DAY) return `${Math.ceil(seconds / SECONDS_PER_MINUTE)}`;
4178
+ return `${Math.ceil(seconds / SECONDS_PER_DAY)}D`;
4179
+ }
4180
+ function info(timeframe2) {
4181
+ const text2 = timeframe2.trim();
4182
+ const m = /^(\d*)([SDWMT]?)$/.exec(text2);
4183
+ if (!m || text2 === "") throw new RangeError(`Invalid timeframe "${timeframe2}"`);
4184
+ const unit = m[2];
4185
+ const multiplier = m[1] === "" ? 1 : Number(m[1]);
4186
+ return {
4187
+ period: unit === "" ? String(multiplier) : `${multiplier}${unit}`,
4188
+ multiplier,
4189
+ isintraday: unit === "" || unit === "S" || unit === "T",
4190
+ isdaily: unit === "D",
4191
+ isweekly: unit === "W",
4192
+ ismonthly: unit === "M",
4193
+ isminutes: unit === "",
4194
+ isseconds: unit === "S",
4195
+ isticks: unit === "T",
4196
+ isdwm: unit === "D" || unit === "W" || unit === "M"
4197
+ };
4450
4198
  }
4451
4199
 
4452
- // src/str/index.ts
4453
- function length(str2) {
4454
- return str2.length;
4200
+ // src/time/timezone.ts
4201
+ var MS_PER_SECOND = 1e3;
4202
+ var MS_PER_DAY = 864e5;
4203
+ var MAX_OFFSET_SECONDS = 18 * 3600;
4204
+ var OFFSET_NOTATION = /^(?:UTC|GMT)(?:([+-])(\d{1,2})(?::?(\d{2})(?::?(\d{2}))?)?)?$/;
4205
+ var REJECTED_NAMES = /* @__PURE__ */ new Set(["EST", "MST", "HST"]);
4206
+ var canonicalNames;
4207
+ function hasTimeZoneCase(name) {
4208
+ if (name.split("/").some((segment) => !/^[A-Z]/.test(segment))) return false;
4209
+ canonicalNames ??= new Map(Intl.supportedValuesOf("timeZone").map((n) => [n.toLowerCase(), n]));
4210
+ const canonical = canonicalNames.get(name.toLowerCase());
4211
+ return canonical === void 0 || canonical === name;
4455
4212
  }
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");
4213
+ var zones = /* @__PURE__ */ new Map();
4214
+ function resolveZone(timezone) {
4215
+ let zone = zones.get(timezone);
4216
+ if (zone) return zone;
4217
+ const invalid = () => new RangeError(
4218
+ `Invalid time zone "${timezone}": use UTC/GMT offset notation ("UTC-5", "GMT+0530") or an IANA name ("America/New_York")`
4219
+ );
4220
+ const m = OFFSET_NOTATION.exec(timezone);
4221
+ if (m) {
4222
+ const [, sign2, h = "0", mi = "0", s] = m;
4223
+ const minutes = Number(mi);
4224
+ const seconds = s === void 0 ? 0 : Number(s);
4225
+ if (minutes > 59 || seconds > 59 || s !== void 0 && h.length !== 2) throw invalid();
4226
+ const offsetSeconds = Number(h) * 3600 + minutes * 60 + seconds;
4227
+ if (offsetSeconds > MAX_OFFSET_SECONDS) throw invalid();
4228
+ zone = { kind: "fixed", offsetMs: (sign2 === "-" ? -offsetSeconds : offsetSeconds) * MS_PER_SECOND };
4229
+ } else {
4230
+ if (REJECTED_NAMES.has(timezone) || !hasTimeZoneCase(timezone)) throw invalid();
4231
+ let format2;
4232
+ try {
4233
+ format2 = new Intl.DateTimeFormat("en-US", {
4234
+ timeZone: timezone,
4235
+ hourCycle: "h23",
4236
+ year: "numeric",
4237
+ month: "numeric",
4238
+ day: "numeric",
4239
+ hour: "numeric",
4240
+ minute: "numeric",
4241
+ second: "numeric"
4242
+ });
4243
+ } catch {
4244
+ throw invalid();
4474
4245
  }
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");
4246
+ zone = { kind: "iana", format: format2 };
4479
4247
  }
4248
+ zones.set(timezone, zone);
4249
+ return zone;
4480
4250
  }
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);
4251
+ function offsetAt(zone, time2) {
4252
+ if (zone.kind === "fixed") return zone.offsetMs;
4253
+ const wholeSecond = Math.floor(time2 / 1e3) * 1e3;
4254
+ const f = {};
4255
+ for (const part of zone.format.formatToParts(wholeSecond)) {
4256
+ if (part.type !== "literal") f[part.type] = Number(part.value);
4505
4257
  }
4258
+ const local = Date.UTC(f.year, f.month - 1, f.day, f.hour, f.minute, f.second);
4259
+ return local - wholeSecond;
4506
4260
  }
4507
- function replace_all(source, target, replacement) {
4508
- return source.replaceAll(target, replacement);
4509
- }
4510
- function split(str2, separator) {
4511
- return str2.split(separator);
4512
- }
4513
- function concat(...strings) {
4514
- return strings.join("");
4261
+ function zoneOffset(time2, timezone) {
4262
+ return offsetAt(resolveZone(timezone), time2);
4515
4263
  }
4516
- function format(formatStr, ...args) {
4517
- return formatMessage(formatStr, args, "Etc/UTC");
4264
+ function zonedFields(time2, timezone) {
4265
+ const local = new Date(time2 + offsetAt(resolveZone(timezone), time2));
4266
+ return {
4267
+ year: local.getUTCFullYear(),
4268
+ month: local.getUTCMonth() + 1,
4269
+ day: local.getUTCDate(),
4270
+ hour: local.getUTCHours(),
4271
+ minute: local.getUTCMinutes(),
4272
+ second: local.getUTCSeconds(),
4273
+ dayOfWeek: local.getUTCDay() + 1
4274
+ };
4518
4275
  }
4519
- function startswith(source, str2) {
4520
- return source.startsWith(str2);
4276
+ function zonedToUnix(timezone, year3, month3, day, hour3 = 0, minute3 = 0, second3 = 0) {
4277
+ const zone = resolveZone(timezone);
4278
+ const local = Date.UTC(year3, month3 - 1, day, hour3, minute3, second3);
4279
+ if (zone.kind === "fixed") return local - zone.offsetMs;
4280
+ const before = offsetAt(zone, local - MS_PER_DAY);
4281
+ const after = offsetAt(zone, local + MS_PER_DAY);
4282
+ const candidates = [local - before, local - after].filter((t2) => local - offsetAt(zone, t2) === t2);
4283
+ if (candidates.length) return Math.max(...candidates);
4284
+ return local - before;
4521
4285
  }
4522
- function endswith(source, str2) {
4523
- return source.endsWith(str2);
4286
+
4287
+ // src/time/datestring.ts
4288
+ var MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
4289
+ var FULL_MONTHS = [
4290
+ "january",
4291
+ "february",
4292
+ "march",
4293
+ "april",
4294
+ "may",
4295
+ "june",
4296
+ "july",
4297
+ "august",
4298
+ "september",
4299
+ "october",
4300
+ "november",
4301
+ "december"
4302
+ ];
4303
+ var ZONE_NAMES = {
4304
+ ut: 0,
4305
+ utc: 0,
4306
+ gmt: 0,
4307
+ est: -5,
4308
+ edt: -4,
4309
+ cst: -6,
4310
+ cdt: -5,
4311
+ mst: -7,
4312
+ mdt: -6,
4313
+ pst: -8,
4314
+ pdt: -7
4315
+ };
4316
+ var TIME = String.raw`(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3})\d*)?)?`;
4317
+ var ZONE = String.raw`(ut|utc|gmt|[ecmp][sd]t|(?:utc|gmt)?\s*[+-]\d{1,2}(?::?\d{2})?)`;
4318
+ var NUMERIC_ZONE = String.raw`([+-]\d{2}(?::?\d{2})?)`;
4319
+ var DATE = String.raw`(\d{4})([-/])(\d{1,2})\2(\d{1,2})`;
4320
+ var WEEKDAY = String.raw`(?:[a-z]{3,9},?\s+)?`;
4321
+ var ISO_T = new RegExp(String.raw`^${DATE}t${TIME}\s*${NUMERIC_ZONE}?$`, "i");
4322
+ var ISO = new RegExp(String.raw`^${DATE}(?:\s+${TIME})?\s*${ZONE}?$`, "i");
4323
+ var DAY_FIRST = new RegExp(String.raw`^${WEEKDAY}(\d{1,2})(?:\s+|-)([a-z]+)\.?,?(?:\s+|-)(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
4324
+ var MONTH_FIRST = new RegExp(String.raw`^${WEEKDAY}([a-z]+)\.?\s+(\d{1,2}),?\s+(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
4325
+ function monthNumber(name) {
4326
+ const lower2 = name.toLowerCase();
4327
+ const i = FULL_MONTHS.findIndex((full, k) => lower2.startsWith(MONTHS[k]) && full.startsWith(lower2));
4328
+ return i + 1;
4524
4329
  }
4525
- function charAt(str2, pos2) {
4526
- return str2.charAt(pos2);
4330
+ function zoneMinutes(zone) {
4331
+ if (!zone) return 0;
4332
+ const lower2 = zone.toLowerCase().replace(/\s+/g, "");
4333
+ const named = ZONE_NAMES[lower2];
4334
+ if (named !== void 0) return named * 60;
4335
+ const m = /^(?:utc|gmt)?([+-])(\d{1,2})(?::?(\d{2}))?$/.exec(lower2);
4336
+ if (!m) return NaN;
4337
+ const minutes = Number(m[2]) * 60 + (m[3] ? Number(m[3]) : 0);
4338
+ return m[1] === "-" ? -minutes : minutes;
4527
4339
  }
4528
- function trim(str2) {
4529
- return str2.trim();
4340
+ function parseDateString(dateString) {
4341
+ const text2 = dateString.trim();
4342
+ let year3, month3, day;
4343
+ let rest;
4344
+ let m;
4345
+ if (m = ISO_T.exec(text2) ?? ISO.exec(text2)) {
4346
+ [year3, month3, day] = [m[1], Number(m[3]), m[4]];
4347
+ rest = m.slice(5);
4348
+ } else if (m = DAY_FIRST.exec(text2)) {
4349
+ [day, month3, year3] = [m[1], monthNumber(m[2]), m[3]];
4350
+ rest = m.slice(4);
4351
+ } else if (m = MONTH_FIRST.exec(text2)) {
4352
+ [month3, day, year3] = [monthNumber(m[1]), m[2], m[3]];
4353
+ rest = m.slice(4);
4354
+ } else {
4355
+ throw new SyntaxError(`Invalid date string "${dateString}": use RFC 2822 or ISO 8601 ("20 Feb 2020", "2020-02-20T14:48:00")`);
4356
+ }
4357
+ const [hour3 = "0", minute3 = "0", second3 = "0", millis = "0", zone] = rest;
4358
+ const d = Number(day);
4359
+ const h = Number(hour3);
4360
+ const mi = Number(minute3);
4361
+ const s = Number(second3);
4362
+ const offset = zoneMinutes(zone);
4363
+ const endOfDay = h === 24 && mi === 0 && s === 0 && Number(millis) === 0;
4364
+ if (month3 < 1 || month3 > 12 || d < 1 || d > 31 || h > 23 && !endOfDay || mi > 59 || s > 59 || Number.isNaN(offset)) {
4365
+ throw new SyntaxError(`Invalid date string "${dateString}": a field is out of range`);
4366
+ }
4367
+ const ms = Number(millis.padEnd(3, "0"));
4368
+ return Date.UTC(Number(year3), month3 - 1, d, h, mi, s, ms) - offset * 6e4;
4530
4369
  }
4531
- function trimLeft(str2) {
4532
- return str2.trimStart();
4370
+
4371
+ // src/time/session.ts
4372
+ var PERIOD = /^(\d{2})(\d{2})-(\d{2})(\d{2})$/;
4373
+ var sessions = /* @__PURE__ */ new Map();
4374
+ function parseSession(session2) {
4375
+ let parsed = sessions.get(session2);
4376
+ if (parsed) return parsed;
4377
+ const invalid = () => new SyntaxError(`Invalid session "${session2}": expected "HHmm-HHmm[,HHmm-HHmm...][:days]"`);
4378
+ const text2 = session2.trim() === "24x7" ? "0000-0000" : session2.replace(/\s+/g, "");
4379
+ const [periodText = "", dayText, extra] = text2.split(":");
4380
+ if (extra !== void 0 || periodText === "") throw invalid();
4381
+ const periods = periodText.split(",").map((p) => {
4382
+ const m = PERIOD.exec(p);
4383
+ if (!m) throw invalid();
4384
+ const [h1, m1, h2, m2] = [m[1], m[2], m[3], m[4]].map(Number);
4385
+ if (h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) throw invalid();
4386
+ return { start: h1 * 60 + m1, end: h2 * 60 + m2 };
4387
+ });
4388
+ const dayDigits = dayText ?? (periods.length > 1 ? "23456" : "1234567");
4389
+ if (!/^[1-7]+$/.test(dayDigits)) throw invalid();
4390
+ const days = new Array(8).fill(false);
4391
+ for (const d of dayDigits) days[Number(d)] = true;
4392
+ parsed = { periods, days };
4393
+ sessions.set(session2, parsed);
4394
+ return parsed;
4533
4395
  }
4534
- function trimRight(str2) {
4535
- return str2.trimEnd();
4396
+ function inSession(time2, session2, timezone) {
4397
+ if (Number.isNaN(time2)) return false;
4398
+ const { periods, days } = parseSession(session2);
4399
+ const f = zonedFields(time2, timezone);
4400
+ const minute3 = f.hour * 60 + f.minute;
4401
+ const nextDay = f.dayOfWeek % 7 + 1;
4402
+ for (const { start, end } of periods) {
4403
+ if (start < end || start === 0 && end === 0) {
4404
+ if (minute3 >= start && (minute3 < end || end === 0) && days[f.dayOfWeek]) return true;
4405
+ } else {
4406
+ if (minute3 >= start && days[nextDay]) return true;
4407
+ if (minute3 < end && days[f.dayOfWeek]) return true;
4408
+ }
4409
+ }
4410
+ return false;
4536
4411
  }
4537
- function match(source, regex) {
4538
- return new RegExp(regex).test(source);
4412
+
4413
+ // src/time/index.ts
4414
+ function timestamp(first, ...rest) {
4415
+ if (typeof first === "string") {
4416
+ if (rest.length === 0) return parseDateString(first);
4417
+ const [year3, month4, day2, hour4, minute4, second4] = rest;
4418
+ return zonedToUnix(first, year3, month4, day2, hour4 ?? 0, minute4 ?? 0, second4 ?? 0);
4419
+ }
4420
+ const [month3, day, hour3, minute3, second3] = rest;
4421
+ return new Date(first, month3 - 1, day, hour3 || 0, minute3 || 0, second3 || 0).getTime();
4539
4422
  }
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);
4423
+ function field(time2, timezone, pick) {
4424
+ return Number.isNaN(time2) ? NaN : pick(zonedFields(time2, timezone));
4544
4425
  }
4545
- function format_time(time2, format2 = "yyyy-MM-dd'T'HH:mm:ssZ", timezone = "Etc/UTC") {
4546
- return formatDate(time2, format2, timezone);
4426
+ function year(time2, timezone) {
4427
+ return field(time2, timezone, (f) => f.year);
4547
4428
  }
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]) };
4429
+ function month(time2, timezone) {
4430
+ return field(time2, timezone, (f) => f.month);
4563
4431
  }
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
- };
4432
+ function dayofmonth(time2, timezone) {
4433
+ return field(time2, timezone, (f) => f.day);
4434
+ }
4435
+ function dayofweek(time2, timezone) {
4436
+ return field(time2, timezone, (f) => f.dayOfWeek);
4437
+ }
4438
+ function weekofyear(time2, timezone) {
4439
+ return field(time2, timezone, (f) => {
4440
+ const date = Date.UTC(f.year, f.month - 1, f.day);
4441
+ const mondayBased = (f.dayOfWeek + 5) % 7;
4442
+ const thursday = new Date(date + (3 - mondayBased) * 864e5);
4443
+ const yearStart = Date.UTC(thursday.getUTCFullYear(), 0, 1);
4444
+ return 1 + Math.floor((thursday.getTime() - yearStart) / (7 * 864e5));
4579
4445
  });
4580
4446
  }
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 };
4447
+ function hour(time2, timezone) {
4448
+ return field(time2, timezone, (f) => f.hour);
4590
4449
  }
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);
4611
- }
4612
- const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? "").split(",");
4613
- this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));
4614
- }
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 };
4450
+ function minute(time2, timezone) {
4451
+ return field(time2, timezone, (f) => f.minute);
4452
+ }
4453
+ function second(time2, timezone) {
4454
+ return field(time2, timezone, (f) => f.second);
4455
+ }
4456
+ function inSession2(time2, session2, timezone) {
4457
+ return inSession(time2, session2, timezone);
4458
+ }
4459
+
4460
+ // src/str/index.ts
4461
+ var str_exports = {};
4462
+ __export(str_exports, {
4463
+ charAt: () => charAt,
4464
+ concat: () => concat,
4465
+ contains: () => contains,
4466
+ endswith: () => endswith,
4467
+ format: () => format,
4468
+ format_time: () => format_time,
4469
+ length: () => length,
4470
+ lower: () => lower,
4471
+ match: () => match,
4472
+ pos: () => pos,
4473
+ repeat: () => repeat,
4474
+ replace: () => replace,
4475
+ replace_all: () => replace_all,
4476
+ split: () => split,
4477
+ startswith: () => startswith,
4478
+ substring: () => substring,
4479
+ tonumber: () => tonumber,
4480
+ tostring: () => tostring,
4481
+ trim: () => trim,
4482
+ trimLeft: () => trimLeft,
4483
+ trimRight: () => trimRight,
4484
+ upper: () => upper
4485
+ });
4486
+
4487
+ // src/str/numberformat.ts
4488
+ var patterns = /* @__PURE__ */ new Map();
4489
+ function parsePattern(pattern) {
4490
+ const cached = patterns.get(pattern);
4491
+ if (cached) return cached;
4492
+ let prefix = "";
4493
+ let suffix = "";
4494
+ let number2 = "";
4495
+ let percent = false;
4496
+ let phase = "prefix";
4497
+ for (let i = 0; i < pattern.length; i++) {
4498
+ const ch = pattern[i];
4499
+ let literal;
4500
+ if (ch === "'") {
4501
+ const end = pattern.indexOf("'", i + 1);
4502
+ if (end === i + 1) {
4503
+ literal = "'";
4504
+ i = end;
4505
+ } else {
4506
+ literal = pattern.slice(i + 1, end === -1 ? void 0 : end);
4507
+ i = end === -1 ? pattern.length : end;
4508
+ }
4509
+ } else if (phase !== "suffix" && ("#0,.".includes(ch) || phase === "number" && ch === "E")) {
4510
+ phase = "number";
4511
+ number2 += ch;
4512
+ continue;
4513
+ } else {
4514
+ if (ch === "%") percent = true;
4515
+ literal = ch;
4665
4516
  }
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;
4517
+ if (phase === "number") phase = "suffix";
4518
+ if (phase === "prefix") prefix += literal;
4519
+ else suffix += literal;
4677
4520
  }
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 };
4521
+ const [mantissa = "", exp2] = number2.split("E");
4522
+ const [intPart = "", fracPart = ""] = mantissa.split(".");
4523
+ const lastComma = intPart.lastIndexOf(",");
4524
+ const parsed = {
4525
+ prefix,
4526
+ suffix,
4527
+ // as in Java, a pattern with a decimal point and no '0' at all ("#.##") shows one integer digit
4528
+ minInt: !mantissa.includes("0") && mantissa.includes(".") ? 1 : [...intPart].filter((c) => c === "0").length,
4529
+ minFrac: [...fracPart].filter((c) => c === "0").length,
4530
+ maxFrac: [...fracPart].filter((c) => c === "0" || c === "#").length,
4531
+ grouping: lastComma === -1 ? 0 : intPart.length - lastComma - 1,
4532
+ percent,
4533
+ exponent: exp2 === void 0 ? null : exp2.length,
4534
+ hasDigits: /[0#]/.test(mantissa)
4535
+ };
4536
+ patterns.set(pattern, parsed);
4537
+ return parsed;
4686
4538
  }
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);
4539
+ function shortestDigits(x) {
4540
+ const m = /^(\d+)(?:\.(\d+))?(?:e([+-]\d+))?$/.exec(String(Math.abs(x)));
4541
+ const all7 = m[1] + (m[2] ?? "");
4542
+ let intLen = m[1].length + Number(m[3] ?? 0);
4543
+ const lead = all7.length - all7.replace(/^0+/, "").length;
4544
+ intLen -= lead;
4545
+ const digits = all7.slice(lead).replace(/0+$/, "");
4546
+ return { digits, intLen: digits ? intLen : 0 };
4547
+ }
4548
+ function exactDigits(x) {
4549
+ const abs2 = Math.abs(x);
4550
+ if (abs2 === 0) return { digits: "", intLen: 0 };
4551
+ const view = new DataView(new ArrayBuffer(8));
4552
+ view.setFloat64(0, abs2);
4553
+ const bits = view.getBigUint64(0);
4554
+ const expBits = Number(bits >> 52n & 0x7ffn);
4555
+ let mant = bits & 0xfffffffffffffn;
4556
+ const e2 = expBits === 0 ? -1074 : expBits - 1075;
4557
+ if (expBits !== 0) mant |= 1n << 52n;
4558
+ let str2;
4559
+ let fracDigits;
4560
+ if (e2 >= 0) {
4561
+ str2 = (mant << BigInt(e2)).toString();
4562
+ fracDigits = 0;
4563
+ } else {
4564
+ str2 = (mant * 5n ** BigInt(-e2)).toString();
4565
+ fracDigits = -e2;
4697
4566
  }
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);
4708
- }
4709
- this.yearDays.set(year3, days);
4710
- }
4711
- return days;
4567
+ const intLen = str2.length - fracDigits;
4568
+ const lead = str2.length - str2.replace(/^0+/, "").length;
4569
+ const digits = str2.slice(lead).replace(/0+$/, "");
4570
+ return { digits, intLen: intLen - lead };
4571
+ }
4572
+ function roundDigits(d, frac, mode3) {
4573
+ const keep = d.intLen + frac;
4574
+ if (keep >= d.digits.length) return d;
4575
+ if (keep < 0) return { digits: "", intLen: 0 };
4576
+ const first = d.digits.charCodeAt(keep) - 48;
4577
+ let up;
4578
+ if (mode3 === "halfUpShortest") up = first >= 5;
4579
+ else if (first !== 5) up = first > 5;
4580
+ else if (d.digits.length > keep + 1) up = true;
4581
+ else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
4582
+ let kept = d.digits.slice(0, keep);
4583
+ let intLen = d.intLen;
4584
+ if (up) {
4585
+ const n = (BigInt(kept || "0") + 1n).toString();
4586
+ if (n.length > kept.length) intLen += 1;
4587
+ kept = n;
4712
4588
  }
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)];
4589
+ kept = kept.replace(/0+$/, "");
4590
+ return { digits: kept, intLen: kept ? intLen : 0 };
4591
+ }
4592
+ function group(int, size) {
4593
+ if (!size || int.length <= size) return int;
4594
+ const parts = [];
4595
+ for (let end = int.length; end > 0; end -= size) parts.unshift(int.slice(Math.max(0, end - size), end));
4596
+ return parts.join(",");
4597
+ }
4598
+ function layout(d, p) {
4599
+ let int = d.intLen > 0 ? d.digits.slice(0, d.intLen).padEnd(d.intLen, "0") : "";
4600
+ int = int.replace(/^0+/, "").padStart(p.minInt, "0");
4601
+ let frac = d.intLen < 0 ? "0".repeat(-d.intLen) + d.digits : d.digits.slice(Math.max(0, d.intLen));
4602
+ frac = frac.slice(0, p.maxFrac).replace(/0+$/, "").padEnd(p.minFrac, "0");
4603
+ if (!int && !frac) int = "0";
4604
+ return group(int, p.grouping) + (frac ? "." + frac : "");
4605
+ }
4606
+ function formatNumber(value, pattern, mode3) {
4607
+ if (Number.isNaN(value)) return "NaN";
4608
+ const p = parsePattern(pattern);
4609
+ if (!Number.isFinite(value)) return (value < 0 ? "-" : "") + p.prefix + "\u221E" + p.suffix;
4610
+ const x = p.percent ? value * 100 : value;
4611
+ const toDigits = mode3 === "halfUpShortest" ? shortestDigits : exactDigits;
4612
+ let body;
4613
+ let isZero;
4614
+ if (p.exponent !== null) {
4615
+ const rounded = roundDigits(toDigits(x), p.maxFrac, mode3);
4616
+ const intDigits = Math.max(p.minInt, 1);
4617
+ isZero = rounded.digits === "";
4618
+ let exponent = isZero ? 0 : rounded.intLen - intDigits;
4619
+ let mant = roundDigits({ digits: rounded.digits, intLen: isZero ? 0 : intDigits }, p.maxFrac, mode3);
4620
+ if (mant.intLen > intDigits) {
4621
+ exponent += 1;
4622
+ mant = { digits: mant.digits, intLen: intDigits };
4735
4623
  }
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];
4624
+ const e2 = String(Math.abs(exponent)).padStart(p.exponent, "0");
4625
+ body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e2;
4626
+ } else {
4627
+ const rounded = roundDigits(toDigits(x), p.hasDigits ? p.maxFrac : 0, mode3);
4628
+ isZero = rounded.digits === "";
4629
+ body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });
4748
4630
  }
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);
4631
+ const negative = x < 0 && (!isZero || !p.hasDigits);
4632
+ return (negative ? "-" : "") + p.prefix + body + p.suffix;
4633
+ }
4634
+
4635
+ // src/str/dateformat.ts
4636
+ var MONTHS2 = [
4637
+ "January",
4638
+ "February",
4639
+ "March",
4640
+ "April",
4641
+ "May",
4642
+ "June",
4643
+ "July",
4644
+ "August",
4645
+ "September",
4646
+ "October",
4647
+ "November",
4648
+ "December"
4649
+ ];
4650
+ var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
4651
+ var pad = (n, width) => String(n).padStart(width, "0");
4652
+ function zoneName(time2, timezone, offsetMs) {
4653
+ if (timezone === "Etc/UTC") return "UTC";
4654
+ if (/^(UTC|GMT)/.test(timezone)) {
4655
+ if (offsetMs === 0) return "GMT";
4656
+ const minutes = Math.abs(offsetMs) / 6e4;
4657
+ return `GMT${offsetMs < 0 ? "-" : "+"}${pad(Math.floor(minutes / 60), 2)}:${pad(minutes % 60, 2)}`;
4764
4658
  }
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);
4659
+ const part = new Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(time2).find((p) => p.type === "timeZoneName");
4660
+ return part?.value ?? timezone;
4661
+ }
4662
+ function weekOfYear(year3, month3, day) {
4663
+ const date = Date.UTC(year3, month3 - 1, day);
4664
+ const dow = new Date(date).getUTCDay();
4665
+ const weekStart2 = date - dow * 864e5;
4666
+ if (weekStart2 + 6 * 864e5 >= Date.UTC(year3 + 1, 0, 1)) return 1;
4667
+ const jan1 = Date.UTC(year3, 0, 1);
4668
+ const jan1Dow = new Date(jan1).getUTCDay();
4669
+ return Math.floor((date - jan1 + jan1Dow * 864e5) / (7 * 864e5)) + 1;
4670
+ }
4671
+ function formatDate(time2, pattern, timezone) {
4672
+ if (Number.isNaN(time2)) return "NaN";
4673
+ const f = zonedFields(time2, timezone);
4674
+ const offset = zoneOffset(time2, timezone);
4675
+ const ms = (time2 % 1e3 + 1e3) % 1e3;
4676
+ const dayOfYear = (Date.UTC(f.year, f.month - 1, f.day) - Date.UTC(f.year, 0, 1)) / 864e5 + 1;
4677
+ let out = "";
4678
+ for (let i = 0; i < pattern.length; ) {
4679
+ const ch = pattern[i];
4680
+ if (ch === "'") {
4681
+ const end = pattern.indexOf("'", i + 1);
4682
+ if (end === i + 1) {
4683
+ out += "'";
4684
+ i += 2;
4685
+ } else {
4686
+ out += pattern.slice(i + 1, end === -1 ? void 0 : end);
4687
+ i = end === -1 ? pattern.length : end + 1;
4688
+ }
4689
+ continue;
4781
4690
  }
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);
4691
+ if (!/[A-Za-z]/.test(ch)) {
4692
+ out += ch;
4693
+ i++;
4694
+ continue;
4793
4695
  }
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);
4696
+ let n = 1;
4697
+ while (pattern[i + n] === ch) n++;
4698
+ i += n;
4699
+ const hour12 = f.hour % 12;
4700
+ switch (ch) {
4701
+ case "y":
4702
+ out += n === 2 ? pad(f.year % 100, 2) : pad(f.year, n);
4703
+ break;
4704
+ case "M":
4705
+ out += n >= 4 ? MONTHS2[f.month - 1] : n === 3 ? MONTHS2[f.month - 1].slice(0, 3) : pad(f.month, n);
4706
+ break;
4707
+ case "d":
4708
+ out += pad(f.day, n);
4709
+ break;
4710
+ case "E":
4711
+ out += n >= 4 ? DAYS[f.dayOfWeek - 1] : DAYS[f.dayOfWeek - 1].slice(0, 3);
4712
+ break;
4713
+ case "u":
4714
+ out += pad(f.dayOfWeek === 1 ? 7 : f.dayOfWeek - 1, n);
4715
+ break;
4716
+ case "D":
4717
+ out += pad(dayOfYear, n);
4718
+ break;
4719
+ case "w":
4720
+ out += pad(weekOfYear(f.year, f.month, f.day), n);
4721
+ break;
4722
+ case "H":
4723
+ out += pad(f.hour, n);
4724
+ break;
4725
+ case "k":
4726
+ out += pad(f.hour === 0 ? 24 : f.hour, n);
4727
+ break;
4728
+ case "K":
4729
+ out += pad(hour12, n);
4730
+ break;
4731
+ case "h":
4732
+ out += pad(hour12 === 0 ? 12 : hour12, n);
4733
+ break;
4734
+ case "a":
4735
+ out += f.hour < 12 ? "AM" : "PM";
4736
+ break;
4737
+ case "m":
4738
+ out += pad(f.minute, n);
4739
+ break;
4740
+ case "s":
4741
+ out += pad(f.second, n);
4742
+ break;
4743
+ case "S":
4744
+ out += pad(ms, n);
4745
+ break;
4746
+ case "Z": {
4747
+ const minutes = Math.abs(offset) / 6e4;
4748
+ out += (offset < 0 ? "-" : "+") + pad(Math.floor(minutes / 60), 2) + pad(minutes % 60, 2);
4749
+ break;
4750
+ }
4751
+ case "z":
4752
+ out += zoneName(time2, timezone, offset);
4753
+ break;
4754
+ default:
4755
+ throw new SyntaxError(`Unsupported date pattern letter "${ch}" in "${pattern}"`);
4808
4756
  }
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
4757
  }
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;
4758
+ return out;
4759
+ }
4760
+
4761
+ // src/str/messageformat.ts
4762
+ var DATE_STYLES = {
4763
+ short: "M/d/yy",
4764
+ medium: "MMM d, yyyy",
4765
+ long: "MMMM d, yyyy",
4766
+ full: "EEEE, MMMM d, yyyy"
4767
+ };
4768
+ var TIME_STYLES = {
4769
+ short: "h:mm\u202Fa",
4770
+ medium: "h:mm:ss\u202Fa",
4771
+ long: "h:mm:ss\u202Fa z",
4772
+ full: "h:mm:ss\u202Fa z"
4773
+ };
4774
+ function number(value, pattern) {
4775
+ return typeof value === "number" ? formatNumber(value, pattern, "halfEvenExact") : String(value);
4776
+ }
4777
+ function choice(value, spec) {
4778
+ const options = spec.split("|").map((part) => {
4779
+ const m = /^\s*(-?[\d.]+|-?∞)\s*([#<≤])(.*)$/.exec(part);
4780
+ if (!m) throw new SyntaxError(`Invalid choice format "${spec}"`);
4781
+ const limit = m[1].includes("\u221E") ? m[1].startsWith("-") ? -Infinity : Infinity : Number(m[1]);
4782
+ return { limit: m[2] === "<" ? limit + Number.EPSILON * Math.max(1, Math.abs(limit)) : limit, text: m[3] };
4783
+ });
4784
+ let chosen = options[0];
4785
+ for (const o of options) if (value >= o.limit) chosen = o;
4786
+ return chosen.text;
4787
+ }
4788
+ function formatArgument(value, type, style, timezone) {
4789
+ if (value === void 0) return "";
4790
+ if (!type) return typeof value === "number" ? number(value, "#,##0.###") : String(value);
4791
+ switch (type.trim()) {
4792
+ case "number": {
4793
+ const s = style?.trim();
4794
+ if (!s) return number(value, "#,##0.###");
4795
+ if (s === "integer") return number(value, "#,##0");
4796
+ if (s === "percent") return number(value, "#,##0%");
4797
+ if (s === "currency") return number(value, "'$'#,##0.00");
4798
+ return number(value, style);
4799
+ }
4800
+ case "date":
4801
+ case "time": {
4802
+ const styles = type.trim() === "date" ? DATE_STYLES : TIME_STYLES;
4803
+ const s = style?.trim() ?? "medium";
4804
+ return formatDate(Number(value), styles[s] ?? style, typeof timezone === "function" ? timezone() : timezone);
4805
+ }
4806
+ case "choice":
4807
+ return choice(Number(value), style ?? "");
4808
+ default:
4809
+ throw new SyntaxError(`Unknown format type "${type}"`);
4827
4810
  }
4828
- };
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;
4811
+ }
4812
+ function formatMessage(pattern, args, timezone) {
4813
+ let out = "";
4814
+ for (let i = 0; i < pattern.length; ) {
4815
+ const ch = pattern[i];
4816
+ if (ch === "'") {
4817
+ if (pattern[i + 1] === "'") {
4818
+ out += "'";
4819
+ i += 2;
4820
+ continue;
4821
+ }
4822
+ const end = pattern.indexOf("'", i + 1);
4823
+ out += pattern.slice(i + 1, end === -1 ? void 0 : end);
4824
+ i = end === -1 ? pattern.length : end + 1;
4848
4825
  continue;
4849
4826
  }
4850
- const at = bars2.calendar.locate(t2);
4851
- if (!at) {
4852
- previousPeriod = previousRegular = "";
4827
+ if (ch !== "{") {
4828
+ out += ch;
4829
+ i++;
4853
4830
  continue;
4854
4831
  }
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];
4832
+ let depth = 1;
4833
+ let j = i + 1;
4834
+ let inQuote = false;
4835
+ for (; j < pattern.length && depth > 0; j++) {
4836
+ const c = pattern[j];
4837
+ if (c === "'") inQuote = !inQuote;
4838
+ else if (!inQuote && c === "{") depth++;
4839
+ else if (!inQuote && c === "}") depth--;
4867
4840
  }
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;
4841
+ const inner = pattern.slice(i + 1, j - 1);
4842
+ const [index = "", type, ...rest] = inner.split(",");
4843
+ const style = rest.length ? rest.join(",") : void 0;
4844
+ const n = Number(index.trim());
4845
+ out += n < args.length ? formatArgument(args[n], type, style, timezone) : `{${inner}}`;
4846
+ i = j;
4873
4847
  }
4874
4848
  return out;
4875
4849
  }
4876
4850
 
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);
4851
+ // src/str/index.ts
4852
+ function length(str2) {
4853
+ return str2.length;
4854
+ }
4855
+ function tostring(value, format2) {
4856
+ if (typeof value !== "number") return String(value);
4857
+ if (!Number.isFinite(value)) return String(value);
4858
+ if (format2 === void 0 || format2 === "") {
4859
+ if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
4860
+ return String(value).replace("e+", "E").replace(/\.0+E/, "E");
4891
4861
  }
4892
- group2.push(out.length - 1);
4893
- });
4894
- return { bars: out, group: group2 };
4862
+ return formatNumber(value, "#.##########", "halfUpShortest");
4863
+ }
4864
+ switch (format2) {
4865
+ case "percent":
4866
+ return formatNumber(value, "0.00", "halfUpShortest") + "%";
4867
+ case "volume": {
4868
+ const abs2 = Math.abs(value);
4869
+ for (const [size, unit] of [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]]) {
4870
+ if (abs2 >= size) return formatNumber(value / size, "#.###", "halfUpShortest") + unit;
4871
+ }
4872
+ return formatNumber(value, "#", "halfUpShortest");
4873
+ }
4874
+ case "mintick":
4875
+ throw new Error("str.tostring(x, format.mintick) needs the symbol tick size (syminfo.mintick), which is not known here");
4876
+ default:
4877
+ return formatNumber(value, format2, "halfUpShortest");
4878
+ }
4895
4879
  }
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 };
4880
+ function tonumber(str2) {
4881
+ const num = parseFloat(str2);
4882
+ return isNaN(num) ? null : num;
4909
4883
  }
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
- });
4884
+ function substring(str2, begin, end) {
4885
+ return str2.substring(begin, end);
4920
4886
  }
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;
4887
+ function upper(str2) {
4888
+ return str2.toUpperCase();
4930
4889
  }
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);
4890
+ function lower(str2) {
4891
+ return str2.toLowerCase();
4892
+ }
4893
+ function contains(source, str2) {
4894
+ return source.includes(str2);
4895
+ }
4896
+ function pos(source, str2) {
4897
+ return source.indexOf(str2);
4898
+ }
4899
+ function replace(source, target, replacement, occurrence) {
4900
+ if (occurrence === 0) {
4901
+ return source.replace(target, replacement);
4902
+ } else {
4903
+ return source.replaceAll(target, replacement);
4975
4904
  }
4976
- return Math.abs(value);
4977
4905
  }
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));
4987
- }
4988
- return Series.fromArray(bars2, result);
4989
- }
4990
- return Math.ceil(value);
4906
+ function replace_all(source, target, replacement) {
4907
+ return source.replaceAll(target, replacement);
4908
+ }
4909
+ function split(str2, separator) {
4910
+ return str2.split(separator);
4911
+ }
4912
+ function concat(...strings) {
4913
+ return strings.join("");
4914
+ }
4915
+ function format(formatStr, ...args) {
4916
+ return formatMessage(formatStr, args, "Etc/UTC");
4917
+ }
4918
+ function startswith(source, str2) {
4919
+ return source.startsWith(str2);
4920
+ }
4921
+ function endswith(source, str2) {
4922
+ return source.endsWith(str2);
4923
+ }
4924
+ function charAt(str2, pos2) {
4925
+ return str2.charAt(pos2);
4926
+ }
4927
+ function trim(str2) {
4928
+ return str2.trim();
4929
+ }
4930
+ function trimLeft(str2) {
4931
+ return str2.trimStart();
4991
4932
  }
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);
4933
+ function trimRight(str2) {
4934
+ return str2.trimEnd();
5005
4935
  }
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
- }
5023
- }
5024
- return Series.fromArray(bars2, result);
5025
- }
5026
- if (precision === void 0) {
5027
- return Math.round(value);
5028
- }
5029
- const multiplier = Math.pow(10, precision);
5030
- return Math.round(value * multiplier) / multiplier;
4936
+ function match(source, regex) {
4937
+ return new RegExp(regex).test(source);
5031
4938
  }
5032
- function max3(...values) {
5033
- const hasSeries = values.some((v) => v instanceof Series);
5034
- if (!hasSeries) {
5035
- return Math.max(...values);
5036
- }
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));
5049
- }
5050
- return Series.fromArray(bars2, result);
4939
+ function repeat(source, count, separator = "") {
4940
+ if (source == null) return null;
4941
+ if (count <= 0) return "";
4942
+ return Array(count).fill(source).join(separator);
5051
4943
  }
5052
- function min3(...values) {
5053
- const hasSeries = values.some((v) => v instanceof Series);
5054
- if (!hasSeries) {
5055
- return Math.min(...values);
5056
- }
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));
5069
- }
5070
- return Series.fromArray(bars2, result);
4944
+ function format_time(time2, format2 = "yyyy-MM-dd'T'HH:mm:ssZ", timezone = "Etc/UTC") {
4945
+ return formatDate(time2, format2, timezone);
5071
4946
  }
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);
5090
- }
5091
- return Series.fromArray(bars2, result);
4947
+
4948
+ // src/session/calendar.ts
4949
+ var toDateNum = (y, m, d) => y * 1e4 + m * 100 + d;
4950
+ var utcOf = (date) => Date.UTC(Math.floor(date / 1e4), Math.floor(date / 100) % 100 - 1, date % 100);
4951
+ var fromUtc = (ms) => {
4952
+ const d = new Date(ms);
4953
+ return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
4954
+ };
4955
+ var addDays = (date, n) => fromUtc(utcOf(date) + n * 864e5);
4956
+ var weekdayOf = (date) => new Date(utcOf(date)).getUTCDay() + 1;
4957
+ var dateUtcMs = utcOf;
4958
+ function parseTime(token, spec) {
4959
+ const m = /^(\d{2})(\d{2})(?:F(\d+))?$/.exec(token.trim());
4960
+ if (!m) throw new SyntaxError(`Invalid session "${spec}"`);
4961
+ return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === void 0 ? void 0 : Number(m[3]) };
5092
4962
  }
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();
4963
+ function parsePeriods(text2, spec) {
4964
+ if (text2.trim() === "24x7") return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];
4965
+ return text2.split(",").map((p) => {
4966
+ const [a = "", b2 = ""] = p.split("-");
4967
+ const s = parseTime(a, spec);
4968
+ const e2 = parseTime(b2, spec);
4969
+ if (s.minutes === 0 && e2.minutes === 0 && s.offset === void 0 && e2.offset === void 0) {
4970
+ return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };
5103
4971
  }
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);
4972
+ return {
4973
+ start: s.minutes,
4974
+ end: e2.minutes,
4975
+ startOffset: s.offset ?? (e2.minutes <= s.minutes ? 1 : 0),
4976
+ endOffset: e2.offset ?? 0
4977
+ };
4978
+ });
4979
+ }
4980
+ function parseVersion(text2, from, spec, rule) {
4981
+ const [periodText = "", dayText] = text2.trim().split(":");
4982
+ const periods = parsePeriods(periodText, spec);
4983
+ const allDays = periodText.trim() === "24x7" || rule === "pine" && periods.length === 1;
4984
+ const digits = dayText ?? (allDays ? "1234567" : "23456");
4985
+ if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in "${spec}"`);
4986
+ const days = new Array(8).fill(false);
4987
+ for (const d of digits) days[Number(d)] = true;
4988
+ return { from, periods, days };
4989
+ }
4990
+ var TradingCalendar = class {
4991
+ /**
4992
+ * @param timezone - Time zone of the session hours (the exchange time zone)
4993
+ * @param spec - Session information (hours, corrections, holidays)
4994
+ * @param rule - Days without ":days": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)
4995
+ */
4996
+ constructor(timezone, spec, rule = "symbol") {
4997
+ this.timezone = timezone;
4998
+ const [first = "", ...changes] = spec.session.split("#");
4999
+ this.versions = [parseVersion(first, 0, spec.session, rule)];
5000
+ for (const change3 of changes) {
5001
+ const m = /^(\d{8})\/(.+)$/.exec(change3.trim());
5002
+ if (!m) throw new SyntaxError(`Invalid session change in "${spec.session}"`);
5003
+ this.versions.push(parseVersion(m[2], Number(m[1]), spec.session, rule));
5004
+ }
5005
+ for (const part of (spec.corrections ?? "").split(";").filter((p) => p.trim())) {
5006
+ const i = part.lastIndexOf(":");
5007
+ const text2 = part.slice(0, i).trim();
5008
+ const periods = text2 === "dayoff" ? null : parsePeriods(text2, part);
5009
+ for (const date of part.slice(i + 1).split(",")) this.corrections.set(Number(date), periods);
5110
5010
  }
5011
+ const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? "").split(",");
5012
+ this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));
5111
5013
  }
5112
- if (source instanceof Series) {
5113
- return Series.fromArray(source.bars, result);
5014
+ timezone;
5015
+ versions;
5016
+ corrections = /* @__PURE__ */ new Map();
5017
+ holidays;
5018
+ cache = /* @__PURE__ */ new Map();
5019
+ /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */
5020
+ periods(date) {
5021
+ let out = this.cache.get(date);
5022
+ if (out) return out;
5023
+ let periods = [];
5024
+ const correction = this.corrections.get(date);
5025
+ if (correction !== void 0) {
5026
+ periods = correction ?? [];
5027
+ } else if (!this.holidays.has(date)) {
5028
+ const version = [...this.versions].reverse().find((v) => v.from <= date);
5029
+ if (version.days[weekdayOf(date)]) periods = version.periods;
5030
+ }
5031
+ const y = Math.floor(date / 1e4);
5032
+ const m = Math.floor(date / 100) % 100;
5033
+ const d = date % 100;
5034
+ out = periods.map((p) => [
5035
+ zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),
5036
+ zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)
5037
+ ]);
5038
+ this.cache.set(date, out);
5039
+ return out;
5114
5040
  }
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));
5041
+ isTradingDay(date) {
5042
+ return this.periods(date).length > 0;
5043
+ }
5044
+ /**
5045
+ * Trading day and period of a time: the period that contains it, else the last period that started
5046
+ * before it. Null when no trading day is found near the time.
5047
+ */
5048
+ locate(time2) {
5049
+ const f = zonedFields(time2, this.timezone);
5050
+ const local = toDateNum(f.year, f.month, f.day);
5051
+ for (let k = 0; k <= 5; k++) {
5052
+ const date = addDays(local, k);
5053
+ const index = this.periods(date).findIndex(([s, e2]) => s <= time2 && time2 < e2);
5054
+ if (index !== -1) return { date, index, inside: true };
5055
+ }
5056
+ for (let k = 5; k >= -7; k--) {
5057
+ const date = addDays(local, k);
5058
+ const periods = this.periods(date);
5059
+ let index = -1;
5060
+ periods.forEach(([s], i) => {
5061
+ if (s <= time2) index = i;
5062
+ });
5063
+ if (index !== -1) return { date, index, inside: false };
5126
5064
  }
5127
- return Series.fromArray(bars2, result);
5065
+ return null;
5128
5066
  }
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);
5067
+ /** First trading day in [from, to] (dates), or null. */
5068
+ firstTradingDay(from, to) {
5069
+ for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;
5070
+ return null;
5136
5071
  }
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));
5072
+ /** Last trading day in [from, to] (dates), or null. */
5073
+ lastTradingDay(from, to) {
5074
+ for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;
5075
+ return null;
5146
5076
  }
5147
- return Series.fromArray(bars2, result);
5077
+ };
5078
+ var weekStart = (date) => addDays(date, -((weekdayOf(date) + 5) % 7));
5079
+ var monthStart = (date) => Math.floor(date / 100) * 100 + 1;
5080
+ var monthEnd = (date) => addDays(monthStart(addDays(monthStart(date), 32)), -1);
5081
+
5082
+ // src/session/bars.ts
5083
+ function unitOf(tf) {
5084
+ return { unit: tf.isdaily ? "D" : tf.isweekly ? "W" : "M", n: tf.multiplier };
5148
5085
  }
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));
5158
- }
5159
- return Series.fromArray(bars2, result);
5086
+ var yearOf = (date) => Math.floor(date / 1e4);
5087
+ var firstMonday = (year3) => {
5088
+ const jan1 = year3 * 1e4 + 101;
5089
+ const monday = weekStart(jan1);
5090
+ return monday === jan1 ? jan1 : addDays(monday, 7);
5091
+ };
5092
+ var SessionBars = class {
5093
+ constructor(calendar2, chartTimeframe2) {
5094
+ this.calendar = calendar2;
5095
+ this.chart = info(chartTimeframe2);
5160
5096
  }
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));
5097
+ calendar;
5098
+ chart;
5099
+ yearDays = /* @__PURE__ */ new Map();
5100
+ /** Trading days of a calendar year, in order. */
5101
+ tradingDaysOf(year3) {
5102
+ let days = this.yearDays.get(year3);
5103
+ if (!days) {
5104
+ days = [];
5105
+ for (let date = year3 * 1e4 + 101; yearOf(date) === year3; date = addDays(date, 1)) {
5106
+ if (this.calendar.isTradingDay(date)) days.push(date);
5107
+ }
5108
+ this.yearDays.set(year3, days);
5172
5109
  }
5173
- return Series.fromArray(bars2, result);
5110
+ return days;
5174
5111
  }
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));
5112
+ /**
5113
+ * Dates [from, to] of the `n`-`unit` period that contains `date` (for "D": its first and last trading days).
5114
+ * Periods restart each calendar year.
5115
+ */
5116
+ periodRange(date, unit, n) {
5117
+ if (unit === "D") {
5118
+ if (n === 1) return [date, date];
5119
+ const days = this.tradingDaysOf(yearOf(date));
5120
+ let k = days.findIndex((d) => d >= date);
5121
+ if (k === -1) return this.periodRange(yearOf(date) * 1e4 + 1e4 + 101, unit, n);
5122
+ k -= k % n;
5123
+ return [days[k], days[Math.min(k + n, days.length) - 1]];
5186
5124
  }
5187
- return Series.fromArray(bars2, result);
5188
- }
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));
5125
+ if (unit === "W") {
5126
+ const monday = weekStart(date);
5127
+ const year4 = yearOf(monday);
5128
+ const first = firstMonday(year4);
5129
+ const next = firstMonday(year4 + 1);
5130
+ const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 864e5));
5131
+ const from = addDays(first, (k - k % n) * 7);
5132
+ const to = addDays(from, n * 7 - 1);
5133
+ return [from, to < next ? to : addDays(next, -1)];
5200
5134
  }
5201
- return Series.fromArray(bars2, result);
5135
+ const year3 = yearOf(date);
5136
+ const month3 = Math.floor(date / 100) % 100;
5137
+ const firstMonth = month3 - (month3 - 1) % n;
5138
+ const lastMonth = Math.min(firstMonth + n - 1, 12);
5139
+ return [monthStart(year3 * 1e4 + firstMonth * 100 + 1), monthEnd(year3 * 1e4 + lastMonth * 100 + 1)];
5202
5140
  }
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));
5214
- }
5215
- return Series.fromArray(bars2, result);
5141
+ dayStart(date) {
5142
+ return this.calendar.periods(date)[0][0];
5216
5143
  }
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);
5144
+ dayEnd(date) {
5145
+ const p = this.calendar.periods(date);
5146
+ return p[p.length - 1][1];
5230
5147
  }
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));
5148
+ /** PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. */
5149
+ timeOf(time2, timeframe2) {
5150
+ const tf = info(timeframe2);
5151
+ const at = this.calendar.locate(time2);
5152
+ if (!at) return NaN;
5153
+ if (tf.isintraday) {
5154
+ if (this.chart.isdwm) return time2;
5155
+ const [start] = this.calendar.periods(at.date)[at.index];
5156
+ const ms = in_seconds(tf.period) * 1e3;
5157
+ return start + Math.floor((time2 - start) / ms) * ms;
5242
5158
  }
5243
- return Series.fromArray(bars2, result);
5159
+ const { unit, n } = unitOf(tf);
5160
+ const [from, to] = this.periodRange(at.date, unit, n);
5161
+ const first = this.calendar.firstTradingDay(from, to);
5162
+ return first === null ? NaN : this.dayStart(first);
5244
5163
  }
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));
5164
+ /** PineScript `time_close(tf)` for a bar that opens at `time`. */
5165
+ closeOf(time2, timeframe2) {
5166
+ const tf = info(timeframe2);
5167
+ const at = this.calendar.locate(time2);
5168
+ if (!at) return NaN;
5169
+ if (tf.isintraday) {
5170
+ const [, end] = this.calendar.periods(at.date)[at.index];
5171
+ if (this.chart.isdwm) return end;
5172
+ return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
5256
5173
  }
5257
- return Series.fromArray(bars2, result);
5258
- }
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));
5174
+ const { unit, n } = unitOf(tf);
5175
+ const [from, to] = this.periodRange(at.date, unit, n);
5176
+ if (this.chart.isintraday && (unit !== "D" || n > 1)) {
5177
+ const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);
5178
+ const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));
5179
+ return first === null ? NaN : this.dayStart(first);
5270
5180
  }
5271
- return Series.fromArray(bars2, result);
5181
+ const last = this.calendar.lastTradingDay(from, to);
5182
+ return last === null ? NaN : this.dayEnd(last);
5272
5183
  }
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));
5184
+ /** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */
5185
+ tradingDay(time2) {
5186
+ const at = this.calendar.locate(time2);
5187
+ if (!at) return null;
5188
+ if (this.chart.isdwm) {
5189
+ const { unit, n } = unitOf(this.chart);
5190
+ const [from, to] = this.periodRange(at.date, unit, n);
5191
+ return this.calendar.lastTradingDay(from, to);
5284
5192
  }
5285
- return Series.fromArray(bars2, result);
5193
+ return at.date;
5286
5194
  }
5287
- return degrees * (Math.PI / 180);
5288
- }
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));
5195
+ /**
5196
+ * End of the `timeframe` period that contains `time`: the intraday close, the session end of the day,
5197
+ * or the session end of the last trading day of the period. Used to know whether the last loaded
5198
+ * bar completes a higher-timeframe period (request.security).
5199
+ */
5200
+ periodEnd(time2, timeframe2) {
5201
+ const tf = info(timeframe2);
5202
+ const at = this.calendar.locate(time2);
5203
+ if (!at) return NaN;
5204
+ if (tf.isintraday) {
5205
+ const [, end] = this.calendar.periods(at.date)[at.index];
5206
+ return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
5298
5207
  }
5299
- return Series.fromArray(bars2, result);
5208
+ const { unit, n } = unitOf(tf);
5209
+ const [from, to] = this.periodRange(at.date, unit, n);
5210
+ const last = this.calendar.lastTradingDay(from, to);
5211
+ return last === null ? NaN : this.dayEnd(last);
5300
5212
  }
5301
- return radians * (180 / Math.PI);
5302
- }
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);
5213
+ /** PineScript `time_tradingday`: 00:00 UTC of the trading day. */
5214
+ tradingDayTime(time2) {
5215
+ const day = this.tradingDay(time2);
5216
+ return day === null ? NaN : dateUtcMs(day);
5307
5217
  }
5308
- return rand;
5309
- }
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);
5218
+ /** True when the bar at `time` is inside the session. */
5219
+ inside(time2) {
5220
+ return this.calendar.locate(time2)?.inside ?? false;
5221
+ }
5222
+ /** Session start and end of the trading day of `time`, or null. */
5223
+ dayBounds(time2) {
5224
+ const at = this.calendar.locate(time2);
5225
+ return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;
5226
+ }
5227
+ };
5228
+ function sessionFlags(times, bars2, regular) {
5229
+ const n = times.length;
5230
+ const flag2 = () => new Array(n).fill(false);
5231
+ const out = {
5232
+ isfirstbar: flag2(),
5233
+ islastbar: flag2(),
5234
+ ismarket: flag2(),
5235
+ ispremarket: flag2(),
5236
+ ispostmarket: flag2(),
5237
+ isfirstbar_regular: flag2(),
5238
+ islastbar_regular: flag2()
5239
+ };
5240
+ const chart2 = bars2.chart;
5241
+ let previousPeriod = "";
5242
+ let previousRegular = "";
5243
+ for (let i = 0; i < n; i++) {
5244
+ const t2 = times[i];
5245
+ if (chart2.isdwm) {
5246
+ for (const k of ["isfirstbar", "islastbar", "ismarket", "isfirstbar_regular", "islastbar_regular"]) out[k][i] = true;
5247
+ continue;
5319
5248
  }
5320
- return Series.fromArray(bars2, result);
5249
+ const at = bars2.calendar.locate(t2);
5250
+ if (!at) {
5251
+ previousPeriod = previousRegular = "";
5252
+ continue;
5253
+ }
5254
+ const close2 = bars2.closeOf(t2, chart2.period);
5255
+ const [, end] = bars2.calendar.periods(at.date)[at.index];
5256
+ const period = `${at.date}/${at.index}`;
5257
+ out.isfirstbar[i] = period !== previousPeriod;
5258
+ out.islastbar[i] = close2 >= end;
5259
+ const regularPeriods = regular.calendar.periods(at.date);
5260
+ const r2 = regularPeriods.findIndex(([s, e2]) => s <= t2 && t2 < e2);
5261
+ const market = r2 !== -1;
5262
+ out.ismarket[i] = market;
5263
+ if (!market && regularPeriods.length) {
5264
+ out.ispremarket[i] = t2 < regularPeriods[0][0];
5265
+ out.ispostmarket[i] = t2 >= regularPeriods[regularPeriods.length - 1][1];
5266
+ }
5267
+ const regularPeriod = market ? `${at.date}/${r2}` : "";
5268
+ out.isfirstbar_regular[i] = market && regularPeriod !== previousRegular;
5269
+ out.islastbar_regular[i] = market && close2 >= regularPeriods[r2][1];
5270
+ previousPeriod = period;
5271
+ previousRegular = regularPeriod;
5321
5272
  }
5322
- return value > 0 ? 1 : value < 0 ? -1 : 0;
5273
+ return out;
5323
5274
  }
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
- }
5275
+
5276
+ // src/security/resample.ts
5277
+ function resample(bars2, periodStart) {
5278
+ const out = [];
5279
+ const group2 = [];
5280
+ bars2.forEach((b2, i) => {
5281
+ const start = periodStart[i];
5282
+ const last = out[out.length - 1];
5283
+ if (!last || last.time !== start) {
5284
+ out.push({ time: start, open: b2.open, high: b2.high, low: b2.low, close: b2.close, volume: b2.volume ?? NaN });
5339
5285
  } 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
- }
5286
+ last.high = Math.max(last.high, b2.high);
5287
+ last.low = Math.min(last.low, b2.low);
5288
+ last.close = b2.close;
5289
+ last.volume = (last.volume ?? 0) + (b2.volume ?? NaN);
5348
5290
  }
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;
5291
+ group2.push(out.length - 1);
5292
+ });
5293
+ return { bars: out, group: group2 };
5363
5294
  }
5364
- var pi = Math.PI;
5365
- var e = Math.E;
5366
- var phi = 1.618033988749895;
5367
- var rphi = 0.618033988749895;
5295
+ function periodsOf(starts, dayPeriods, reaches) {
5296
+ const n = starts.length;
5297
+ const group2 = [];
5298
+ starts.forEach((s, i) => group2.push(i === 0 ? 0 : group2[i - 1] + (s !== starts[i - 1] ? 1 : 0)));
5299
+ const latest = [];
5300
+ starts.forEach((s, i) => {
5301
+ let done = i === 0 ? -1 : latest[i - 1];
5302
+ if (i > 0 && group2[i] !== group2[i - 1]) done = Math.max(done, group2[i] - 1);
5303
+ const ends = i < n - 1 ? starts[i + 1] !== s || dayPeriods[i + 1] !== dayPeriods[i] : true;
5304
+ if (ends && reaches[i]) done = Math.max(done, group2[i]);
5305
+ latest.push(done);
5306
+ });
5307
+ return { group: group2, latest };
5308
+ }
5309
+ function mapToChart(values, group2, latest, lookahead, gaps) {
5310
+ return group2.map((g2, i) => {
5311
+ if (lookahead) {
5312
+ const first = i === 0 || group2[i - 1] !== g2;
5313
+ return gaps && !first ? NaN : values[g2] ?? NaN;
5314
+ }
5315
+ const done = latest[i];
5316
+ if (done < 0 || gaps && i > 0 && latest[i - 1] === done) return NaN;
5317
+ return values[done] ?? NaN;
5318
+ });
5319
+ }
5320
+ function heikinAshi(bars2) {
5321
+ const out = [];
5322
+ bars2.forEach((b2, i) => {
5323
+ const close2 = (b2.open + b2.high + b2.low + b2.close) / 4;
5324
+ const prev = out[i - 1];
5325
+ const open2 = prev ? (prev.open + prev.close) / 2 : (b2.open + b2.close) / 2;
5326
+ 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 });
5327
+ });
5328
+ return out;
5329
+ }
5330
+
5331
+ // src/strategy/index.ts
5332
+ var STRATEGY_CONSTANTS = {
5333
+ long: "long",
5334
+ short: "short",
5335
+ fixed: "fixed",
5336
+ cash: "cash",
5337
+ percent_of_equity: "percent_of_equity",
5338
+ commission: { percent: "percent", cash_per_contract: "cash_per_contract", cash_per_order: "cash_per_order" },
5339
+ direction: { all: "all", long: "long", short: "short" },
5340
+ oca: { none: "none", cancel: "cancel", reduce: "reduce" }
5341
+ };
5342
+ var STRATEGY_DEFAULTS = {
5343
+ pyramiding: 0,
5344
+ calc_on_order_fills: false,
5345
+ calc_on_every_tick: false,
5346
+ backtest_fill_limits_assumption: 0,
5347
+ default_qty_type: "fixed",
5348
+ default_qty_value: 1,
5349
+ initial_capital: 1e6,
5350
+ slippage: 0,
5351
+ commission_type: "percent",
5352
+ commission_value: 0,
5353
+ process_orders_on_close: false,
5354
+ close_entries_rule: "FIFO",
5355
+ margin_long: 100,
5356
+ margin_short: 100,
5357
+ calc_bars_count: 0,
5358
+ risk_free_rate: 2,
5359
+ use_bar_magnifier: false,
5360
+ fill_orders_on_standard_ohlc: false
5361
+ };
5362
+ var STRATEGY_NUMBER_VARIABLES = [
5363
+ "position_size",
5364
+ "position_avg_price",
5365
+ "equity",
5366
+ "initial_capital",
5367
+ "netprofit",
5368
+ "netprofit_percent",
5369
+ "openprofit",
5370
+ "openprofit_percent",
5371
+ "grossprofit",
5372
+ "grossprofit_percent",
5373
+ "grossloss",
5374
+ "grossloss_percent",
5375
+ "opentrades",
5376
+ "closedtrades",
5377
+ "wintrades",
5378
+ "losstrades",
5379
+ "eventrades",
5380
+ "avg_trade",
5381
+ "avg_trade_percent",
5382
+ "avg_winning_trade",
5383
+ "avg_winning_trade_percent",
5384
+ "avg_losing_trade",
5385
+ "avg_losing_trade_percent",
5386
+ "max_drawdown",
5387
+ "max_drawdown_percent",
5388
+ "max_runup",
5389
+ "max_runup_percent",
5390
+ "max_contracts_held_all",
5391
+ "max_contracts_held_long",
5392
+ "max_contracts_held_short",
5393
+ "margin_liquidation_price"
5394
+ ];
5395
+ var STRATEGY_TEXT_VARIABLES = ["position_entry_name", "account_currency"];
5368
5396
 
5369
5397
  // src/script/index.ts
5370
5398
  var ctxBars = new BarData([]);
@@ -5406,9 +5434,11 @@ function slugId(title, fallback, taken) {
5406
5434
  taken.add(id);
5407
5435
  return id;
5408
5436
  }
5409
- function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
5437
+ var scriptOptions = {};
5438
+ function executeScript(body, barsInput, inputs = {}, chart2 = {}, options = {}) {
5410
5439
  ctxBars.setAll(barsInput);
5411
5440
  chartCtx = { ...chart2 };
5441
+ scriptOptions = options;
5412
5442
  ctx = freshCollector(inputs);
5413
5443
  resetDrawings();
5414
5444
  try {
@@ -5430,6 +5460,7 @@ function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
5430
5460
  barColorConfig: c.barColorConfig,
5431
5461
  defaultInputs: c.defaultInputs,
5432
5462
  alertConfig: c.alertConfig,
5463
+ strategyConfig: c.strategy?.properties,
5433
5464
  result: {
5434
5465
  metadata: { title: c.title, shorttitle: c.shortTitle, overlay: c.overlay, precision: c.precision },
5435
5466
  plots: c.plots,
@@ -5446,6 +5477,7 @@ function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
5446
5477
  }
5447
5478
  function indicator(title, options = {}) {
5448
5479
  const c = collector();
5480
+ declare(c, "indicator");
5449
5481
  c.title = title;
5450
5482
  c.shortTitle = options.shorttitle;
5451
5483
  c.overlay = options.overlay ?? true;
@@ -5458,6 +5490,12 @@ function indicator(title, options = {}) {
5458
5490
  polyline: options.max_polylines_count
5459
5491
  });
5460
5492
  }
5493
+ function declare(c, kind) {
5494
+ if (c.declaration) {
5495
+ throw new Error(`${kind}(): the script already declared ${c.declaration}(); a script has one declaration.`);
5496
+ }
5497
+ c.declaration = kind;
5498
+ }
5461
5499
  function registerInput(config, fallbackId) {
5462
5500
  const c = collector();
5463
5501
  const id = slugId(config.title, fallbackId, c.inputIds);
@@ -5800,6 +5838,139 @@ function eachBar(fn) {
5800
5838
  return Series.fromArray(ctxBars, out);
5801
5839
  }
5802
5840
  var currentBar = -1;
5841
+ function strategyRun(caller) {
5842
+ const run = collector().strategy;
5843
+ if (!run) throw new Error(`${caller}: declare the script with strategy() first.`);
5844
+ return run;
5845
+ }
5846
+ function strategyEngine(caller) {
5847
+ const run = strategyRun(caller);
5848
+ if (!run.engine) {
5849
+ throw new Error(
5850
+ `${caller}: no strategy engine; pass it as executeScript(body, bars, inputs, chart, { strategyEngine }).`
5851
+ );
5852
+ }
5853
+ return run.engine;
5854
+ }
5855
+ function orderEngine(caller) {
5856
+ const engine = strategyEngine(caller);
5857
+ if (!strategyRun(caller).inLoop) throw new Error(`${caller} must be called inside strategy.eachBar().`);
5858
+ return engine;
5859
+ }
5860
+ function given(options) {
5861
+ const out = {};
5862
+ for (const [key, value] of Object.entries(options)) {
5863
+ if (value === void 0 || typeof value === "number" && Number.isNaN(value)) continue;
5864
+ out[key] = value;
5865
+ }
5866
+ return out;
5867
+ }
5868
+ function strategyVariable(name) {
5869
+ const value = strategyEngine(`strategy.${name}`).get(name);
5870
+ if (value === void 0) throw new Error(`strategy.${name} is not provided by the strategy engine.`);
5871
+ return value;
5872
+ }
5873
+ function riskRule(rule) {
5874
+ const engine = strategyEngine(`strategy.risk.${rule.rule}()`);
5875
+ if (!engine.risk) throw new Error(`strategy.risk.${rule.rule}() is not provided by the strategy engine.`);
5876
+ engine.risk(rule);
5877
+ }
5878
+ function declareStrategy(title, options = {}) {
5879
+ const c = collector();
5880
+ declare(c, "strategy");
5881
+ c.title = title;
5882
+ c.shortTitle = options.shorttitle;
5883
+ c.overlay = options.overlay ?? false;
5884
+ c.precision = options.precision;
5885
+ c.format = options.format;
5886
+ setDrawingLimits({
5887
+ line: options.max_lines_count,
5888
+ label: options.max_labels_count,
5889
+ box: options.max_boxes_count,
5890
+ polyline: options.max_polylines_count
5891
+ });
5892
+ const properties = { ...STRATEGY_DEFAULTS };
5893
+ const target = properties;
5894
+ for (const key of [...Object.keys(STRATEGY_DEFAULTS), "currency"]) {
5895
+ const value = options[key];
5896
+ if (value !== void 0) target[key] = value;
5897
+ }
5898
+ const factory = scriptOptions.strategyEngine;
5899
+ c.strategy = {
5900
+ properties,
5901
+ engine: factory?.({ properties, bars: ctxBars.bars, chart: chartCtx }),
5902
+ looped: false,
5903
+ inLoop: false
5904
+ };
5905
+ }
5906
+ function strategyEachBar(fn) {
5907
+ const run = strategyRun("strategy.eachBar()");
5908
+ const engine = strategyEngine("strategy.eachBar()");
5909
+ if (run.looped) throw new Error("strategy.eachBar() runs once per script run.");
5910
+ run.looped = true;
5911
+ return eachBar((c) => {
5912
+ engine.processBar(c.i);
5913
+ run.inLoop = true;
5914
+ try {
5915
+ return fn(c);
5916
+ } finally {
5917
+ run.inLoop = false;
5918
+ engine.processClose(c.i);
5919
+ }
5920
+ });
5921
+ }
5922
+ var strategyMembers = {
5923
+ ...STRATEGY_CONSTANTS,
5924
+ eachBar: strategyEachBar,
5925
+ entry(id, direction, options = {}) {
5926
+ orderEngine("strategy.entry()").entry(id, direction, given(options));
5927
+ },
5928
+ order(id, direction, options = {}) {
5929
+ orderEngine("strategy.order()").order(id, direction, given(options));
5930
+ },
5931
+ exit(id, options = {}) {
5932
+ orderEngine("strategy.exit()").exit(id, given(options));
5933
+ },
5934
+ close(id, options = {}) {
5935
+ orderEngine("strategy.close()").close(id, given(options));
5936
+ },
5937
+ close_all(options = {}) {
5938
+ orderEngine("strategy.close_all()").close_all(given(options));
5939
+ },
5940
+ cancel(id) {
5941
+ orderEngine("strategy.cancel()").cancel(id);
5942
+ },
5943
+ cancel_all() {
5944
+ orderEngine("strategy.cancel_all()").cancel_all();
5945
+ },
5946
+ opentrade(index) {
5947
+ return strategyEngine("strategy.opentrade()").openTrade(index);
5948
+ },
5949
+ closedtrade(index) {
5950
+ return strategyEngine("strategy.closedtrade()").closedTrade(index);
5951
+ },
5952
+ default_entry_qty(fill_price) {
5953
+ const engine = strategyEngine("strategy.default_entry_qty()");
5954
+ if (!engine.defaultEntryQty) throw new Error("strategy.default_entry_qty() is not provided by the strategy engine.");
5955
+ return engine.defaultEntryQty(fill_price);
5956
+ },
5957
+ risk: {
5958
+ allow_entry_in: (direction) => riskRule({ rule: "allow_entry_in", direction }),
5959
+ max_cons_loss_days: (count, alert_message) => riskRule(given({ rule: "max_cons_loss_days", count, alert_message })),
5960
+ max_drawdown: (value, type, alert_message) => riskRule(given({ rule: "max_drawdown", value, type, alert_message })),
5961
+ max_intraday_filled_orders: (count, alert_message) => riskRule(given({ rule: "max_intraday_filled_orders", count, alert_message })),
5962
+ max_intraday_loss: (value, type, alert_message) => riskRule(given({ rule: "max_intraday_loss", value, type, alert_message })),
5963
+ max_position_size: (contracts) => riskRule({ rule: "max_position_size", contracts })
5964
+ }
5965
+ };
5966
+ var variableGetters = {};
5967
+ for (const name of [...STRATEGY_NUMBER_VARIABLES, ...STRATEGY_TEXT_VARIABLES]) {
5968
+ variableGetters[name] = { get: () => strategyVariable(name), enumerable: true };
5969
+ }
5970
+ var strategy = Object.defineProperties(
5971
+ Object.assign((title, options) => declareStrategy(title, options), strategyMembers),
5972
+ variableGetters
5973
+ );
5803
5974
  var chartCtx = {};
5804
5975
  function chartTimeframe() {
5805
5976
  collector();
@@ -5859,7 +6030,24 @@ var timeframe = {
5859
6030
  return in_seconds(tf === void 0 || tf === "" ? chartTimeframe().period : tf);
5860
6031
  }
5861
6032
  };
6033
+ function symbolProperty(key) {
6034
+ collector();
6035
+ const value = chartCtx[key];
6036
+ if (value === void 0) {
6037
+ throw new Error(`syminfo.${key} is not known: pass it as executeScript(body, bars, inputs, { ${key} }).`);
6038
+ }
6039
+ return value;
6040
+ }
5862
6041
  var syminfo = {
6042
+ get mintick() {
6043
+ return symbolProperty("mintick");
6044
+ },
6045
+ get pointvalue() {
6046
+ return symbolProperty("pointvalue");
6047
+ },
6048
+ get mincontract() {
6049
+ return symbolProperty("mincontract");
6050
+ },
5863
6051
  get timezone() {
5864
6052
  return exchangeTimezone();
5865
6053
  },
@@ -6128,3 +6316,10 @@ function alertcondition(condition, title, message) {
6128
6316
  const last = condition.length() ? condition.last() : NaN;
6129
6317
  c.alerts.push({ id, title, message, triggered: !isNA(last) && last !== 0 });
6130
6318
  }
6319
+ var math = {
6320
+ ...math_exports,
6321
+ round_to_mintick: ((number2, mintick) => {
6322
+ const tick = mintick ?? syminfo.mintick;
6323
+ return number2 instanceof Series ? round_to_mintick(number2, tick) : round_to_mintick(number2, tick);
6324
+ })
6325
+ };