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.mjs
CHANGED
|
@@ -259,7 +259,7 @@ var Series = class _Series {
|
|
|
259
259
|
return new _Series(this.dataSource, (bar, i, data) => {
|
|
260
260
|
const a = this.extractor(bar, i, data);
|
|
261
261
|
const b2 = typeof other === "number" ? other : other.extractor(bar, i, data);
|
|
262
|
-
return
|
|
262
|
+
return a / b2;
|
|
263
263
|
});
|
|
264
264
|
}
|
|
265
265
|
/**
|
|
@@ -632,7 +632,7 @@ function sma(source, length2) {
|
|
|
632
632
|
const window = [];
|
|
633
633
|
for (let i = 0; i < source.length; i++) {
|
|
634
634
|
const v = source[i];
|
|
635
|
-
if (v !== void 0 && v !== null &&
|
|
635
|
+
if (v !== void 0 && v !== null && Number.isFinite(v)) {
|
|
636
636
|
window.push(v);
|
|
637
637
|
if (window.length > len) window.shift();
|
|
638
638
|
}
|
|
@@ -817,7 +817,7 @@ function tr(handle_na = false, high2, low2, close2) {
|
|
|
817
817
|
const result = [];
|
|
818
818
|
for (let i = 0; i < high2.length; i++) {
|
|
819
819
|
if (i === 0) {
|
|
820
|
-
result.push(high2[i] - low2[i]);
|
|
820
|
+
result.push(handle_na ? high2[i] - low2[i] : NaN);
|
|
821
821
|
} else {
|
|
822
822
|
const prevClose = close2[i - 1];
|
|
823
823
|
if (isNaN(prevClose)) {
|
|
@@ -844,7 +844,7 @@ function atr(length2, high2, low2, close2) {
|
|
|
844
844
|
"ta.atr() requires high, low, and close series. Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data."
|
|
845
845
|
);
|
|
846
846
|
}
|
|
847
|
-
const trueRange = tr(
|
|
847
|
+
const trueRange = tr(true, high2, low2, close2);
|
|
848
848
|
return rma(trueRange, length2);
|
|
849
849
|
}
|
|
850
850
|
function supertrend(factor, atrPeriod, high2, low2, close2, wicks = false) {
|
|
@@ -976,7 +976,7 @@ function lowest(source, length2) {
|
|
|
976
976
|
function cum(source) {
|
|
977
977
|
let sum2 = 0;
|
|
978
978
|
return source.map((v) => {
|
|
979
|
-
if (Number.
|
|
979
|
+
if (!Number.isFinite(v)) return NaN;
|
|
980
980
|
sum2 += v;
|
|
981
981
|
return sum2;
|
|
982
982
|
});
|
|
@@ -994,41 +994,24 @@ function cross(source1, source2) {
|
|
|
994
994
|
}
|
|
995
995
|
return result;
|
|
996
996
|
}
|
|
997
|
-
function
|
|
997
|
+
function monotonic(source, length2, step) {
|
|
998
998
|
const result = [];
|
|
999
|
+
const values = [];
|
|
999
1000
|
for (let i = 0; i < source.length; i++) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
isRising = false;
|
|
1007
|
-
break;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
result.push(isRising);
|
|
1011
|
-
}
|
|
1001
|
+
const v = source[i];
|
|
1002
|
+
if (!Number.isNaN(v)) values.push(v);
|
|
1003
|
+
const n = values.length;
|
|
1004
|
+
let ok = n > length2;
|
|
1005
|
+
for (let j = 1; ok && j <= length2; j++) ok = step(values[n - j], values[n - j - 1]);
|
|
1006
|
+
result.push(ok);
|
|
1012
1007
|
}
|
|
1013
1008
|
return result;
|
|
1014
1009
|
}
|
|
1010
|
+
function rising(source, length2) {
|
|
1011
|
+
return monotonic(source, length2, gt);
|
|
1012
|
+
}
|
|
1015
1013
|
function falling(source, length2) {
|
|
1016
|
-
|
|
1017
|
-
for (let i = 0; i < source.length; i++) {
|
|
1018
|
-
if (i < length2) {
|
|
1019
|
-
result.push(false);
|
|
1020
|
-
} else {
|
|
1021
|
-
let isFalling = true;
|
|
1022
|
-
for (let j = 1; j <= length2; j++) {
|
|
1023
|
-
if (source[i - j + 1] >= source[i - j]) {
|
|
1024
|
-
isFalling = false;
|
|
1025
|
-
break;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
result.push(isFalling);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
return result;
|
|
1014
|
+
return monotonic(source, length2, lt);
|
|
1032
1015
|
}
|
|
1033
1016
|
function roc(source, length2) {
|
|
1034
1017
|
const result = [];
|
|
@@ -1252,33 +1235,16 @@ function correlation(source1, source2, length2) {
|
|
|
1252
1235
|
function percentrank(source, length2) {
|
|
1253
1236
|
const result = [];
|
|
1254
1237
|
for (let i = 0; i < source.length; i++) {
|
|
1255
|
-
|
|
1238
|
+
const currentValue = source[i];
|
|
1239
|
+
if (i < length2 || Number.isNaN(currentValue)) {
|
|
1256
1240
|
result.push(NaN);
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
let hasNaN = false;
|
|
1263
|
-
for (let j = 1; j <= length2; j++) {
|
|
1264
|
-
if (isNaN(source[i - j])) {
|
|
1265
|
-
hasNaN = true;
|
|
1266
|
-
break;
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
if (hasNaN) {
|
|
1270
|
-
result.push(NaN);
|
|
1271
|
-
} else {
|
|
1272
|
-
const currentValue = source[i];
|
|
1273
|
-
let countLessOrEqual = 0;
|
|
1274
|
-
for (let j = 1; j <= length2; j++) {
|
|
1275
|
-
if (source[i - j] <= currentValue) {
|
|
1276
|
-
countLessOrEqual++;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
result.push(countLessOrEqual / length2 * 100);
|
|
1280
|
-
}
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
let countLessOrEqual = 0;
|
|
1244
|
+
for (let j = 1; j <= length2; j++) {
|
|
1245
|
+
if (le(source[i - j], currentValue)) countLessOrEqual++;
|
|
1281
1246
|
}
|
|
1247
|
+
result.push(countLessOrEqual / length2 * 100);
|
|
1282
1248
|
}
|
|
1283
1249
|
return result;
|
|
1284
1250
|
}
|
|
@@ -1321,48 +1287,26 @@ function mfi(source, length2, volume2) {
|
|
|
1321
1287
|
"ta.mfi() requires volume series. Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data."
|
|
1322
1288
|
);
|
|
1323
1289
|
}
|
|
1324
|
-
const
|
|
1325
|
-
const
|
|
1326
|
-
for (let i = 1; i < source.length; i++) {
|
|
1327
|
-
changes.push(source[i] - source[i - 1]);
|
|
1328
|
-
}
|
|
1329
|
-
const positiveFlow = [];
|
|
1330
|
-
const negativeFlow = [];
|
|
1290
|
+
const upperFlow = [];
|
|
1291
|
+
const lowerFlow = [];
|
|
1331
1292
|
for (let i = 0; i < source.length; i++) {
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
} else {
|
|
1350
|
-
let posSum = 0;
|
|
1351
|
-
let negSum = 0;
|
|
1352
|
-
for (let j = 0; j < length2; j++) {
|
|
1353
|
-
posSum += positiveFlow[i - j];
|
|
1354
|
-
negSum += negativeFlow[i - j];
|
|
1355
|
-
}
|
|
1356
|
-
if (negSum === 0) {
|
|
1357
|
-
result.push(100);
|
|
1358
|
-
} else {
|
|
1359
|
-
const moneyRatio = posSum / negSum;
|
|
1360
|
-
const mfiValue = 100 - 100 / (1 + moneyRatio);
|
|
1361
|
-
result.push(mfiValue);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return result;
|
|
1293
|
+
const change3 = i === 0 ? NaN : source[i] - source[i - 1];
|
|
1294
|
+
const flow = volume2[i] * source[i];
|
|
1295
|
+
upperFlow.push(le(change3, 0) ? 0 : flow);
|
|
1296
|
+
lowerFlow.push(ge(change3, 0) ? 0 : flow);
|
|
1297
|
+
}
|
|
1298
|
+
const sumAt = (flow, i) => {
|
|
1299
|
+
const values = lastValues(flow, i, Math.floor(length2));
|
|
1300
|
+
return values ? values.reduce((a, b2) => a + b2, 0) : NaN;
|
|
1301
|
+
};
|
|
1302
|
+
const upper2 = upperFlow.map((_, i) => sumAt(upperFlow, i));
|
|
1303
|
+
const lower2 = lowerFlow.map((_, i) => sumAt(lowerFlow, i));
|
|
1304
|
+
return upper2.map((u, i) => {
|
|
1305
|
+
const l = lower2[i];
|
|
1306
|
+
if (Number.isNaN(u) || Number.isNaN(l)) return NaN;
|
|
1307
|
+
if (l === 0) return 100;
|
|
1308
|
+
return 100 - 100 / (1 + u / l);
|
|
1309
|
+
});
|
|
1366
1310
|
}
|
|
1367
1311
|
function hma(source, length2) {
|
|
1368
1312
|
const halfLength = Math.floor(length2 / 2);
|
|
@@ -1526,50 +1470,32 @@ function dmi(diLength, adxSmoothing, high2, low2, close2) {
|
|
|
1526
1470
|
const len = Math.max(high2.length, low2.length, close2.length);
|
|
1527
1471
|
const plusDM = [];
|
|
1528
1472
|
const minusDM = [];
|
|
1529
|
-
const trueRangeValues = tr(false, high2, low2, close2);
|
|
1530
1473
|
for (let i = 0; i < len; i++) {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
const
|
|
1550
|
-
const
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
plusDI.push(0);
|
|
1557
|
-
minusDI.push(0);
|
|
1558
|
-
} else {
|
|
1559
|
-
plusDI.push(smoothedPlusDM[i] / smoothedTR[i] * 100);
|
|
1560
|
-
minusDI.push(smoothedMinusDM[i] / smoothedTR[i] * 100);
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
const dx = [];
|
|
1564
|
-
for (let i = 0; i < len; i++) {
|
|
1565
|
-
const sum2 = plusDI[i] + minusDI[i];
|
|
1566
|
-
if (sum2 === 0) {
|
|
1567
|
-
dx.push(0);
|
|
1568
|
-
} else {
|
|
1569
|
-
dx.push(Math.abs(plusDI[i] - minusDI[i]) / sum2 * 100);
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
const adx = rma(dx, adxSmoothing);
|
|
1474
|
+
const up = i === 0 ? NaN : high2[i] - high2[i - 1];
|
|
1475
|
+
const down = i === 0 ? NaN : low2[i - 1] - low2[i];
|
|
1476
|
+
plusDM.push(Number.isNaN(up) ? NaN : gt(up, down) && gt(up, 0) ? up : 0);
|
|
1477
|
+
minusDM.push(Number.isNaN(down) ? NaN : gt(down, up) && gt(down, 0) ? down : 0);
|
|
1478
|
+
}
|
|
1479
|
+
const trur = rma(tr(false, high2, low2, close2), diLength);
|
|
1480
|
+
const smoothedPlus = rma(plusDM, diLength);
|
|
1481
|
+
const smoothedMinus = rma(minusDM, diLength);
|
|
1482
|
+
const directional = (smoothed) => {
|
|
1483
|
+
let last = NaN;
|
|
1484
|
+
return smoothed.map((v, i) => {
|
|
1485
|
+
const t2 = trur[i];
|
|
1486
|
+
const value = t2 === 0 ? NaN : 100 * v / t2;
|
|
1487
|
+
if (!Number.isNaN(value)) last = value;
|
|
1488
|
+
return last;
|
|
1489
|
+
});
|
|
1490
|
+
};
|
|
1491
|
+
const plusDI = directional(smoothedPlus);
|
|
1492
|
+
const minusDI = directional(smoothedMinus);
|
|
1493
|
+
const dx = plusDI.map((plus, i) => {
|
|
1494
|
+
const minus = minusDI[i];
|
|
1495
|
+
const sum2 = plus + minus;
|
|
1496
|
+
return Math.abs(plus - minus) / (eq(sum2, 0) ? 1 : sum2);
|
|
1497
|
+
});
|
|
1498
|
+
const adx = rma(dx, adxSmoothing).map((v) => 100 * v);
|
|
1573
1499
|
return [plusDI, minusDI, adx];
|
|
1574
1500
|
}
|
|
1575
1501
|
function tsi(source, shortLength, longLength) {
|
|
@@ -1845,63 +1771,36 @@ function mode(source, length2) {
|
|
|
1845
1771
|
return result;
|
|
1846
1772
|
}
|
|
1847
1773
|
function percentile_linear_interpolation(source, length2, percentage) {
|
|
1774
|
+
return sortedWindow(source, length2, (s) => {
|
|
1775
|
+
const pos2 = percentage / 100 * length2 - 0.5;
|
|
1776
|
+
if (pos2 <= 0) return s[0];
|
|
1777
|
+
if (pos2 >= length2 - 1) return s[length2 - 1];
|
|
1778
|
+
const lo = Math.floor(pos2);
|
|
1779
|
+
return s[lo] + (pos2 - lo) * (s[lo + 1] - s[lo]);
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
function sortedWindow(source, length2, fn) {
|
|
1848
1783
|
const result = [];
|
|
1784
|
+
const values = [];
|
|
1785
|
+
const bars2 = [];
|
|
1849
1786
|
for (let i = 0; i < source.length; i++) {
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
const
|
|
1857
|
-
values.
|
|
1858
|
-
|
|
1859
|
-
if (values.some((v) => isNaN(v))) {
|
|
1860
|
-
result.push(NaN);
|
|
1861
|
-
continue;
|
|
1862
|
-
}
|
|
1863
|
-
const sorted = [...values].sort((a, b2) => a - b2);
|
|
1864
|
-
const position = percentage / 100 * (sorted.length - 1);
|
|
1865
|
-
const lowerIndex = Math.floor(position);
|
|
1866
|
-
const upperIndex = Math.ceil(position);
|
|
1867
|
-
if (lowerIndex === upperIndex) {
|
|
1868
|
-
result.push(sorted[lowerIndex]);
|
|
1869
|
-
} else {
|
|
1870
|
-
const fraction = position - lowerIndex;
|
|
1871
|
-
const interpolated = sorted[lowerIndex] + fraction * (sorted[upperIndex] - sorted[lowerIndex]);
|
|
1872
|
-
result.push(interpolated);
|
|
1787
|
+
const v = source[i];
|
|
1788
|
+
let k = 0;
|
|
1789
|
+
while (k < values.length && !(values[k] >= v)) k++;
|
|
1790
|
+
values.splice(k, 0, v);
|
|
1791
|
+
bars2.splice(k, 0, i);
|
|
1792
|
+
if (i >= length2) {
|
|
1793
|
+
const old = bars2.indexOf(i - length2);
|
|
1794
|
+
values.splice(old, 1);
|
|
1795
|
+
bars2.splice(old, 1);
|
|
1873
1796
|
}
|
|
1797
|
+
result.push(i < length2 - 1 ? NaN : fn(values));
|
|
1874
1798
|
}
|
|
1875
1799
|
return result;
|
|
1876
1800
|
}
|
|
1877
1801
|
function percentile_nearest_rank(source, length2, percentage) {
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
if (i < length2 - 1) {
|
|
1881
|
-
result.push(NaN);
|
|
1882
|
-
continue;
|
|
1883
|
-
}
|
|
1884
|
-
const values = [];
|
|
1885
|
-
for (let j = 0; j < length2; j++) {
|
|
1886
|
-
const value = source[i - j];
|
|
1887
|
-
if (!isNaN(value)) {
|
|
1888
|
-
values.push(value);
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
if (values.length === 0) {
|
|
1892
|
-
result.push(NaN);
|
|
1893
|
-
continue;
|
|
1894
|
-
}
|
|
1895
|
-
const sorted = [...values].sort((a, b2) => a - b2);
|
|
1896
|
-
if (percentage >= 100) {
|
|
1897
|
-
result.push(sorted[sorted.length - 1]);
|
|
1898
|
-
continue;
|
|
1899
|
-
}
|
|
1900
|
-
const rank = Math.ceil(percentage / 100 * sorted.length);
|
|
1901
|
-
const index = Math.max(0, rank - 1);
|
|
1902
|
-
result.push(sorted[index]);
|
|
1903
|
-
}
|
|
1904
|
-
return result;
|
|
1802
|
+
const rank = Math.min(length2, Math.max(1, Math.ceil(percentage / 100 * length2)));
|
|
1803
|
+
return sortedWindow(source, length2, (s) => s[rank - 1]);
|
|
1905
1804
|
}
|
|
1906
1805
|
function rci(source, length2) {
|
|
1907
1806
|
const result = [];
|
|
@@ -2767,11 +2666,13 @@ __export(color_exports, {
|
|
|
2767
2666
|
white: () => white,
|
|
2768
2667
|
yellow: () => yellow
|
|
2769
2668
|
});
|
|
2669
|
+
var channel = (v) => v === null || v === void 0 || Number.isNaN(v) ? 0 : Math.max(0, Math.min(255, Math.trunc(v)));
|
|
2670
|
+
var alphaOf = (transp) => transp === null || Number.isNaN(transp) ? 0 : 1 - Math.max(0, Math.min(100, transp)) / 100;
|
|
2770
2671
|
function rgb(red2, green2, blue2, transp) {
|
|
2771
|
-
const r2 =
|
|
2772
|
-
const g2 =
|
|
2773
|
-
const b2 =
|
|
2774
|
-
const a = transp !== void 0 ?
|
|
2672
|
+
const r2 = channel(red2);
|
|
2673
|
+
const g2 = channel(green2);
|
|
2674
|
+
const b2 = channel(blue2);
|
|
2675
|
+
const a = transp !== void 0 ? alphaOf(transp) : 1;
|
|
2775
2676
|
if (a === 1) {
|
|
2776
2677
|
return `rgb(${r2}, ${g2}, ${b2})`;
|
|
2777
2678
|
}
|
|
@@ -2798,7 +2699,8 @@ function b(clr) {
|
|
|
2798
2699
|
return parseColor(clr).b;
|
|
2799
2700
|
}
|
|
2800
2701
|
function t(clr) {
|
|
2801
|
-
|
|
2702
|
+
const alpha = Math.round((1 - parseColor(clr).t / 100) * 255);
|
|
2703
|
+
return Math.round((1 - alpha / 255) * 100);
|
|
2802
2704
|
}
|
|
2803
2705
|
function parseColor(clr) {
|
|
2804
2706
|
if (typeof clr === "string") {
|
|
@@ -2851,13 +2753,12 @@ function from_gradient(value, bottom_value, top_value, bottom_color, top_color)
|
|
|
2851
2753
|
};
|
|
2852
2754
|
const c1 = rgba(bottom_color);
|
|
2853
2755
|
const c2 = rgba(top_color);
|
|
2854
|
-
const
|
|
2855
|
-
const w2 = c2.a * k;
|
|
2856
|
-
const a = w1 + w2;
|
|
2756
|
+
const a = c1.a * (1 - k) + c2.a * k;
|
|
2857
2757
|
if (a === 0) return transparent;
|
|
2858
|
-
const mix = (x1, x2) => Math.floor((x1 *
|
|
2758
|
+
const mix = (x1, x2) => Math.floor((x1 / 255 * c1.a * (1 - k) + x2 / 255 * c2.a * k) / (a / 255));
|
|
2859
2759
|
const [r2, g2, b2] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
|
|
2860
|
-
|
|
2760
|
+
const alpha = Math.floor(a);
|
|
2761
|
+
return alpha === 255 ? `rgb(${r2}, ${g2}, ${b2})` : `rgba(${r2}, ${g2}, ${b2}, ${alpha / 255})`;
|
|
2861
2762
|
}
|
|
2862
2763
|
|
|
2863
2764
|
// src/drawing/registry.ts
|
|
@@ -2921,7 +2822,7 @@ function setDrawingLimits(newLimits) {
|
|
|
2921
2822
|
|
|
2922
2823
|
// src/utils/index.ts
|
|
2923
2824
|
function isNA(value) {
|
|
2924
|
-
return value === null || value === void 0 || Number.isNaN(value) || isDeleted(value);
|
|
2825
|
+
return value === null || value === void 0 || Number.isNaN(value) || value === Infinity || value === -Infinity || isDeleted(value);
|
|
2925
2826
|
}
|
|
2926
2827
|
function nz(value, defaultValue = 0) {
|
|
2927
2828
|
return isNA(value) ? defaultValue : value;
|
|
@@ -2930,7 +2831,7 @@ function fixnan(source) {
|
|
|
2930
2831
|
let last;
|
|
2931
2832
|
let seen = false;
|
|
2932
2833
|
return source.map((v) => {
|
|
2933
|
-
if (!
|
|
2834
|
+
if (!(v === null || v === void 0 || Number.isNaN(v) || isDeleted(v))) {
|
|
2934
2835
|
last = v;
|
|
2935
2836
|
seen = true;
|
|
2936
2837
|
return v;
|
|
@@ -3654,1640 +3555,1707 @@ var format_none = 0;
|
|
|
3654
3555
|
var format_bold = 1;
|
|
3655
3556
|
var format_italic = 2;
|
|
3656
3557
|
|
|
3657
|
-
// src/
|
|
3658
|
-
var
|
|
3659
|
-
__export(
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3558
|
+
// src/math/index.ts
|
|
3559
|
+
var math_exports = {};
|
|
3560
|
+
__export(math_exports, {
|
|
3561
|
+
abs: () => abs,
|
|
3562
|
+
acos: () => acos,
|
|
3563
|
+
asin: () => asin,
|
|
3564
|
+
atan: () => atan,
|
|
3565
|
+
avg: () => avg,
|
|
3566
|
+
ceil: () => ceil,
|
|
3567
|
+
cos: () => cos,
|
|
3568
|
+
e: () => e,
|
|
3569
|
+
exp: () => exp,
|
|
3570
|
+
floor: () => floor,
|
|
3571
|
+
log: () => log,
|
|
3572
|
+
log10: () => log10,
|
|
3573
|
+
max: () => max3,
|
|
3574
|
+
min: () => min3,
|
|
3575
|
+
phi: () => phi,
|
|
3576
|
+
pi: () => pi,
|
|
3577
|
+
pow: () => pow,
|
|
3578
|
+
random: () => random,
|
|
3579
|
+
round: () => round,
|
|
3580
|
+
round_to_mintick: () => round_to_mintick,
|
|
3581
|
+
rphi: () => rphi,
|
|
3582
|
+
sign: () => sign,
|
|
3583
|
+
sin: () => sin,
|
|
3584
|
+
sqrt: () => sqrt,
|
|
3585
|
+
sum: () => sum,
|
|
3586
|
+
tan: () => tan,
|
|
3587
|
+
todegrees: () => todegrees,
|
|
3588
|
+
toradians: () => toradians
|
|
3663
3589
|
});
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
};
|
|
3676
|
-
var SECOND_TIMEFRAMES = [1, 5, 10, 15, 30];
|
|
3677
|
-
function in_seconds(timeframe2) {
|
|
3678
|
-
const text2 = timeframe2.trim();
|
|
3679
|
-
if (text2 === "") {
|
|
3680
|
-
throw new RangeError("timeframe.in_seconds() needs a timeframe: the chart timeframe is not known here");
|
|
3590
|
+
function abs(value) {
|
|
3591
|
+
if (value instanceof Series) {
|
|
3592
|
+
const bars2 = value.bars;
|
|
3593
|
+
const valueArray = value.toArray();
|
|
3594
|
+
const length2 = bars2.length;
|
|
3595
|
+
const result = [];
|
|
3596
|
+
for (let i = 0; i < length2; i++) {
|
|
3597
|
+
const v = valueArray[i] ?? NaN;
|
|
3598
|
+
result.push(Math.abs(v));
|
|
3599
|
+
}
|
|
3600
|
+
return Series.fromArray(bars2, result);
|
|
3681
3601
|
}
|
|
3682
|
-
|
|
3683
|
-
const after = before ? null : /^([SDWMT])(\d+)$/.exec(text2);
|
|
3684
|
-
if (!before && !after) throw new RangeError(`Invalid timeframe "${timeframe2}"`);
|
|
3685
|
-
const [digits, unit] = before ? [before[1], before[2]] : [after[2], after[1]];
|
|
3686
|
-
if (unit === "T") return NaN;
|
|
3687
|
-
const multiplier = digits === "" ? 1 : Number(digits);
|
|
3688
|
-
return multiplier * UNIT_SECONDS[unit];
|
|
3602
|
+
return Math.abs(value);
|
|
3689
3603
|
}
|
|
3690
|
-
function
|
|
3691
|
-
if (
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3604
|
+
function ceil(value) {
|
|
3605
|
+
if (value instanceof Series) {
|
|
3606
|
+
const bars2 = value.bars;
|
|
3607
|
+
const valueArray = value.toArray();
|
|
3608
|
+
const length2 = bars2.length;
|
|
3609
|
+
const result = [];
|
|
3610
|
+
for (let i = 0; i < length2; i++) {
|
|
3611
|
+
const v = valueArray[i] ?? NaN;
|
|
3612
|
+
result.push(Math.ceil(v));
|
|
3613
|
+
}
|
|
3614
|
+
return Series.fromArray(bars2, result);
|
|
3698
3615
|
}
|
|
3699
|
-
|
|
3700
|
-
if (second3 !== void 0) return `${second3}S`;
|
|
3701
|
-
if (seconds < SECONDS_PER_DAY) return `${Math.ceil(seconds / SECONDS_PER_MINUTE)}`;
|
|
3702
|
-
return `${Math.ceil(seconds / SECONDS_PER_DAY)}D`;
|
|
3616
|
+
return Math.ceil(value);
|
|
3703
3617
|
}
|
|
3704
|
-
function
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
isminutes: unit === "",
|
|
3718
|
-
isseconds: unit === "S",
|
|
3719
|
-
isticks: unit === "T",
|
|
3720
|
-
isdwm: unit === "D" || unit === "W" || unit === "M"
|
|
3721
|
-
};
|
|
3618
|
+
function floor(value) {
|
|
3619
|
+
if (value instanceof Series) {
|
|
3620
|
+
const bars2 = value.bars;
|
|
3621
|
+
const valueArray = value.toArray();
|
|
3622
|
+
const length2 = bars2.length;
|
|
3623
|
+
const result = [];
|
|
3624
|
+
for (let i = 0; i < length2; i++) {
|
|
3625
|
+
const v = valueArray[i] ?? NaN;
|
|
3626
|
+
result.push(Math.floor(v));
|
|
3627
|
+
}
|
|
3628
|
+
return Series.fromArray(bars2, result);
|
|
3629
|
+
}
|
|
3630
|
+
return Math.floor(value);
|
|
3722
3631
|
}
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
if (zone) return zone;
|
|
3741
|
-
const invalid = () => new RangeError(
|
|
3742
|
-
`Invalid time zone "${timezone}": use UTC/GMT offset notation ("UTC-5", "GMT+0530") or an IANA name ("America/New_York")`
|
|
3743
|
-
);
|
|
3744
|
-
const m = OFFSET_NOTATION.exec(timezone);
|
|
3745
|
-
if (m) {
|
|
3746
|
-
const [, sign2, h = "0", mi = "0", s] = m;
|
|
3747
|
-
const minutes = Number(mi);
|
|
3748
|
-
const seconds = s === void 0 ? 0 : Number(s);
|
|
3749
|
-
if (minutes > 59 || seconds > 59 || s !== void 0 && h.length !== 2) throw invalid();
|
|
3750
|
-
const offsetSeconds = Number(h) * 3600 + minutes * 60 + seconds;
|
|
3751
|
-
if (offsetSeconds > MAX_OFFSET_SECONDS) throw invalid();
|
|
3752
|
-
zone = { kind: "fixed", offsetMs: (sign2 === "-" ? -offsetSeconds : offsetSeconds) * MS_PER_SECOND };
|
|
3753
|
-
} else {
|
|
3754
|
-
if (REJECTED_NAMES.has(timezone) || !hasTimeZoneCase(timezone)) throw invalid();
|
|
3755
|
-
let format2;
|
|
3756
|
-
try {
|
|
3757
|
-
format2 = new Intl.DateTimeFormat("en-US", {
|
|
3758
|
-
timeZone: timezone,
|
|
3759
|
-
hourCycle: "h23",
|
|
3760
|
-
year: "numeric",
|
|
3761
|
-
month: "numeric",
|
|
3762
|
-
day: "numeric",
|
|
3763
|
-
hour: "numeric",
|
|
3764
|
-
minute: "numeric",
|
|
3765
|
-
second: "numeric"
|
|
3766
|
-
});
|
|
3767
|
-
} catch {
|
|
3768
|
-
throw invalid();
|
|
3632
|
+
function round(value, precision) {
|
|
3633
|
+
if (value instanceof Series) {
|
|
3634
|
+
const bars2 = value.bars;
|
|
3635
|
+
const valueArray = value.toArray();
|
|
3636
|
+
const length2 = bars2.length;
|
|
3637
|
+
const result = [];
|
|
3638
|
+
if (precision === void 0) {
|
|
3639
|
+
for (let i = 0; i < length2; i++) {
|
|
3640
|
+
const v = valueArray[i] ?? NaN;
|
|
3641
|
+
result.push(Math.round(v));
|
|
3642
|
+
}
|
|
3643
|
+
} else {
|
|
3644
|
+
const multiplier2 = Math.pow(10, precision);
|
|
3645
|
+
for (let i = 0; i < length2; i++) {
|
|
3646
|
+
const v = valueArray[i] ?? NaN;
|
|
3647
|
+
result.push(Math.round(v * multiplier2) / multiplier2);
|
|
3648
|
+
}
|
|
3769
3649
|
}
|
|
3770
|
-
|
|
3650
|
+
return Series.fromArray(bars2, result);
|
|
3771
3651
|
}
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
}
|
|
3775
|
-
function offsetAt(zone, time2) {
|
|
3776
|
-
if (zone.kind === "fixed") return zone.offsetMs;
|
|
3777
|
-
const wholeSecond = Math.floor(time2 / 1e3) * 1e3;
|
|
3778
|
-
const f = {};
|
|
3779
|
-
for (const part of zone.format.formatToParts(wholeSecond)) {
|
|
3780
|
-
if (part.type !== "literal") f[part.type] = Number(part.value);
|
|
3652
|
+
if (precision === void 0) {
|
|
3653
|
+
return Math.round(value);
|
|
3781
3654
|
}
|
|
3782
|
-
const
|
|
3783
|
-
return
|
|
3784
|
-
}
|
|
3785
|
-
function zoneOffset(time2, timezone) {
|
|
3786
|
-
return offsetAt(resolveZone(timezone), time2);
|
|
3787
|
-
}
|
|
3788
|
-
function zonedFields(time2, timezone) {
|
|
3789
|
-
const local = new Date(time2 + offsetAt(resolveZone(timezone), time2));
|
|
3790
|
-
return {
|
|
3791
|
-
year: local.getUTCFullYear(),
|
|
3792
|
-
month: local.getUTCMonth() + 1,
|
|
3793
|
-
day: local.getUTCDate(),
|
|
3794
|
-
hour: local.getUTCHours(),
|
|
3795
|
-
minute: local.getUTCMinutes(),
|
|
3796
|
-
second: local.getUTCSeconds(),
|
|
3797
|
-
dayOfWeek: local.getUTCDay() + 1
|
|
3798
|
-
};
|
|
3655
|
+
const multiplier = Math.pow(10, precision);
|
|
3656
|
+
return Math.round(value * multiplier) / multiplier;
|
|
3799
3657
|
}
|
|
3800
|
-
function
|
|
3801
|
-
const
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
const
|
|
3806
|
-
const
|
|
3807
|
-
|
|
3808
|
-
|
|
3658
|
+
function max3(...values) {
|
|
3659
|
+
const hasSeries = values.some((v) => v instanceof Series);
|
|
3660
|
+
if (!hasSeries) {
|
|
3661
|
+
return Math.max(...values);
|
|
3662
|
+
}
|
|
3663
|
+
const firstSeries = values.find((v) => v instanceof Series);
|
|
3664
|
+
const bars2 = firstSeries.bars;
|
|
3665
|
+
const length2 = bars2.length;
|
|
3666
|
+
const valueArrays = values.map(
|
|
3667
|
+
(v) => v instanceof Series ? v.toArray() : null
|
|
3668
|
+
);
|
|
3669
|
+
const result = [];
|
|
3670
|
+
for (let i = 0; i < length2; i++) {
|
|
3671
|
+
const nums = values.map(
|
|
3672
|
+
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
3673
|
+
);
|
|
3674
|
+
result.push(Math.max(...nums));
|
|
3675
|
+
}
|
|
3676
|
+
return Series.fromArray(bars2, result);
|
|
3809
3677
|
}
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
utc: 0,
|
|
3830
|
-
gmt: 0,
|
|
3831
|
-
est: -5,
|
|
3832
|
-
edt: -4,
|
|
3833
|
-
cst: -6,
|
|
3834
|
-
cdt: -5,
|
|
3835
|
-
mst: -7,
|
|
3836
|
-
mdt: -6,
|
|
3837
|
-
pst: -8,
|
|
3838
|
-
pdt: -7
|
|
3839
|
-
};
|
|
3840
|
-
var TIME = String.raw`(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3})\d*)?)?`;
|
|
3841
|
-
var ZONE = String.raw`(ut|utc|gmt|[ecmp][sd]t|(?:utc|gmt)?\s*[+-]\d{1,2}(?::?\d{2})?)`;
|
|
3842
|
-
var NUMERIC_ZONE = String.raw`([+-]\d{2}(?::?\d{2})?)`;
|
|
3843
|
-
var DATE = String.raw`(\d{4})([-/])(\d{1,2})\2(\d{1,2})`;
|
|
3844
|
-
var WEEKDAY = String.raw`(?:[a-z]{3,9},?\s+)?`;
|
|
3845
|
-
var ISO_T = new RegExp(String.raw`^${DATE}t${TIME}\s*${NUMERIC_ZONE}?$`, "i");
|
|
3846
|
-
var ISO = new RegExp(String.raw`^${DATE}(?:\s+${TIME})?\s*${ZONE}?$`, "i");
|
|
3847
|
-
var DAY_FIRST = new RegExp(String.raw`^${WEEKDAY}(\d{1,2})(?:\s+|-)([a-z]+)\.?,?(?:\s+|-)(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
|
|
3848
|
-
var MONTH_FIRST = new RegExp(String.raw`^${WEEKDAY}([a-z]+)\.?\s+(\d{1,2}),?\s+(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
|
|
3849
|
-
function monthNumber(name) {
|
|
3850
|
-
const lower2 = name.toLowerCase();
|
|
3851
|
-
const i = FULL_MONTHS.findIndex((full, k) => lower2.startsWith(MONTHS[k]) && full.startsWith(lower2));
|
|
3852
|
-
return i + 1;
|
|
3678
|
+
function min3(...values) {
|
|
3679
|
+
const hasSeries = values.some((v) => v instanceof Series);
|
|
3680
|
+
if (!hasSeries) {
|
|
3681
|
+
return Math.min(...values);
|
|
3682
|
+
}
|
|
3683
|
+
const firstSeries = values.find((v) => v instanceof Series);
|
|
3684
|
+
const bars2 = firstSeries.bars;
|
|
3685
|
+
const length2 = bars2.length;
|
|
3686
|
+
const valueArrays = values.map(
|
|
3687
|
+
(v) => v instanceof Series ? v.toArray() : null
|
|
3688
|
+
);
|
|
3689
|
+
const result = [];
|
|
3690
|
+
for (let i = 0; i < length2; i++) {
|
|
3691
|
+
const nums = values.map(
|
|
3692
|
+
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
3693
|
+
);
|
|
3694
|
+
result.push(Math.min(...nums));
|
|
3695
|
+
}
|
|
3696
|
+
return Series.fromArray(bars2, result);
|
|
3853
3697
|
}
|
|
3854
|
-
function
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
const
|
|
3860
|
-
|
|
3861
|
-
const
|
|
3862
|
-
|
|
3698
|
+
function avg(...values) {
|
|
3699
|
+
const hasSeries = values.some((v) => v instanceof Series);
|
|
3700
|
+
if (!hasSeries) {
|
|
3701
|
+
return values.reduce((a, b2) => a + b2, 0) / values.length;
|
|
3702
|
+
}
|
|
3703
|
+
const firstSeries = values.find((v) => v instanceof Series);
|
|
3704
|
+
const bars2 = firstSeries.bars;
|
|
3705
|
+
const length2 = bars2.length;
|
|
3706
|
+
const valueArrays = values.map(
|
|
3707
|
+
(v) => v instanceof Series ? v.toArray() : null
|
|
3708
|
+
);
|
|
3709
|
+
const result = [];
|
|
3710
|
+
for (let i = 0; i < length2; i++) {
|
|
3711
|
+
const nums = values.map(
|
|
3712
|
+
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
3713
|
+
);
|
|
3714
|
+
const sum2 = nums.reduce((a, b2) => a + b2, 0);
|
|
3715
|
+
result.push(sum2 / nums.length);
|
|
3716
|
+
}
|
|
3717
|
+
return Series.fromArray(bars2, result);
|
|
3863
3718
|
}
|
|
3864
|
-
function
|
|
3865
|
-
const
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3719
|
+
function sum(source, length2) {
|
|
3720
|
+
const sourceArray = source instanceof Series ? source.toArray() : source;
|
|
3721
|
+
const result = [];
|
|
3722
|
+
const len = Math.floor(length2);
|
|
3723
|
+
const window = [];
|
|
3724
|
+
for (let i = 0; i < sourceArray.length; i++) {
|
|
3725
|
+
const v = sourceArray[i];
|
|
3726
|
+
if (v !== void 0 && v !== null && !Number.isNaN(v)) {
|
|
3727
|
+
window.push(v);
|
|
3728
|
+
if (window.length > len) window.shift();
|
|
3729
|
+
}
|
|
3730
|
+
if (window.length < len) {
|
|
3731
|
+
result.push(NaN);
|
|
3732
|
+
} else {
|
|
3733
|
+
let total = 0;
|
|
3734
|
+
for (const x of window) total += x;
|
|
3735
|
+
result.push(total);
|
|
3736
|
+
}
|
|
3880
3737
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
const h = Number(hour3);
|
|
3884
|
-
const mi = Number(minute3);
|
|
3885
|
-
const s = Number(second3);
|
|
3886
|
-
const offset = zoneMinutes(zone);
|
|
3887
|
-
const endOfDay = h === 24 && mi === 0 && s === 0 && Number(millis) === 0;
|
|
3888
|
-
if (month3 < 1 || month3 > 12 || d < 1 || d > 31 || h > 23 && !endOfDay || mi > 59 || s > 59 || Number.isNaN(offset)) {
|
|
3889
|
-
throw new SyntaxError(`Invalid date string "${dateString}": a field is out of range`);
|
|
3738
|
+
if (source instanceof Series) {
|
|
3739
|
+
return Series.fromArray(source.bars, result);
|
|
3890
3740
|
}
|
|
3891
|
-
|
|
3892
|
-
return Date.UTC(Number(year3), month3 - 1, d, h, mi, s, ms) - offset * 6e4;
|
|
3741
|
+
return result;
|
|
3893
3742
|
}
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
if (!m) throw invalid();
|
|
3908
|
-
const [h1, m1, h2, m2] = [m[1], m[2], m[3], m[4]].map(Number);
|
|
3909
|
-
if (h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) throw invalid();
|
|
3910
|
-
return { start: h1 * 60 + m1, end: h2 * 60 + m2 };
|
|
3911
|
-
});
|
|
3912
|
-
const dayDigits = dayText ?? (periods.length > 1 ? "23456" : "1234567");
|
|
3913
|
-
if (!/^[1-7]+$/.test(dayDigits)) throw invalid();
|
|
3914
|
-
const days = new Array(8).fill(false);
|
|
3915
|
-
for (const d of dayDigits) days[Number(d)] = true;
|
|
3916
|
-
parsed = { periods, days };
|
|
3917
|
-
sessions.set(session2, parsed);
|
|
3918
|
-
return parsed;
|
|
3743
|
+
function sqrt(value) {
|
|
3744
|
+
if (value instanceof Series) {
|
|
3745
|
+
const bars2 = value.bars;
|
|
3746
|
+
const valueArray = value.toArray();
|
|
3747
|
+
const length2 = bars2.length;
|
|
3748
|
+
const result = [];
|
|
3749
|
+
for (let i = 0; i < length2; i++) {
|
|
3750
|
+
const v = valueArray[i] ?? NaN;
|
|
3751
|
+
result.push(Math.sqrt(v));
|
|
3752
|
+
}
|
|
3753
|
+
return Series.fromArray(bars2, result);
|
|
3754
|
+
}
|
|
3755
|
+
return Math.sqrt(value);
|
|
3919
3756
|
}
|
|
3920
|
-
function
|
|
3921
|
-
|
|
3922
|
-
const
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3757
|
+
function pow(base, exponent) {
|
|
3758
|
+
const baseIsSeries = base instanceof Series;
|
|
3759
|
+
const expIsSeries = exponent instanceof Series;
|
|
3760
|
+
if (!baseIsSeries && !expIsSeries) {
|
|
3761
|
+
return Math.pow(base, exponent);
|
|
3762
|
+
}
|
|
3763
|
+
const bars2 = baseIsSeries ? base.bars : exponent.bars;
|
|
3764
|
+
const baseArray = baseIsSeries ? base.toArray() : null;
|
|
3765
|
+
const expArray = expIsSeries ? exponent.toArray() : null;
|
|
3766
|
+
const length2 = bars2.length;
|
|
3767
|
+
const result = [];
|
|
3768
|
+
for (let i = 0; i < length2; i++) {
|
|
3769
|
+
const b2 = baseArray ? baseArray[i] ?? NaN : base;
|
|
3770
|
+
const e2 = expArray ? expArray[i] ?? NaN : exponent;
|
|
3771
|
+
result.push(Math.pow(b2, e2));
|
|
3772
|
+
}
|
|
3773
|
+
return Series.fromArray(bars2, result);
|
|
3774
|
+
}
|
|
3775
|
+
function exp(value) {
|
|
3776
|
+
if (value instanceof Series) {
|
|
3777
|
+
const bars2 = value.bars;
|
|
3778
|
+
const valueArray = value.toArray();
|
|
3779
|
+
const length2 = bars2.length;
|
|
3780
|
+
const result = [];
|
|
3781
|
+
for (let i = 0; i < length2; i++) {
|
|
3782
|
+
const v = valueArray[i] ?? NaN;
|
|
3783
|
+
result.push(Math.exp(v));
|
|
3932
3784
|
}
|
|
3785
|
+
return Series.fromArray(bars2, result);
|
|
3933
3786
|
}
|
|
3934
|
-
return
|
|
3787
|
+
return Math.exp(value);
|
|
3935
3788
|
}
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
const
|
|
3942
|
-
|
|
3789
|
+
function log(value) {
|
|
3790
|
+
if (value instanceof Series) {
|
|
3791
|
+
const bars2 = value.bars;
|
|
3792
|
+
const valueArray = value.toArray();
|
|
3793
|
+
const length2 = bars2.length;
|
|
3794
|
+
const result = [];
|
|
3795
|
+
for (let i = 0; i < length2; i++) {
|
|
3796
|
+
const v = valueArray[i] ?? NaN;
|
|
3797
|
+
result.push(Math.log(v));
|
|
3798
|
+
}
|
|
3799
|
+
return Series.fromArray(bars2, result);
|
|
3943
3800
|
}
|
|
3944
|
-
|
|
3945
|
-
return new Date(first, month3 - 1, day, hour3 || 0, minute3 || 0, second3 || 0).getTime();
|
|
3801
|
+
return Math.log(value);
|
|
3946
3802
|
}
|
|
3947
|
-
function
|
|
3948
|
-
|
|
3803
|
+
function log10(value) {
|
|
3804
|
+
if (value instanceof Series) {
|
|
3805
|
+
const bars2 = value.bars;
|
|
3806
|
+
const valueArray = value.toArray();
|
|
3807
|
+
const length2 = bars2.length;
|
|
3808
|
+
const result = [];
|
|
3809
|
+
for (let i = 0; i < length2; i++) {
|
|
3810
|
+
const v = valueArray[i] ?? NaN;
|
|
3811
|
+
result.push(Math.log10(v));
|
|
3812
|
+
}
|
|
3813
|
+
return Series.fromArray(bars2, result);
|
|
3814
|
+
}
|
|
3815
|
+
return Math.log10(value);
|
|
3949
3816
|
}
|
|
3950
|
-
function
|
|
3951
|
-
|
|
3817
|
+
function sin(value) {
|
|
3818
|
+
if (value instanceof Series) {
|
|
3819
|
+
const bars2 = value.bars;
|
|
3820
|
+
const valueArray = value.toArray();
|
|
3821
|
+
const length2 = bars2.length;
|
|
3822
|
+
const result = [];
|
|
3823
|
+
for (let i = 0; i < length2; i++) {
|
|
3824
|
+
const v = valueArray[i] ?? NaN;
|
|
3825
|
+
result.push(Math.sin(v));
|
|
3826
|
+
}
|
|
3827
|
+
return Series.fromArray(bars2, result);
|
|
3828
|
+
}
|
|
3829
|
+
return Math.sin(value);
|
|
3952
3830
|
}
|
|
3953
|
-
function
|
|
3954
|
-
|
|
3831
|
+
function cos(value) {
|
|
3832
|
+
if (value instanceof Series) {
|
|
3833
|
+
const bars2 = value.bars;
|
|
3834
|
+
const valueArray = value.toArray();
|
|
3835
|
+
const length2 = bars2.length;
|
|
3836
|
+
const result = [];
|
|
3837
|
+
for (let i = 0; i < length2; i++) {
|
|
3838
|
+
const v = valueArray[i] ?? NaN;
|
|
3839
|
+
result.push(Math.cos(v));
|
|
3840
|
+
}
|
|
3841
|
+
return Series.fromArray(bars2, result);
|
|
3842
|
+
}
|
|
3843
|
+
return Math.cos(value);
|
|
3955
3844
|
}
|
|
3956
|
-
function
|
|
3957
|
-
|
|
3845
|
+
function tan(value) {
|
|
3846
|
+
if (value instanceof Series) {
|
|
3847
|
+
const bars2 = value.bars;
|
|
3848
|
+
const valueArray = value.toArray();
|
|
3849
|
+
const length2 = bars2.length;
|
|
3850
|
+
const result = [];
|
|
3851
|
+
for (let i = 0; i < length2; i++) {
|
|
3852
|
+
const v = valueArray[i] ?? NaN;
|
|
3853
|
+
result.push(Math.tan(v));
|
|
3854
|
+
}
|
|
3855
|
+
return Series.fromArray(bars2, result);
|
|
3856
|
+
}
|
|
3857
|
+
return Math.tan(value);
|
|
3958
3858
|
}
|
|
3959
|
-
function
|
|
3960
|
-
|
|
3859
|
+
function asin(value) {
|
|
3860
|
+
if (value instanceof Series) {
|
|
3861
|
+
const bars2 = value.bars;
|
|
3862
|
+
const valueArray = value.toArray();
|
|
3863
|
+
const length2 = bars2.length;
|
|
3864
|
+
const result = [];
|
|
3865
|
+
for (let i = 0; i < length2; i++) {
|
|
3866
|
+
const v = valueArray[i] ?? NaN;
|
|
3867
|
+
result.push(Math.asin(v));
|
|
3868
|
+
}
|
|
3869
|
+
return Series.fromArray(bars2, result);
|
|
3870
|
+
}
|
|
3871
|
+
return Math.asin(value);
|
|
3961
3872
|
}
|
|
3962
|
-
function
|
|
3963
|
-
|
|
3964
|
-
const
|
|
3965
|
-
const
|
|
3966
|
-
const
|
|
3967
|
-
const
|
|
3968
|
-
|
|
3969
|
-
|
|
3873
|
+
function acos(value) {
|
|
3874
|
+
if (value instanceof Series) {
|
|
3875
|
+
const bars2 = value.bars;
|
|
3876
|
+
const valueArray = value.toArray();
|
|
3877
|
+
const length2 = bars2.length;
|
|
3878
|
+
const result = [];
|
|
3879
|
+
for (let i = 0; i < length2; i++) {
|
|
3880
|
+
const v = valueArray[i] ?? NaN;
|
|
3881
|
+
result.push(Math.acos(v));
|
|
3882
|
+
}
|
|
3883
|
+
return Series.fromArray(bars2, result);
|
|
3884
|
+
}
|
|
3885
|
+
return Math.acos(value);
|
|
3970
3886
|
}
|
|
3971
|
-
function
|
|
3972
|
-
|
|
3887
|
+
function atan(value) {
|
|
3888
|
+
if (value instanceof Series) {
|
|
3889
|
+
const bars2 = value.bars;
|
|
3890
|
+
const valueArray = value.toArray();
|
|
3891
|
+
const length2 = bars2.length;
|
|
3892
|
+
const result = [];
|
|
3893
|
+
for (let i = 0; i < length2; i++) {
|
|
3894
|
+
const v = valueArray[i] ?? NaN;
|
|
3895
|
+
result.push(Math.atan(v));
|
|
3896
|
+
}
|
|
3897
|
+
return Series.fromArray(bars2, result);
|
|
3898
|
+
}
|
|
3899
|
+
return Math.atan(value);
|
|
3973
3900
|
}
|
|
3974
|
-
function
|
|
3975
|
-
|
|
3901
|
+
function toradians(degrees) {
|
|
3902
|
+
if (degrees instanceof Series) {
|
|
3903
|
+
const bars2 = degrees.bars;
|
|
3904
|
+
const valueArray = degrees.toArray();
|
|
3905
|
+
const length2 = bars2.length;
|
|
3906
|
+
const result = [];
|
|
3907
|
+
for (let i = 0; i < length2; i++) {
|
|
3908
|
+
const v = valueArray[i] ?? NaN;
|
|
3909
|
+
result.push(v * (Math.PI / 180));
|
|
3910
|
+
}
|
|
3911
|
+
return Series.fromArray(bars2, result);
|
|
3912
|
+
}
|
|
3913
|
+
return degrees * (Math.PI / 180);
|
|
3976
3914
|
}
|
|
3977
|
-
function
|
|
3978
|
-
|
|
3915
|
+
function todegrees(radians) {
|
|
3916
|
+
if (radians instanceof Series) {
|
|
3917
|
+
const bars2 = radians.bars;
|
|
3918
|
+
const valueArray = radians.toArray();
|
|
3919
|
+
const length2 = bars2.length;
|
|
3920
|
+
const result = [];
|
|
3921
|
+
for (let i = 0; i < length2; i++) {
|
|
3922
|
+
const v = valueArray[i] ?? NaN;
|
|
3923
|
+
result.push(v * (180 / Math.PI));
|
|
3924
|
+
}
|
|
3925
|
+
return Series.fromArray(bars2, result);
|
|
3926
|
+
}
|
|
3927
|
+
return radians * (180 / Math.PI);
|
|
3979
3928
|
}
|
|
3980
|
-
function
|
|
3981
|
-
|
|
3929
|
+
function random(min4, max4, _seed) {
|
|
3930
|
+
const rand = Math.random();
|
|
3931
|
+
if (min4 !== void 0 && max4 !== void 0) {
|
|
3932
|
+
return min4 + rand * (max4 - min4);
|
|
3933
|
+
}
|
|
3934
|
+
return rand;
|
|
3982
3935
|
}
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
// src/str/numberformat.ts
|
|
4012
|
-
var patterns = /* @__PURE__ */ new Map();
|
|
4013
|
-
function parsePattern(pattern) {
|
|
4014
|
-
const cached = patterns.get(pattern);
|
|
4015
|
-
if (cached) return cached;
|
|
4016
|
-
let prefix = "";
|
|
4017
|
-
let suffix = "";
|
|
4018
|
-
let number2 = "";
|
|
4019
|
-
let percent = false;
|
|
4020
|
-
let phase = "prefix";
|
|
4021
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
4022
|
-
const ch = pattern[i];
|
|
4023
|
-
let literal;
|
|
4024
|
-
if (ch === "'") {
|
|
4025
|
-
const end = pattern.indexOf("'", i + 1);
|
|
4026
|
-
if (end === i + 1) {
|
|
4027
|
-
literal = "'";
|
|
4028
|
-
i = end;
|
|
4029
|
-
} else {
|
|
4030
|
-
literal = pattern.slice(i + 1, end === -1 ? void 0 : end);
|
|
4031
|
-
i = end === -1 ? pattern.length : end;
|
|
3936
|
+
function sign(value) {
|
|
3937
|
+
if (value instanceof Series) {
|
|
3938
|
+
const bars2 = value.bars;
|
|
3939
|
+
const valueArray = value.toArray();
|
|
3940
|
+
const length2 = bars2.length;
|
|
3941
|
+
const result = [];
|
|
3942
|
+
for (let i = 0; i < length2; i++) {
|
|
3943
|
+
const v = valueArray[i] ?? NaN;
|
|
3944
|
+
result.push(v > 0 ? 1 : v < 0 ? -1 : 0);
|
|
3945
|
+
}
|
|
3946
|
+
return Series.fromArray(bars2, result);
|
|
3947
|
+
}
|
|
3948
|
+
return value > 0 ? 1 : value < 0 ? -1 : 0;
|
|
3949
|
+
}
|
|
3950
|
+
function round_to_mintick(number2, mintick) {
|
|
3951
|
+
if (number2 instanceof Series) {
|
|
3952
|
+
const bars2 = number2.bars;
|
|
3953
|
+
const valueArray = number2.toArray();
|
|
3954
|
+
const length2 = bars2.length;
|
|
3955
|
+
if (mintick === void 0) {
|
|
3956
|
+
throw new Error(
|
|
3957
|
+
"math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3960
|
+
const result = [];
|
|
3961
|
+
if (mintick === 0) {
|
|
3962
|
+
for (let i = 0; i < length2; i++) {
|
|
3963
|
+
result.push(valueArray[i] ?? NaN);
|
|
4032
3964
|
}
|
|
4033
|
-
} else if (phase !== "suffix" && ("#0,.".includes(ch) || phase === "number" && ch === "E")) {
|
|
4034
|
-
phase = "number";
|
|
4035
|
-
number2 += ch;
|
|
4036
|
-
continue;
|
|
4037
3965
|
} else {
|
|
4038
|
-
|
|
4039
|
-
|
|
3966
|
+
for (let i = 0; i < length2; i++) {
|
|
3967
|
+
const v = valueArray[i];
|
|
3968
|
+
if (v === void 0 || isNaN(v)) {
|
|
3969
|
+
result.push(NaN);
|
|
3970
|
+
} else {
|
|
3971
|
+
result.push(Math.round(v / mintick) * mintick);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
4040
3974
|
}
|
|
4041
|
-
|
|
4042
|
-
if (phase === "prefix") prefix += literal;
|
|
4043
|
-
else suffix += literal;
|
|
3975
|
+
return Series.fromArray(bars2, result);
|
|
4044
3976
|
}
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
exponent: exp2 === void 0 ? null : exp2.length,
|
|
4058
|
-
hasDigits: /[0#]/.test(mantissa)
|
|
4059
|
-
};
|
|
4060
|
-
patterns.set(pattern, parsed);
|
|
4061
|
-
return parsed;
|
|
4062
|
-
}
|
|
4063
|
-
function shortestDigits(x) {
|
|
4064
|
-
const m = /^(\d+)(?:\.(\d+))?(?:e([+-]\d+))?$/.exec(String(Math.abs(x)));
|
|
4065
|
-
const all7 = m[1] + (m[2] ?? "");
|
|
4066
|
-
let intLen = m[1].length + Number(m[3] ?? 0);
|
|
4067
|
-
const lead = all7.length - all7.replace(/^0+/, "").length;
|
|
4068
|
-
intLen -= lead;
|
|
4069
|
-
const digits = all7.slice(lead).replace(/0+$/, "");
|
|
4070
|
-
return { digits, intLen: digits ? intLen : 0 };
|
|
3977
|
+
if (isNaN(number2)) {
|
|
3978
|
+
return NaN;
|
|
3979
|
+
}
|
|
3980
|
+
if (mintick === void 0) {
|
|
3981
|
+
throw new Error(
|
|
3982
|
+
"math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
if (mintick === 0) {
|
|
3986
|
+
return number2;
|
|
3987
|
+
}
|
|
3988
|
+
return Math.round(number2 / mintick) * mintick;
|
|
4071
3989
|
}
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
3990
|
+
var pi = Math.PI;
|
|
3991
|
+
var e = Math.E;
|
|
3992
|
+
var phi = 1.618033988749895;
|
|
3993
|
+
var rphi = 0.618033988749895;
|
|
3994
|
+
|
|
3995
|
+
// src/timeframe/index.ts
|
|
3996
|
+
var timeframe_exports = {};
|
|
3997
|
+
__export(timeframe_exports, {
|
|
3998
|
+
from_seconds: () => from_seconds,
|
|
3999
|
+
in_seconds: () => in_seconds,
|
|
4000
|
+
info: () => info
|
|
4001
|
+
});
|
|
4002
|
+
var SECONDS_PER_MINUTE = 60;
|
|
4003
|
+
var SECONDS_PER_DAY = 86400;
|
|
4004
|
+
var SECONDS_PER_WEEK = 604800;
|
|
4005
|
+
var SECONDS_PER_MONTH = 2628003;
|
|
4006
|
+
var SECONDS_365_DAYS = 365 * SECONDS_PER_DAY;
|
|
4007
|
+
var UNIT_SECONDS = {
|
|
4008
|
+
S: 1,
|
|
4009
|
+
"": SECONDS_PER_MINUTE,
|
|
4010
|
+
D: SECONDS_PER_DAY,
|
|
4011
|
+
W: SECONDS_PER_WEEK,
|
|
4012
|
+
M: SECONDS_PER_MONTH
|
|
4013
|
+
};
|
|
4014
|
+
var SECOND_TIMEFRAMES = [1, 5, 10, 15, 30];
|
|
4015
|
+
function in_seconds(timeframe2) {
|
|
4016
|
+
const text2 = timeframe2.trim();
|
|
4017
|
+
if (text2 === "") {
|
|
4018
|
+
throw new RangeError("timeframe.in_seconds() needs a timeframe: the chart timeframe is not known here");
|
|
4090
4019
|
}
|
|
4091
|
-
const
|
|
4092
|
-
const
|
|
4093
|
-
|
|
4094
|
-
|
|
4020
|
+
const before = /^(\d*)([SDWMT]?)$/.exec(text2);
|
|
4021
|
+
const after = before ? null : /^([SDWMT])(\d+)$/.exec(text2);
|
|
4022
|
+
if (!before && !after) throw new RangeError(`Invalid timeframe "${timeframe2}"`);
|
|
4023
|
+
const [digits, unit] = before ? [before[1], before[2]] : [after[2], after[1]];
|
|
4024
|
+
if (unit === "T") return NaN;
|
|
4025
|
+
const multiplier = digits === "" ? 1 : Number(digits);
|
|
4026
|
+
return multiplier * UNIT_SECONDS[unit];
|
|
4095
4027
|
}
|
|
4096
|
-
function
|
|
4097
|
-
|
|
4098
|
-
if (
|
|
4099
|
-
if (
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
else if (d.digits.length > keep + 1) up = true;
|
|
4105
|
-
else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
|
|
4106
|
-
let kept = d.digits.slice(0, keep);
|
|
4107
|
-
let intLen = d.intLen;
|
|
4108
|
-
if (up) {
|
|
4109
|
-
const n = (BigInt(kept || "0") + 1n).toString();
|
|
4110
|
-
if (n.length > kept.length) intLen += 1;
|
|
4111
|
-
kept = n;
|
|
4028
|
+
function from_seconds(seconds) {
|
|
4029
|
+
if (Number.isNaN(seconds)) throw new RangeError("timeframe.from_seconds() needs a number of seconds");
|
|
4030
|
+
if (seconds >= SECONDS_365_DAYS) return "12M";
|
|
4031
|
+
if (seconds > 0) {
|
|
4032
|
+
if (seconds % SECONDS_PER_MONTH === 0) return `${seconds / SECONDS_PER_MONTH}M`;
|
|
4033
|
+
if (seconds % SECONDS_PER_WEEK === 0) return `${seconds / SECONDS_PER_WEEK}W`;
|
|
4034
|
+
if (seconds % SECONDS_PER_DAY === 0) return `${seconds / SECONDS_PER_DAY}D`;
|
|
4035
|
+
if (seconds % SECONDS_PER_MINUTE === 0 && seconds < SECONDS_PER_DAY) return `${seconds / SECONDS_PER_MINUTE}`;
|
|
4112
4036
|
}
|
|
4113
|
-
|
|
4114
|
-
|
|
4037
|
+
const second3 = SECOND_TIMEFRAMES.find((m) => seconds <= m);
|
|
4038
|
+
if (second3 !== void 0) return `${second3}S`;
|
|
4039
|
+
if (seconds < SECONDS_PER_DAY) return `${Math.ceil(seconds / SECONDS_PER_MINUTE)}`;
|
|
4040
|
+
return `${Math.ceil(seconds / SECONDS_PER_DAY)}D`;
|
|
4115
4041
|
}
|
|
4116
|
-
function
|
|
4117
|
-
|
|
4118
|
-
const
|
|
4119
|
-
|
|
4120
|
-
|
|
4042
|
+
function info(timeframe2) {
|
|
4043
|
+
const text2 = timeframe2.trim();
|
|
4044
|
+
const m = /^(\d*)([SDWMT]?)$/.exec(text2);
|
|
4045
|
+
if (!m || text2 === "") throw new RangeError(`Invalid timeframe "${timeframe2}"`);
|
|
4046
|
+
const unit = m[2];
|
|
4047
|
+
const multiplier = m[1] === "" ? 1 : Number(m[1]);
|
|
4048
|
+
return {
|
|
4049
|
+
period: unit === "" ? String(multiplier) : `${multiplier}${unit}`,
|
|
4050
|
+
multiplier,
|
|
4051
|
+
isintraday: unit === "" || unit === "S" || unit === "T",
|
|
4052
|
+
isdaily: unit === "D",
|
|
4053
|
+
isweekly: unit === "W",
|
|
4054
|
+
ismonthly: unit === "M",
|
|
4055
|
+
isminutes: unit === "",
|
|
4056
|
+
isseconds: unit === "S",
|
|
4057
|
+
isticks: unit === "T",
|
|
4058
|
+
isdwm: unit === "D" || unit === "W" || unit === "M"
|
|
4059
|
+
};
|
|
4121
4060
|
}
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4061
|
+
|
|
4062
|
+
// src/time/timezone.ts
|
|
4063
|
+
var MS_PER_SECOND = 1e3;
|
|
4064
|
+
var MS_PER_DAY = 864e5;
|
|
4065
|
+
var MAX_OFFSET_SECONDS = 18 * 3600;
|
|
4066
|
+
var OFFSET_NOTATION = /^(?:UTC|GMT)(?:([+-])(\d{1,2})(?::?(\d{2})(?::?(\d{2}))?)?)?$/;
|
|
4067
|
+
var REJECTED_NAMES = /* @__PURE__ */ new Set(["EST", "MST", "HST"]);
|
|
4068
|
+
var canonicalNames;
|
|
4069
|
+
function hasTimeZoneCase(name) {
|
|
4070
|
+
if (name.split("/").some((segment) => !/^[A-Z]/.test(segment))) return false;
|
|
4071
|
+
canonicalNames ??= new Map(Intl.supportedValuesOf("timeZone").map((n) => [n.toLowerCase(), n]));
|
|
4072
|
+
const canonical = canonicalNames.get(name.toLowerCase());
|
|
4073
|
+
return canonical === void 0 || canonical === name;
|
|
4129
4074
|
}
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
if (
|
|
4134
|
-
const
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
if (
|
|
4139
|
-
const
|
|
4140
|
-
const
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
if (
|
|
4145
|
-
|
|
4146
|
-
mant = { digits: mant.digits, intLen: intDigits };
|
|
4147
|
-
}
|
|
4148
|
-
const e2 = String(Math.abs(exponent)).padStart(p.exponent, "0");
|
|
4149
|
-
body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e2;
|
|
4075
|
+
var zones = /* @__PURE__ */ new Map();
|
|
4076
|
+
function resolveZone(timezone) {
|
|
4077
|
+
let zone = zones.get(timezone);
|
|
4078
|
+
if (zone) return zone;
|
|
4079
|
+
const invalid = () => new RangeError(
|
|
4080
|
+
`Invalid time zone "${timezone}": use UTC/GMT offset notation ("UTC-5", "GMT+0530") or an IANA name ("America/New_York")`
|
|
4081
|
+
);
|
|
4082
|
+
const m = OFFSET_NOTATION.exec(timezone);
|
|
4083
|
+
if (m) {
|
|
4084
|
+
const [, sign2, h = "0", mi = "0", s] = m;
|
|
4085
|
+
const minutes = Number(mi);
|
|
4086
|
+
const seconds = s === void 0 ? 0 : Number(s);
|
|
4087
|
+
if (minutes > 59 || seconds > 59 || s !== void 0 && h.length !== 2) throw invalid();
|
|
4088
|
+
const offsetSeconds = Number(h) * 3600 + minutes * 60 + seconds;
|
|
4089
|
+
if (offsetSeconds > MAX_OFFSET_SECONDS) throw invalid();
|
|
4090
|
+
zone = { kind: "fixed", offsetMs: (sign2 === "-" ? -offsetSeconds : offsetSeconds) * MS_PER_SECOND };
|
|
4150
4091
|
} else {
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4092
|
+
if (REJECTED_NAMES.has(timezone) || !hasTimeZoneCase(timezone)) throw invalid();
|
|
4093
|
+
let format2;
|
|
4094
|
+
try {
|
|
4095
|
+
format2 = new Intl.DateTimeFormat("en-US", {
|
|
4096
|
+
timeZone: timezone,
|
|
4097
|
+
hourCycle: "h23",
|
|
4098
|
+
year: "numeric",
|
|
4099
|
+
month: "numeric",
|
|
4100
|
+
day: "numeric",
|
|
4101
|
+
hour: "numeric",
|
|
4102
|
+
minute: "numeric",
|
|
4103
|
+
second: "numeric"
|
|
4104
|
+
});
|
|
4105
|
+
} catch {
|
|
4106
|
+
throw invalid();
|
|
4107
|
+
}
|
|
4108
|
+
zone = { kind: "iana", format: format2 };
|
|
4154
4109
|
}
|
|
4155
|
-
|
|
4156
|
-
return
|
|
4110
|
+
zones.set(timezone, zone);
|
|
4111
|
+
return zone;
|
|
4157
4112
|
}
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
"April",
|
|
4165
|
-
"May",
|
|
4166
|
-
"June",
|
|
4167
|
-
"July",
|
|
4168
|
-
"August",
|
|
4169
|
-
"September",
|
|
4170
|
-
"October",
|
|
4171
|
-
"November",
|
|
4172
|
-
"December"
|
|
4173
|
-
];
|
|
4174
|
-
var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
4175
|
-
var pad = (n, width) => String(n).padStart(width, "0");
|
|
4176
|
-
function zoneName(time2, timezone, offsetMs) {
|
|
4177
|
-
if (timezone === "Etc/UTC") return "UTC";
|
|
4178
|
-
if (/^(UTC|GMT)/.test(timezone)) {
|
|
4179
|
-
if (offsetMs === 0) return "GMT";
|
|
4180
|
-
const minutes = Math.abs(offsetMs) / 6e4;
|
|
4181
|
-
return `GMT${offsetMs < 0 ? "-" : "+"}${pad(Math.floor(minutes / 60), 2)}:${pad(minutes % 60, 2)}`;
|
|
4113
|
+
function offsetAt(zone, time2) {
|
|
4114
|
+
if (zone.kind === "fixed") return zone.offsetMs;
|
|
4115
|
+
const wholeSecond = Math.floor(time2 / 1e3) * 1e3;
|
|
4116
|
+
const f = {};
|
|
4117
|
+
for (const part of zone.format.formatToParts(wholeSecond)) {
|
|
4118
|
+
if (part.type !== "literal") f[part.type] = Number(part.value);
|
|
4182
4119
|
}
|
|
4183
|
-
const
|
|
4184
|
-
return
|
|
4120
|
+
const local = Date.UTC(f.year, f.month - 1, f.day, f.hour, f.minute, f.second);
|
|
4121
|
+
return local - wholeSecond;
|
|
4185
4122
|
}
|
|
4186
|
-
function
|
|
4187
|
-
|
|
4188
|
-
const dow = new Date(date).getUTCDay();
|
|
4189
|
-
const weekStart2 = date - dow * 864e5;
|
|
4190
|
-
if (weekStart2 + 6 * 864e5 >= Date.UTC(year3 + 1, 0, 1)) return 1;
|
|
4191
|
-
const jan1 = Date.UTC(year3, 0, 1);
|
|
4192
|
-
const jan1Dow = new Date(jan1).getUTCDay();
|
|
4193
|
-
return Math.floor((date - jan1 + jan1Dow * 864e5) / (7 * 864e5)) + 1;
|
|
4123
|
+
function zoneOffset(time2, timezone) {
|
|
4124
|
+
return offsetAt(resolveZone(timezone), time2);
|
|
4194
4125
|
}
|
|
4195
|
-
function
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
out += ch;
|
|
4217
|
-
i++;
|
|
4218
|
-
continue;
|
|
4219
|
-
}
|
|
4220
|
-
let n = 1;
|
|
4221
|
-
while (pattern[i + n] === ch) n++;
|
|
4222
|
-
i += n;
|
|
4223
|
-
const hour12 = f.hour % 12;
|
|
4224
|
-
switch (ch) {
|
|
4225
|
-
case "y":
|
|
4226
|
-
out += n === 2 ? pad(f.year % 100, 2) : pad(f.year, n);
|
|
4227
|
-
break;
|
|
4228
|
-
case "M":
|
|
4229
|
-
out += n >= 4 ? MONTHS2[f.month - 1] : n === 3 ? MONTHS2[f.month - 1].slice(0, 3) : pad(f.month, n);
|
|
4230
|
-
break;
|
|
4231
|
-
case "d":
|
|
4232
|
-
out += pad(f.day, n);
|
|
4233
|
-
break;
|
|
4234
|
-
case "E":
|
|
4235
|
-
out += n >= 4 ? DAYS[f.dayOfWeek - 1] : DAYS[f.dayOfWeek - 1].slice(0, 3);
|
|
4236
|
-
break;
|
|
4237
|
-
case "u":
|
|
4238
|
-
out += pad(f.dayOfWeek === 1 ? 7 : f.dayOfWeek - 1, n);
|
|
4239
|
-
break;
|
|
4240
|
-
case "D":
|
|
4241
|
-
out += pad(dayOfYear, n);
|
|
4242
|
-
break;
|
|
4243
|
-
case "w":
|
|
4244
|
-
out += pad(weekOfYear(f.year, f.month, f.day), n);
|
|
4245
|
-
break;
|
|
4246
|
-
case "H":
|
|
4247
|
-
out += pad(f.hour, n);
|
|
4248
|
-
break;
|
|
4249
|
-
case "k":
|
|
4250
|
-
out += pad(f.hour === 0 ? 24 : f.hour, n);
|
|
4251
|
-
break;
|
|
4252
|
-
case "K":
|
|
4253
|
-
out += pad(hour12, n);
|
|
4254
|
-
break;
|
|
4255
|
-
case "h":
|
|
4256
|
-
out += pad(hour12 === 0 ? 12 : hour12, n);
|
|
4257
|
-
break;
|
|
4258
|
-
case "a":
|
|
4259
|
-
out += f.hour < 12 ? "AM" : "PM";
|
|
4260
|
-
break;
|
|
4261
|
-
case "m":
|
|
4262
|
-
out += pad(f.minute, n);
|
|
4263
|
-
break;
|
|
4264
|
-
case "s":
|
|
4265
|
-
out += pad(f.second, n);
|
|
4266
|
-
break;
|
|
4267
|
-
case "S":
|
|
4268
|
-
out += pad(ms, n);
|
|
4269
|
-
break;
|
|
4270
|
-
case "Z": {
|
|
4271
|
-
const minutes = Math.abs(offset) / 6e4;
|
|
4272
|
-
out += (offset < 0 ? "-" : "+") + pad(Math.floor(minutes / 60), 2) + pad(minutes % 60, 2);
|
|
4273
|
-
break;
|
|
4274
|
-
}
|
|
4275
|
-
case "z":
|
|
4276
|
-
out += zoneName(time2, timezone, offset);
|
|
4277
|
-
break;
|
|
4278
|
-
default:
|
|
4279
|
-
throw new SyntaxError(`Unsupported date pattern letter "${ch}" in "${pattern}"`);
|
|
4280
|
-
}
|
|
4281
|
-
}
|
|
4282
|
-
return out;
|
|
4126
|
+
function zonedFields(time2, timezone) {
|
|
4127
|
+
const local = new Date(time2 + offsetAt(resolveZone(timezone), time2));
|
|
4128
|
+
return {
|
|
4129
|
+
year: local.getUTCFullYear(),
|
|
4130
|
+
month: local.getUTCMonth() + 1,
|
|
4131
|
+
day: local.getUTCDate(),
|
|
4132
|
+
hour: local.getUTCHours(),
|
|
4133
|
+
minute: local.getUTCMinutes(),
|
|
4134
|
+
second: local.getUTCSeconds(),
|
|
4135
|
+
dayOfWeek: local.getUTCDay() + 1
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
function zonedToUnix(timezone, year3, month3, day, hour3 = 0, minute3 = 0, second3 = 0) {
|
|
4139
|
+
const zone = resolveZone(timezone);
|
|
4140
|
+
const local = Date.UTC(year3, month3 - 1, day, hour3, minute3, second3);
|
|
4141
|
+
if (zone.kind === "fixed") return local - zone.offsetMs;
|
|
4142
|
+
const before = offsetAt(zone, local - MS_PER_DAY);
|
|
4143
|
+
const after = offsetAt(zone, local + MS_PER_DAY);
|
|
4144
|
+
const candidates = [local - before, local - after].filter((t2) => local - offsetAt(zone, t2) === t2);
|
|
4145
|
+
if (candidates.length) return Math.max(...candidates);
|
|
4146
|
+
return local - before;
|
|
4283
4147
|
}
|
|
4284
4148
|
|
|
4285
|
-
// src/
|
|
4286
|
-
var
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4149
|
+
// src/time/datestring.ts
|
|
4150
|
+
var MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
|
|
4151
|
+
var FULL_MONTHS = [
|
|
4152
|
+
"january",
|
|
4153
|
+
"february",
|
|
4154
|
+
"march",
|
|
4155
|
+
"april",
|
|
4156
|
+
"may",
|
|
4157
|
+
"june",
|
|
4158
|
+
"july",
|
|
4159
|
+
"august",
|
|
4160
|
+
"september",
|
|
4161
|
+
"october",
|
|
4162
|
+
"november",
|
|
4163
|
+
"december"
|
|
4164
|
+
];
|
|
4165
|
+
var ZONE_NAMES = {
|
|
4166
|
+
ut: 0,
|
|
4167
|
+
utc: 0,
|
|
4168
|
+
gmt: 0,
|
|
4169
|
+
est: -5,
|
|
4170
|
+
edt: -4,
|
|
4171
|
+
cst: -6,
|
|
4172
|
+
cdt: -5,
|
|
4173
|
+
mst: -7,
|
|
4174
|
+
mdt: -6,
|
|
4175
|
+
pst: -8,
|
|
4176
|
+
pdt: -7
|
|
4297
4177
|
};
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
}
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
return
|
|
4178
|
+
var TIME = String.raw`(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3})\d*)?)?`;
|
|
4179
|
+
var ZONE = String.raw`(ut|utc|gmt|[ecmp][sd]t|(?:utc|gmt)?\s*[+-]\d{1,2}(?::?\d{2})?)`;
|
|
4180
|
+
var NUMERIC_ZONE = String.raw`([+-]\d{2}(?::?\d{2})?)`;
|
|
4181
|
+
var DATE = String.raw`(\d{4})([-/])(\d{1,2})\2(\d{1,2})`;
|
|
4182
|
+
var WEEKDAY = String.raw`(?:[a-z]{3,9},?\s+)?`;
|
|
4183
|
+
var ISO_T = new RegExp(String.raw`^${DATE}t${TIME}\s*${NUMERIC_ZONE}?$`, "i");
|
|
4184
|
+
var ISO = new RegExp(String.raw`^${DATE}(?:\s+${TIME})?\s*${ZONE}?$`, "i");
|
|
4185
|
+
var DAY_FIRST = new RegExp(String.raw`^${WEEKDAY}(\d{1,2})(?:\s+|-)([a-z]+)\.?,?(?:\s+|-)(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
|
|
4186
|
+
var MONTH_FIRST = new RegExp(String.raw`^${WEEKDAY}([a-z]+)\.?\s+(\d{1,2}),?\s+(\d{4})(?:,?\s+${TIME})?\s*${ZONE}?$`, "i");
|
|
4187
|
+
function monthNumber(name) {
|
|
4188
|
+
const lower2 = name.toLowerCase();
|
|
4189
|
+
const i = FULL_MONTHS.findIndex((full, k) => lower2.startsWith(MONTHS[k]) && full.startsWith(lower2));
|
|
4190
|
+
return i + 1;
|
|
4311
4191
|
}
|
|
4312
|
-
function
|
|
4313
|
-
if (
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
if (s === "currency") return number(value, "'$'#,##0.00");
|
|
4322
|
-
return number(value, style);
|
|
4323
|
-
}
|
|
4324
|
-
case "date":
|
|
4325
|
-
case "time": {
|
|
4326
|
-
const styles = type.trim() === "date" ? DATE_STYLES : TIME_STYLES;
|
|
4327
|
-
const s = style?.trim() ?? "medium";
|
|
4328
|
-
return formatDate(Number(value), styles[s] ?? style, typeof timezone === "function" ? timezone() : timezone);
|
|
4329
|
-
}
|
|
4330
|
-
case "choice":
|
|
4331
|
-
return choice(Number(value), style ?? "");
|
|
4332
|
-
default:
|
|
4333
|
-
throw new SyntaxError(`Unknown format type "${type}"`);
|
|
4334
|
-
}
|
|
4192
|
+
function zoneMinutes(zone) {
|
|
4193
|
+
if (!zone) return 0;
|
|
4194
|
+
const lower2 = zone.toLowerCase().replace(/\s+/g, "");
|
|
4195
|
+
const named = ZONE_NAMES[lower2];
|
|
4196
|
+
if (named !== void 0) return named * 60;
|
|
4197
|
+
const m = /^(?:utc|gmt)?([+-])(\d{1,2})(?::?(\d{2}))?$/.exec(lower2);
|
|
4198
|
+
if (!m) return NaN;
|
|
4199
|
+
const minutes = Number(m[2]) * 60 + (m[3] ? Number(m[3]) : 0);
|
|
4200
|
+
return m[1] === "-" ? -minutes : minutes;
|
|
4335
4201
|
}
|
|
4336
|
-
function
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
out += ch;
|
|
4353
|
-
i++;
|
|
4354
|
-
continue;
|
|
4355
|
-
}
|
|
4356
|
-
let depth = 1;
|
|
4357
|
-
let j = i + 1;
|
|
4358
|
-
let inQuote = false;
|
|
4359
|
-
for (; j < pattern.length && depth > 0; j++) {
|
|
4360
|
-
const c = pattern[j];
|
|
4361
|
-
if (c === "'") inQuote = !inQuote;
|
|
4362
|
-
else if (!inQuote && c === "{") depth++;
|
|
4363
|
-
else if (!inQuote && c === "}") depth--;
|
|
4364
|
-
}
|
|
4365
|
-
const inner = pattern.slice(i + 1, j - 1);
|
|
4366
|
-
const [index = "", type, ...rest] = inner.split(",");
|
|
4367
|
-
const style = rest.length ? rest.join(",") : void 0;
|
|
4368
|
-
const n = Number(index.trim());
|
|
4369
|
-
out += n < args.length ? formatArgument(args[n], type, style, timezone) : `{${inner}}`;
|
|
4370
|
-
i = j;
|
|
4202
|
+
function parseDateString(dateString) {
|
|
4203
|
+
const text2 = dateString.trim();
|
|
4204
|
+
let year3, month3, day;
|
|
4205
|
+
let rest;
|
|
4206
|
+
let m;
|
|
4207
|
+
if (m = ISO_T.exec(text2) ?? ISO.exec(text2)) {
|
|
4208
|
+
[year3, month3, day] = [m[1], Number(m[3]), m[4]];
|
|
4209
|
+
rest = m.slice(5);
|
|
4210
|
+
} else if (m = DAY_FIRST.exec(text2)) {
|
|
4211
|
+
[day, month3, year3] = [m[1], monthNumber(m[2]), m[3]];
|
|
4212
|
+
rest = m.slice(4);
|
|
4213
|
+
} else if (m = MONTH_FIRST.exec(text2)) {
|
|
4214
|
+
[month3, day, year3] = [monthNumber(m[1]), m[2], m[3]];
|
|
4215
|
+
rest = m.slice(4);
|
|
4216
|
+
} else {
|
|
4217
|
+
throw new SyntaxError(`Invalid date string "${dateString}": use RFC 2822 or ISO 8601 ("20 Feb 2020", "2020-02-20T14:48:00")`);
|
|
4371
4218
|
}
|
|
4372
|
-
|
|
4219
|
+
const [hour3 = "0", minute3 = "0", second3 = "0", millis = "0", zone] = rest;
|
|
4220
|
+
const d = Number(day);
|
|
4221
|
+
const h = Number(hour3);
|
|
4222
|
+
const mi = Number(minute3);
|
|
4223
|
+
const s = Number(second3);
|
|
4224
|
+
const offset = zoneMinutes(zone);
|
|
4225
|
+
const endOfDay = h === 24 && mi === 0 && s === 0 && Number(millis) === 0;
|
|
4226
|
+
if (month3 < 1 || month3 > 12 || d < 1 || d > 31 || h > 23 && !endOfDay || mi > 59 || s > 59 || Number.isNaN(offset)) {
|
|
4227
|
+
throw new SyntaxError(`Invalid date string "${dateString}": a field is out of range`);
|
|
4228
|
+
}
|
|
4229
|
+
const ms = Number(millis.padEnd(3, "0"));
|
|
4230
|
+
return Date.UTC(Number(year3), month3 - 1, d, h, mi, s, ms) - offset * 6e4;
|
|
4373
4231
|
}
|
|
4374
4232
|
|
|
4375
|
-
// src/
|
|
4376
|
-
|
|
4377
|
-
|
|
4233
|
+
// src/time/session.ts
|
|
4234
|
+
var PERIOD = /^(\d{2})(\d{2})-(\d{2})(\d{2})$/;
|
|
4235
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
4236
|
+
function parseSession(session2) {
|
|
4237
|
+
let parsed = sessions.get(session2);
|
|
4238
|
+
if (parsed) return parsed;
|
|
4239
|
+
const invalid = () => new SyntaxError(`Invalid session "${session2}": expected "HHmm-HHmm[,HHmm-HHmm...][:days]"`);
|
|
4240
|
+
const text2 = session2.trim() === "24x7" ? "0000-0000" : session2.replace(/\s+/g, "");
|
|
4241
|
+
const [periodText = "", dayText, extra] = text2.split(":");
|
|
4242
|
+
if (extra !== void 0 || periodText === "") throw invalid();
|
|
4243
|
+
const periods = periodText.split(",").map((p) => {
|
|
4244
|
+
const m = PERIOD.exec(p);
|
|
4245
|
+
if (!m) throw invalid();
|
|
4246
|
+
const [h1, m1, h2, m2] = [m[1], m[2], m[3], m[4]].map(Number);
|
|
4247
|
+
if (h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) throw invalid();
|
|
4248
|
+
return { start: h1 * 60 + m1, end: h2 * 60 + m2 };
|
|
4249
|
+
});
|
|
4250
|
+
const dayDigits = dayText ?? (periods.length > 1 ? "23456" : "1234567");
|
|
4251
|
+
if (!/^[1-7]+$/.test(dayDigits)) throw invalid();
|
|
4252
|
+
const days = new Array(8).fill(false);
|
|
4253
|
+
for (const d of dayDigits) days[Number(d)] = true;
|
|
4254
|
+
parsed = { periods, days };
|
|
4255
|
+
sessions.set(session2, parsed);
|
|
4256
|
+
return parsed;
|
|
4378
4257
|
}
|
|
4379
|
-
function
|
|
4380
|
-
if (
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
case "volume": {
|
|
4392
|
-
const abs2 = Math.abs(value);
|
|
4393
|
-
for (const [size, unit] of [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]]) {
|
|
4394
|
-
if (abs2 >= size) return formatNumber(value / size, "#.###", "halfUpShortest") + unit;
|
|
4395
|
-
}
|
|
4396
|
-
return formatNumber(value, "#", "halfUpShortest");
|
|
4258
|
+
function inSession(time2, session2, timezone) {
|
|
4259
|
+
if (Number.isNaN(time2)) return false;
|
|
4260
|
+
const { periods, days } = parseSession(session2);
|
|
4261
|
+
const f = zonedFields(time2, timezone);
|
|
4262
|
+
const minute3 = f.hour * 60 + f.minute;
|
|
4263
|
+
const nextDay = f.dayOfWeek % 7 + 1;
|
|
4264
|
+
for (const { start, end } of periods) {
|
|
4265
|
+
if (start < end || start === 0 && end === 0) {
|
|
4266
|
+
if (minute3 >= start && (minute3 < end || end === 0) && days[f.dayOfWeek]) return true;
|
|
4267
|
+
} else {
|
|
4268
|
+
if (minute3 >= start && days[nextDay]) return true;
|
|
4269
|
+
if (minute3 < end && days[f.dayOfWeek]) return true;
|
|
4397
4270
|
}
|
|
4398
|
-
case "mintick":
|
|
4399
|
-
throw new Error("str.tostring(x, format.mintick) needs the symbol tick size (syminfo.mintick), which is not known here");
|
|
4400
|
-
default:
|
|
4401
|
-
return formatNumber(value, format2, "halfUpShortest");
|
|
4402
4271
|
}
|
|
4272
|
+
return false;
|
|
4403
4273
|
}
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
function upper(str2) {
|
|
4412
|
-
return str2.toUpperCase();
|
|
4413
|
-
}
|
|
4414
|
-
function lower(str2) {
|
|
4415
|
-
return str2.toLowerCase();
|
|
4416
|
-
}
|
|
4417
|
-
function contains(source, str2) {
|
|
4418
|
-
return source.includes(str2);
|
|
4419
|
-
}
|
|
4420
|
-
function pos(source, str2) {
|
|
4421
|
-
return source.indexOf(str2);
|
|
4422
|
-
}
|
|
4423
|
-
function replace(source, target, replacement, occurrence) {
|
|
4424
|
-
if (occurrence === 0) {
|
|
4425
|
-
return source.replace(target, replacement);
|
|
4426
|
-
} else {
|
|
4427
|
-
return source.replaceAll(target, replacement);
|
|
4274
|
+
|
|
4275
|
+
// src/time/index.ts
|
|
4276
|
+
function timestamp(first, ...rest) {
|
|
4277
|
+
if (typeof first === "string") {
|
|
4278
|
+
if (rest.length === 0) return parseDateString(first);
|
|
4279
|
+
const [year3, month4, day2, hour4, minute4, second4] = rest;
|
|
4280
|
+
return zonedToUnix(first, year3, month4, day2, hour4 ?? 0, minute4 ?? 0, second4 ?? 0);
|
|
4428
4281
|
}
|
|
4282
|
+
const [month3, day, hour3, minute3, second3] = rest;
|
|
4283
|
+
return new Date(first, month3 - 1, day, hour3 || 0, minute3 || 0, second3 || 0).getTime();
|
|
4429
4284
|
}
|
|
4430
|
-
function
|
|
4431
|
-
return
|
|
4285
|
+
function field(time2, timezone, pick) {
|
|
4286
|
+
return Number.isNaN(time2) ? NaN : pick(zonedFields(time2, timezone));
|
|
4432
4287
|
}
|
|
4433
|
-
function
|
|
4434
|
-
return
|
|
4288
|
+
function year(time2, timezone) {
|
|
4289
|
+
return field(time2, timezone, (f) => f.year);
|
|
4435
4290
|
}
|
|
4436
|
-
function
|
|
4437
|
-
return
|
|
4291
|
+
function month(time2, timezone) {
|
|
4292
|
+
return field(time2, timezone, (f) => f.month);
|
|
4438
4293
|
}
|
|
4439
|
-
function
|
|
4440
|
-
return
|
|
4294
|
+
function dayofmonth(time2, timezone) {
|
|
4295
|
+
return field(time2, timezone, (f) => f.day);
|
|
4441
4296
|
}
|
|
4442
|
-
function
|
|
4443
|
-
return
|
|
4297
|
+
function dayofweek(time2, timezone) {
|
|
4298
|
+
return field(time2, timezone, (f) => f.dayOfWeek);
|
|
4444
4299
|
}
|
|
4445
|
-
function
|
|
4446
|
-
return
|
|
4300
|
+
function weekofyear(time2, timezone) {
|
|
4301
|
+
return field(time2, timezone, (f) => {
|
|
4302
|
+
const date = Date.UTC(f.year, f.month - 1, f.day);
|
|
4303
|
+
const mondayBased = (f.dayOfWeek + 5) % 7;
|
|
4304
|
+
const thursday = new Date(date + (3 - mondayBased) * 864e5);
|
|
4305
|
+
const yearStart = Date.UTC(thursday.getUTCFullYear(), 0, 1);
|
|
4306
|
+
return 1 + Math.floor((thursday.getTime() - yearStart) / (7 * 864e5));
|
|
4307
|
+
});
|
|
4447
4308
|
}
|
|
4448
|
-
function
|
|
4449
|
-
return
|
|
4309
|
+
function hour(time2, timezone) {
|
|
4310
|
+
return field(time2, timezone, (f) => f.hour);
|
|
4450
4311
|
}
|
|
4451
|
-
function
|
|
4452
|
-
return
|
|
4312
|
+
function minute(time2, timezone) {
|
|
4313
|
+
return field(time2, timezone, (f) => f.minute);
|
|
4453
4314
|
}
|
|
4454
|
-
function
|
|
4455
|
-
return
|
|
4315
|
+
function second(time2, timezone) {
|
|
4316
|
+
return field(time2, timezone, (f) => f.second);
|
|
4456
4317
|
}
|
|
4457
|
-
function
|
|
4458
|
-
return
|
|
4318
|
+
function inSession2(time2, session2, timezone) {
|
|
4319
|
+
return inSession(time2, session2, timezone);
|
|
4459
4320
|
}
|
|
4460
|
-
|
|
4461
|
-
|
|
4321
|
+
|
|
4322
|
+
// src/str/index.ts
|
|
4323
|
+
var str_exports = {};
|
|
4324
|
+
__export(str_exports, {
|
|
4325
|
+
charAt: () => charAt,
|
|
4326
|
+
concat: () => concat,
|
|
4327
|
+
contains: () => contains,
|
|
4328
|
+
endswith: () => endswith,
|
|
4329
|
+
format: () => format,
|
|
4330
|
+
format_time: () => format_time,
|
|
4331
|
+
length: () => length,
|
|
4332
|
+
lower: () => lower,
|
|
4333
|
+
match: () => match,
|
|
4334
|
+
pos: () => pos,
|
|
4335
|
+
repeat: () => repeat,
|
|
4336
|
+
replace: () => replace,
|
|
4337
|
+
replace_all: () => replace_all,
|
|
4338
|
+
split: () => split,
|
|
4339
|
+
startswith: () => startswith,
|
|
4340
|
+
substring: () => substring,
|
|
4341
|
+
tonumber: () => tonumber,
|
|
4342
|
+
tostring: () => tostring,
|
|
4343
|
+
trim: () => trim,
|
|
4344
|
+
trimLeft: () => trimLeft,
|
|
4345
|
+
trimRight: () => trimRight,
|
|
4346
|
+
upper: () => upper
|
|
4347
|
+
});
|
|
4348
|
+
|
|
4349
|
+
// src/str/numberformat.ts
|
|
4350
|
+
var patterns = /* @__PURE__ */ new Map();
|
|
4351
|
+
function parsePattern(pattern) {
|
|
4352
|
+
const cached = patterns.get(pattern);
|
|
4353
|
+
if (cached) return cached;
|
|
4354
|
+
let prefix = "";
|
|
4355
|
+
let suffix = "";
|
|
4356
|
+
let number2 = "";
|
|
4357
|
+
let percent = false;
|
|
4358
|
+
let phase = "prefix";
|
|
4359
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
4360
|
+
const ch = pattern[i];
|
|
4361
|
+
let literal;
|
|
4362
|
+
if (ch === "'") {
|
|
4363
|
+
const end = pattern.indexOf("'", i + 1);
|
|
4364
|
+
if (end === i + 1) {
|
|
4365
|
+
literal = "'";
|
|
4366
|
+
i = end;
|
|
4367
|
+
} else {
|
|
4368
|
+
literal = pattern.slice(i + 1, end === -1 ? void 0 : end);
|
|
4369
|
+
i = end === -1 ? pattern.length : end;
|
|
4370
|
+
}
|
|
4371
|
+
} else if (phase !== "suffix" && ("#0,.".includes(ch) || phase === "number" && ch === "E")) {
|
|
4372
|
+
phase = "number";
|
|
4373
|
+
number2 += ch;
|
|
4374
|
+
continue;
|
|
4375
|
+
} else {
|
|
4376
|
+
if (ch === "%") percent = true;
|
|
4377
|
+
literal = ch;
|
|
4378
|
+
}
|
|
4379
|
+
if (phase === "number") phase = "suffix";
|
|
4380
|
+
if (phase === "prefix") prefix += literal;
|
|
4381
|
+
else suffix += literal;
|
|
4382
|
+
}
|
|
4383
|
+
const [mantissa = "", exp2] = number2.split("E");
|
|
4384
|
+
const [intPart = "", fracPart = ""] = mantissa.split(".");
|
|
4385
|
+
const lastComma = intPart.lastIndexOf(",");
|
|
4386
|
+
const parsed = {
|
|
4387
|
+
prefix,
|
|
4388
|
+
suffix,
|
|
4389
|
+
// as in Java, a pattern with a decimal point and no '0' at all ("#.##") shows one integer digit
|
|
4390
|
+
minInt: !mantissa.includes("0") && mantissa.includes(".") ? 1 : [...intPart].filter((c) => c === "0").length,
|
|
4391
|
+
minFrac: [...fracPart].filter((c) => c === "0").length,
|
|
4392
|
+
maxFrac: [...fracPart].filter((c) => c === "0" || c === "#").length,
|
|
4393
|
+
grouping: lastComma === -1 ? 0 : intPart.length - lastComma - 1,
|
|
4394
|
+
percent,
|
|
4395
|
+
exponent: exp2 === void 0 ? null : exp2.length,
|
|
4396
|
+
hasDigits: /[0#]/.test(mantissa)
|
|
4397
|
+
};
|
|
4398
|
+
patterns.set(pattern, parsed);
|
|
4399
|
+
return parsed;
|
|
4462
4400
|
}
|
|
4463
|
-
function
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4401
|
+
function shortestDigits(x) {
|
|
4402
|
+
const m = /^(\d+)(?:\.(\d+))?(?:e([+-]\d+))?$/.exec(String(Math.abs(x)));
|
|
4403
|
+
const all7 = m[1] + (m[2] ?? "");
|
|
4404
|
+
let intLen = m[1].length + Number(m[3] ?? 0);
|
|
4405
|
+
const lead = all7.length - all7.replace(/^0+/, "").length;
|
|
4406
|
+
intLen -= lead;
|
|
4407
|
+
const digits = all7.slice(lead).replace(/0+$/, "");
|
|
4408
|
+
return { digits, intLen: digits ? intLen : 0 };
|
|
4467
4409
|
}
|
|
4468
|
-
function
|
|
4469
|
-
|
|
4410
|
+
function exactDigits(x) {
|
|
4411
|
+
const abs2 = Math.abs(x);
|
|
4412
|
+
if (abs2 === 0) return { digits: "", intLen: 0 };
|
|
4413
|
+
const view = new DataView(new ArrayBuffer(8));
|
|
4414
|
+
view.setFloat64(0, abs2);
|
|
4415
|
+
const bits = view.getBigUint64(0);
|
|
4416
|
+
const expBits = Number(bits >> 52n & 0x7ffn);
|
|
4417
|
+
let mant = bits & 0xfffffffffffffn;
|
|
4418
|
+
const e2 = expBits === 0 ? -1074 : expBits - 1075;
|
|
4419
|
+
if (expBits !== 0) mant |= 1n << 52n;
|
|
4420
|
+
let str2;
|
|
4421
|
+
let fracDigits;
|
|
4422
|
+
if (e2 >= 0) {
|
|
4423
|
+
str2 = (mant << BigInt(e2)).toString();
|
|
4424
|
+
fracDigits = 0;
|
|
4425
|
+
} else {
|
|
4426
|
+
str2 = (mant * 5n ** BigInt(-e2)).toString();
|
|
4427
|
+
fracDigits = -e2;
|
|
4428
|
+
}
|
|
4429
|
+
const intLen = str2.length - fracDigits;
|
|
4430
|
+
const lead = str2.length - str2.replace(/^0+/, "").length;
|
|
4431
|
+
const digits = str2.slice(lead).replace(/0+$/, "");
|
|
4432
|
+
return { digits, intLen: intLen - lead };
|
|
4470
4433
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4434
|
+
function roundDigits(d, frac, mode3) {
|
|
4435
|
+
const keep = d.intLen + frac;
|
|
4436
|
+
if (keep >= d.digits.length) return d;
|
|
4437
|
+
if (keep < 0) return { digits: "", intLen: 0 };
|
|
4438
|
+
const first = d.digits.charCodeAt(keep) - 48;
|
|
4439
|
+
let up;
|
|
4440
|
+
if (mode3 === "halfUpShortest") up = first >= 5;
|
|
4441
|
+
else if (first !== 5) up = first > 5;
|
|
4442
|
+
else if (d.digits.length > keep + 1) up = true;
|
|
4443
|
+
else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
|
|
4444
|
+
let kept = d.digits.slice(0, keep);
|
|
4445
|
+
let intLen = d.intLen;
|
|
4446
|
+
if (up) {
|
|
4447
|
+
const n = (BigInt(kept || "0") + 1n).toString();
|
|
4448
|
+
if (n.length > kept.length) intLen += 1;
|
|
4449
|
+
kept = n;
|
|
4450
|
+
}
|
|
4451
|
+
kept = kept.replace(/0+$/, "");
|
|
4452
|
+
return { digits: kept, intLen: kept ? intLen : 0 };
|
|
4486
4453
|
}
|
|
4487
|
-
function
|
|
4488
|
-
if (
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
const e2 = parseTime(b2, spec);
|
|
4493
|
-
if (s.minutes === 0 && e2.minutes === 0 && s.offset === void 0 && e2.offset === void 0) {
|
|
4494
|
-
return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };
|
|
4495
|
-
}
|
|
4496
|
-
return {
|
|
4497
|
-
start: s.minutes,
|
|
4498
|
-
end: e2.minutes,
|
|
4499
|
-
startOffset: s.offset ?? (e2.minutes <= s.minutes ? 1 : 0),
|
|
4500
|
-
endOffset: e2.offset ?? 0
|
|
4501
|
-
};
|
|
4502
|
-
});
|
|
4454
|
+
function group(int, size) {
|
|
4455
|
+
if (!size || int.length <= size) return int;
|
|
4456
|
+
const parts = [];
|
|
4457
|
+
for (let end = int.length; end > 0; end -= size) parts.unshift(int.slice(Math.max(0, end - size), end));
|
|
4458
|
+
return parts.join(",");
|
|
4503
4459
|
}
|
|
4504
|
-
function
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
if (
|
|
4510
|
-
|
|
4511
|
-
for (const d of digits) days[Number(d)] = true;
|
|
4512
|
-
return { from, periods, days };
|
|
4460
|
+
function layout(d, p) {
|
|
4461
|
+
let int = d.intLen > 0 ? d.digits.slice(0, d.intLen).padEnd(d.intLen, "0") : "";
|
|
4462
|
+
int = int.replace(/^0+/, "").padStart(p.minInt, "0");
|
|
4463
|
+
let frac = d.intLen < 0 ? "0".repeat(-d.intLen) + d.digits : d.digits.slice(Math.max(0, d.intLen));
|
|
4464
|
+
frac = frac.slice(0, p.maxFrac).replace(/0+$/, "").padEnd(p.minFrac, "0");
|
|
4465
|
+
if (!int && !frac) int = "0";
|
|
4466
|
+
return group(int, p.grouping) + (frac ? "." + frac : "");
|
|
4513
4467
|
}
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
const text2 = part.slice(0, i).trim();
|
|
4532
|
-
const periods = text2 === "dayoff" ? null : parsePeriods(text2, part);
|
|
4533
|
-
for (const date of part.slice(i + 1).split(",")) this.corrections.set(Number(date), periods);
|
|
4468
|
+
function formatNumber(value, pattern, mode3) {
|
|
4469
|
+
if (Number.isNaN(value)) return "NaN";
|
|
4470
|
+
const p = parsePattern(pattern);
|
|
4471
|
+
if (!Number.isFinite(value)) return (value < 0 ? "-" : "") + p.prefix + "\u221E" + p.suffix;
|
|
4472
|
+
const x = p.percent ? value * 100 : value;
|
|
4473
|
+
const toDigits = mode3 === "halfUpShortest" ? shortestDigits : exactDigits;
|
|
4474
|
+
let body;
|
|
4475
|
+
let isZero;
|
|
4476
|
+
if (p.exponent !== null) {
|
|
4477
|
+
const rounded = roundDigits(toDigits(x), p.maxFrac, mode3);
|
|
4478
|
+
const intDigits = Math.max(p.minInt, 1);
|
|
4479
|
+
isZero = rounded.digits === "";
|
|
4480
|
+
let exponent = isZero ? 0 : rounded.intLen - intDigits;
|
|
4481
|
+
let mant = roundDigits({ digits: rounded.digits, intLen: isZero ? 0 : intDigits }, p.maxFrac, mode3);
|
|
4482
|
+
if (mant.intLen > intDigits) {
|
|
4483
|
+
exponent += 1;
|
|
4484
|
+
mant = { digits: mant.digits, intLen: intDigits };
|
|
4534
4485
|
}
|
|
4535
|
-
const
|
|
4536
|
-
|
|
4486
|
+
const e2 = String(Math.abs(exponent)).padStart(p.exponent, "0");
|
|
4487
|
+
body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e2;
|
|
4488
|
+
} else {
|
|
4489
|
+
const rounded = roundDigits(toDigits(x), p.hasDigits ? p.maxFrac : 0, mode3);
|
|
4490
|
+
isZero = rounded.digits === "";
|
|
4491
|
+
body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });
|
|
4537
4492
|
}
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
corrections = /* @__PURE__ */ new Map();
|
|
4541
|
-
holidays;
|
|
4542
|
-
cache = /* @__PURE__ */ new Map();
|
|
4543
|
-
/** Session periods [start, end) of the trading day `date`; empty when the day has no session. */
|
|
4544
|
-
periods(date) {
|
|
4545
|
-
let out = this.cache.get(date);
|
|
4546
|
-
if (out) return out;
|
|
4547
|
-
let periods = [];
|
|
4548
|
-
const correction = this.corrections.get(date);
|
|
4549
|
-
if (correction !== void 0) {
|
|
4550
|
-
periods = correction ?? [];
|
|
4551
|
-
} else if (!this.holidays.has(date)) {
|
|
4552
|
-
const version = [...this.versions].reverse().find((v) => v.from <= date);
|
|
4553
|
-
if (version.days[weekdayOf(date)]) periods = version.periods;
|
|
4554
|
-
}
|
|
4555
|
-
const y = Math.floor(date / 1e4);
|
|
4556
|
-
const m = Math.floor(date / 100) % 100;
|
|
4557
|
-
const d = date % 100;
|
|
4558
|
-
out = periods.map((p) => [
|
|
4559
|
-
zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),
|
|
4560
|
-
zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)
|
|
4561
|
-
]);
|
|
4562
|
-
this.cache.set(date, out);
|
|
4563
|
-
return out;
|
|
4564
|
-
}
|
|
4565
|
-
isTradingDay(date) {
|
|
4566
|
-
return this.periods(date).length > 0;
|
|
4567
|
-
}
|
|
4568
|
-
/**
|
|
4569
|
-
* Trading day and period of a time: the period that contains it, else the last period that started
|
|
4570
|
-
* before it. Null when no trading day is found near the time.
|
|
4571
|
-
*/
|
|
4572
|
-
locate(time2) {
|
|
4573
|
-
const f = zonedFields(time2, this.timezone);
|
|
4574
|
-
const local = toDateNum(f.year, f.month, f.day);
|
|
4575
|
-
for (let k = 0; k <= 5; k++) {
|
|
4576
|
-
const date = addDays(local, k);
|
|
4577
|
-
const index = this.periods(date).findIndex(([s, e2]) => s <= time2 && time2 < e2);
|
|
4578
|
-
if (index !== -1) return { date, index, inside: true };
|
|
4579
|
-
}
|
|
4580
|
-
for (let k = 5; k >= -7; k--) {
|
|
4581
|
-
const date = addDays(local, k);
|
|
4582
|
-
const periods = this.periods(date);
|
|
4583
|
-
let index = -1;
|
|
4584
|
-
periods.forEach(([s], i) => {
|
|
4585
|
-
if (s <= time2) index = i;
|
|
4586
|
-
});
|
|
4587
|
-
if (index !== -1) return { date, index, inside: false };
|
|
4588
|
-
}
|
|
4589
|
-
return null;
|
|
4590
|
-
}
|
|
4591
|
-
/** First trading day in [from, to] (dates), or null. */
|
|
4592
|
-
firstTradingDay(from, to) {
|
|
4593
|
-
for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;
|
|
4594
|
-
return null;
|
|
4595
|
-
}
|
|
4596
|
-
/** Last trading day in [from, to] (dates), or null. */
|
|
4597
|
-
lastTradingDay(from, to) {
|
|
4598
|
-
for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;
|
|
4599
|
-
return null;
|
|
4600
|
-
}
|
|
4601
|
-
};
|
|
4602
|
-
var weekStart = (date) => addDays(date, -((weekdayOf(date) + 5) % 7));
|
|
4603
|
-
var monthStart = (date) => Math.floor(date / 100) * 100 + 1;
|
|
4604
|
-
var monthEnd = (date) => addDays(monthStart(addDays(monthStart(date), 32)), -1);
|
|
4605
|
-
|
|
4606
|
-
// src/session/bars.ts
|
|
4607
|
-
function unitOf(tf) {
|
|
4608
|
-
return { unit: tf.isdaily ? "D" : tf.isweekly ? "W" : "M", n: tf.multiplier };
|
|
4493
|
+
const negative = x < 0 && (!isZero || !p.hasDigits);
|
|
4494
|
+
return (negative ? "-" : "") + p.prefix + body + p.suffix;
|
|
4609
4495
|
}
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4496
|
+
|
|
4497
|
+
// src/str/dateformat.ts
|
|
4498
|
+
var MONTHS2 = [
|
|
4499
|
+
"January",
|
|
4500
|
+
"February",
|
|
4501
|
+
"March",
|
|
4502
|
+
"April",
|
|
4503
|
+
"May",
|
|
4504
|
+
"June",
|
|
4505
|
+
"July",
|
|
4506
|
+
"August",
|
|
4507
|
+
"September",
|
|
4508
|
+
"October",
|
|
4509
|
+
"November",
|
|
4510
|
+
"December"
|
|
4511
|
+
];
|
|
4512
|
+
var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
4513
|
+
var pad = (n, width) => String(n).padStart(width, "0");
|
|
4514
|
+
function zoneName(time2, timezone, offsetMs) {
|
|
4515
|
+
if (timezone === "Etc/UTC") return "UTC";
|
|
4516
|
+
if (/^(UTC|GMT)/.test(timezone)) {
|
|
4517
|
+
if (offsetMs === 0) return "GMT";
|
|
4518
|
+
const minutes = Math.abs(offsetMs) / 6e4;
|
|
4519
|
+
return `GMT${offsetMs < 0 ? "-" : "+"}${pad(Math.floor(minutes / 60), 2)}:${pad(minutes % 60, 2)}`;
|
|
4620
4520
|
}
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4521
|
+
const part = new Intl.DateTimeFormat("en-US", { timeZone: timezone, timeZoneName: "short" }).formatToParts(time2).find((p) => p.type === "timeZoneName");
|
|
4522
|
+
return part?.value ?? timezone;
|
|
4523
|
+
}
|
|
4524
|
+
function weekOfYear(year3, month3, day) {
|
|
4525
|
+
const date = Date.UTC(year3, month3 - 1, day);
|
|
4526
|
+
const dow = new Date(date).getUTCDay();
|
|
4527
|
+
const weekStart2 = date - dow * 864e5;
|
|
4528
|
+
if (weekStart2 + 6 * 864e5 >= Date.UTC(year3 + 1, 0, 1)) return 1;
|
|
4529
|
+
const jan1 = Date.UTC(year3, 0, 1);
|
|
4530
|
+
const jan1Dow = new Date(jan1).getUTCDay();
|
|
4531
|
+
return Math.floor((date - jan1 + jan1Dow * 864e5) / (7 * 864e5)) + 1;
|
|
4532
|
+
}
|
|
4533
|
+
function formatDate(time2, pattern, timezone) {
|
|
4534
|
+
if (Number.isNaN(time2)) return "NaN";
|
|
4535
|
+
const f = zonedFields(time2, timezone);
|
|
4536
|
+
const offset = zoneOffset(time2, timezone);
|
|
4537
|
+
const ms = (time2 % 1e3 + 1e3) % 1e3;
|
|
4538
|
+
const dayOfYear = (Date.UTC(f.year, f.month - 1, f.day) - Date.UTC(f.year, 0, 1)) / 864e5 + 1;
|
|
4539
|
+
let out = "";
|
|
4540
|
+
for (let i = 0; i < pattern.length; ) {
|
|
4541
|
+
const ch = pattern[i];
|
|
4542
|
+
if (ch === "'") {
|
|
4543
|
+
const end = pattern.indexOf("'", i + 1);
|
|
4544
|
+
if (end === i + 1) {
|
|
4545
|
+
out += "'";
|
|
4546
|
+
i += 2;
|
|
4547
|
+
} else {
|
|
4548
|
+
out += pattern.slice(i + 1, end === -1 ? void 0 : end);
|
|
4549
|
+
i = end === -1 ? pattern.length : end + 1;
|
|
4631
4550
|
}
|
|
4632
|
-
|
|
4633
|
-
}
|
|
4634
|
-
return days;
|
|
4635
|
-
}
|
|
4636
|
-
/**
|
|
4637
|
-
* Dates [from, to] of the `n`-`unit` period that contains `date` (for "D": its first and last trading days).
|
|
4638
|
-
* Periods restart each calendar year.
|
|
4639
|
-
*/
|
|
4640
|
-
periodRange(date, unit, n) {
|
|
4641
|
-
if (unit === "D") {
|
|
4642
|
-
if (n === 1) return [date, date];
|
|
4643
|
-
const days = this.tradingDaysOf(yearOf(date));
|
|
4644
|
-
let k = days.findIndex((d) => d >= date);
|
|
4645
|
-
if (k === -1) return this.periodRange(yearOf(date) * 1e4 + 1e4 + 101, unit, n);
|
|
4646
|
-
k -= k % n;
|
|
4647
|
-
return [days[k], days[Math.min(k + n, days.length) - 1]];
|
|
4648
|
-
}
|
|
4649
|
-
if (unit === "W") {
|
|
4650
|
-
const monday = weekStart(date);
|
|
4651
|
-
const year4 = yearOf(monday);
|
|
4652
|
-
const first = firstMonday(year4);
|
|
4653
|
-
const next = firstMonday(year4 + 1);
|
|
4654
|
-
const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 864e5));
|
|
4655
|
-
const from = addDays(first, (k - k % n) * 7);
|
|
4656
|
-
const to = addDays(from, n * 7 - 1);
|
|
4657
|
-
return [from, to < next ? to : addDays(next, -1)];
|
|
4658
|
-
}
|
|
4659
|
-
const year3 = yearOf(date);
|
|
4660
|
-
const month3 = Math.floor(date / 100) % 100;
|
|
4661
|
-
const firstMonth = month3 - (month3 - 1) % n;
|
|
4662
|
-
const lastMonth = Math.min(firstMonth + n - 1, 12);
|
|
4663
|
-
return [monthStart(year3 * 1e4 + firstMonth * 100 + 1), monthEnd(year3 * 1e4 + lastMonth * 100 + 1)];
|
|
4664
|
-
}
|
|
4665
|
-
dayStart(date) {
|
|
4666
|
-
return this.calendar.periods(date)[0][0];
|
|
4667
|
-
}
|
|
4668
|
-
dayEnd(date) {
|
|
4669
|
-
const p = this.calendar.periods(date);
|
|
4670
|
-
return p[p.length - 1][1];
|
|
4671
|
-
}
|
|
4672
|
-
/** PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. */
|
|
4673
|
-
timeOf(time2, timeframe2) {
|
|
4674
|
-
const tf = info(timeframe2);
|
|
4675
|
-
const at = this.calendar.locate(time2);
|
|
4676
|
-
if (!at) return NaN;
|
|
4677
|
-
if (tf.isintraday) {
|
|
4678
|
-
if (this.chart.isdwm) return time2;
|
|
4679
|
-
const [start] = this.calendar.periods(at.date)[at.index];
|
|
4680
|
-
const ms = in_seconds(tf.period) * 1e3;
|
|
4681
|
-
return start + Math.floor((time2 - start) / ms) * ms;
|
|
4682
|
-
}
|
|
4683
|
-
const { unit, n } = unitOf(tf);
|
|
4684
|
-
const [from, to] = this.periodRange(at.date, unit, n);
|
|
4685
|
-
const first = this.calendar.firstTradingDay(from, to);
|
|
4686
|
-
return first === null ? NaN : this.dayStart(first);
|
|
4687
|
-
}
|
|
4688
|
-
/** PineScript `time_close(tf)` for a bar that opens at `time`. */
|
|
4689
|
-
closeOf(time2, timeframe2) {
|
|
4690
|
-
const tf = info(timeframe2);
|
|
4691
|
-
const at = this.calendar.locate(time2);
|
|
4692
|
-
if (!at) return NaN;
|
|
4693
|
-
if (tf.isintraday) {
|
|
4694
|
-
const [, end] = this.calendar.periods(at.date)[at.index];
|
|
4695
|
-
if (this.chart.isdwm) return end;
|
|
4696
|
-
return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
|
|
4697
|
-
}
|
|
4698
|
-
const { unit, n } = unitOf(tf);
|
|
4699
|
-
const [from, to] = this.periodRange(at.date, unit, n);
|
|
4700
|
-
if (this.chart.isintraday && (unit !== "D" || n > 1)) {
|
|
4701
|
-
const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);
|
|
4702
|
-
const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));
|
|
4703
|
-
return first === null ? NaN : this.dayStart(first);
|
|
4551
|
+
continue;
|
|
4704
4552
|
}
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
tradingDay(time2) {
|
|
4710
|
-
const at = this.calendar.locate(time2);
|
|
4711
|
-
if (!at) return null;
|
|
4712
|
-
if (this.chart.isdwm) {
|
|
4713
|
-
const { unit, n } = unitOf(this.chart);
|
|
4714
|
-
const [from, to] = this.periodRange(at.date, unit, n);
|
|
4715
|
-
return this.calendar.lastTradingDay(from, to);
|
|
4553
|
+
if (!/[A-Za-z]/.test(ch)) {
|
|
4554
|
+
out += ch;
|
|
4555
|
+
i++;
|
|
4556
|
+
continue;
|
|
4716
4557
|
}
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4558
|
+
let n = 1;
|
|
4559
|
+
while (pattern[i + n] === ch) n++;
|
|
4560
|
+
i += n;
|
|
4561
|
+
const hour12 = f.hour % 12;
|
|
4562
|
+
switch (ch) {
|
|
4563
|
+
case "y":
|
|
4564
|
+
out += n === 2 ? pad(f.year % 100, 2) : pad(f.year, n);
|
|
4565
|
+
break;
|
|
4566
|
+
case "M":
|
|
4567
|
+
out += n >= 4 ? MONTHS2[f.month - 1] : n === 3 ? MONTHS2[f.month - 1].slice(0, 3) : pad(f.month, n);
|
|
4568
|
+
break;
|
|
4569
|
+
case "d":
|
|
4570
|
+
out += pad(f.day, n);
|
|
4571
|
+
break;
|
|
4572
|
+
case "E":
|
|
4573
|
+
out += n >= 4 ? DAYS[f.dayOfWeek - 1] : DAYS[f.dayOfWeek - 1].slice(0, 3);
|
|
4574
|
+
break;
|
|
4575
|
+
case "u":
|
|
4576
|
+
out += pad(f.dayOfWeek === 1 ? 7 : f.dayOfWeek - 1, n);
|
|
4577
|
+
break;
|
|
4578
|
+
case "D":
|
|
4579
|
+
out += pad(dayOfYear, n);
|
|
4580
|
+
break;
|
|
4581
|
+
case "w":
|
|
4582
|
+
out += pad(weekOfYear(f.year, f.month, f.day), n);
|
|
4583
|
+
break;
|
|
4584
|
+
case "H":
|
|
4585
|
+
out += pad(f.hour, n);
|
|
4586
|
+
break;
|
|
4587
|
+
case "k":
|
|
4588
|
+
out += pad(f.hour === 0 ? 24 : f.hour, n);
|
|
4589
|
+
break;
|
|
4590
|
+
case "K":
|
|
4591
|
+
out += pad(hour12, n);
|
|
4592
|
+
break;
|
|
4593
|
+
case "h":
|
|
4594
|
+
out += pad(hour12 === 0 ? 12 : hour12, n);
|
|
4595
|
+
break;
|
|
4596
|
+
case "a":
|
|
4597
|
+
out += f.hour < 12 ? "AM" : "PM";
|
|
4598
|
+
break;
|
|
4599
|
+
case "m":
|
|
4600
|
+
out += pad(f.minute, n);
|
|
4601
|
+
break;
|
|
4602
|
+
case "s":
|
|
4603
|
+
out += pad(f.second, n);
|
|
4604
|
+
break;
|
|
4605
|
+
case "S":
|
|
4606
|
+
out += pad(ms, n);
|
|
4607
|
+
break;
|
|
4608
|
+
case "Z": {
|
|
4609
|
+
const minutes = Math.abs(offset) / 6e4;
|
|
4610
|
+
out += (offset < 0 ? "-" : "+") + pad(Math.floor(minutes / 60), 2) + pad(minutes % 60, 2);
|
|
4611
|
+
break;
|
|
4612
|
+
}
|
|
4613
|
+
case "z":
|
|
4614
|
+
out += zoneName(time2, timezone, offset);
|
|
4615
|
+
break;
|
|
4616
|
+
default:
|
|
4617
|
+
throw new SyntaxError(`Unsupported date pattern letter "${ch}" in "${pattern}"`);
|
|
4731
4618
|
}
|
|
4732
|
-
const { unit, n } = unitOf(tf);
|
|
4733
|
-
const [from, to] = this.periodRange(at.date, unit, n);
|
|
4734
|
-
const last = this.calendar.lastTradingDay(from, to);
|
|
4735
|
-
return last === null ? NaN : this.dayEnd(last);
|
|
4736
|
-
}
|
|
4737
|
-
/** PineScript `time_tradingday`: 00:00 UTC of the trading day. */
|
|
4738
|
-
tradingDayTime(time2) {
|
|
4739
|
-
const day = this.tradingDay(time2);
|
|
4740
|
-
return day === null ? NaN : dateUtcMs(day);
|
|
4741
|
-
}
|
|
4742
|
-
/** True when the bar at `time` is inside the session. */
|
|
4743
|
-
inside(time2) {
|
|
4744
|
-
return this.calendar.locate(time2)?.inside ?? false;
|
|
4745
|
-
}
|
|
4746
|
-
/** Session start and end of the trading day of `time`, or null. */
|
|
4747
|
-
dayBounds(time2) {
|
|
4748
|
-
const at = this.calendar.locate(time2);
|
|
4749
|
-
return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;
|
|
4750
4619
|
}
|
|
4620
|
+
return out;
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
// src/str/messageformat.ts
|
|
4624
|
+
var DATE_STYLES = {
|
|
4625
|
+
short: "M/d/yy",
|
|
4626
|
+
medium: "MMM d, yyyy",
|
|
4627
|
+
long: "MMMM d, yyyy",
|
|
4628
|
+
full: "EEEE, MMMM d, yyyy"
|
|
4751
4629
|
};
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4630
|
+
var TIME_STYLES = {
|
|
4631
|
+
short: "h:mm\u202Fa",
|
|
4632
|
+
medium: "h:mm:ss\u202Fa",
|
|
4633
|
+
long: "h:mm:ss\u202Fa z",
|
|
4634
|
+
full: "h:mm:ss\u202Fa z"
|
|
4635
|
+
};
|
|
4636
|
+
function number(value, pattern) {
|
|
4637
|
+
return typeof value === "number" ? formatNumber(value, pattern, "halfEvenExact") : String(value);
|
|
4638
|
+
}
|
|
4639
|
+
function choice(value, spec) {
|
|
4640
|
+
const options = spec.split("|").map((part) => {
|
|
4641
|
+
const m = /^\s*(-?[\d.]+|-?∞)\s*([#<≤])(.*)$/.exec(part);
|
|
4642
|
+
if (!m) throw new SyntaxError(`Invalid choice format "${spec}"`);
|
|
4643
|
+
const limit = m[1].includes("\u221E") ? m[1].startsWith("-") ? -Infinity : Infinity : Number(m[1]);
|
|
4644
|
+
return { limit: m[2] === "<" ? limit + Number.EPSILON * Math.max(1, Math.abs(limit)) : limit, text: m[3] };
|
|
4645
|
+
});
|
|
4646
|
+
let chosen = options[0];
|
|
4647
|
+
for (const o of options) if (value >= o.limit) chosen = o;
|
|
4648
|
+
return chosen.text;
|
|
4649
|
+
}
|
|
4650
|
+
function formatArgument(value, type, style, timezone) {
|
|
4651
|
+
if (value === void 0) return "";
|
|
4652
|
+
if (!type) return typeof value === "number" ? number(value, "#,##0.###") : String(value);
|
|
4653
|
+
switch (type.trim()) {
|
|
4654
|
+
case "number": {
|
|
4655
|
+
const s = style?.trim();
|
|
4656
|
+
if (!s) return number(value, "#,##0.###");
|
|
4657
|
+
if (s === "integer") return number(value, "#,##0");
|
|
4658
|
+
if (s === "percent") return number(value, "#,##0%");
|
|
4659
|
+
if (s === "currency") return number(value, "'$'#,##0.00");
|
|
4660
|
+
return number(value, style);
|
|
4661
|
+
}
|
|
4662
|
+
case "date":
|
|
4663
|
+
case "time": {
|
|
4664
|
+
const styles = type.trim() === "date" ? DATE_STYLES : TIME_STYLES;
|
|
4665
|
+
const s = style?.trim() ?? "medium";
|
|
4666
|
+
return formatDate(Number(value), styles[s] ?? style, typeof timezone === "function" ? timezone() : timezone);
|
|
4667
|
+
}
|
|
4668
|
+
case "choice":
|
|
4669
|
+
return choice(Number(value), style ?? "");
|
|
4670
|
+
default:
|
|
4671
|
+
throw new SyntaxError(`Unknown format type "${type}"`);
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
function formatMessage(pattern, args, timezone) {
|
|
4675
|
+
let out = "";
|
|
4676
|
+
for (let i = 0; i < pattern.length; ) {
|
|
4677
|
+
const ch = pattern[i];
|
|
4678
|
+
if (ch === "'") {
|
|
4679
|
+
if (pattern[i + 1] === "'") {
|
|
4680
|
+
out += "'";
|
|
4681
|
+
i += 2;
|
|
4682
|
+
continue;
|
|
4683
|
+
}
|
|
4684
|
+
const end = pattern.indexOf("'", i + 1);
|
|
4685
|
+
out += pattern.slice(i + 1, end === -1 ? void 0 : end);
|
|
4686
|
+
i = end === -1 ? pattern.length : end + 1;
|
|
4771
4687
|
continue;
|
|
4772
4688
|
}
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4689
|
+
if (ch !== "{") {
|
|
4690
|
+
out += ch;
|
|
4691
|
+
i++;
|
|
4776
4692
|
continue;
|
|
4777
4693
|
}
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
out.ismarket[i] = market;
|
|
4787
|
-
if (!market && regularPeriods.length) {
|
|
4788
|
-
out.ispremarket[i] = t2 < regularPeriods[0][0];
|
|
4789
|
-
out.ispostmarket[i] = t2 >= regularPeriods[regularPeriods.length - 1][1];
|
|
4694
|
+
let depth = 1;
|
|
4695
|
+
let j = i + 1;
|
|
4696
|
+
let inQuote = false;
|
|
4697
|
+
for (; j < pattern.length && depth > 0; j++) {
|
|
4698
|
+
const c = pattern[j];
|
|
4699
|
+
if (c === "'") inQuote = !inQuote;
|
|
4700
|
+
else if (!inQuote && c === "{") depth++;
|
|
4701
|
+
else if (!inQuote && c === "}") depth--;
|
|
4790
4702
|
}
|
|
4791
|
-
const
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4703
|
+
const inner = pattern.slice(i + 1, j - 1);
|
|
4704
|
+
const [index = "", type, ...rest] = inner.split(",");
|
|
4705
|
+
const style = rest.length ? rest.join(",") : void 0;
|
|
4706
|
+
const n = Number(index.trim());
|
|
4707
|
+
out += n < args.length ? formatArgument(args[n], type, style, timezone) : `{${inner}}`;
|
|
4708
|
+
i = j;
|
|
4796
4709
|
}
|
|
4797
4710
|
return out;
|
|
4798
4711
|
}
|
|
4799
4712
|
|
|
4800
|
-
// src/
|
|
4801
|
-
function
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
last.high = Math.max(last.high, b2.high);
|
|
4811
|
-
last.low = Math.min(last.low, b2.low);
|
|
4812
|
-
last.close = b2.close;
|
|
4813
|
-
last.volume = (last.volume ?? 0) + (b2.volume ?? NaN);
|
|
4713
|
+
// src/str/index.ts
|
|
4714
|
+
function length(str2) {
|
|
4715
|
+
return str2.length;
|
|
4716
|
+
}
|
|
4717
|
+
function tostring(value, format2) {
|
|
4718
|
+
if (typeof value !== "number") return String(value);
|
|
4719
|
+
if (!Number.isFinite(value)) return String(value);
|
|
4720
|
+
if (format2 === void 0 || format2 === "") {
|
|
4721
|
+
if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
|
|
4722
|
+
return String(value).replace("e+", "E").replace(/\.0+E/, "E");
|
|
4814
4723
|
}
|
|
4815
|
-
|
|
4816
|
-
}
|
|
4817
|
-
|
|
4724
|
+
return formatNumber(value, "#.##########", "halfUpShortest");
|
|
4725
|
+
}
|
|
4726
|
+
switch (format2) {
|
|
4727
|
+
case "percent":
|
|
4728
|
+
return formatNumber(value, "0.00", "halfUpShortest") + "%";
|
|
4729
|
+
case "volume": {
|
|
4730
|
+
const abs2 = Math.abs(value);
|
|
4731
|
+
for (const [size, unit] of [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]]) {
|
|
4732
|
+
if (abs2 >= size) return formatNumber(value / size, "#.###", "halfUpShortest") + unit;
|
|
4733
|
+
}
|
|
4734
|
+
return formatNumber(value, "#", "halfUpShortest");
|
|
4735
|
+
}
|
|
4736
|
+
case "mintick":
|
|
4737
|
+
throw new Error("str.tostring(x, format.mintick) needs the symbol tick size (syminfo.mintick), which is not known here");
|
|
4738
|
+
default:
|
|
4739
|
+
return formatNumber(value, format2, "halfUpShortest");
|
|
4740
|
+
}
|
|
4818
4741
|
}
|
|
4819
|
-
function
|
|
4820
|
-
const
|
|
4821
|
-
|
|
4822
|
-
starts.forEach((s, i) => group2.push(i === 0 ? 0 : group2[i - 1] + (s !== starts[i - 1] ? 1 : 0)));
|
|
4823
|
-
const latest = [];
|
|
4824
|
-
starts.forEach((s, i) => {
|
|
4825
|
-
let done = i === 0 ? -1 : latest[i - 1];
|
|
4826
|
-
if (i > 0 && group2[i] !== group2[i - 1]) done = Math.max(done, group2[i] - 1);
|
|
4827
|
-
const ends = i < n - 1 ? starts[i + 1] !== s || dayPeriods[i + 1] !== dayPeriods[i] : true;
|
|
4828
|
-
if (ends && reaches[i]) done = Math.max(done, group2[i]);
|
|
4829
|
-
latest.push(done);
|
|
4830
|
-
});
|
|
4831
|
-
return { group: group2, latest };
|
|
4742
|
+
function tonumber(str2) {
|
|
4743
|
+
const num = parseFloat(str2);
|
|
4744
|
+
return isNaN(num) ? null : num;
|
|
4832
4745
|
}
|
|
4833
|
-
function
|
|
4834
|
-
return
|
|
4835
|
-
if (lookahead) {
|
|
4836
|
-
const first = i === 0 || group2[i - 1] !== g2;
|
|
4837
|
-
return gaps && !first ? NaN : values[g2] ?? NaN;
|
|
4838
|
-
}
|
|
4839
|
-
const done = latest[i];
|
|
4840
|
-
if (done < 0 || gaps && i > 0 && latest[i - 1] === done) return NaN;
|
|
4841
|
-
return values[done] ?? NaN;
|
|
4842
|
-
});
|
|
4746
|
+
function substring(str2, begin, end) {
|
|
4747
|
+
return str2.substring(begin, end);
|
|
4843
4748
|
}
|
|
4844
|
-
function
|
|
4845
|
-
|
|
4846
|
-
bars2.forEach((b2, i) => {
|
|
4847
|
-
const close2 = (b2.open + b2.high + b2.low + b2.close) / 4;
|
|
4848
|
-
const prev = out[i - 1];
|
|
4849
|
-
const open2 = prev ? (prev.open + prev.close) / 2 : (b2.open + b2.close) / 2;
|
|
4850
|
-
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 });
|
|
4851
|
-
});
|
|
4852
|
-
return out;
|
|
4749
|
+
function upper(str2) {
|
|
4750
|
+
return str2.toUpperCase();
|
|
4853
4751
|
}
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
log: () => log,
|
|
4869
|
-
log10: () => log10,
|
|
4870
|
-
max: () => max3,
|
|
4871
|
-
min: () => min3,
|
|
4872
|
-
phi: () => phi,
|
|
4873
|
-
pi: () => pi,
|
|
4874
|
-
pow: () => pow,
|
|
4875
|
-
random: () => random,
|
|
4876
|
-
round: () => round,
|
|
4877
|
-
round_to_mintick: () => round_to_mintick,
|
|
4878
|
-
rphi: () => rphi,
|
|
4879
|
-
sign: () => sign,
|
|
4880
|
-
sin: () => sin,
|
|
4881
|
-
sqrt: () => sqrt,
|
|
4882
|
-
sum: () => sum,
|
|
4883
|
-
tan: () => tan,
|
|
4884
|
-
todegrees: () => todegrees,
|
|
4885
|
-
toradians: () => toradians
|
|
4886
|
-
});
|
|
4887
|
-
function abs(value) {
|
|
4888
|
-
if (value instanceof Series) {
|
|
4889
|
-
const bars2 = value.bars;
|
|
4890
|
-
const valueArray = value.toArray();
|
|
4891
|
-
const length2 = bars2.length;
|
|
4892
|
-
const result = [];
|
|
4893
|
-
for (let i = 0; i < length2; i++) {
|
|
4894
|
-
const v = valueArray[i] ?? NaN;
|
|
4895
|
-
result.push(Math.abs(v));
|
|
4896
|
-
}
|
|
4897
|
-
return Series.fromArray(bars2, result);
|
|
4752
|
+
function lower(str2) {
|
|
4753
|
+
return str2.toLowerCase();
|
|
4754
|
+
}
|
|
4755
|
+
function contains(source, str2) {
|
|
4756
|
+
return source.includes(str2);
|
|
4757
|
+
}
|
|
4758
|
+
function pos(source, str2) {
|
|
4759
|
+
return source.indexOf(str2);
|
|
4760
|
+
}
|
|
4761
|
+
function replace(source, target, replacement, occurrence) {
|
|
4762
|
+
if (occurrence === 0) {
|
|
4763
|
+
return source.replace(target, replacement);
|
|
4764
|
+
} else {
|
|
4765
|
+
return source.replaceAll(target, replacement);
|
|
4898
4766
|
}
|
|
4899
|
-
return Math.abs(value);
|
|
4900
4767
|
}
|
|
4901
|
-
function
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4768
|
+
function replace_all(source, target, replacement) {
|
|
4769
|
+
return source.replaceAll(target, replacement);
|
|
4770
|
+
}
|
|
4771
|
+
function split(str2, separator) {
|
|
4772
|
+
return str2.split(separator);
|
|
4773
|
+
}
|
|
4774
|
+
function concat(...strings) {
|
|
4775
|
+
return strings.join("");
|
|
4776
|
+
}
|
|
4777
|
+
function format(formatStr, ...args) {
|
|
4778
|
+
return formatMessage(formatStr, args, "Etc/UTC");
|
|
4779
|
+
}
|
|
4780
|
+
function startswith(source, str2) {
|
|
4781
|
+
return source.startsWith(str2);
|
|
4782
|
+
}
|
|
4783
|
+
function endswith(source, str2) {
|
|
4784
|
+
return source.endsWith(str2);
|
|
4785
|
+
}
|
|
4786
|
+
function charAt(str2, pos2) {
|
|
4787
|
+
return str2.charAt(pos2);
|
|
4788
|
+
}
|
|
4789
|
+
function trim(str2) {
|
|
4790
|
+
return str2.trim();
|
|
4791
|
+
}
|
|
4792
|
+
function trimLeft(str2) {
|
|
4793
|
+
return str2.trimStart();
|
|
4794
|
+
}
|
|
4795
|
+
function trimRight(str2) {
|
|
4796
|
+
return str2.trimEnd();
|
|
4797
|
+
}
|
|
4798
|
+
function match(source, regex) {
|
|
4799
|
+
return new RegExp(regex).test(source);
|
|
4800
|
+
}
|
|
4801
|
+
function repeat(source, count, separator = "") {
|
|
4802
|
+
if (source == null) return null;
|
|
4803
|
+
if (count <= 0) return "";
|
|
4804
|
+
return Array(count).fill(source).join(separator);
|
|
4805
|
+
}
|
|
4806
|
+
function format_time(time2, format2 = "yyyy-MM-dd'T'HH:mm:ssZ", timezone = "Etc/UTC") {
|
|
4807
|
+
return formatDate(time2, format2, timezone);
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4810
|
+
// src/session/calendar.ts
|
|
4811
|
+
var toDateNum = (y, m, d) => y * 1e4 + m * 100 + d;
|
|
4812
|
+
var utcOf = (date) => Date.UTC(Math.floor(date / 1e4), Math.floor(date / 100) % 100 - 1, date % 100);
|
|
4813
|
+
var fromUtc = (ms) => {
|
|
4814
|
+
const d = new Date(ms);
|
|
4815
|
+
return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
4816
|
+
};
|
|
4817
|
+
var addDays = (date, n) => fromUtc(utcOf(date) + n * 864e5);
|
|
4818
|
+
var weekdayOf = (date) => new Date(utcOf(date)).getUTCDay() + 1;
|
|
4819
|
+
var dateUtcMs = utcOf;
|
|
4820
|
+
function parseTime(token, spec) {
|
|
4821
|
+
const m = /^(\d{2})(\d{2})(?:F(\d+))?$/.exec(token.trim());
|
|
4822
|
+
if (!m) throw new SyntaxError(`Invalid session "${spec}"`);
|
|
4823
|
+
return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === void 0 ? void 0 : Number(m[3]) };
|
|
4824
|
+
}
|
|
4825
|
+
function parsePeriods(text2, spec) {
|
|
4826
|
+
if (text2.trim() === "24x7") return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];
|
|
4827
|
+
return text2.split(",").map((p) => {
|
|
4828
|
+
const [a = "", b2 = ""] = p.split("-");
|
|
4829
|
+
const s = parseTime(a, spec);
|
|
4830
|
+
const e2 = parseTime(b2, spec);
|
|
4831
|
+
if (s.minutes === 0 && e2.minutes === 0 && s.offset === void 0 && e2.offset === void 0) {
|
|
4832
|
+
return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };
|
|
4910
4833
|
}
|
|
4911
|
-
return
|
|
4912
|
-
|
|
4913
|
-
|
|
4834
|
+
return {
|
|
4835
|
+
start: s.minutes,
|
|
4836
|
+
end: e2.minutes,
|
|
4837
|
+
startOffset: s.offset ?? (e2.minutes <= s.minutes ? 1 : 0),
|
|
4838
|
+
endOffset: e2.offset ?? 0
|
|
4839
|
+
};
|
|
4840
|
+
});
|
|
4914
4841
|
}
|
|
4915
|
-
function
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
}
|
|
4925
|
-
return Series.fromArray(bars2, result);
|
|
4926
|
-
}
|
|
4927
|
-
return Math.floor(value);
|
|
4842
|
+
function parseVersion(text2, from, spec, rule) {
|
|
4843
|
+
const [periodText = "", dayText] = text2.trim().split(":");
|
|
4844
|
+
const periods = parsePeriods(periodText, spec);
|
|
4845
|
+
const allDays = periodText.trim() === "24x7" || rule === "pine" && periods.length === 1;
|
|
4846
|
+
const digits = dayText ?? (allDays ? "1234567" : "23456");
|
|
4847
|
+
if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in "${spec}"`);
|
|
4848
|
+
const days = new Array(8).fill(false);
|
|
4849
|
+
for (const d of digits) days[Number(d)] = true;
|
|
4850
|
+
return { from, periods, days };
|
|
4928
4851
|
}
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
const v = valueArray[i] ?? NaN;
|
|
4944
|
-
result.push(Math.round(v * multiplier2) / multiplier2);
|
|
4945
|
-
}
|
|
4852
|
+
var TradingCalendar = class {
|
|
4853
|
+
/**
|
|
4854
|
+
* @param timezone - Time zone of the session hours (the exchange time zone)
|
|
4855
|
+
* @param spec - Session information (hours, corrections, holidays)
|
|
4856
|
+
* @param rule - Days without ":days": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)
|
|
4857
|
+
*/
|
|
4858
|
+
constructor(timezone, spec, rule = "symbol") {
|
|
4859
|
+
this.timezone = timezone;
|
|
4860
|
+
const [first = "", ...changes] = spec.session.split("#");
|
|
4861
|
+
this.versions = [parseVersion(first, 0, spec.session, rule)];
|
|
4862
|
+
for (const change3 of changes) {
|
|
4863
|
+
const m = /^(\d{8})\/(.+)$/.exec(change3.trim());
|
|
4864
|
+
if (!m) throw new SyntaxError(`Invalid session change in "${spec.session}"`);
|
|
4865
|
+
this.versions.push(parseVersion(m[2], Number(m[1]), spec.session, rule));
|
|
4946
4866
|
}
|
|
4947
|
-
|
|
4867
|
+
for (const part of (spec.corrections ?? "").split(";").filter((p) => p.trim())) {
|
|
4868
|
+
const i = part.lastIndexOf(":");
|
|
4869
|
+
const text2 = part.slice(0, i).trim();
|
|
4870
|
+
const periods = text2 === "dayoff" ? null : parsePeriods(text2, part);
|
|
4871
|
+
for (const date of part.slice(i + 1).split(",")) this.corrections.set(Number(date), periods);
|
|
4872
|
+
}
|
|
4873
|
+
const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? "").split(",");
|
|
4874
|
+
this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));
|
|
4948
4875
|
}
|
|
4949
|
-
|
|
4950
|
-
|
|
4876
|
+
timezone;
|
|
4877
|
+
versions;
|
|
4878
|
+
corrections = /* @__PURE__ */ new Map();
|
|
4879
|
+
holidays;
|
|
4880
|
+
cache = /* @__PURE__ */ new Map();
|
|
4881
|
+
/** Session periods [start, end) of the trading day `date`; empty when the day has no session. */
|
|
4882
|
+
periods(date) {
|
|
4883
|
+
let out = this.cache.get(date);
|
|
4884
|
+
if (out) return out;
|
|
4885
|
+
let periods = [];
|
|
4886
|
+
const correction = this.corrections.get(date);
|
|
4887
|
+
if (correction !== void 0) {
|
|
4888
|
+
periods = correction ?? [];
|
|
4889
|
+
} else if (!this.holidays.has(date)) {
|
|
4890
|
+
const version = [...this.versions].reverse().find((v) => v.from <= date);
|
|
4891
|
+
if (version.days[weekdayOf(date)]) periods = version.periods;
|
|
4892
|
+
}
|
|
4893
|
+
const y = Math.floor(date / 1e4);
|
|
4894
|
+
const m = Math.floor(date / 100) % 100;
|
|
4895
|
+
const d = date % 100;
|
|
4896
|
+
out = periods.map((p) => [
|
|
4897
|
+
zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),
|
|
4898
|
+
zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)
|
|
4899
|
+
]);
|
|
4900
|
+
this.cache.set(date, out);
|
|
4901
|
+
return out;
|
|
4951
4902
|
}
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
}
|
|
4955
|
-
function max3(...values) {
|
|
4956
|
-
const hasSeries = values.some((v) => v instanceof Series);
|
|
4957
|
-
if (!hasSeries) {
|
|
4958
|
-
return Math.max(...values);
|
|
4903
|
+
isTradingDay(date) {
|
|
4904
|
+
return this.periods(date).length > 0;
|
|
4959
4905
|
}
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
(
|
|
4970
|
-
|
|
4971
|
-
|
|
4906
|
+
/**
|
|
4907
|
+
* Trading day and period of a time: the period that contains it, else the last period that started
|
|
4908
|
+
* before it. Null when no trading day is found near the time.
|
|
4909
|
+
*/
|
|
4910
|
+
locate(time2) {
|
|
4911
|
+
const f = zonedFields(time2, this.timezone);
|
|
4912
|
+
const local = toDateNum(f.year, f.month, f.day);
|
|
4913
|
+
for (let k = 0; k <= 5; k++) {
|
|
4914
|
+
const date = addDays(local, k);
|
|
4915
|
+
const index = this.periods(date).findIndex(([s, e2]) => s <= time2 && time2 < e2);
|
|
4916
|
+
if (index !== -1) return { date, index, inside: true };
|
|
4917
|
+
}
|
|
4918
|
+
for (let k = 5; k >= -7; k--) {
|
|
4919
|
+
const date = addDays(local, k);
|
|
4920
|
+
const periods = this.periods(date);
|
|
4921
|
+
let index = -1;
|
|
4922
|
+
periods.forEach(([s], i) => {
|
|
4923
|
+
if (s <= time2) index = i;
|
|
4924
|
+
});
|
|
4925
|
+
if (index !== -1) return { date, index, inside: false };
|
|
4926
|
+
}
|
|
4927
|
+
return null;
|
|
4972
4928
|
}
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
if (!hasSeries) {
|
|
4978
|
-
return Math.min(...values);
|
|
4929
|
+
/** First trading day in [from, to] (dates), or null. */
|
|
4930
|
+
firstTradingDay(from, to) {
|
|
4931
|
+
for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;
|
|
4932
|
+
return null;
|
|
4979
4933
|
}
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
(v) => v instanceof Series ? v.toArray() : null
|
|
4985
|
-
);
|
|
4986
|
-
const result = [];
|
|
4987
|
-
for (let i = 0; i < length2; i++) {
|
|
4988
|
-
const nums = values.map(
|
|
4989
|
-
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
4990
|
-
);
|
|
4991
|
-
result.push(Math.min(...nums));
|
|
4934
|
+
/** Last trading day in [from, to] (dates), or null. */
|
|
4935
|
+
lastTradingDay(from, to) {
|
|
4936
|
+
for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;
|
|
4937
|
+
return null;
|
|
4992
4938
|
}
|
|
4993
|
-
|
|
4939
|
+
};
|
|
4940
|
+
var weekStart = (date) => addDays(date, -((weekdayOf(date) + 5) % 7));
|
|
4941
|
+
var monthStart = (date) => Math.floor(date / 100) * 100 + 1;
|
|
4942
|
+
var monthEnd = (date) => addDays(monthStart(addDays(monthStart(date), 32)), -1);
|
|
4943
|
+
|
|
4944
|
+
// src/session/bars.ts
|
|
4945
|
+
function unitOf(tf) {
|
|
4946
|
+
return { unit: tf.isdaily ? "D" : tf.isweekly ? "W" : "M", n: tf.multiplier };
|
|
4994
4947
|
}
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
);
|
|
5006
|
-
const result = [];
|
|
5007
|
-
for (let i = 0; i < length2; i++) {
|
|
5008
|
-
const nums = values.map(
|
|
5009
|
-
(v, idx) => valueArrays[idx] ? valueArrays[idx][i] ?? NaN : v
|
|
5010
|
-
);
|
|
5011
|
-
const sum2 = nums.reduce((a, b2) => a + b2, 0);
|
|
5012
|
-
result.push(sum2 / nums.length);
|
|
4948
|
+
var yearOf = (date) => Math.floor(date / 1e4);
|
|
4949
|
+
var firstMonday = (year3) => {
|
|
4950
|
+
const jan1 = year3 * 1e4 + 101;
|
|
4951
|
+
const monday = weekStart(jan1);
|
|
4952
|
+
return monday === jan1 ? jan1 : addDays(monday, 7);
|
|
4953
|
+
};
|
|
4954
|
+
var SessionBars = class {
|
|
4955
|
+
constructor(calendar2, chartTimeframe2) {
|
|
4956
|
+
this.calendar = calendar2;
|
|
4957
|
+
this.chart = info(chartTimeframe2);
|
|
5013
4958
|
}
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
}
|
|
5027
|
-
if (window.length < len) {
|
|
5028
|
-
result.push(NaN);
|
|
5029
|
-
} else {
|
|
5030
|
-
let total = 0;
|
|
5031
|
-
for (const x of window) total += x;
|
|
5032
|
-
result.push(total);
|
|
4959
|
+
calendar;
|
|
4960
|
+
chart;
|
|
4961
|
+
yearDays = /* @__PURE__ */ new Map();
|
|
4962
|
+
/** Trading days of a calendar year, in order. */
|
|
4963
|
+
tradingDaysOf(year3) {
|
|
4964
|
+
let days = this.yearDays.get(year3);
|
|
4965
|
+
if (!days) {
|
|
4966
|
+
days = [];
|
|
4967
|
+
for (let date = year3 * 1e4 + 101; yearOf(date) === year3; date = addDays(date, 1)) {
|
|
4968
|
+
if (this.calendar.isTradingDay(date)) days.push(date);
|
|
4969
|
+
}
|
|
4970
|
+
this.yearDays.set(year3, days);
|
|
5033
4971
|
}
|
|
4972
|
+
return days;
|
|
5034
4973
|
}
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
const v = valueArray[i] ?? NaN;
|
|
5048
|
-
result.push(Math.sqrt(v));
|
|
4974
|
+
/**
|
|
4975
|
+
* Dates [from, to] of the `n`-`unit` period that contains `date` (for "D": its first and last trading days).
|
|
4976
|
+
* Periods restart each calendar year.
|
|
4977
|
+
*/
|
|
4978
|
+
periodRange(date, unit, n) {
|
|
4979
|
+
if (unit === "D") {
|
|
4980
|
+
if (n === 1) return [date, date];
|
|
4981
|
+
const days = this.tradingDaysOf(yearOf(date));
|
|
4982
|
+
let k = days.findIndex((d) => d >= date);
|
|
4983
|
+
if (k === -1) return this.periodRange(yearOf(date) * 1e4 + 1e4 + 101, unit, n);
|
|
4984
|
+
k -= k % n;
|
|
4985
|
+
return [days[k], days[Math.min(k + n, days.length) - 1]];
|
|
5049
4986
|
}
|
|
5050
|
-
|
|
4987
|
+
if (unit === "W") {
|
|
4988
|
+
const monday = weekStart(date);
|
|
4989
|
+
const year4 = yearOf(monday);
|
|
4990
|
+
const first = firstMonday(year4);
|
|
4991
|
+
const next = firstMonday(year4 + 1);
|
|
4992
|
+
const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 864e5));
|
|
4993
|
+
const from = addDays(first, (k - k % n) * 7);
|
|
4994
|
+
const to = addDays(from, n * 7 - 1);
|
|
4995
|
+
return [from, to < next ? to : addDays(next, -1)];
|
|
4996
|
+
}
|
|
4997
|
+
const year3 = yearOf(date);
|
|
4998
|
+
const month3 = Math.floor(date / 100) % 100;
|
|
4999
|
+
const firstMonth = month3 - (month3 - 1) % n;
|
|
5000
|
+
const lastMonth = Math.min(firstMonth + n - 1, 12);
|
|
5001
|
+
return [monthStart(year3 * 1e4 + firstMonth * 100 + 1), monthEnd(year3 * 1e4 + lastMonth * 100 + 1)];
|
|
5051
5002
|
}
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
function pow(base, exponent) {
|
|
5055
|
-
const baseIsSeries = base instanceof Series;
|
|
5056
|
-
const expIsSeries = exponent instanceof Series;
|
|
5057
|
-
if (!baseIsSeries && !expIsSeries) {
|
|
5058
|
-
return Math.pow(base, exponent);
|
|
5003
|
+
dayStart(date) {
|
|
5004
|
+
return this.calendar.periods(date)[0][0];
|
|
5059
5005
|
}
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
const length2 = bars2.length;
|
|
5064
|
-
const result = [];
|
|
5065
|
-
for (let i = 0; i < length2; i++) {
|
|
5066
|
-
const b2 = baseArray ? baseArray[i] ?? NaN : base;
|
|
5067
|
-
const e2 = expArray ? expArray[i] ?? NaN : exponent;
|
|
5068
|
-
result.push(Math.pow(b2, e2));
|
|
5006
|
+
dayEnd(date) {
|
|
5007
|
+
const p = this.calendar.periods(date);
|
|
5008
|
+
return p[p.length - 1][1];
|
|
5069
5009
|
}
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
result.push(Math.exp(v));
|
|
5010
|
+
/** PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. */
|
|
5011
|
+
timeOf(time2, timeframe2) {
|
|
5012
|
+
const tf = info(timeframe2);
|
|
5013
|
+
const at = this.calendar.locate(time2);
|
|
5014
|
+
if (!at) return NaN;
|
|
5015
|
+
if (tf.isintraday) {
|
|
5016
|
+
if (this.chart.isdwm) return time2;
|
|
5017
|
+
const [start] = this.calendar.periods(at.date)[at.index];
|
|
5018
|
+
const ms = in_seconds(tf.period) * 1e3;
|
|
5019
|
+
return start + Math.floor((time2 - start) / ms) * ms;
|
|
5081
5020
|
}
|
|
5082
|
-
|
|
5021
|
+
const { unit, n } = unitOf(tf);
|
|
5022
|
+
const [from, to] = this.periodRange(at.date, unit, n);
|
|
5023
|
+
const first = this.calendar.firstTradingDay(from, to);
|
|
5024
|
+
return first === null ? NaN : this.dayStart(first);
|
|
5083
5025
|
}
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
const v = valueArray[i] ?? NaN;
|
|
5094
|
-
result.push(Math.log(v));
|
|
5026
|
+
/** PineScript `time_close(tf)` for a bar that opens at `time`. */
|
|
5027
|
+
closeOf(time2, timeframe2) {
|
|
5028
|
+
const tf = info(timeframe2);
|
|
5029
|
+
const at = this.calendar.locate(time2);
|
|
5030
|
+
if (!at) return NaN;
|
|
5031
|
+
if (tf.isintraday) {
|
|
5032
|
+
const [, end] = this.calendar.periods(at.date)[at.index];
|
|
5033
|
+
if (this.chart.isdwm) return end;
|
|
5034
|
+
return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
|
|
5095
5035
|
}
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
const bars2 = value.bars;
|
|
5103
|
-
const valueArray = value.toArray();
|
|
5104
|
-
const length2 = bars2.length;
|
|
5105
|
-
const result = [];
|
|
5106
|
-
for (let i = 0; i < length2; i++) {
|
|
5107
|
-
const v = valueArray[i] ?? NaN;
|
|
5108
|
-
result.push(Math.log10(v));
|
|
5036
|
+
const { unit, n } = unitOf(tf);
|
|
5037
|
+
const [from, to] = this.periodRange(at.date, unit, n);
|
|
5038
|
+
if (this.chart.isintraday && (unit !== "D" || n > 1)) {
|
|
5039
|
+
const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);
|
|
5040
|
+
const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));
|
|
5041
|
+
return first === null ? NaN : this.dayStart(first);
|
|
5109
5042
|
}
|
|
5110
|
-
|
|
5043
|
+
const last = this.calendar.lastTradingDay(from, to);
|
|
5044
|
+
return last === null ? NaN : this.dayEnd(last);
|
|
5111
5045
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
for (let i = 0; i < length2; i++) {
|
|
5121
|
-
const v = valueArray[i] ?? NaN;
|
|
5122
|
-
result.push(Math.sin(v));
|
|
5046
|
+
/** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */
|
|
5047
|
+
tradingDay(time2) {
|
|
5048
|
+
const at = this.calendar.locate(time2);
|
|
5049
|
+
if (!at) return null;
|
|
5050
|
+
if (this.chart.isdwm) {
|
|
5051
|
+
const { unit, n } = unitOf(this.chart);
|
|
5052
|
+
const [from, to] = this.periodRange(at.date, unit, n);
|
|
5053
|
+
return this.calendar.lastTradingDay(from, to);
|
|
5123
5054
|
}
|
|
5124
|
-
return
|
|
5055
|
+
return at.date;
|
|
5125
5056
|
}
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
const
|
|
5133
|
-
const
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5057
|
+
/**
|
|
5058
|
+
* End of the `timeframe` period that contains `time`: the intraday close, the session end of the day,
|
|
5059
|
+
* or the session end of the last trading day of the period. Used to know whether the last loaded
|
|
5060
|
+
* bar completes a higher-timeframe period (request.security).
|
|
5061
|
+
*/
|
|
5062
|
+
periodEnd(time2, timeframe2) {
|
|
5063
|
+
const tf = info(timeframe2);
|
|
5064
|
+
const at = this.calendar.locate(time2);
|
|
5065
|
+
if (!at) return NaN;
|
|
5066
|
+
if (tf.isintraday) {
|
|
5067
|
+
const [, end] = this.calendar.periods(at.date)[at.index];
|
|
5068
|
+
return Math.min(this.timeOf(time2, timeframe2) + in_seconds(tf.period) * 1e3, end);
|
|
5137
5069
|
}
|
|
5138
|
-
|
|
5070
|
+
const { unit, n } = unitOf(tf);
|
|
5071
|
+
const [from, to] = this.periodRange(at.date, unit, n);
|
|
5072
|
+
const last = this.calendar.lastTradingDay(from, to);
|
|
5073
|
+
return last === null ? NaN : this.dayEnd(last);
|
|
5139
5074
|
}
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
const bars2 = value.bars;
|
|
5145
|
-
const valueArray = value.toArray();
|
|
5146
|
-
const length2 = bars2.length;
|
|
5147
|
-
const result = [];
|
|
5148
|
-
for (let i = 0; i < length2; i++) {
|
|
5149
|
-
const v = valueArray[i] ?? NaN;
|
|
5150
|
-
result.push(Math.tan(v));
|
|
5151
|
-
}
|
|
5152
|
-
return Series.fromArray(bars2, result);
|
|
5075
|
+
/** PineScript `time_tradingday`: 00:00 UTC of the trading day. */
|
|
5076
|
+
tradingDayTime(time2) {
|
|
5077
|
+
const day = this.tradingDay(time2);
|
|
5078
|
+
return day === null ? NaN : dateUtcMs(day);
|
|
5153
5079
|
}
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
if (value instanceof Series) {
|
|
5158
|
-
const bars2 = value.bars;
|
|
5159
|
-
const valueArray = value.toArray();
|
|
5160
|
-
const length2 = bars2.length;
|
|
5161
|
-
const result = [];
|
|
5162
|
-
for (let i = 0; i < length2; i++) {
|
|
5163
|
-
const v = valueArray[i] ?? NaN;
|
|
5164
|
-
result.push(Math.asin(v));
|
|
5165
|
-
}
|
|
5166
|
-
return Series.fromArray(bars2, result);
|
|
5080
|
+
/** True when the bar at `time` is inside the session. */
|
|
5081
|
+
inside(time2) {
|
|
5082
|
+
return this.calendar.locate(time2)?.inside ?? false;
|
|
5167
5083
|
}
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
const bars2 = value.bars;
|
|
5173
|
-
const valueArray = value.toArray();
|
|
5174
|
-
const length2 = bars2.length;
|
|
5175
|
-
const result = [];
|
|
5176
|
-
for (let i = 0; i < length2; i++) {
|
|
5177
|
-
const v = valueArray[i] ?? NaN;
|
|
5178
|
-
result.push(Math.acos(v));
|
|
5179
|
-
}
|
|
5180
|
-
return Series.fromArray(bars2, result);
|
|
5084
|
+
/** Session start and end of the trading day of `time`, or null. */
|
|
5085
|
+
dayBounds(time2) {
|
|
5086
|
+
const at = this.calendar.locate(time2);
|
|
5087
|
+
return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;
|
|
5181
5088
|
}
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5089
|
+
};
|
|
5090
|
+
function sessionFlags(times, bars2, regular) {
|
|
5091
|
+
const n = times.length;
|
|
5092
|
+
const flag2 = () => new Array(n).fill(false);
|
|
5093
|
+
const out = {
|
|
5094
|
+
isfirstbar: flag2(),
|
|
5095
|
+
islastbar: flag2(),
|
|
5096
|
+
ismarket: flag2(),
|
|
5097
|
+
ispremarket: flag2(),
|
|
5098
|
+
ispostmarket: flag2(),
|
|
5099
|
+
isfirstbar_regular: flag2(),
|
|
5100
|
+
islastbar_regular: flag2()
|
|
5101
|
+
};
|
|
5102
|
+
const chart2 = bars2.chart;
|
|
5103
|
+
let previousPeriod = "";
|
|
5104
|
+
let previousRegular = "";
|
|
5105
|
+
for (let i = 0; i < n; i++) {
|
|
5106
|
+
const t2 = times[i];
|
|
5107
|
+
if (chart2.isdwm) {
|
|
5108
|
+
for (const k of ["isfirstbar", "islastbar", "ismarket", "isfirstbar_regular", "islastbar_regular"]) out[k][i] = true;
|
|
5109
|
+
continue;
|
|
5193
5110
|
}
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
function toradians(degrees) {
|
|
5199
|
-
if (degrees instanceof Series) {
|
|
5200
|
-
const bars2 = degrees.bars;
|
|
5201
|
-
const valueArray = degrees.toArray();
|
|
5202
|
-
const length2 = bars2.length;
|
|
5203
|
-
const result = [];
|
|
5204
|
-
for (let i = 0; i < length2; i++) {
|
|
5205
|
-
const v = valueArray[i] ?? NaN;
|
|
5206
|
-
result.push(v * (Math.PI / 180));
|
|
5111
|
+
const at = bars2.calendar.locate(t2);
|
|
5112
|
+
if (!at) {
|
|
5113
|
+
previousPeriod = previousRegular = "";
|
|
5114
|
+
continue;
|
|
5207
5115
|
}
|
|
5208
|
-
|
|
5116
|
+
const close2 = bars2.closeOf(t2, chart2.period);
|
|
5117
|
+
const [, end] = bars2.calendar.periods(at.date)[at.index];
|
|
5118
|
+
const period = `${at.date}/${at.index}`;
|
|
5119
|
+
out.isfirstbar[i] = period !== previousPeriod;
|
|
5120
|
+
out.islastbar[i] = close2 >= end;
|
|
5121
|
+
const regularPeriods = regular.calendar.periods(at.date);
|
|
5122
|
+
const r2 = regularPeriods.findIndex(([s, e2]) => s <= t2 && t2 < e2);
|
|
5123
|
+
const market = r2 !== -1;
|
|
5124
|
+
out.ismarket[i] = market;
|
|
5125
|
+
if (!market && regularPeriods.length) {
|
|
5126
|
+
out.ispremarket[i] = t2 < regularPeriods[0][0];
|
|
5127
|
+
out.ispostmarket[i] = t2 >= regularPeriods[regularPeriods.length - 1][1];
|
|
5128
|
+
}
|
|
5129
|
+
const regularPeriod = market ? `${at.date}/${r2}` : "";
|
|
5130
|
+
out.isfirstbar_regular[i] = market && regularPeriod !== previousRegular;
|
|
5131
|
+
out.islastbar_regular[i] = market && close2 >= regularPeriods[r2][1];
|
|
5132
|
+
previousPeriod = period;
|
|
5133
|
+
previousRegular = regularPeriod;
|
|
5209
5134
|
}
|
|
5210
|
-
return
|
|
5135
|
+
return out;
|
|
5211
5136
|
}
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5137
|
+
|
|
5138
|
+
// src/security/resample.ts
|
|
5139
|
+
function resample(bars2, periodStart) {
|
|
5140
|
+
const out = [];
|
|
5141
|
+
const group2 = [];
|
|
5142
|
+
bars2.forEach((b2, i) => {
|
|
5143
|
+
const start = periodStart[i];
|
|
5144
|
+
const last = out[out.length - 1];
|
|
5145
|
+
if (!last || last.time !== start) {
|
|
5146
|
+
out.push({ time: start, open: b2.open, high: b2.high, low: b2.low, close: b2.close, volume: b2.volume ?? NaN });
|
|
5147
|
+
} else {
|
|
5148
|
+
last.high = Math.max(last.high, b2.high);
|
|
5149
|
+
last.low = Math.min(last.low, b2.low);
|
|
5150
|
+
last.close = b2.close;
|
|
5151
|
+
last.volume = (last.volume ?? 0) + (b2.volume ?? NaN);
|
|
5221
5152
|
}
|
|
5222
|
-
|
|
5223
|
-
}
|
|
5224
|
-
return
|
|
5153
|
+
group2.push(out.length - 1);
|
|
5154
|
+
});
|
|
5155
|
+
return { bars: out, group: group2 };
|
|
5225
5156
|
}
|
|
5226
|
-
function
|
|
5227
|
-
const
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5157
|
+
function periodsOf(starts, dayPeriods, reaches) {
|
|
5158
|
+
const n = starts.length;
|
|
5159
|
+
const group2 = [];
|
|
5160
|
+
starts.forEach((s, i) => group2.push(i === 0 ? 0 : group2[i - 1] + (s !== starts[i - 1] ? 1 : 0)));
|
|
5161
|
+
const latest = [];
|
|
5162
|
+
starts.forEach((s, i) => {
|
|
5163
|
+
let done = i === 0 ? -1 : latest[i - 1];
|
|
5164
|
+
if (i > 0 && group2[i] !== group2[i - 1]) done = Math.max(done, group2[i] - 1);
|
|
5165
|
+
const ends = i < n - 1 ? starts[i + 1] !== s || dayPeriods[i + 1] !== dayPeriods[i] : true;
|
|
5166
|
+
if (ends && reaches[i]) done = Math.max(done, group2[i]);
|
|
5167
|
+
latest.push(done);
|
|
5168
|
+
});
|
|
5169
|
+
return { group: group2, latest };
|
|
5232
5170
|
}
|
|
5233
|
-
function
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
const result = [];
|
|
5239
|
-
for (let i = 0; i < length2; i++) {
|
|
5240
|
-
const v = valueArray[i] ?? NaN;
|
|
5241
|
-
result.push(v > 0 ? 1 : v < 0 ? -1 : 0);
|
|
5171
|
+
function mapToChart(values, group2, latest, lookahead, gaps) {
|
|
5172
|
+
return group2.map((g2, i) => {
|
|
5173
|
+
if (lookahead) {
|
|
5174
|
+
const first = i === 0 || group2[i - 1] !== g2;
|
|
5175
|
+
return gaps && !first ? NaN : values[g2] ?? NaN;
|
|
5242
5176
|
}
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5177
|
+
const done = latest[i];
|
|
5178
|
+
if (done < 0 || gaps && i > 0 && latest[i - 1] === done) return NaN;
|
|
5179
|
+
return values[done] ?? NaN;
|
|
5180
|
+
});
|
|
5246
5181
|
}
|
|
5247
|
-
function
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
const
|
|
5251
|
-
const
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
}
|
|
5257
|
-
const result = [];
|
|
5258
|
-
if (mintick === 0) {
|
|
5259
|
-
for (let i = 0; i < length2; i++) {
|
|
5260
|
-
result.push(valueArray[i] ?? NaN);
|
|
5261
|
-
}
|
|
5262
|
-
} else {
|
|
5263
|
-
for (let i = 0; i < length2; i++) {
|
|
5264
|
-
const v = valueArray[i];
|
|
5265
|
-
if (v === void 0 || isNaN(v)) {
|
|
5266
|
-
result.push(NaN);
|
|
5267
|
-
} else {
|
|
5268
|
-
result.push(Math.round(v / mintick) * mintick);
|
|
5269
|
-
}
|
|
5270
|
-
}
|
|
5271
|
-
}
|
|
5272
|
-
return Series.fromArray(bars2, result);
|
|
5273
|
-
}
|
|
5274
|
-
if (isNaN(number2)) {
|
|
5275
|
-
return NaN;
|
|
5276
|
-
}
|
|
5277
|
-
if (mintick === void 0) {
|
|
5278
|
-
throw new Error(
|
|
5279
|
-
"math.round_to_mintick() requires mintick value. Either pass it explicitly or use createContext({ syminfo: { mintick } }) for implicit data."
|
|
5280
|
-
);
|
|
5281
|
-
}
|
|
5282
|
-
if (mintick === 0) {
|
|
5283
|
-
return number2;
|
|
5284
|
-
}
|
|
5285
|
-
return Math.round(number2 / mintick) * mintick;
|
|
5182
|
+
function heikinAshi(bars2) {
|
|
5183
|
+
const out = [];
|
|
5184
|
+
bars2.forEach((b2, i) => {
|
|
5185
|
+
const close2 = (b2.open + b2.high + b2.low + b2.close) / 4;
|
|
5186
|
+
const prev = out[i - 1];
|
|
5187
|
+
const open2 = prev ? (prev.open + prev.close) / 2 : (b2.open + b2.close) / 2;
|
|
5188
|
+
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 });
|
|
5189
|
+
});
|
|
5190
|
+
return out;
|
|
5286
5191
|
}
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
var
|
|
5290
|
-
|
|
5192
|
+
|
|
5193
|
+
// src/strategy/index.ts
|
|
5194
|
+
var STRATEGY_CONSTANTS = {
|
|
5195
|
+
long: "long",
|
|
5196
|
+
short: "short",
|
|
5197
|
+
fixed: "fixed",
|
|
5198
|
+
cash: "cash",
|
|
5199
|
+
percent_of_equity: "percent_of_equity",
|
|
5200
|
+
commission: { percent: "percent", cash_per_contract: "cash_per_contract", cash_per_order: "cash_per_order" },
|
|
5201
|
+
direction: { all: "all", long: "long", short: "short" },
|
|
5202
|
+
oca: { none: "none", cancel: "cancel", reduce: "reduce" }
|
|
5203
|
+
};
|
|
5204
|
+
var STRATEGY_DEFAULTS = {
|
|
5205
|
+
pyramiding: 0,
|
|
5206
|
+
calc_on_order_fills: false,
|
|
5207
|
+
calc_on_every_tick: false,
|
|
5208
|
+
backtest_fill_limits_assumption: 0,
|
|
5209
|
+
// v6 values; PineScript v5: fixed / 1 / 1000000 and margins 0
|
|
5210
|
+
default_qty_type: "percent_of_equity",
|
|
5211
|
+
default_qty_value: 100,
|
|
5212
|
+
initial_capital: 1e5,
|
|
5213
|
+
slippage: 0,
|
|
5214
|
+
commission_type: "percent",
|
|
5215
|
+
commission_value: 0,
|
|
5216
|
+
process_orders_on_close: false,
|
|
5217
|
+
close_entries_rule: "FIFO",
|
|
5218
|
+
margin_long: 100,
|
|
5219
|
+
margin_short: 100,
|
|
5220
|
+
calc_bars_count: 0,
|
|
5221
|
+
risk_free_rate: 2,
|
|
5222
|
+
use_bar_magnifier: false,
|
|
5223
|
+
fill_orders_on_standard_ohlc: false
|
|
5224
|
+
};
|
|
5225
|
+
var STRATEGY_NUMBER_VARIABLES = [
|
|
5226
|
+
"position_size",
|
|
5227
|
+
"position_avg_price",
|
|
5228
|
+
"equity",
|
|
5229
|
+
"initial_capital",
|
|
5230
|
+
"netprofit",
|
|
5231
|
+
"netprofit_percent",
|
|
5232
|
+
"openprofit",
|
|
5233
|
+
"openprofit_percent",
|
|
5234
|
+
"grossprofit",
|
|
5235
|
+
"grossprofit_percent",
|
|
5236
|
+
"grossloss",
|
|
5237
|
+
"grossloss_percent",
|
|
5238
|
+
"opentrades",
|
|
5239
|
+
"closedtrades",
|
|
5240
|
+
"wintrades",
|
|
5241
|
+
"losstrades",
|
|
5242
|
+
"eventrades",
|
|
5243
|
+
"avg_trade",
|
|
5244
|
+
"avg_trade_percent",
|
|
5245
|
+
"avg_winning_trade",
|
|
5246
|
+
"avg_winning_trade_percent",
|
|
5247
|
+
"avg_losing_trade",
|
|
5248
|
+
"avg_losing_trade_percent",
|
|
5249
|
+
"max_drawdown",
|
|
5250
|
+
"max_drawdown_percent",
|
|
5251
|
+
"max_runup",
|
|
5252
|
+
"max_runup_percent",
|
|
5253
|
+
"max_contracts_held_all",
|
|
5254
|
+
"max_contracts_held_long",
|
|
5255
|
+
"max_contracts_held_short",
|
|
5256
|
+
"margin_liquidation_price"
|
|
5257
|
+
];
|
|
5258
|
+
var STRATEGY_TEXT_VARIABLES = ["position_entry_name", "account_currency"];
|
|
5291
5259
|
|
|
5292
5260
|
// src/script/index.ts
|
|
5293
5261
|
var ctxBars = new BarData([]);
|
|
@@ -5315,6 +5283,8 @@ function freshCollector(inputValues) {
|
|
|
5315
5283
|
fillConfig: [],
|
|
5316
5284
|
shapeConfig: [],
|
|
5317
5285
|
markers: [],
|
|
5286
|
+
arrowConfig: [],
|
|
5287
|
+
arrows: [],
|
|
5318
5288
|
barColorConfig: [],
|
|
5319
5289
|
bgcolors: [],
|
|
5320
5290
|
barcolors: [],
|
|
@@ -5329,9 +5299,11 @@ function slugId(title, fallback, taken) {
|
|
|
5329
5299
|
taken.add(id);
|
|
5330
5300
|
return id;
|
|
5331
5301
|
}
|
|
5332
|
-
|
|
5302
|
+
var scriptOptions = {};
|
|
5303
|
+
function executeScript(body, barsInput, inputs = {}, chart2 = {}, options = {}) {
|
|
5333
5304
|
ctxBars.setAll(barsInput);
|
|
5334
5305
|
chartCtx = { ...chart2 };
|
|
5306
|
+
scriptOptions = options;
|
|
5335
5307
|
ctx = freshCollector(inputs);
|
|
5336
5308
|
resetDrawings();
|
|
5337
5309
|
try {
|
|
@@ -5350,14 +5322,17 @@ function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
|
|
|
5350
5322
|
hlineConfig: c.hlineConfig,
|
|
5351
5323
|
fillConfig: c.fillConfig,
|
|
5352
5324
|
shapeConfig: c.shapeConfig,
|
|
5325
|
+
arrowConfig: c.arrowConfig,
|
|
5353
5326
|
barColorConfig: c.barColorConfig,
|
|
5354
5327
|
defaultInputs: c.defaultInputs,
|
|
5355
5328
|
alertConfig: c.alertConfig,
|
|
5329
|
+
strategyConfig: c.strategy?.properties,
|
|
5356
5330
|
result: {
|
|
5357
5331
|
metadata: { title: c.title, shorttitle: c.shortTitle, overlay: c.overlay, precision: c.precision },
|
|
5358
5332
|
plots: c.plots,
|
|
5359
5333
|
fills: c.fills.length ? c.fills : void 0,
|
|
5360
5334
|
markers: c.markers.length ? c.markers : void 0,
|
|
5335
|
+
arrows: c.arrows.length ? c.arrows : void 0,
|
|
5361
5336
|
bgcolors: c.bgcolors.length ? c.bgcolors : void 0,
|
|
5362
5337
|
barcolors: c.barcolors.length ? c.barcolors : void 0,
|
|
5363
5338
|
alerts: c.alerts.length ? c.alerts : void 0
|
|
@@ -5369,6 +5344,7 @@ function executeScript(body, barsInput, inputs = {}, chart2 = {}) {
|
|
|
5369
5344
|
}
|
|
5370
5345
|
function indicator(title, options = {}) {
|
|
5371
5346
|
const c = collector();
|
|
5347
|
+
declare(c, "indicator");
|
|
5372
5348
|
c.title = title;
|
|
5373
5349
|
c.shortTitle = options.shorttitle;
|
|
5374
5350
|
c.overlay = options.overlay ?? true;
|
|
@@ -5381,6 +5357,21 @@ function indicator(title, options = {}) {
|
|
|
5381
5357
|
polyline: options.max_polylines_count
|
|
5382
5358
|
});
|
|
5383
5359
|
}
|
|
5360
|
+
function declare(c, kind) {
|
|
5361
|
+
if (c.declaration) {
|
|
5362
|
+
throw new Error(`${kind}(): the script already declared ${c.declaration}(); a script has one declaration.`);
|
|
5363
|
+
}
|
|
5364
|
+
c.declaration = kind;
|
|
5365
|
+
}
|
|
5366
|
+
function common(opts) {
|
|
5367
|
+
const out = {};
|
|
5368
|
+
if (opts.group !== void 0) out.group = opts.group;
|
|
5369
|
+
if (opts.inline !== void 0) out.inline = opts.inline;
|
|
5370
|
+
if (opts.tooltip !== void 0) out.tooltip = opts.tooltip;
|
|
5371
|
+
if (opts.confirm !== void 0) out.confirm = opts.confirm;
|
|
5372
|
+
if (opts.display !== void 0) out.display = opts.display;
|
|
5373
|
+
return out;
|
|
5374
|
+
}
|
|
5384
5375
|
function registerInput(config, fallbackId) {
|
|
5385
5376
|
const c = collector();
|
|
5386
5377
|
const id = slugId(config.title, fallbackId, c.inputIds);
|
|
@@ -5391,7 +5382,7 @@ function registerInput(config, fallbackId) {
|
|
|
5391
5382
|
}
|
|
5392
5383
|
function registerNumeric(type, defval, title, opts = {}) {
|
|
5393
5384
|
const v = registerInput(
|
|
5394
|
-
{ type, defval, title, min: opts.minval, max: opts.maxval, step: opts.step },
|
|
5385
|
+
{ type, defval, title, min: opts.minval, max: opts.maxval, step: opts.step, ...common(opts) },
|
|
5395
5386
|
`input_${type}`
|
|
5396
5387
|
);
|
|
5397
5388
|
return typeof v === "number" ? v : defval;
|
|
@@ -5403,31 +5394,37 @@ var input = {
|
|
|
5403
5394
|
float(defval, title, opts = {}) {
|
|
5404
5395
|
return registerNumeric("float", defval, title, opts);
|
|
5405
5396
|
},
|
|
5406
|
-
bool(defval, title) {
|
|
5407
|
-
return registerInput({ type: "bool", defval, title }, "input_bool");
|
|
5397
|
+
bool(defval, title, opts = {}) {
|
|
5398
|
+
return registerInput({ type: "bool", defval, title, ...common(opts) }, "input_bool");
|
|
5408
5399
|
},
|
|
5409
5400
|
string(defval, title, opts = {}) {
|
|
5410
|
-
return registerInput({ type: "string", defval, title, options: opts.options }, "input_string");
|
|
5401
|
+
return registerInput({ type: "string", defval, title, options: opts.options, ...common(opts) }, "input_string");
|
|
5411
5402
|
},
|
|
5412
|
-
color(defval, title) {
|
|
5413
|
-
return registerInput({ type: "color", defval, title }, "input_color");
|
|
5403
|
+
color(defval, title, opts = {}) {
|
|
5404
|
+
return registerInput({ type: "color", defval, title, ...common(opts) }, "input_color");
|
|
5414
5405
|
},
|
|
5415
5406
|
/** Declares a source input and returns it as a Series. */
|
|
5416
|
-
source(defval = "close", title) {
|
|
5417
|
-
const name = registerInput({ type: "source", defval, title }, "input_source");
|
|
5407
|
+
source(defval = "close", title, opts = {}) {
|
|
5408
|
+
const name = registerInput({ type: "source", defval, title, ...common(opts) }, "input_source");
|
|
5418
5409
|
return sourceSeries(name);
|
|
5419
5410
|
},
|
|
5420
5411
|
/** Timeframe string ("60", "1D", "W"). As in PineScript, "" means the chart timeframe. */
|
|
5421
5412
|
timeframe(defval, title, opts = {}) {
|
|
5422
|
-
return registerInput(
|
|
5413
|
+
return registerInput(
|
|
5414
|
+
{ type: "timeframe", defval, title, options: opts.options, ...common(opts) },
|
|
5415
|
+
"input_timeframe"
|
|
5416
|
+
);
|
|
5423
5417
|
},
|
|
5424
5418
|
/** Session string ("0930-1600", "0930-1600:23456"). */
|
|
5425
5419
|
session(defval, title, opts = {}) {
|
|
5426
|
-
return registerInput(
|
|
5420
|
+
return registerInput(
|
|
5421
|
+
{ type: "session", defval, title, options: opts.options, ...common(opts) },
|
|
5422
|
+
"input_session"
|
|
5423
|
+
);
|
|
5427
5424
|
},
|
|
5428
5425
|
/** Date and time as a UNIX time in milliseconds, e.g. `input.time(timestamp('2024-01-01'), 'Start')`. */
|
|
5429
|
-
time(defval, title) {
|
|
5430
|
-
const v = registerInput({ type: "time", defval, title }, "input_time");
|
|
5426
|
+
time(defval, title, opts = {}) {
|
|
5427
|
+
const v = registerInput({ type: "time", defval, title, ...common(opts) }, "input_time");
|
|
5431
5428
|
return typeof v === "number" ? v : defval;
|
|
5432
5429
|
}
|
|
5433
5430
|
};
|
|
@@ -5481,6 +5478,8 @@ var ta2 = {
|
|
|
5481
5478
|
const a = anchor ?? changeOf("1D");
|
|
5482
5479
|
return stdev_mult === void 0 ? vwap2(source, volume, a) : vwapBands(source, volume, a, stdev_mult);
|
|
5483
5480
|
}),
|
|
5481
|
+
/** `ta.mfi(source, length)` with the chart volume (PineScript takes 2 arguments). */
|
|
5482
|
+
mfi: (source, length2) => mfi2(source, length2, volume),
|
|
5484
5483
|
/** `ta.pivothigh(source, leftbars, rightbars)` or `ta.pivothigh(leftbars, rightbars)` (source = high).
|
|
5485
5484
|
* The value appears `rightbars` bars after the pivot bar, as in PineScript. */
|
|
5486
5485
|
pivothigh: (sourceOrLeft, leftOrRight, rightbars) => typeof sourceOrLeft === "number" ? pivothigh2(high, sourceOrLeft, leftOrRight) : pivothigh2(sourceOrLeft, leftOrRight, rightbars),
|
|
@@ -5507,7 +5506,7 @@ var color = {
|
|
|
5507
5506
|
new: new_color,
|
|
5508
5507
|
when: colorWhen
|
|
5509
5508
|
};
|
|
5510
|
-
function
|
|
5509
|
+
function plotImpl(series, title, options = {}) {
|
|
5511
5510
|
const c = collector();
|
|
5512
5511
|
const id = `plot${c.plotConfig.length}`;
|
|
5513
5512
|
const perBar2 = Array.isArray(options.color) ? options.color : void 0;
|
|
@@ -5518,6 +5517,7 @@ function plot(series, title, options = {}) {
|
|
|
5518
5517
|
color: staticColor,
|
|
5519
5518
|
lineWidth: options.linewidth,
|
|
5520
5519
|
style: options.style,
|
|
5520
|
+
linestyle: options.linestyle,
|
|
5521
5521
|
display: options.display,
|
|
5522
5522
|
histbase: options.histbase
|
|
5523
5523
|
});
|
|
@@ -5526,13 +5526,29 @@ function plot(series, title, options = {}) {
|
|
|
5526
5526
|
const data = [];
|
|
5527
5527
|
for (let i = 0; i < b2.length; i++) {
|
|
5528
5528
|
const value = values[i];
|
|
5529
|
-
if (value === void 0 || Number.
|
|
5529
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
5530
5530
|
data.push(perBar2 ? { time: b2[i].time, value, color: perBar2[i] } : { time: b2[i].time, value });
|
|
5531
5531
|
}
|
|
5532
5532
|
c.plots[id] = data;
|
|
5533
5533
|
return { id, kind: "plot" };
|
|
5534
5534
|
}
|
|
5535
|
-
|
|
5535
|
+
var plot = Object.assign(plotImpl, {
|
|
5536
|
+
style_line: "line",
|
|
5537
|
+
style_linebr: "linebr",
|
|
5538
|
+
style_stepline: "stepline",
|
|
5539
|
+
style_steplinebr: "steplinebr",
|
|
5540
|
+
style_stepline_diamond: "stepline_diamond",
|
|
5541
|
+
style_histogram: "histogram",
|
|
5542
|
+
style_columns: "columns",
|
|
5543
|
+
style_area: "area",
|
|
5544
|
+
style_areabr: "areabr",
|
|
5545
|
+
style_circles: "circles",
|
|
5546
|
+
style_cross: "cross",
|
|
5547
|
+
linestyle_solid: "solid",
|
|
5548
|
+
linestyle_dotted: "dotted",
|
|
5549
|
+
linestyle_dashed: "dashed"
|
|
5550
|
+
});
|
|
5551
|
+
function hlineImpl(price, title, options = {}) {
|
|
5536
5552
|
const c = collector();
|
|
5537
5553
|
const id = `hline${c.hlineConfig.length}`;
|
|
5538
5554
|
c.hlineConfig.push({
|
|
@@ -5540,11 +5556,16 @@ function hline(price, title, options = {}) {
|
|
|
5540
5556
|
price,
|
|
5541
5557
|
title,
|
|
5542
5558
|
color: options.color,
|
|
5543
|
-
linestyle: options.linestyle,
|
|
5559
|
+
linestyle: options.linestyle ?? "dashed",
|
|
5544
5560
|
linewidth: options.linewidth
|
|
5545
5561
|
});
|
|
5546
5562
|
return { id, kind: "hline" };
|
|
5547
5563
|
}
|
|
5564
|
+
var hline = Object.assign(hlineImpl, {
|
|
5565
|
+
style_solid: "solid",
|
|
5566
|
+
style_dotted: "dotted",
|
|
5567
|
+
style_dashed: "dashed"
|
|
5568
|
+
});
|
|
5548
5569
|
function perBarValues(v, n) {
|
|
5549
5570
|
return v instanceof Series ? v.toArray().slice(0, n) : new Array(n).fill(v);
|
|
5550
5571
|
}
|
|
@@ -5647,6 +5668,35 @@ function plotshape(condition, title, options = {}) {
|
|
|
5647
5668
|
function plotchar(condition, title, options = {}) {
|
|
5648
5669
|
emitMarkers("char", condition, title, options);
|
|
5649
5670
|
}
|
|
5671
|
+
function plotarrow(series, title, options = {}) {
|
|
5672
|
+
const c = collector();
|
|
5673
|
+
const id = `arrow${c.arrowConfig.length}`;
|
|
5674
|
+
const pick = (col2, fallback, i) => col2 === void 0 ? fallback : typeof col2 === "string" ? col2 : col2[i];
|
|
5675
|
+
const colorup = typeof options.colorup === "string" ? options.colorup : "#00FF00";
|
|
5676
|
+
const colordown = typeof options.colordown === "string" ? options.colordown : "#FF0000";
|
|
5677
|
+
c.arrowConfig.push({
|
|
5678
|
+
id,
|
|
5679
|
+
title,
|
|
5680
|
+
colorup,
|
|
5681
|
+
colordown,
|
|
5682
|
+
minheight: options.minheight ?? 5,
|
|
5683
|
+
maxheight: options.maxheight ?? 100,
|
|
5684
|
+
offset: options.offset,
|
|
5685
|
+
display: options.display
|
|
5686
|
+
});
|
|
5687
|
+
const values = series.toArray();
|
|
5688
|
+
const b2 = bars();
|
|
5689
|
+
const off = options.offset ?? 0;
|
|
5690
|
+
for (let i = 0; i < b2.length; i++) {
|
|
5691
|
+
const v = values[i];
|
|
5692
|
+
if (v === void 0 || Number.isNaN(v) || v === 0) continue;
|
|
5693
|
+
const j = i + off;
|
|
5694
|
+
if (j < 0 || j >= b2.length) continue;
|
|
5695
|
+
const color2 = v > 0 ? pick(options.colorup, "#00FF00", i) : pick(options.colordown, "#FF0000", i);
|
|
5696
|
+
if (color2 === void 0) continue;
|
|
5697
|
+
c.arrows.push({ time: b2[j].time, id, value: v, color: color2 });
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5650
5700
|
function emitBarColors(kind, colors, options) {
|
|
5651
5701
|
const c = collector();
|
|
5652
5702
|
const id = `${kind}${c.barColorConfig.filter((x) => x.kind === kind).length}`;
|
|
@@ -5723,6 +5773,139 @@ function eachBar(fn) {
|
|
|
5723
5773
|
return Series.fromArray(ctxBars, out);
|
|
5724
5774
|
}
|
|
5725
5775
|
var currentBar = -1;
|
|
5776
|
+
function strategyRun(caller) {
|
|
5777
|
+
const run = collector().strategy;
|
|
5778
|
+
if (!run) throw new Error(`${caller}: declare the script with strategy() first.`);
|
|
5779
|
+
return run;
|
|
5780
|
+
}
|
|
5781
|
+
function strategyEngine(caller) {
|
|
5782
|
+
const run = strategyRun(caller);
|
|
5783
|
+
if (!run.engine) {
|
|
5784
|
+
throw new Error(
|
|
5785
|
+
`${caller}: no strategy engine; pass it as executeScript(body, bars, inputs, chart, { strategyEngine }).`
|
|
5786
|
+
);
|
|
5787
|
+
}
|
|
5788
|
+
return run.engine;
|
|
5789
|
+
}
|
|
5790
|
+
function orderEngine(caller) {
|
|
5791
|
+
const engine = strategyEngine(caller);
|
|
5792
|
+
if (!strategyRun(caller).inLoop) throw new Error(`${caller} must be called inside strategy.eachBar().`);
|
|
5793
|
+
return engine;
|
|
5794
|
+
}
|
|
5795
|
+
function given(options) {
|
|
5796
|
+
const out = {};
|
|
5797
|
+
for (const [key, value] of Object.entries(options)) {
|
|
5798
|
+
if (value === void 0 || typeof value === "number" && Number.isNaN(value)) continue;
|
|
5799
|
+
out[key] = value;
|
|
5800
|
+
}
|
|
5801
|
+
return out;
|
|
5802
|
+
}
|
|
5803
|
+
function strategyVariable(name) {
|
|
5804
|
+
const value = strategyEngine(`strategy.${name}`).get(name);
|
|
5805
|
+
if (value === void 0) throw new Error(`strategy.${name} is not provided by the strategy engine.`);
|
|
5806
|
+
return value;
|
|
5807
|
+
}
|
|
5808
|
+
function riskRule(rule) {
|
|
5809
|
+
const engine = strategyEngine(`strategy.risk.${rule.rule}()`);
|
|
5810
|
+
if (!engine.risk) throw new Error(`strategy.risk.${rule.rule}() is not provided by the strategy engine.`);
|
|
5811
|
+
engine.risk(rule);
|
|
5812
|
+
}
|
|
5813
|
+
function declareStrategy(title, options = {}) {
|
|
5814
|
+
const c = collector();
|
|
5815
|
+
declare(c, "strategy");
|
|
5816
|
+
c.title = title;
|
|
5817
|
+
c.shortTitle = options.shorttitle;
|
|
5818
|
+
c.overlay = options.overlay ?? false;
|
|
5819
|
+
c.precision = options.precision;
|
|
5820
|
+
c.format = options.format;
|
|
5821
|
+
setDrawingLimits({
|
|
5822
|
+
line: options.max_lines_count,
|
|
5823
|
+
label: options.max_labels_count,
|
|
5824
|
+
box: options.max_boxes_count,
|
|
5825
|
+
polyline: options.max_polylines_count
|
|
5826
|
+
});
|
|
5827
|
+
const properties = { ...STRATEGY_DEFAULTS };
|
|
5828
|
+
const target = properties;
|
|
5829
|
+
for (const key of [...Object.keys(STRATEGY_DEFAULTS), "currency"]) {
|
|
5830
|
+
const value = options[key];
|
|
5831
|
+
if (value !== void 0) target[key] = value;
|
|
5832
|
+
}
|
|
5833
|
+
const factory = scriptOptions.strategyEngine;
|
|
5834
|
+
c.strategy = {
|
|
5835
|
+
properties,
|
|
5836
|
+
engine: factory?.({ properties, bars: ctxBars.bars, chart: chartCtx }),
|
|
5837
|
+
looped: false,
|
|
5838
|
+
inLoop: false
|
|
5839
|
+
};
|
|
5840
|
+
}
|
|
5841
|
+
function strategyEachBar(fn) {
|
|
5842
|
+
const run = strategyRun("strategy.eachBar()");
|
|
5843
|
+
const engine = strategyEngine("strategy.eachBar()");
|
|
5844
|
+
if (run.looped) throw new Error("strategy.eachBar() runs once per script run.");
|
|
5845
|
+
run.looped = true;
|
|
5846
|
+
return eachBar((c) => {
|
|
5847
|
+
engine.processBar(c.i);
|
|
5848
|
+
run.inLoop = true;
|
|
5849
|
+
try {
|
|
5850
|
+
return fn(c);
|
|
5851
|
+
} finally {
|
|
5852
|
+
run.inLoop = false;
|
|
5853
|
+
engine.processClose(c.i);
|
|
5854
|
+
}
|
|
5855
|
+
});
|
|
5856
|
+
}
|
|
5857
|
+
var strategyMembers = {
|
|
5858
|
+
...STRATEGY_CONSTANTS,
|
|
5859
|
+
eachBar: strategyEachBar,
|
|
5860
|
+
entry(id, direction, options = {}) {
|
|
5861
|
+
orderEngine("strategy.entry()").entry(id, direction, given(options));
|
|
5862
|
+
},
|
|
5863
|
+
order(id, direction, options = {}) {
|
|
5864
|
+
orderEngine("strategy.order()").order(id, direction, given(options));
|
|
5865
|
+
},
|
|
5866
|
+
exit(id, options = {}) {
|
|
5867
|
+
orderEngine("strategy.exit()").exit(id, given(options));
|
|
5868
|
+
},
|
|
5869
|
+
close(id, options = {}) {
|
|
5870
|
+
orderEngine("strategy.close()").close(id, given(options));
|
|
5871
|
+
},
|
|
5872
|
+
close_all(options = {}) {
|
|
5873
|
+
orderEngine("strategy.close_all()").close_all(given(options));
|
|
5874
|
+
},
|
|
5875
|
+
cancel(id) {
|
|
5876
|
+
orderEngine("strategy.cancel()").cancel(id);
|
|
5877
|
+
},
|
|
5878
|
+
cancel_all() {
|
|
5879
|
+
orderEngine("strategy.cancel_all()").cancel_all();
|
|
5880
|
+
},
|
|
5881
|
+
opentrade(index) {
|
|
5882
|
+
return strategyEngine("strategy.opentrade()").openTrade(index);
|
|
5883
|
+
},
|
|
5884
|
+
closedtrade(index) {
|
|
5885
|
+
return strategyEngine("strategy.closedtrade()").closedTrade(index);
|
|
5886
|
+
},
|
|
5887
|
+
default_entry_qty(fill_price) {
|
|
5888
|
+
const engine = strategyEngine("strategy.default_entry_qty()");
|
|
5889
|
+
if (!engine.defaultEntryQty) throw new Error("strategy.default_entry_qty() is not provided by the strategy engine.");
|
|
5890
|
+
return engine.defaultEntryQty(fill_price);
|
|
5891
|
+
},
|
|
5892
|
+
risk: {
|
|
5893
|
+
allow_entry_in: (direction) => riskRule({ rule: "allow_entry_in", direction }),
|
|
5894
|
+
max_cons_loss_days: (count, alert_message) => riskRule(given({ rule: "max_cons_loss_days", count, alert_message })),
|
|
5895
|
+
max_drawdown: (value, type, alert_message) => riskRule(given({ rule: "max_drawdown", value, type, alert_message })),
|
|
5896
|
+
max_intraday_filled_orders: (count, alert_message) => riskRule(given({ rule: "max_intraday_filled_orders", count, alert_message })),
|
|
5897
|
+
max_intraday_loss: (value, type, alert_message) => riskRule(given({ rule: "max_intraday_loss", value, type, alert_message })),
|
|
5898
|
+
max_position_size: (contracts) => riskRule({ rule: "max_position_size", contracts })
|
|
5899
|
+
}
|
|
5900
|
+
};
|
|
5901
|
+
var variableGetters = {};
|
|
5902
|
+
for (const name of [...STRATEGY_NUMBER_VARIABLES, ...STRATEGY_TEXT_VARIABLES]) {
|
|
5903
|
+
variableGetters[name] = { get: () => strategyVariable(name), enumerable: true };
|
|
5904
|
+
}
|
|
5905
|
+
var strategy = Object.defineProperties(
|
|
5906
|
+
Object.assign((title, options) => declareStrategy(title, options), strategyMembers),
|
|
5907
|
+
variableGetters
|
|
5908
|
+
);
|
|
5726
5909
|
var chartCtx = {};
|
|
5727
5910
|
function chartTimeframe() {
|
|
5728
5911
|
collector();
|
|
@@ -5782,7 +5965,24 @@ var timeframe = {
|
|
|
5782
5965
|
return in_seconds(tf === void 0 || tf === "" ? chartTimeframe().period : tf);
|
|
5783
5966
|
}
|
|
5784
5967
|
};
|
|
5968
|
+
function symbolProperty(key) {
|
|
5969
|
+
collector();
|
|
5970
|
+
const value = chartCtx[key];
|
|
5971
|
+
if (value === void 0) {
|
|
5972
|
+
throw new Error(`syminfo.${key} is not known: pass it as executeScript(body, bars, inputs, { ${key} }).`);
|
|
5973
|
+
}
|
|
5974
|
+
return value;
|
|
5975
|
+
}
|
|
5785
5976
|
var syminfo = {
|
|
5977
|
+
get mintick() {
|
|
5978
|
+
return symbolProperty("mintick");
|
|
5979
|
+
},
|
|
5980
|
+
get pointvalue() {
|
|
5981
|
+
return symbolProperty("pointvalue");
|
|
5982
|
+
},
|
|
5983
|
+
get mincontract() {
|
|
5984
|
+
return symbolProperty("mincontract");
|
|
5985
|
+
},
|
|
5786
5986
|
get timezone() {
|
|
5787
5987
|
return exchangeTimezone();
|
|
5788
5988
|
},
|
|
@@ -5898,6 +6098,37 @@ function flag(key) {
|
|
|
5898
6098
|
const values = sessionFlags(ctxBars.bars.map(barTime), bars2, regular)[key];
|
|
5899
6099
|
return Series.fromArray(ctxBars, values.map((v) => v ? 1 : 0));
|
|
5900
6100
|
}
|
|
6101
|
+
function barFlag(test) {
|
|
6102
|
+
const n = ctxBars.bars.length;
|
|
6103
|
+
return Series.fromArray(ctxBars, Array.from({ length: n }, (_, i) => test(i, n) ? 1 : 0));
|
|
6104
|
+
}
|
|
6105
|
+
var lastRealtime = (i, n) => i === n - 1 && chartCtx.realtime === true;
|
|
6106
|
+
var barstate = {
|
|
6107
|
+
get isfirst() {
|
|
6108
|
+
return barFlag((i) => i === 0);
|
|
6109
|
+
},
|
|
6110
|
+
get islast() {
|
|
6111
|
+
return barFlag((i, n) => i === n - 1);
|
|
6112
|
+
},
|
|
6113
|
+
get isconfirmed() {
|
|
6114
|
+
return barFlag((i, n) => i < n - 1 || chartCtx.lastBarConfirmed === true);
|
|
6115
|
+
},
|
|
6116
|
+
get isrealtime() {
|
|
6117
|
+
return barFlag(lastRealtime);
|
|
6118
|
+
},
|
|
6119
|
+
get ishistory() {
|
|
6120
|
+
return barFlag((i, n) => !lastRealtime(i, n));
|
|
6121
|
+
},
|
|
6122
|
+
get isnew() {
|
|
6123
|
+
return barFlag((i, n) => !lastRealtime(i, n) || chartCtx.lastBarNew !== false);
|
|
6124
|
+
},
|
|
6125
|
+
get islastconfirmedhistory() {
|
|
6126
|
+
return barFlag((i, n) => {
|
|
6127
|
+
const lastConfirmed = chartCtx.lastBarConfirmed === true && chartCtx.realtime !== true;
|
|
6128
|
+
return i === (lastConfirmed ? n - 1 : n - 2);
|
|
6129
|
+
});
|
|
6130
|
+
}
|
|
6131
|
+
};
|
|
5901
6132
|
var session = {
|
|
5902
6133
|
regular: "regular",
|
|
5903
6134
|
extended: "extended",
|
|
@@ -6051,12 +6282,21 @@ function alertcondition(condition, title, message) {
|
|
|
6051
6282
|
const last = condition.length() ? condition.last() : NaN;
|
|
6052
6283
|
c.alerts.push({ id, title, message, triggered: !isNA(last) && last !== 0 });
|
|
6053
6284
|
}
|
|
6285
|
+
var math = {
|
|
6286
|
+
...math_exports,
|
|
6287
|
+
round_to_mintick: ((number2, mintick) => {
|
|
6288
|
+
const tick = mintick ?? syminfo.mintick;
|
|
6289
|
+
return number2 instanceof Series ? round_to_mintick(number2, tick) : round_to_mintick(number2, tick);
|
|
6290
|
+
})
|
|
6291
|
+
};
|
|
6054
6292
|
export {
|
|
6055
6293
|
BarData,
|
|
6294
|
+
STRATEGY_DEFAULTS,
|
|
6056
6295
|
Series,
|
|
6057
6296
|
alertcondition,
|
|
6058
6297
|
barcolor,
|
|
6059
6298
|
barmerge,
|
|
6299
|
+
barstate,
|
|
6060
6300
|
bgcolor,
|
|
6061
6301
|
box,
|
|
6062
6302
|
chart,
|
|
@@ -6082,7 +6322,7 @@ export {
|
|
|
6082
6322
|
line,
|
|
6083
6323
|
linefill,
|
|
6084
6324
|
low,
|
|
6085
|
-
|
|
6325
|
+
math,
|
|
6086
6326
|
minute2 as minute,
|
|
6087
6327
|
month2 as month,
|
|
6088
6328
|
isNA as na,
|
|
@@ -6090,6 +6330,7 @@ export {
|
|
|
6090
6330
|
ohlc4,
|
|
6091
6331
|
open,
|
|
6092
6332
|
plot,
|
|
6333
|
+
plotarrow,
|
|
6093
6334
|
plotchar,
|
|
6094
6335
|
plotshape,
|
|
6095
6336
|
polyline,
|
|
@@ -6098,6 +6339,7 @@ export {
|
|
|
6098
6339
|
seriesOf,
|
|
6099
6340
|
session,
|
|
6100
6341
|
str,
|
|
6342
|
+
strategy,
|
|
6101
6343
|
syminfo,
|
|
6102
6344
|
ta2 as ta,
|
|
6103
6345
|
text,
|