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.
- package/dist/callsite/index.d.ts +6 -0
- package/dist/callsite/index.d.ts.map +1 -0
- package/dist/index.cjs +258 -130
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +258 -130
- package/dist/script/index.cjs +1775 -1580
- package/dist/script/index.d.ts +100 -2
- package/dist/script/index.d.ts.map +1 -1
- package/dist/script/index.mjs +1775 -1580
- package/dist/strategy/index.d.ts +195 -0
- package/dist/strategy/index.d.ts.map +1 -0
- package/dist/ta/index.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/script/index.cjs
CHANGED
|
@@ -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: () =>
|
|
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(
|
|
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
|
|
1402
|
-
const
|
|
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
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
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
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
const
|
|
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/
|
|
3735
|
-
var
|
|
3736
|
-
__export(
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
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
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
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
|
-
|
|
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
|
|
3768
|
-
if (
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
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
|
-
|
|
3766
|
+
return Series.fromArray(bars2, result);
|
|
3848
3767
|
}
|
|
3849
|
-
|
|
3850
|
-
return zone;
|
|
3768
|
+
return Math.floor(value);
|
|
3851
3769
|
}
|
|
3852
|
-
function
|
|
3853
|
-
if (
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
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
|
-
|
|
3860
|
-
|
|
3861
|
-
}
|
|
3862
|
-
|
|
3863
|
-
return
|
|
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
|
|
3866
|
-
const
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
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
|
|
3942
|
-
const
|
|
3943
|
-
|
|
3944
|
-
|
|
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
|
|
3959
|
-
const
|
|
3960
|
-
const
|
|
3961
|
-
const
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
const
|
|
3965
|
-
|
|
3966
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
const
|
|
3979
|
-
const
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
const
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
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
|
|
3998
|
-
|
|
3999
|
-
const
|
|
4000
|
-
const
|
|
4001
|
-
const
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
if (
|
|
4005
|
-
|
|
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
|
-
|
|
4008
|
-
|
|
3871
|
+
let total = 0;
|
|
3872
|
+
for (const x of window) total += x;
|
|
3873
|
+
result.push(total);
|
|
4009
3874
|
}
|
|
4010
3875
|
}
|
|
4011
|
-
|
|
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
|
-
|
|
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
|
|
4031
|
-
|
|
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
|
|
4034
|
-
|
|
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
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
const
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
}
|
|
4048
|
-
|
|
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
|
|
4055
|
-
|
|
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
|
|
4058
|
-
|
|
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
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
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
|
-
|
|
4119
|
-
if (phase === "prefix") prefix += literal;
|
|
4120
|
-
else suffix += literal;
|
|
3965
|
+
return Series.fromArray(bars2, result);
|
|
4121
3966
|
}
|
|
4122
|
-
|
|
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
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
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
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
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
|
-
|
|
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
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
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
|
-
|
|
4191
|
-
return { digits: kept, intLen: kept ? intLen : 0 };
|
|
4009
|
+
return Math.asin(value);
|
|
4192
4010
|
}
|
|
4193
|
-
function
|
|
4194
|
-
if (
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
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
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
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
|
|
4208
|
-
if (
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4233
|
-
return (negative ? "-" : "") + p.prefix + body + p.suffix;
|
|
4051
|
+
return degrees * (Math.PI / 180);
|
|
4234
4052
|
}
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
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
|
-
|
|
4261
|
-
return part?.value ?? timezone;
|
|
4065
|
+
return radians * (180 / Math.PI);
|
|
4262
4066
|
}
|
|
4263
|
-
function
|
|
4264
|
-
const
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
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
|
|
4273
|
-
if (
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
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
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
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
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
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
|
-
|
|
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/
|
|
4363
|
-
var
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
};
|
|
4369
|
-
var
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
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
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
if (
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
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
|
-
|
|
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/
|
|
4453
|
-
|
|
4454
|
-
|
|
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
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
if (
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
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
|
-
|
|
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
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
}
|
|
4485
|
-
|
|
4486
|
-
|
|
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
|
|
4508
|
-
return
|
|
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
|
|
4517
|
-
|
|
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
|
|
4520
|
-
|
|
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
|
-
|
|
4523
|
-
|
|
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
|
|
4526
|
-
|
|
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
|
|
4529
|
-
|
|
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
|
-
|
|
4532
|
-
|
|
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
|
|
4535
|
-
|
|
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
|
-
|
|
4538
|
-
|
|
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
|
|
4541
|
-
|
|
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
|
|
4546
|
-
return
|
|
4426
|
+
function year(time2, timezone) {
|
|
4427
|
+
return field(time2, timezone, (f) => f.year);
|
|
4547
4428
|
}
|
|
4548
|
-
|
|
4549
|
-
|
|
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
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
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
|
|
4582
|
-
|
|
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
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
if (
|
|
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
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
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
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
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
|
-
|
|
4688
|
-
|
|
4689
|
-
const
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
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
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
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
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
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
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
const
|
|
4740
|
-
|
|
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
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
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
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
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
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
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
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
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
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
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
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
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
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4827
|
+
if (ch !== "{") {
|
|
4828
|
+
out += ch;
|
|
4829
|
+
i++;
|
|
4853
4830
|
continue;
|
|
4854
4831
|
}
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
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
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
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/
|
|
4878
|
-
function
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
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
|
-
|
|
4893
|
-
}
|
|
4894
|
-
|
|
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
|
|
4897
|
-
const
|
|
4898
|
-
|
|
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
|
|
4911
|
-
return
|
|
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
|
|
4922
|
-
|
|
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
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
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
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
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
|
|
4993
|
-
|
|
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
|
|
5007
|
-
|
|
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
|
|
5033
|
-
|
|
5034
|
-
if (
|
|
5035
|
-
|
|
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
|
|
5053
|
-
|
|
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
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
const
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
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
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
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
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
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
|
-
|
|
5113
|
-
|
|
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
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
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
|
|
5065
|
+
return null;
|
|
5128
5066
|
}
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
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
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
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
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
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
|
|
5110
|
+
return days;
|
|
5174
5111
|
}
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
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
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
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
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
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
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
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
|
-
|
|
5181
|
+
const last = this.calendar.lastTradingDay(from, to);
|
|
5182
|
+
return last === null ? NaN : this.dayEnd(last);
|
|
5272
5183
|
}
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
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
|
|
5193
|
+
return at.date;
|
|
5286
5194
|
}
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
const
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
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
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
const
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
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
|
-
|
|
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
|
|
5273
|
+
return out;
|
|
5323
5274
|
}
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
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
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
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
|
-
|
|
5350
|
-
}
|
|
5351
|
-
|
|
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
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
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
|
-
|
|
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
|
+
};
|