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