oakscriptjs 0.9.5 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/array/index.d.ts +93 -50
- package/dist/cjs/array/index.d.ts.map +1 -1
- package/dist/cjs/array/index.js +239 -78
- package/dist/cjs/array/index.js.map +3 -3
- package/dist/cjs/callsite/index.d.ts +81 -2
- package/dist/cjs/callsite/index.d.ts.map +1 -1
- package/dist/cjs/callsite/index.js +62 -1
- package/dist/cjs/callsite/index.js.map +2 -2
- package/dist/cjs/color/index.d.ts +7 -3
- package/dist/cjs/color/index.d.ts.map +1 -1
- package/dist/cjs/color/index.js +6 -1
- package/dist/cjs/color/index.js.map +2 -2
- package/dist/cjs/drawing/bar-time.d.ts +24 -0
- package/dist/cjs/drawing/bar-time.d.ts.map +1 -0
- package/dist/cjs/drawing/bar-time.js +47 -0
- package/dist/cjs/drawing/bar-time.js.map +7 -0
- package/dist/cjs/drawing/output.d.ts +32 -0
- package/dist/cjs/drawing/output.d.ts.map +1 -0
- package/dist/cjs/drawing/output.js +184 -0
- package/dist/cjs/drawing/output.js.map +7 -0
- package/dist/cjs/drawing/registry.d.ts +5 -4
- package/dist/cjs/drawing/registry.d.ts.map +1 -1
- package/dist/cjs/drawing/registry.js +11 -2
- package/dist/cjs/drawing/registry.js.map +2 -2
- package/dist/cjs/index.d.ts +6 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/label/index.d.ts.map +1 -1
- package/dist/cjs/label/index.js +2 -1
- package/dist/cjs/label/index.js.map +2 -2
- package/dist/cjs/linefill/index.d.ts.map +1 -1
- package/dist/cjs/linefill/index.js +1 -0
- package/dist/cjs/linefill/index.js.map +2 -2
- package/dist/cjs/math/index.d.ts +76 -21
- package/dist/cjs/math/index.d.ts.map +1 -1
- package/dist/cjs/math/index.js +155 -41
- package/dist/cjs/math/index.js.map +3 -3
- package/dist/cjs/matrix/index.d.ts.map +1 -1
- package/dist/cjs/matrix/index.js +42 -36
- package/dist/cjs/matrix/index.js.map +2 -2
- package/dist/cjs/polyline/index.d.ts +3 -5
- package/dist/cjs/polyline/index.d.ts.map +1 -1
- package/dist/cjs/polyline/index.js +0 -5
- package/dist/cjs/polyline/index.js.map +2 -2
- package/dist/cjs/runtime/series.d.ts +9 -0
- package/dist/cjs/runtime/series.d.ts.map +1 -1
- package/dist/cjs/runtime/series.js +16 -2
- package/dist/cjs/runtime/series.js.map +2 -2
- package/dist/cjs/runtime/types.d.ts +22 -0
- package/dist/cjs/runtime/types.d.ts.map +1 -1
- package/dist/cjs/runtime/types.js.map +1 -1
- package/dist/cjs/script/index.d.ts +97 -36
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +173 -26
- package/dist/cjs/script/index.js.map +3 -3
- package/dist/cjs/session/calendar.d.ts.map +1 -1
- package/dist/cjs/session/calendar.js +15 -2
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/str/index.d.ts +50 -38
- package/dist/cjs/str/index.d.ts.map +1 -1
- package/dist/cjs/str/index.js +43 -15
- package/dist/cjs/str/index.js.map +2 -2
- package/dist/cjs/str/messageformat.d.ts +2 -1
- package/dist/cjs/str/messageformat.d.ts.map +1 -1
- package/dist/cjs/str/messageformat.js +4 -1
- package/dist/cjs/str/messageformat.js.map +2 -2
- package/dist/cjs/str/numberformat.d.ts +4 -0
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +26 -4
- package/dist/cjs/str/numberformat.js.map +3 -3
- package/dist/cjs/ta/index.d.ts +86 -43
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +173 -267
- package/dist/cjs/ta/index.js.map +3 -3
- package/dist/cjs/ta/running-sum.d.ts +54 -2
- package/dist/cjs/ta/running-sum.d.ts.map +1 -1
- package/dist/cjs/ta/running-sum.js +132 -52
- package/dist/cjs/ta/running-sum.js.map +2 -2
- package/dist/cjs/ta-series.d.ts +4 -4
- package/dist/cjs/ta-series.d.ts.map +1 -1
- package/dist/cjs/ta-series.js +4 -3
- package/dist/cjs/ta-series.js.map +2 -2
- package/dist/cjs/table/index.d.ts +87 -0
- package/dist/cjs/table/index.d.ts.map +1 -0
- package/dist/cjs/table/index.js +150 -0
- package/dist/cjs/table/index.js.map +7 -0
- package/dist/cjs/text/index.d.ts +17 -2
- package/dist/cjs/text/index.d.ts.map +1 -1
- package/dist/cjs/text/index.js +15 -1
- package/dist/cjs/text/index.js.map +2 -2
- package/dist/cjs/time/datestring.d.ts +1 -0
- package/dist/cjs/time/datestring.d.ts.map +1 -1
- package/dist/cjs/time/datestring.js +7 -0
- package/dist/cjs/time/datestring.js.map +2 -2
- package/dist/cjs/time/index.d.ts.map +1 -1
- package/dist/cjs/time/index.js +2 -1
- package/dist/cjs/time/index.js.map +2 -2
- package/dist/cjs/time/session.d.ts +28 -9
- package/dist/cjs/time/session.d.ts.map +1 -1
- package/dist/cjs/time/session.js +74 -28
- package/dist/cjs/time/session.js.map +2 -2
- package/dist/cjs/time/timezone.d.ts +5 -0
- package/dist/cjs/time/timezone.d.ts.map +1 -1
- package/dist/cjs/time/timezone.js +15 -1
- package/dist/cjs/time/timezone.js.map +2 -2
- package/dist/cjs/types/index.d.ts +30 -1
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +12 -0
- package/dist/cjs/types/index.js.map +2 -2
- package/dist/cjs/types/metadata.d.ts +258 -26
- package/dist/cjs/types/metadata.d.ts.map +1 -1
- package/dist/cjs/types/metadata.js.map +1 -1
- package/dist/esm/array/index.d.ts +93 -50
- package/dist/esm/array/index.d.ts.map +1 -1
- package/dist/esm/array/index.js +383 -133
- package/dist/esm/array/index.js.map +1 -1
- package/dist/esm/callsite/index.d.ts +81 -2
- package/dist/esm/callsite/index.d.ts.map +1 -1
- package/dist/esm/callsite/index.js +145 -3
- package/dist/esm/callsite/index.js.map +1 -1
- package/dist/esm/color/index.d.ts +7 -3
- package/dist/esm/color/index.d.ts.map +1 -1
- package/dist/esm/color/index.js +15 -3
- package/dist/esm/color/index.js.map +1 -1
- package/dist/esm/drawing/bar-time.d.ts +24 -0
- package/dist/esm/drawing/bar-time.d.ts.map +1 -0
- package/dist/esm/drawing/bar-time.js +44 -0
- package/dist/esm/drawing/bar-time.js.map +1 -0
- package/dist/esm/drawing/output.d.ts +32 -0
- package/dist/esm/drawing/output.d.ts.map +1 -0
- package/dist/esm/drawing/output.js +190 -0
- package/dist/esm/drawing/output.js.map +1 -0
- package/dist/esm/drawing/registry.d.ts +5 -4
- package/dist/esm/drawing/registry.d.ts.map +1 -1
- package/dist/esm/drawing/registry.js +12 -2
- package/dist/esm/drawing/registry.js.map +1 -1
- package/dist/esm/index.d.ts +6 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +7 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/label/index.d.ts.map +1 -1
- package/dist/esm/label/index.js +2 -1
- package/dist/esm/label/index.js.map +1 -1
- package/dist/esm/linefill/index.d.ts.map +1 -1
- package/dist/esm/linefill/index.js +3 -0
- package/dist/esm/linefill/index.js.map +1 -1
- package/dist/esm/math/index.d.ts +76 -21
- package/dist/esm/math/index.d.ts.map +1 -1
- package/dist/esm/math/index.js +227 -61
- package/dist/esm/math/index.js.map +1 -1
- package/dist/esm/matrix/index.d.ts.map +1 -1
- package/dist/esm/matrix/index.js +57 -43
- package/dist/esm/matrix/index.js.map +1 -1
- package/dist/esm/polyline/index.d.ts +3 -5
- package/dist/esm/polyline/index.d.ts.map +1 -1
- package/dist/esm/polyline/index.js +3 -9
- package/dist/esm/polyline/index.js.map +1 -1
- package/dist/esm/runtime/series.d.ts +9 -0
- package/dist/esm/runtime/series.d.ts.map +1 -1
- package/dist/esm/runtime/series.js +18 -1
- package/dist/esm/runtime/series.js.map +1 -1
- package/dist/esm/runtime/types.d.ts +22 -0
- package/dist/esm/runtime/types.d.ts.map +1 -1
- package/dist/esm/script/index.d.ts +97 -36
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +211 -22
- package/dist/esm/script/index.js.map +1 -1
- package/dist/esm/session/calendar.d.ts.map +1 -1
- package/dist/esm/session/calendar.js +22 -2
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/str/index.d.ts +50 -38
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +103 -52
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/messageformat.d.ts +2 -1
- package/dist/esm/str/messageformat.d.ts.map +1 -1
- package/dist/esm/str/messageformat.js +7 -2
- package/dist/esm/str/messageformat.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +4 -0
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +38 -4
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/ta/index.d.ts +86 -43
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +296 -348
- package/dist/esm/ta/index.js.map +1 -1
- package/dist/esm/ta/running-sum.d.ts +54 -2
- package/dist/esm/ta/running-sum.d.ts.map +1 -1
- package/dist/esm/ta/running-sum.js +166 -71
- package/dist/esm/ta/running-sum.js.map +1 -1
- package/dist/esm/ta-series.d.ts +4 -4
- package/dist/esm/ta-series.d.ts.map +1 -1
- package/dist/esm/ta-series.js +5 -4
- package/dist/esm/ta-series.js.map +1 -1
- package/dist/esm/table/index.d.ts +87 -0
- package/dist/esm/table/index.d.ts.map +1 -0
- package/dist/esm/table/index.js +169 -0
- package/dist/esm/table/index.js.map +1 -0
- package/dist/esm/text/index.d.ts +17 -2
- package/dist/esm/text/index.d.ts.map +1 -1
- package/dist/esm/text/index.js +17 -2
- package/dist/esm/text/index.js.map +1 -1
- package/dist/esm/time/datestring.d.ts +1 -0
- package/dist/esm/time/datestring.d.ts.map +1 -1
- package/dist/esm/time/datestring.js +12 -0
- package/dist/esm/time/datestring.js.map +1 -1
- package/dist/esm/time/index.d.ts.map +1 -1
- package/dist/esm/time/index.js +3 -1
- package/dist/esm/time/index.js.map +1 -1
- package/dist/esm/time/session.d.ts +28 -9
- package/dist/esm/time/session.d.ts.map +1 -1
- package/dist/esm/time/session.js +113 -42
- package/dist/esm/time/session.js.map +1 -1
- package/dist/esm/time/timezone.d.ts +5 -0
- package/dist/esm/time/timezone.d.ts.map +1 -1
- package/dist/esm/time/timezone.js +21 -1
- package/dist/esm/time/timezone.js.map +1 -1
- package/dist/esm/types/index.d.ts +30 -1
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/esm/types/index.js +14 -0
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/metadata.d.ts +258 -26
- package/dist/esm/types/metadata.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/array/index.ts +364 -150
- package/src/callsite/index.ts +147 -3
- package/src/color/index.ts +15 -5
- package/src/drawing/bar-time.ts +42 -0
- package/src/drawing/output.ts +214 -0
- package/src/drawing/registry.ts +16 -6
- package/src/index.ts +9 -3
- package/src/label/index.ts +2 -1
- package/src/linefill/index.ts +2 -0
- package/src/math/index.ts +256 -74
- package/src/matrix/index.ts +52 -52
- package/src/polyline/index.ts +3 -12
- package/src/runtime/series.ts +22 -1
- package/src/runtime/types.ts +23 -0
- package/src/script/index.ts +307 -25
- package/src/session/calendar.ts +22 -2
- package/src/str/index.ts +95 -51
- package/src/str/messageformat.ts +7 -2
- package/src/str/numberformat.ts +39 -4
- package/src/ta/index.ts +306 -386
- package/src/ta/running-sum.ts +187 -74
- package/src/ta-series.ts +8 -7
- package/src/table/index.ts +221 -0
- package/src/text/index.ts +18 -2
- package/src/time/datestring.ts +10 -0
- package/src/time/index.ts +3 -1
- package/src/time/session.ts +97 -47
- package/src/time/timezone.ts +21 -1
- package/src/types/index.ts +32 -1
- package/src/types/metadata.ts +325 -26
|
@@ -29,6 +29,7 @@ __export(calendar_exports, {
|
|
|
29
29
|
});
|
|
30
30
|
module.exports = __toCommonJS(calendar_exports);
|
|
31
31
|
var import_timezone = require("../time/timezone.js");
|
|
32
|
+
var import_session = require("../time/session.js");
|
|
32
33
|
const toDateNum = (y, m, d) => y * 1e4 + m * 100 + d;
|
|
33
34
|
const utcOf = (date) => Date.UTC(Math.floor(date / 1e4), Math.floor(date / 100) % 100 - 1, date % 100);
|
|
34
35
|
const fromUtc = (ms) => {
|
|
@@ -60,10 +61,21 @@ function parsePeriods(text, spec) {
|
|
|
60
61
|
};
|
|
61
62
|
});
|
|
62
63
|
}
|
|
64
|
+
function pineVersion(text, from) {
|
|
65
|
+
const ranges = (0, import_session.parsePineSession)(text);
|
|
66
|
+
if (ranges === null) throw new SyntaxError(`Session "${text}" is the symbol session`);
|
|
67
|
+
const byDay = Array.from({ length: 8 }, () => []);
|
|
68
|
+
for (const { start, end, days } of ranges) {
|
|
69
|
+
const period = start === 0 && end === 0 ? { start: 0, end: 1440, startOffset: 0, endOffset: 0 } : { start, end, startOffset: end <= start ? 1 : 0, endOffset: 0 };
|
|
70
|
+
for (const d of days) byDay[d].push(period);
|
|
71
|
+
}
|
|
72
|
+
return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };
|
|
73
|
+
}
|
|
63
74
|
function parseVersion(text, from, spec, rule) {
|
|
75
|
+
if (rule === "pine") return pineVersion(text, from);
|
|
64
76
|
const [periodText = "", dayText] = text.trim().split(":");
|
|
65
77
|
const periods = parsePeriods(periodText, spec);
|
|
66
|
-
const allDays = periodText.trim() === "24x7"
|
|
78
|
+
const allDays = periodText.trim() === "24x7";
|
|
67
79
|
const digits = dayText ?? (allDays ? "1234567" : "23456");
|
|
68
80
|
if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in "${spec}"`);
|
|
69
81
|
const days = new Array(8).fill(false);
|
|
@@ -109,7 +121,8 @@ class TradingCalendar {
|
|
|
109
121
|
periods = correction ?? [];
|
|
110
122
|
} else if (!this.holidays.has(date)) {
|
|
111
123
|
const version = [...this.versions].reverse().find((v) => v.from <= date);
|
|
112
|
-
if (version.
|
|
124
|
+
if (version.byDay) periods = version.byDay[weekdayOf(date)];
|
|
125
|
+
else if (version.days[weekdayOf(date)]) periods = version.periods;
|
|
113
126
|
}
|
|
114
127
|
const y = Math.floor(date / 1e4);
|
|
115
128
|
const m = Math.floor(date / 100) % 100;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/session/calendar.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Trading calendar: the session periods of each trading day of a symbol, from the symbol session\n * information (issue #100).\n *\n * Session format (symbol session information, also PineScript session strings):\n * - periods \"HHMM-HHMM\", several separated by commas; \"24x7\" is the whole day, every day\n * - a period whose end is not after its start (\"1700-1600\", \"1700-1700\") starts the day before the\n * trading day: the session that opens Sunday 17:00 is Monday's\n * - \"HHMMF<n>\" moves a time <n> days before the trading day (\"1700F2-1200F1\")\n * - optional days after \":\" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to\n * Friday (\"24x7\": every day), and a PineScript session string follows PineScript's rules (one period:\n * every day; several periods: Monday to Friday)\n * - \"#YYYYMMDD/<session>\" changes the session from that date (\"0900-1746#20251201/0800-2200\")\n * - corrections (\"session-correction\"): \"<session>:<dates>;dayoff:<dates>\" replace the session of these\n * dates (early closes, days off); holidays (\"session_holidays\") are dates without session\n */\n\nimport { zonedFields, zonedToUnix } from '../time/timezone.js';\n\n/** Session information of a symbol (hours, corrections, holidays). */\nexport interface SessionSpec {\n /** Session hours: \"0930-1600\", \"1700-1600\", \"24x7\", \"0400-2000\" ... */\n session: string;\n /** Exceptions by date (symbol info \"session-correction\"): \"0930-1300:20251128,20251224;dayoff:20250109\" */\n corrections?: string;\n /** Exchange holidays as \"YYYYMMDD\" (symbol info \"session_holidays\"), comma-separated or as an array */\n holidays?: string | string[];\n}\n\n/** Date as YYYYMMDD. */\nexport type DateNum = number;\n\ninterface Period {\n /** Minutes after midnight */\n start: number;\n end: number;\n /** Days before the trading day */\n startOffset: number;\n endOffset: number;\n}\n\ninterface Version {\n from: DateNum;\n periods: Period[];\n /** Index 1 (Sunday) to 7 (Saturday) */\n days: boolean[];\n}\n\nexport const toDateNum = (y: number, m: number, d: number): DateNum => y * 10000 + m * 100 + d;\nconst utcOf = (date: DateNum): number => Date.UTC(Math.floor(date / 10000), Math.floor(date / 100) % 100 - 1, date % 100);\nconst fromUtc = (ms: number): DateNum => {\n const d = new Date(ms);\n return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n};\nexport const addDays = (date: DateNum, n: number): DateNum => fromUtc(utcOf(date) + n * 86_400_000);\n/** 1 = Sunday ... 7 = Saturday */\nexport const weekdayOf = (date: DateNum): number => new Date(utcOf(date)).getUTCDay() + 1;\n/** 00:00 UTC of the date, in ms (PineScript `time_tradingday`). */\nexport const dateUtcMs = utcOf;\n\nfunction parseTime(token: string, spec: string): { minutes: number; offset?: number } {\n const m = /^(\\d{2})(\\d{2})(?:F(\\d+))?$/.exec(token.trim());\n if (!m) throw new SyntaxError(`Invalid session \"${spec}\"`);\n return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === undefined ? undefined : Number(m[3]) };\n}\n\nfunction parsePeriods(text: string, spec: string): Period[] {\n if (text.trim() === '24x7') return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];\n return text.split(',').map((p) => {\n const [a = '', b = ''] = p.split('-');\n const s = parseTime(a, spec);\n const e = parseTime(b, spec);\n if (s.minutes === 0 && e.minutes === 0 && s.offset === undefined && e.offset === undefined) {\n return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };\n }\n return {\n start: s.minutes,\n end: e.minutes,\n startOffset: s.offset ?? (e.minutes <= s.minutes ? 1 : 0),\n endOffset: e.offset ?? 0,\n };\n });\n}\n\nfunction parseVersion(text: string, from: DateNum, spec: string, rule: 'symbol' | 'pine'): Version {\n const [periodText = '', dayText] = text.trim().split(':');\n const periods = parsePeriods(periodText, spec);\n const allDays = periodText.trim() === '24x7' || (rule === 'pine' && periods.length === 1);\n const digits = dayText ?? (allDays ? '1234567' : '23456');\n if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in \"${spec}\"`);\n const days = new Array<boolean>(8).fill(false);\n for (const d of digits) days[Number(d)] = true;\n return { from, periods, days };\n}\n\n/** Session periods of every trading day of a symbol, as UNIX times (ms). */\nexport class TradingCalendar {\n private readonly versions: Version[];\n private readonly corrections = new Map<DateNum, Period[] | null>();\n private readonly holidays: Set<DateNum>;\n private readonly cache = new Map<DateNum, Array<[number, number]>>();\n\n /**\n * @param timezone - Time zone of the session hours (the exchange time zone)\n * @param spec - Session information (hours, corrections, holidays)\n * @param rule - Days without \":days\": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)\n */\n constructor(\n readonly timezone: string,\n spec: SessionSpec,\n rule: 'symbol' | 'pine' = 'symbol'\n ) {\n const [first = '', ...changes] = spec.session.split('#');\n this.versions = [parseVersion(first, 0, spec.session, rule)];\n for (const change of changes) {\n const m = /^(\\d{8})\\/(.+)$/.exec(change.trim());\n if (!m) throw new SyntaxError(`Invalid session change in \"${spec.session}\"`);\n this.versions.push(parseVersion(m[2]!, Number(m[1]), spec.session, rule));\n }\n for (const part of (spec.corrections ?? '').split(';').filter((p) => p.trim())) {\n const i = part.lastIndexOf(':');\n const text = part.slice(0, i).trim();\n const periods = text === 'dayoff' ? null : parsePeriods(text, part);\n for (const date of part.slice(i + 1).split(',')) this.corrections.set(Number(date), periods);\n }\n const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? '').split(',');\n this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));\n }\n\n /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */\n periods(date: DateNum): Array<[number, number]> {\n let out = this.cache.get(date);\n if (out) return out;\n let periods: Period[] = [];\n const correction = this.corrections.get(date);\n if (correction !== undefined) {\n periods = correction ?? [];\n } else if (!this.holidays.has(date)) {\n const version = [...this.versions].reverse().find((v) => v.from <= date)!;\n if (version.days[weekdayOf(date)]) periods = version.periods;\n }\n const y = Math.floor(date / 10000);\n const m = Math.floor(date / 100) % 100;\n const d = date % 100;\n out = periods.map((p) => [\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),\n zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60),\n ]);\n this.cache.set(date, out);\n return out;\n }\n\n isTradingDay(date: DateNum): boolean {\n return this.periods(date).length > 0;\n }\n\n /**\n * Trading day and period of a time: the period that contains it, else the last period that started\n * before it. Null when no trading day is found near the time.\n */\n locate(time: number): { date: DateNum; index: number; inside: boolean } | null {\n const f = zonedFields(time, this.timezone);\n const local = toDateNum(f.year, f.month, f.day);\n for (let k = 0; k <= 5; k++) {\n const date = addDays(local, k);\n const index = this.periods(date).findIndex(([s, e]) => s <= time && time < e);\n if (index !== -1) return { date, index, inside: true };\n }\n for (let k = 5; k >= -7; k--) {\n const date = addDays(local, k);\n const periods = this.periods(date);\n let index = -1;\n periods.forEach(([s], i) => {\n if (s <= time) index = i;\n });\n if (index !== -1) return { date, index, inside: false };\n }\n return null;\n }\n\n /** First trading day in [from, to] (dates), or null. */\n firstTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n\n /** Last trading day in [from, to] (dates), or null. */\n lastTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n}\n\n/** Monday of the week of `date` (weeks run Monday to Sunday). */\nexport const weekStart = (date: DateNum): DateNum => addDays(date, -((weekdayOf(date) + 5) % 7));\n/** First day of the month of `date`. */\nexport const monthStart = (date: DateNum): DateNum => Math.floor(date / 100) * 100 + 1;\n/** Last day of the month of `date`. */\nexport const monthEnd = (date: DateNum): DateNum => addDays(monthStart(addDays(monthStart(date), 32)), -1);\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,sBAAyC;
|
|
4
|
+
"sourcesContent": ["/**\n * Trading calendar: the session periods of each trading day of a symbol, from the symbol session\n * information (issue #100).\n *\n * Session format (symbol session information, also PineScript session strings):\n * - periods \"HHMM-HHMM\", several separated by commas; \"24x7\" is the whole day, every day\n * - a period whose end is not after its start (\"1700-1600\", \"1700-1700\") starts the day before the\n * trading day: the session that opens Sunday 17:00 is Monday's\n * - \"HHMMF<n>\" moves a time <n> days before the trading day (\"1700F2-1200F1\")\n * - optional days after \":\" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to\n * Friday (\"24x7\": every day), and a PineScript session string follows PineScript's rules (one period:\n * every day; several periods: Monday to Friday)\n * - \"#YYYYMMDD/<session>\" changes the session from that date (\"0900-1746#20251201/0800-2200\")\n * - corrections (\"session-correction\"): \"<session>:<dates>;dayoff:<dates>\" replace the session of these\n * dates (early closes, days off); holidays (\"session_holidays\") are dates without session\n */\n\nimport { zonedFields, zonedToUnix } from '../time/timezone.js';\nimport { parsePineSession } from '../time/session.js';\n\n/** Session information of a symbol (hours, corrections, holidays). */\nexport interface SessionSpec {\n /** Session hours: \"0930-1600\", \"1700-1600\", \"24x7\", \"0400-2000\" ... */\n session: string;\n /** Exceptions by date (symbol info \"session-correction\"): \"0930-1300:20251128,20251224;dayoff:20250109\" */\n corrections?: string;\n /** Exchange holidays as \"YYYYMMDD\" (symbol info \"session_holidays\"), comma-separated or as an array */\n holidays?: string | string[];\n}\n\n/** Date as YYYYMMDD. */\nexport type DateNum = number;\n\ninterface Period {\n /** Minutes after midnight */\n start: number;\n end: number;\n /** Days before the trading day */\n startOffset: number;\n endOffset: number;\n}\n\ninterface Version {\n from: DateNum;\n periods: Period[];\n /** Index 1 (Sunday) to 7 (Saturday) */\n days: boolean[];\n /** PineScript session strings: the periods of each weekday (index 1 to 7), from the \"|\" sections */\n byDay?: Period[][];\n}\n\nexport const toDateNum = (y: number, m: number, d: number): DateNum => y * 10000 + m * 100 + d;\nconst utcOf = (date: DateNum): number => Date.UTC(Math.floor(date / 10000), Math.floor(date / 100) % 100 - 1, date % 100);\nconst fromUtc = (ms: number): DateNum => {\n const d = new Date(ms);\n return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n};\nexport const addDays = (date: DateNum, n: number): DateNum => fromUtc(utcOf(date) + n * 86_400_000);\n/** 1 = Sunday ... 7 = Saturday */\nexport const weekdayOf = (date: DateNum): number => new Date(utcOf(date)).getUTCDay() + 1;\n/** 00:00 UTC of the date, in ms (PineScript `time_tradingday`). */\nexport const dateUtcMs = utcOf;\n\nfunction parseTime(token: string, spec: string): { minutes: number; offset?: number } {\n const m = /^(\\d{2})(\\d{2})(?:F(\\d+))?$/.exec(token.trim());\n if (!m) throw new SyntaxError(`Invalid session \"${spec}\"`);\n return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === undefined ? undefined : Number(m[3]) };\n}\n\nfunction parsePeriods(text: string, spec: string): Period[] {\n if (text.trim() === '24x7') return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];\n return text.split(',').map((p) => {\n const [a = '', b = ''] = p.split('-');\n const s = parseTime(a, spec);\n const e = parseTime(b, spec);\n if (s.minutes === 0 && e.minutes === 0 && s.offset === undefined && e.offset === undefined) {\n return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };\n }\n return {\n start: s.minutes,\n end: e.minutes,\n startOffset: s.offset ?? (e.minutes <= s.minutes ? 1 : 0),\n endOffset: e.offset ?? 0,\n };\n });\n}\n\n/** A PineScript session string as periods per weekday (rules in time/session.ts). */\nfunction pineVersion(text: string, from: DateNum): Version {\n const ranges = parsePineSession(text);\n if (ranges === null) throw new SyntaxError(`Session \"${text}\" is the symbol session`);\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const { start, end, days } of ranges) {\n const period: Period =\n start === 0 && end === 0\n ? { start: 0, end: 1440, startOffset: 0, endOffset: 0 }\n : { start, end, startOffset: end <= start ? 1 : 0, endOffset: 0 };\n for (const d of days) byDay[d]!.push(period);\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n}\n\nfunction parseVersion(text: string, from: DateNum, spec: string, rule: 'symbol' | 'pine'): Version {\n if (rule === 'pine') return pineVersion(text, from);\n const [periodText = '', dayText] = text.trim().split(':');\n const periods = parsePeriods(periodText, spec);\n const allDays = periodText.trim() === '24x7';\n const digits = dayText ?? (allDays ? '1234567' : '23456');\n if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in \"${spec}\"`);\n const days = new Array<boolean>(8).fill(false);\n for (const d of digits) days[Number(d)] = true;\n return { from, periods, days };\n}\n\n/** Session periods of every trading day of a symbol, as UNIX times (ms). */\nexport class TradingCalendar {\n private readonly versions: Version[];\n private readonly corrections = new Map<DateNum, Period[] | null>();\n private readonly holidays: Set<DateNum>;\n private readonly cache = new Map<DateNum, Array<[number, number]>>();\n\n /**\n * @param timezone - Time zone of the session hours (the exchange time zone)\n * @param spec - Session information (hours, corrections, holidays)\n * @param rule - Days without \":days\": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)\n */\n constructor(\n readonly timezone: string,\n spec: SessionSpec,\n rule: 'symbol' | 'pine' = 'symbol'\n ) {\n const [first = '', ...changes] = spec.session.split('#');\n this.versions = [parseVersion(first, 0, spec.session, rule)];\n for (const change of changes) {\n const m = /^(\\d{8})\\/(.+)$/.exec(change.trim());\n if (!m) throw new SyntaxError(`Invalid session change in \"${spec.session}\"`);\n this.versions.push(parseVersion(m[2]!, Number(m[1]), spec.session, rule));\n }\n for (const part of (spec.corrections ?? '').split(';').filter((p) => p.trim())) {\n const i = part.lastIndexOf(':');\n const text = part.slice(0, i).trim();\n const periods = text === 'dayoff' ? null : parsePeriods(text, part);\n for (const date of part.slice(i + 1).split(',')) this.corrections.set(Number(date), periods);\n }\n const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? '').split(',');\n this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));\n }\n\n /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */\n periods(date: DateNum): Array<[number, number]> {\n let out = this.cache.get(date);\n if (out) return out;\n let periods: Period[] = [];\n const correction = this.corrections.get(date);\n if (correction !== undefined) {\n periods = correction ?? [];\n } else if (!this.holidays.has(date)) {\n const version = [...this.versions].reverse().find((v) => v.from <= date)!;\n if (version.byDay) periods = version.byDay[weekdayOf(date)]!;\n else if (version.days[weekdayOf(date)]) periods = version.periods;\n }\n const y = Math.floor(date / 10000);\n const m = Math.floor(date / 100) % 100;\n const d = date % 100;\n out = periods.map((p) => [\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),\n zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60),\n ]);\n this.cache.set(date, out);\n return out;\n }\n\n isTradingDay(date: DateNum): boolean {\n return this.periods(date).length > 0;\n }\n\n /**\n * Trading day and period of a time: the period that contains it, else the last period that started\n * before it. Null when no trading day is found near the time.\n */\n locate(time: number): { date: DateNum; index: number; inside: boolean } | null {\n const f = zonedFields(time, this.timezone);\n const local = toDateNum(f.year, f.month, f.day);\n for (let k = 0; k <= 5; k++) {\n const date = addDays(local, k);\n const index = this.periods(date).findIndex(([s, e]) => s <= time && time < e);\n if (index !== -1) return { date, index, inside: true };\n }\n for (let k = 5; k >= -7; k--) {\n const date = addDays(local, k);\n const periods = this.periods(date);\n let index = -1;\n periods.forEach(([s], i) => {\n if (s <= time) index = i;\n });\n if (index !== -1) return { date, index, inside: false };\n }\n return null;\n }\n\n /** First trading day in [from, to] (dates), or null. */\n firstTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n\n /** Last trading day in [from, to] (dates), or null. */\n lastTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n}\n\n/** Monday of the week of `date` (weeks run Monday to Sunday). */\nexport const weekStart = (date: DateNum): DateNum => addDays(date, -((weekdayOf(date) + 5) % 7));\n/** First day of the month of `date`. */\nexport const monthStart = (date: DateNum): DateNum => Math.floor(date / 100) * 100 + 1;\n/** Last day of the month of `date`. */\nexport const monthEnd = (date: DateNum): DateNum => addDays(monthStart(addDays(monthStart(date), 32)), -1);\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/str/index.d.ts
CHANGED
|
@@ -26,20 +26,28 @@ export declare function length(str: simple_string): int;
|
|
|
26
26
|
* Converts a value to its string representation (PineScript `str.tostring`).
|
|
27
27
|
*
|
|
28
28
|
* @param value - The value to convert (number, boolean, string, etc.)
|
|
29
|
-
* @param format - Optional number format: a pattern ("#.##", "#,###.00", "0.00%", "0.00E0") or
|
|
30
|
-
* `format.percent` ("percent"), `format.volume` ("volume"), `format.
|
|
29
|
+
* @param format - Optional number format: a pattern ("#.##", "#,###.00", "0.00%", "0.00E0", "#.##;(#.##)") or
|
|
30
|
+
* `format.percent` ("percent"), `format.volume` ("volume"), `format.mintick` ("mintick"),
|
|
31
|
+
* `format.price` / `format.inherit`
|
|
32
|
+
* @param mintick - Tick size for `format.mintick` (PineScript `syminfo.mintick`, implicit there). Not part of the
|
|
33
|
+
* PineScript signature: the core function cannot see the chart, so pass it, as for `math.round_to_mintick`
|
|
31
34
|
* @returns String representation of the value
|
|
32
35
|
*
|
|
33
36
|
* @remarks
|
|
34
37
|
* As in PineScript:
|
|
35
38
|
* - without format, numbers show up to 10 decimals ("0.3333333333"); 1e21 and more use "1E21"
|
|
36
|
-
* - patterns round half away from zero on the shortest decimal form: 1.005 with "#.##" gives "1.01"
|
|
39
|
+
* - patterns round half away from zero on the shortest decimal form: 1.005 with "#.##" gives "1.01";
|
|
40
|
+
* at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives "0.0012345678901235")
|
|
41
|
+
* - "pos;neg" patterns: a negative number takes the text of the negative subpattern ("#.##;(#.##)": "(3.5)")
|
|
37
42
|
* - "#" is an optional digit ("1.5" with "#.##"), "0" a required digit ("1.50" with "0.00"),
|
|
38
43
|
* "," groups thousands, "%" multiplies by 100, 'text' is literal text
|
|
39
44
|
* - `format.percent` shows 2 decimals and "%" without multiplying; `format.volume` uses K, M, B, T
|
|
40
45
|
* - `format.price` and `format.inherit` give "price1" / "inherit1" in PineScript: they are patterns
|
|
41
46
|
* without digit characters, and oakscriptjs does the same
|
|
42
|
-
* - `format.mintick`
|
|
47
|
+
* - `format.mintick` rounds to the tick (`math.round_to_mintick`) and shows the tick's decimals ("#.##" for 0.01);
|
|
48
|
+
* it throws without `mintick`
|
|
49
|
+
* - na gives "NaN" ("NaN%" with `format.percent`); an infinity gives "NaN" too ("NaN%" for `format.percent`,
|
|
50
|
+
* "NaNT" for `format.volume`), except `format.mintick`, which gives "Infinity" / "-Infinity"
|
|
43
51
|
*
|
|
44
52
|
* @example
|
|
45
53
|
* ```typescript
|
|
@@ -49,26 +57,27 @@ export declare function length(str: simple_string): int;
|
|
|
49
57
|
* str.tostring(12345.678, format.volume) // "12.346K"
|
|
50
58
|
* ```
|
|
51
59
|
*/
|
|
52
|
-
export declare function tostring(value: any, format?: simple_string): string;
|
|
60
|
+
export declare function tostring(value: any, format?: simple_string, mintick?: float): string;
|
|
53
61
|
/**
|
|
54
62
|
* Converts a string to a number (float).
|
|
55
63
|
*
|
|
56
64
|
* @param str - The string to convert
|
|
57
|
-
* @returns The numeric value, or null if conversion fails
|
|
65
|
+
* @returns The numeric value, or null (na) if conversion fails
|
|
58
66
|
*
|
|
59
67
|
* @remarks
|
|
60
|
-
*
|
|
61
|
-
* -
|
|
62
|
-
* -
|
|
63
|
-
* -
|
|
64
|
-
*
|
|
68
|
+
* As in PineScript:
|
|
69
|
+
* - The whole text must be a number: a sign, digits and one optional "." ("0.5", ".5", "5.", "-12"); "12abc" is na
|
|
70
|
+
* - Scientific notation ("1e3"), "Infinity", "NaN", hexadecimal, "1,000" give na
|
|
71
|
+
* - The characters U+0000 to U+0020 (space, tab, line feed...) around the number are ignored; other spaces
|
|
72
|
+
* (no-break space U+00A0, U+3000) give na
|
|
65
73
|
*
|
|
66
74
|
* @example
|
|
67
75
|
* ```typescript
|
|
68
76
|
* str.tonumber("123") // Returns: 123
|
|
69
77
|
* str.tonumber("45.67") // Returns: 45.67
|
|
70
78
|
* str.tonumber("abc") // Returns: null
|
|
71
|
-
* str.tonumber("
|
|
79
|
+
* str.tonumber("12abc") // Returns: null
|
|
80
|
+
* str.tonumber("1e3") // Returns: null
|
|
72
81
|
* ```
|
|
73
82
|
*/
|
|
74
83
|
export declare function tonumber(str: simple_string): float | null;
|
|
@@ -81,6 +90,7 @@ export declare function tonumber(str: simple_string): float | null;
|
|
|
81
90
|
* @returns The extracted substring
|
|
82
91
|
*
|
|
83
92
|
* @remarks
|
|
93
|
+
* - An na `end` (NaN / null) is a runtime error in PineScript: it throws
|
|
84
94
|
* - If begin > end, indices are swapped (JavaScript behavior)
|
|
85
95
|
* - Negative indices are treated as 0
|
|
86
96
|
* - Indices beyond string length return empty string
|
|
@@ -145,39 +155,42 @@ export declare function contains(source: simple_string, str: simple_string): boo
|
|
|
145
155
|
*
|
|
146
156
|
* @param source - The string to search in
|
|
147
157
|
* @param str - The substring to search for
|
|
148
|
-
* @returns The 0-based index of the first occurrence, or
|
|
158
|
+
* @returns The 0-based index of the first occurrence, or na (NaN) if not found
|
|
149
159
|
*
|
|
150
160
|
* @remarks
|
|
151
161
|
* - Search is case-sensitive
|
|
152
162
|
* - Returns 0 for empty search string
|
|
153
|
-
* - Returns
|
|
163
|
+
* - Returns na (NaN) if substring is not found, as in PineScript
|
|
154
164
|
*
|
|
155
165
|
* @example
|
|
156
166
|
* ```typescript
|
|
157
167
|
* str.pos("hello world", "world") // Returns: 6
|
|
158
168
|
* str.pos("hello world", "o") // Returns: 4 (first occurrence)
|
|
159
|
-
* str.pos("hello", "xyz") // Returns:
|
|
169
|
+
* str.pos("hello", "xyz") // Returns: NaN (na)
|
|
160
170
|
* ```
|
|
161
171
|
*/
|
|
162
172
|
export declare function pos(source: simple_string, str: simple_string): int;
|
|
163
173
|
/**
|
|
164
|
-
* Replaces
|
|
174
|
+
* Replaces the Nth occurrence of a substring (PineScript `str.replace`).
|
|
165
175
|
*
|
|
166
176
|
* @param source - The source string
|
|
167
177
|
* @param target - The substring to replace
|
|
168
178
|
* @param replacement - The string to replace with
|
|
169
|
-
* @param occurrence -
|
|
170
|
-
* @returns The string with
|
|
179
|
+
* @param occurrence - 0-based occurrence to replace (default 0, the first one)
|
|
180
|
+
* @returns The string with one occurrence replaced
|
|
171
181
|
*
|
|
172
182
|
* @remarks
|
|
173
|
-
*
|
|
174
|
-
* -
|
|
175
|
-
* -
|
|
183
|
+
* PineScript rules:
|
|
184
|
+
* - only the Nth occurrence is replaced; an na occurrence is 0 (`str.replace("aaa", "a", "-", na)` is "-aa")
|
|
185
|
+
* - occurrences overlap: "aa" occurs at 0 and 1 in "aaa" (`str.replace("aaa", "aa", "-", 1)` is "a-")
|
|
186
|
+
* - an empty target inserts the replacement at character N, at most at the end
|
|
187
|
+
* (`str.replace("abc", "", "-", 2)` is "ab-c")
|
|
188
|
+
* - fewer than N + 1 occurrences (or a negative N) leave the source unchanged
|
|
176
189
|
*
|
|
177
190
|
* @example
|
|
178
191
|
* ```typescript
|
|
179
|
-
* str.replace("hello world hello", "hello", "hi"
|
|
180
|
-
* str.replace("hello world hello", "hello", "hi") // Returns: "
|
|
192
|
+
* str.replace("hello world hello", "hello", "hi") // Returns: "hi world hello"
|
|
193
|
+
* str.replace("hello world hello", "hello", "hi", 1) // Returns: "hello world hi"
|
|
181
194
|
* str.replace("test", "xyz", "abc") // Returns: "test"
|
|
182
195
|
* ```
|
|
183
196
|
*/
|
|
@@ -212,7 +225,7 @@ export declare function replace_all(source: simple_string, target: simple_string
|
|
|
212
225
|
* @returns Array of substrings
|
|
213
226
|
*
|
|
214
227
|
* @remarks
|
|
215
|
-
* - Empty separator splits into individual characters
|
|
228
|
+
* - Empty separator splits into individual characters; an empty string gives [""] (also with "")
|
|
216
229
|
* - If separator is not found, returns array with original string
|
|
217
230
|
* - Consecutive separators create empty strings in result
|
|
218
231
|
*
|
|
@@ -248,7 +261,8 @@ export declare function concat(...strings: simple_string[]): string;
|
|
|
248
261
|
* @remarks
|
|
249
262
|
* As in PineScript: numbers in `{n}` use
|
|
250
263
|
* "#,##0.###" (1234.5678 gives "1,234.568"); number patterns round half to even on the exact
|
|
251
|
-
* value; `'...'` is literal text and `''` a quote;
|
|
264
|
+
* value (16 decimals at most from 1e-3 up); an array gives "[a, b]"; `'...'` is literal text and `''` a quote;
|
|
265
|
+
* a missing argument leaves `{n}` in the text.
|
|
252
266
|
* Dates are formatted in UTC here; PineScript uses the exchange time zone.
|
|
253
267
|
*
|
|
254
268
|
* @example
|
|
@@ -370,25 +384,23 @@ export declare function trimLeft(str: simple_string): string;
|
|
|
370
384
|
*/
|
|
371
385
|
export declare function trimRight(str: simple_string): string;
|
|
372
386
|
/**
|
|
373
|
-
*
|
|
387
|
+
* Returns the first substring of `source` that matches the `regex` (PineScript `str.match`).
|
|
374
388
|
*
|
|
375
|
-
* @param source -
|
|
376
|
-
* @param regex -
|
|
377
|
-
* @returns
|
|
389
|
+
* @param source - Source string
|
|
390
|
+
* @param regex - Regular expression
|
|
391
|
+
* @returns The first match (searched anywhere in the string), or na (null) when nothing matches or the match is empty
|
|
378
392
|
*
|
|
379
393
|
* @remarks
|
|
394
|
+
* - As PineScript: "Hello World!" with "o" gives "o", with "[a-z]+" gives "ello"; no match gives na
|
|
380
395
|
* - Uses JavaScript regex syntax
|
|
381
|
-
* - Match is case-sensitive by default
|
|
382
|
-
* - Pattern is tested against entire string
|
|
383
396
|
*
|
|
384
397
|
* @example
|
|
385
398
|
* ```typescript
|
|
386
|
-
* str.match("
|
|
387
|
-
* str.match("
|
|
388
|
-
* str.match("abc", "[A-Z]+") // Returns: false
|
|
399
|
+
* str.match("abc123def", "[0-9]+") // Returns: "123"
|
|
400
|
+
* str.match("abc", "[0-9]+") // Returns: null (na)
|
|
389
401
|
* ```
|
|
390
402
|
*/
|
|
391
|
-
export declare function match(source: simple_string, regex: simple_string):
|
|
403
|
+
export declare function match(source: simple_string, regex: simple_string): simple_string | null;
|
|
392
404
|
/**
|
|
393
405
|
* Formats a timestamp as a string according to the specified format.
|
|
394
406
|
*
|
|
@@ -429,15 +441,15 @@ export declare function match(source: simple_string, regex: simple_string): bool
|
|
|
429
441
|
* Constructs a new string containing the source string repeated N times with an optional separator.
|
|
430
442
|
*
|
|
431
443
|
* @param source - The string to repeat
|
|
432
|
-
* @param count - Number of times to repeat
|
|
444
|
+
* @param count - Number of times to repeat
|
|
433
445
|
* @param separator - String to inject between repeated instances. Optional, defaults to empty string.
|
|
434
|
-
* @returns The repeated string, or null if source is
|
|
446
|
+
* @returns The repeated string, or null (na) if source is na or count is 0 / negative / na, as in PineScript
|
|
435
447
|
*
|
|
436
448
|
* @example
|
|
437
449
|
* ```typescript
|
|
438
450
|
* str.repeat("ab", 3) // Returns: "ababab"
|
|
439
451
|
* str.repeat("?", 3, ",") // Returns: "?,?,?"
|
|
440
|
-
* str.repeat("hello", 0) // Returns:
|
|
452
|
+
* str.repeat("hello", 0) // Returns: null (na)
|
|
441
453
|
* str.repeat("x", 1) // Returns: "x"
|
|
442
454
|
* ```
|
|
443
455
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/str/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/str/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/str/index.js
CHANGED
|
@@ -45,12 +45,17 @@ module.exports = __toCommonJS(str_exports);
|
|
|
45
45
|
var import_numberformat = require("./numberformat.js");
|
|
46
46
|
var import_messageformat = require("./messageformat.js");
|
|
47
47
|
var import_dateformat = require("./dateformat.js");
|
|
48
|
+
var import_math = require("../math/index.js");
|
|
48
49
|
function length(str) {
|
|
49
50
|
return str.length;
|
|
50
51
|
}
|
|
51
|
-
function tostring(value, format2) {
|
|
52
|
+
function tostring(value, format2, mintick) {
|
|
52
53
|
if (typeof value !== "number") return String(value);
|
|
53
|
-
if (
|
|
54
|
+
if (Number.isNaN(value)) return format2 === "percent" ? "NaN%" : "NaN";
|
|
55
|
+
if (!Number.isFinite(value)) {
|
|
56
|
+
if (format2 === "mintick") return String(value);
|
|
57
|
+
return format2 === "percent" ? "NaN%" : format2 === "volume" ? "NaNT" : "NaN";
|
|
58
|
+
}
|
|
54
59
|
if (format2 === void 0 || format2 === "") {
|
|
55
60
|
if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
|
|
56
61
|
return String(value).replace("e+", "E").replace(/\.0+E/, "E");
|
|
@@ -67,17 +72,30 @@ function tostring(value, format2) {
|
|
|
67
72
|
}
|
|
68
73
|
return (0, import_numberformat.formatNumber)(value, "#", "halfUpShortest");
|
|
69
74
|
}
|
|
70
|
-
case "mintick":
|
|
71
|
-
|
|
75
|
+
case "mintick": {
|
|
76
|
+
if (mintick === void 0) {
|
|
77
|
+
throw new Error("str.tostring(x, format.mintick) needs the tick size: pass it as str.tostring(x, format.mintick, syminfo.mintick)");
|
|
78
|
+
}
|
|
79
|
+
const m = /^\d+(?:\.(\d+))?(?:e-(\d+))?$/.exec(String(mintick));
|
|
80
|
+
const decimals = m ? (m[1]?.length ?? 0) + Number(m[2] ?? 0) : 0;
|
|
81
|
+
const mask = decimals ? "#." + "#".repeat(decimals) : "#";
|
|
82
|
+
return (0, import_numberformat.formatNumber)((0, import_math.round_to_mintick)(value, mintick), mask, "halfUpShortest");
|
|
83
|
+
}
|
|
72
84
|
default:
|
|
73
85
|
return (0, import_numberformat.formatNumber)(value, format2, "halfUpShortest");
|
|
74
86
|
}
|
|
75
87
|
}
|
|
76
88
|
function tonumber(str) {
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
let start = 0;
|
|
90
|
+
let end = str.length;
|
|
91
|
+
while (start < end && str.charCodeAt(start) <= 32) start++;
|
|
92
|
+
while (end > start && str.charCodeAt(end - 1) <= 32) end--;
|
|
93
|
+
const text = str.slice(start, end);
|
|
94
|
+
if (!/^[+-]?(?:\d+\.?\d*|\.\d+)$/.test(text)) return null;
|
|
95
|
+
return parseFloat(text);
|
|
79
96
|
}
|
|
80
97
|
function substring(str, begin, end) {
|
|
98
|
+
if (end === null || Number.isNaN(end)) throw new Error("str.substring: the end position is na");
|
|
81
99
|
return str.substring(begin, end);
|
|
82
100
|
}
|
|
83
101
|
function upper(str) {
|
|
@@ -90,20 +108,29 @@ function contains(source, str) {
|
|
|
90
108
|
return source.includes(str);
|
|
91
109
|
}
|
|
92
110
|
function pos(source, str) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
const index = source.indexOf(str);
|
|
112
|
+
return index === -1 ? NaN : index;
|
|
113
|
+
}
|
|
114
|
+
function replace(source, target, replacement, occurrence = 0) {
|
|
115
|
+
const n = occurrence === null || Number.isNaN(occurrence) ? 0 : Math.trunc(occurrence);
|
|
116
|
+
if (n < 0) return source;
|
|
117
|
+
let index;
|
|
118
|
+
if (target === "") {
|
|
119
|
+
index = Math.min(n, source.length);
|
|
98
120
|
} else {
|
|
99
|
-
|
|
121
|
+
index = -1;
|
|
122
|
+
for (let k = 0; k <= n; k++) {
|
|
123
|
+
index = source.indexOf(target, index + 1);
|
|
124
|
+
if (index === -1) return source;
|
|
125
|
+
}
|
|
100
126
|
}
|
|
127
|
+
return source.slice(0, index) + replacement + source.slice(index + target.length);
|
|
101
128
|
}
|
|
102
129
|
function replace_all(source, target, replacement) {
|
|
103
130
|
return source.replaceAll(target, replacement);
|
|
104
131
|
}
|
|
105
132
|
function split(str, separator) {
|
|
106
|
-
return str.split(separator);
|
|
133
|
+
return str === "" ? [""] : str.split(separator);
|
|
107
134
|
}
|
|
108
135
|
function concat(...strings) {
|
|
109
136
|
return strings.join("");
|
|
@@ -130,11 +157,12 @@ function trimRight(str) {
|
|
|
130
157
|
return str.trimEnd();
|
|
131
158
|
}
|
|
132
159
|
function match(source, regex) {
|
|
133
|
-
|
|
160
|
+
const found = new RegExp(regex).exec(source);
|
|
161
|
+
return found && found[0] !== "" ? found[0] : null;
|
|
134
162
|
}
|
|
135
163
|
function repeat(source, count, separator = "") {
|
|
136
164
|
if (source == null) return null;
|
|
137
|
-
if (count
|
|
165
|
+
if (!(count > 0)) return null;
|
|
138
166
|
return Array(count).fill(source).join(separator);
|
|
139
167
|
}
|
|
140
168
|
function format_time(time, format2 = "yyyy-MM-dd'T'HH:mm:ssZ", timezone = "Etc/UTC") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/str/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * String namespace\n * Mirrors PineScript's str.* functions for string manipulation and analysis.\n *\n * @remarks\n * All string functions in this namespace follow PineScript v6 API specifications.\n * String operations are case-sensitive unless otherwise specified.\n *\n * @version 6\n */\n\nimport { formatNumber } from './numberformat.js';\nimport { formatMessage } from './messageformat.js';\nimport { formatDate } from './dateformat.js';\nimport type { int, bool, float, simple_int, simple_string } from '../types/index.js';\n\n/**\n * Returns the length of a string.\n *\n * @param str - The string to measure\n * @returns The number of characters in the string\n *\n * @example\n * ```typescript\n * str.length(\"Hello\") // Returns: 5\n * str.length(\"\") // Returns: 0\n * ```\n */\nexport function length(str: simple_string): int {\n return str.length;\n}\n\n/**\n * Converts a value to its string representation (PineScript `str.tostring`).\n *\n * @param value - The value to convert (number, boolean, string, etc.)\n * @param format - Optional number format: a pattern (\"#.##\", \"#,###.00\", \"0.00%\", \"0.00E0\") or\n * `format.percent` (\"percent\"), `format.volume` (\"volume\"), `format.price` / `format.inherit`\n * @returns String representation of the value\n *\n * @remarks\n * As in PineScript:\n * - without format, numbers show up to 10 decimals (\"0.3333333333\"); 1e21 and more use \"1E21\"\n * - patterns round half away from zero on the shortest decimal form: 1.005 with \"#.##\" gives \"1.01\"\n * - \"#\" is an optional digit (\"1.5\" with \"#.##\"), \"0\" a required digit (\"1.50\" with \"0.00\"),\n * \",\" groups thousands, \"%\" multiplies by 100, 'text' is literal text\n * - `format.percent` shows 2 decimals and \"%\" without multiplying; `format.volume` uses K, M, B, T\n * - `format.price` and `format.inherit` give \"price1\" / \"inherit1\" in PineScript: they are patterns\n * without digit characters, and oakscriptjs does the same\n * - `format.mintick` needs the symbol's tick size and throws here\n *\n * @example\n * ```typescript\n * str.tostring(123) // \"123\"\n * str.tostring(1.5, \"#.##\") // \"1.5\"\n * str.tostring(0.5, \"#.00\") // \".50\"\n * str.tostring(12345.678, format.volume) // \"12.346K\"\n * ```\n */\nexport function tostring(value: any, format?: simple_string): string {\n if (typeof value !== 'number') return String(value);\n if (!Number.isFinite(value)) return String(value); // NaN (na), Infinity\n if (format === undefined || format === '') {\n if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {\n return String(value).replace('e+', 'E').replace(/\\.0+E/, 'E');\n }\n return formatNumber(value, '#.##########', 'halfUpShortest');\n }\n switch (format) {\n case 'percent':\n return formatNumber(value, '0.00', 'halfUpShortest') + '%';\n case 'volume': {\n const abs = Math.abs(value);\n for (const [size, unit] of [[1e12, 'T'], [1e9, 'B'], [1e6, 'M'], [1e3, 'K']] as const) {\n if (abs >= size) return formatNumber(value / size, '#.###', 'halfUpShortest') + unit;\n }\n return formatNumber(value, '#', 'halfUpShortest');\n }\n case 'mintick':\n throw new Error('str.tostring(x, format.mintick) needs the symbol tick size (syminfo.mintick), which is not known here');\n default:\n return formatNumber(value, format, 'halfUpShortest');\n }\n}\n\n/**\n * Converts a string to a number (float).\n *\n * @param str - The string to convert\n * @returns The numeric value, or null if conversion fails\n *\n * @remarks\n * - Returns null for invalid number strings\n * - Supports scientific notation (e.g., \"1e3\")\n * - Supports decimal numbers (e.g., \"0.5\", \".5\")\n * - Whitespace is ignored\n * - \"NaN\" string returns null\n *\n * @example\n * ```typescript\n * str.tonumber(\"123\") // Returns: 123\n * str.tonumber(\"45.67\") // Returns: 45.67\n * str.tonumber(\"abc\") // Returns: null\n * str.tonumber(\"1e3\") // Returns: 1000\n * ```\n */\nexport function tonumber(str: simple_string): float | null {\n const num = parseFloat(str);\n return isNaN(num) ? null : num;\n}\n\n/**\n * Extracts a substring from a string.\n *\n * @param str - The source string\n * @param begin - Starting index (0-based, inclusive)\n * @param end - Ending index (0-based, exclusive). If omitted, extracts to end of string\n * @returns The extracted substring\n *\n * @remarks\n * - If begin > end, indices are swapped (JavaScript behavior)\n * - Negative indices are treated as 0\n * - Indices beyond string length return empty string\n *\n * @example\n * ```typescript\n * str.substring(\"hello world\", 0, 5) // Returns: \"hello\"\n * str.substring(\"hello world\", 6) // Returns: \"world\"\n * str.substring(\"test\", 1, 3) // Returns: \"es\"\n * ```\n */\nexport function substring(str: simple_string, begin: simple_int, end?: simple_int): string {\n return str.substring(begin, end);\n}\n\n/**\n * Converts all characters in a string to uppercase.\n *\n * @param str - The string to convert\n * @returns The uppercase string\n *\n * @example\n * ```typescript\n * str.upper(\"hello\") // Returns: \"HELLO\"\n * str.upper(\"Hello World\") // Returns: \"HELLO WORLD\"\n * str.upper(\"test123\") // Returns: \"TEST123\"\n * ```\n */\nexport function upper(str: simple_string): string {\n return str.toUpperCase();\n}\n\n/**\n * Converts all characters in a string to lowercase.\n *\n * @param str - The string to convert\n * @returns The lowercase string\n *\n * @example\n * ```typescript\n * str.lower(\"HELLO\") // Returns: \"hello\"\n * str.lower(\"Hello World\") // Returns: \"hello world\"\n * str.lower(\"TEST123\") // Returns: \"test123\"\n * ```\n */\nexport function lower(str: simple_string): string {\n return str.toLowerCase();\n}\n\n/**\n * Checks if a string contains a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns True if source contains str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string is contained in any string\n *\n * @example\n * ```typescript\n * str.contains(\"hello world\", \"world\") // Returns: true\n * str.contains(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.contains(\"test\", \"xyz\") // Returns: false\n * ```\n */\nexport function contains(source: simple_string, str: simple_string): bool {\n return source.includes(str);\n}\n\n/**\n * Returns the position of the first occurrence of a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns The 0-based index of the first occurrence, or -1 if not found\n *\n * @remarks\n * - Search is case-sensitive\n * - Returns 0 for empty search string\n * - Returns -1 if substring is not found\n *\n * @example\n * ```typescript\n * str.pos(\"hello world\", \"world\") // Returns: 6\n * str.pos(\"hello world\", \"o\") // Returns: 4 (first occurrence)\n * str.pos(\"hello\", \"xyz\") // Returns: -1\n * ```\n */\nexport function pos(source: simple_string, str: simple_string): int {\n return source.indexOf(str);\n}\n\n/**\n * Replaces occurrences of a substring with another string.\n *\n * @param source - The source string\n * @param target - The substring to replace\n * @param replacement - The string to replace with\n * @param occurrence - If 0, replaces first occurrence only; otherwise replaces all occurrences (default: all)\n * @returns The string with replacements made\n *\n * @remarks\n * - Search is case-sensitive\n * - If target is not found, returns source unchanged\n * - occurrence parameter: 0 = first only, any other value = all occurrences\n *\n * @example\n * ```typescript\n * str.replace(\"hello world hello\", \"hello\", \"hi\", 0) // Returns: \"hi world hello\"\n * str.replace(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hi\"\n * str.replace(\"test\", \"xyz\", \"abc\") // Returns: \"test\"\n * ```\n */\nexport function replace(source: simple_string, target: simple_string, replacement: simple_string, occurrence?: simple_int): string {\n if (occurrence === 0) {\n return source.replace(target, replacement);\n } else {\n return source.replaceAll(target, replacement);\n }\n}\n\n/**\n * Replaces all occurrences of a target string with a replacement string.\n *\n * @param source - The source string\n * @param target - The substring to find\n * @param replacement - The replacement string\n * @returns String with all occurrences replaced\n *\n * @remarks\n * - Replaces all occurrences of the target string\n * - Case sensitive\n * - If target is empty string, returns original string\n * - This matches PineScript's `str.replace_all()` behavior\n *\n * @example\n * ```typescript\n * str.replace_all(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hi\"\n * str.replace_all(\"a-b-c\", \"-\", \"_\") // Returns: \"a_b_c\"\n * str.replace_all(\"test\", \"x\", \"y\") // Returns: \"test\" (no match)\n * ```\n */\nexport function replace_all(source: simple_string, target: simple_string, replacement: simple_string): string {\n return source.replaceAll(target, replacement);\n}\n\n/**\n * Splits a string into an array of substrings using a separator.\n *\n * @param str - The string to split\n * @param separator - The separator string\n * @returns Array of substrings\n *\n * @remarks\n * - Empty separator splits into individual characters\n * - If separator is not found, returns array with original string\n * - Consecutive separators create empty strings in result\n *\n * @example\n * ```typescript\n * str.split(\"a,b,c\", \",\") // Returns: [\"a\", \"b\", \"c\"]\n * str.split(\"hello\", \"\") // Returns: [\"h\", \"e\", \"l\", \"l\", \"o\"]\n * str.split(\"a,,b\", \",\") // Returns: [\"a\", \"\", \"b\"]\n * ```\n */\nexport function split(str: simple_string, separator: simple_string): string[] {\n return str.split(separator);\n}\n\n/**\n * Concatenates multiple strings into one.\n *\n * @param strings - Variable number of strings to concatenate\n * @returns The concatenated string\n *\n * @example\n * ```typescript\n * str.concat(\"hello\", \" \", \"world\") // Returns: \"hello world\"\n * str.concat(\"a\", \"b\", \"c\") // Returns: \"abc\"\n * str.concat(\"test\") // Returns: \"test\"\n * ```\n */\nexport function concat(...strings: simple_string[]): string {\n return strings.join('');\n}\n\n/**\n * Formats a string with placeholders (PineScript `str.format`, Java MessageFormat syntax).\n *\n * @param formatStr - Pattern with `{0}`, `{1,number,#.##}`, `{0,number,percent}`, `{0,date,yyyy-MM-dd}`...\n * @param args - Values for the placeholders\n * @returns Formatted string\n *\n * @remarks\n * As in PineScript: numbers in `{n}` use\n * \"#,##0.###\" (1234.5678 gives \"1,234.568\"); number patterns round half to even on the exact\n * value; `'...'` is literal text and `''` a quote; a missing argument leaves `{n}` in the text.\n * Dates are formatted in UTC here; PineScript uses the exchange time zone.\n *\n * @example\n * ```typescript\n * str.format('Close: {0,number,#.##}', 101.456) // \"Close: 101.46\"\n * str.format('{0} and {1}', 'a', 2) // \"a and 2\"\n * ```\n */\nexport function format(formatStr: simple_string, ...args: any[]): string {\n return formatMessage(formatStr, args, 'Etc/UTC');\n}\n\n/**\n * Checks if a string starts with a specific prefix.\n *\n * @param source - The string to check\n * @param str - The prefix to look for\n * @returns True if source starts with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string prefix always returns true\n *\n * @example\n * ```typescript\n * str.startswith(\"hello world\", \"hello\") // Returns: true\n * str.startswith(\"hello world\", \"Hello\") // Returns: false (case-sensitive)\n * str.startswith(\"test\", \"testing\") // Returns: false\n * ```\n */\nexport function startswith(source: simple_string, str: simple_string): bool {\n return source.startsWith(str);\n}\n\n/**\n * Checks if a string ends with a specific suffix.\n *\n * @param source - The string to check\n * @param str - The suffix to look for\n * @returns True if source ends with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string suffix always returns true\n *\n * @example\n * ```typescript\n * str.endswith(\"hello world\", \"world\") // Returns: true\n * str.endswith(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.endswith(\"testing\", \"test\") // Returns: false\n * ```\n */\nexport function endswith(source: simple_string, str: simple_string): bool {\n return source.endsWith(str);\n}\n\n/**\n * Returns the character at a specific position in a string.\n *\n * @param str - The source string\n * @param pos - The 0-based position\n * @returns The character at the position, or empty string if out of bounds\n *\n * @remarks\n * - Returns empty string for negative or out-of-bounds positions\n * - Unicode characters may span multiple code units\n *\n * @example\n * ```typescript\n * str.charAt(\"hello\", 0) // Returns: \"h\"\n * str.charAt(\"hello\", 4) // Returns: \"o\"\n * str.charAt(\"hello\", 10) // Returns: \"\"\n * ```\n */\nexport function charAt(str: simple_string, pos: simple_int): string {\n return str.charAt(pos);\n}\n\n/**\n * Removes whitespace from both ends of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Removes spaces, tabs, newlines, and other whitespace characters\n * - Preserves internal whitespace\n *\n * @example\n * ```typescript\n * str.trim(\" hello \") // Returns: \"hello\"\n * str.trim(\" hello world \") // Returns: \"hello world\"\n * str.trim(\"\\thello\\n\") // Returns: \"hello\"\n * ```\n */\nexport function trim(str: simple_string): string {\n return str.trim();\n}\n\n/**\n * Removes whitespace from the left (start) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes leading whitespace\n * - Preserves trailing and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimLeft(\" hello \") // Returns: \"hello \"\n * str.trimLeft(\" hello\") // Returns: \"hello\"\n * str.trimLeft(\"\\thello\") // Returns: \"hello\"\n * ```\n */\nexport function trimLeft(str: simple_string): string {\n return str.trimStart();\n}\n\n/**\n * Removes whitespace from the right (end) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes trailing whitespace\n * - Preserves leading and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimRight(\" hello \") // Returns: \" hello\"\n * str.trimRight(\"hello \") // Returns: \"hello\"\n * str.trimRight(\"hello\\t\") // Returns: \"hello\"\n * ```\n */\nexport function trimRight(str: simple_string): string {\n return str.trimEnd();\n}\n\n/**\n * Tests if a string matches a regular expression pattern.\n *\n * @param source - The string to test\n * @param regex - The regular expression pattern\n * @returns True if the pattern matches, false otherwise\n *\n * @remarks\n * - Uses JavaScript regex syntax\n * - Match is case-sensitive by default\n * - Pattern is tested against entire string\n *\n * @example\n * ```typescript\n * str.match(\"hello123\", \"\\\\d+\") // Returns: true\n * str.match(\"test@example.com\", \"^[a-z]+@[a-z]+\\\\.[a-z]+$\") // Returns: true\n * str.match(\"abc\", \"[A-Z]+\") // Returns: false\n * ```\n */\nexport function match(source: simple_string, regex: simple_string): bool {\n return new RegExp(regex).test(source);\n}\n\n/**\n * Formats a timestamp as a string according to the specified format.\n *\n * @param time - Unix timestamp in milliseconds\n * @param format - Format string using PineScript format specifiers\n * @returns Formatted date/time string\n *\n * @remarks\n * Supported format specifiers (case-sensitive):\n * - `yyyy` - 4-digit year (e.g., \"2024\")\n * - `yy` - 2-digit year (e.g., \"24\")\n * - `MMMM` - Full month name (e.g., \"January\")\n * - `MMM` - Abbreviated month name (e.g., \"Jan\")\n * - `MM` - 2-digit month (01-12)\n * - `M` - Month (1-12)\n * - `dd` - 2-digit day (01-31)\n * - `d` - Day (1-31)\n * - `HH` - 2-digit hour (00-23)\n * - `H` - Hour (0-23)\n * - `hh` - 2-digit hour (01-12)\n * - `h` - Hour (1-12)\n * - `mm` - 2-digit minute (00-59)\n * - `m` - Minute (0-59)\n * - `ss` - 2-digit second (00-59)\n * - `s` - Second (0-59)\n * - `a` - AM/PM\n *\n * @example\n * ```typescript\n * const timestamp = 1609459200000; // Jan 1, 2021 00:00:00 UTC\n * str.format_time(timestamp, \"dd MMM yyyy\") // Returns: \"01 Jan 2021\"\n * str.format_time(timestamp, \"yyyy-MM-dd HH:mm:ss\") // Returns: \"2021-01-01 00:00:00\"\n * str.format_time(timestamp, \"MMM d, yyyy\") // Returns: \"Jan 1, 2021\"\n * str.format_time(timestamp, \"hh:mm a\") // Returns: \"12:00 AM\"\n * ```\n */\n/**\n * Constructs a new string containing the source string repeated N times with an optional separator.\n *\n * @param source - The string to repeat\n * @param count - Number of times to repeat (must be >= 0)\n * @param separator - String to inject between repeated instances. Optional, defaults to empty string.\n * @returns The repeated string, or null if source is null/undefined\n *\n * @example\n * ```typescript\n * str.repeat(\"ab\", 3) // Returns: \"ababab\"\n * str.repeat(\"?\", 3, \",\") // Returns: \"?,?,?\"\n * str.repeat(\"hello\", 0) // Returns: \"\"\n * str.repeat(\"x\", 1) // Returns: \"x\"\n * ```\n */\nexport function repeat(source: simple_string, count: simple_int, separator: simple_string = ''): string | null {\n if (source == null) return null;\n if (count <= 0) return '';\n return Array(count).fill(source).join(separator);\n}\n\n/**\n * Formats a UNIX time (ms) with a date pattern (PineScript `str.format_time`).\n *\n * @param time - UNIX time in milliseconds\n * @param format - Java SimpleDateFormat pattern, e.g. \"yyyy-MM-dd HH:mm:ss\", \"dd MMM yyyy\", \"hh:mm a\"\n * @param timezone - UTC/GMT offset (\"UTC-5\") or IANA name (\"America/New_York\"). PineScript's default is\n * the exchange time zone, which is not known here: the default is UTC\n * @returns Formatted date string\n *\n * @remarks\n * Letters: y, M, d, E, u, D, w,\n * H, k, K, h, a, m, s, S, Z, z; 'text' is literal text.\n */\nexport function format_time(time: simple_int, format: simple_string = \"yyyy-MM-dd'T'HH:mm:ssZ\", timezone: simple_string = 'Etc/UTC'): string {\n return formatDate(time, format, timezone);\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,0BAA6B;AAC7B,2BAA8B;AAC9B,wBAA2B;
|
|
4
|
+
"sourcesContent": ["/**\n * String namespace\n * Mirrors PineScript's str.* functions for string manipulation and analysis.\n *\n * @remarks\n * All string functions in this namespace follow PineScript v6 API specifications.\n * String operations are case-sensitive unless otherwise specified.\n *\n * @version 6\n */\n\nimport { formatNumber } from './numberformat.js';\nimport { formatMessage } from './messageformat.js';\nimport { formatDate } from './dateformat.js';\nimport { round_to_mintick } from '../math/index.js';\nimport type { int, bool, float, simple_int, simple_string } from '../types/index.js';\n\n/**\n * Returns the length of a string.\n *\n * @param str - The string to measure\n * @returns The number of characters in the string\n *\n * @example\n * ```typescript\n * str.length(\"Hello\") // Returns: 5\n * str.length(\"\") // Returns: 0\n * ```\n */\nexport function length(str: simple_string): int {\n return str.length;\n}\n\n/**\n * Converts a value to its string representation (PineScript `str.tostring`).\n *\n * @param value - The value to convert (number, boolean, string, etc.)\n * @param format - Optional number format: a pattern (\"#.##\", \"#,###.00\", \"0.00%\", \"0.00E0\", \"#.##;(#.##)\") or\n * `format.percent` (\"percent\"), `format.volume` (\"volume\"), `format.mintick` (\"mintick\"),\n * `format.price` / `format.inherit`\n * @param mintick - Tick size for `format.mintick` (PineScript `syminfo.mintick`, implicit there). Not part of the\n * PineScript signature: the core function cannot see the chart, so pass it, as for `math.round_to_mintick`\n * @returns String representation of the value\n *\n * @remarks\n * As in PineScript:\n * - without format, numbers show up to 10 decimals (\"0.3333333333\"); 1e21 and more use \"1E21\"\n * - patterns round half away from zero on the shortest decimal form: 1.005 with \"#.##\" gives \"1.01\";\n * at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives \"0.0012345678901235\")\n * - \"pos;neg\" patterns: a negative number takes the text of the negative subpattern (\"#.##;(#.##)\": \"(3.5)\")\n * - \"#\" is an optional digit (\"1.5\" with \"#.##\"), \"0\" a required digit (\"1.50\" with \"0.00\"),\n * \",\" groups thousands, \"%\" multiplies by 100, 'text' is literal text\n * - `format.percent` shows 2 decimals and \"%\" without multiplying; `format.volume` uses K, M, B, T\n * - `format.price` and `format.inherit` give \"price1\" / \"inherit1\" in PineScript: they are patterns\n * without digit characters, and oakscriptjs does the same\n * - `format.mintick` rounds to the tick (`math.round_to_mintick`) and shows the tick's decimals (\"#.##\" for 0.01);\n * it throws without `mintick`\n * - na gives \"NaN\" (\"NaN%\" with `format.percent`); an infinity gives \"NaN\" too (\"NaN%\" for `format.percent`,\n * \"NaNT\" for `format.volume`), except `format.mintick`, which gives \"Infinity\" / \"-Infinity\"\n *\n * @example\n * ```typescript\n * str.tostring(123) // \"123\"\n * str.tostring(1.5, \"#.##\") // \"1.5\"\n * str.tostring(0.5, \"#.00\") // \".50\"\n * str.tostring(12345.678, format.volume) // \"12.346K\"\n * ```\n */\nexport function tostring(value: any, format?: simple_string, mintick?: float): string {\n if (typeof value !== 'number') return String(value);\n if (Number.isNaN(value)) return format === 'percent' ? 'NaN%' : 'NaN';\n if (!Number.isFinite(value)) {\n // the chart formatter has no infinity: NaN with the format's suffix\n if (format === 'mintick') return String(value);\n return format === 'percent' ? 'NaN%' : format === 'volume' ? 'NaNT' : 'NaN';\n }\n if (format === undefined || format === '') {\n if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {\n return String(value).replace('e+', 'E').replace(/\\.0+E/, 'E');\n }\n return formatNumber(value, '#.##########', 'halfUpShortest');\n }\n switch (format) {\n case 'percent':\n return formatNumber(value, '0.00', 'halfUpShortest') + '%';\n case 'volume': {\n const abs = Math.abs(value);\n for (const [size, unit] of [[1e12, 'T'], [1e9, 'B'], [1e6, 'M'], [1e3, 'K']] as const) {\n if (abs >= size) return formatNumber(value / size, '#.###', 'halfUpShortest') + unit;\n }\n return formatNumber(value, '#', 'halfUpShortest');\n }\n case 'mintick': {\n if (mintick === undefined) {\n throw new Error('str.tostring(x, format.mintick) needs the tick size: pass it as str.tostring(x, format.mintick, syminfo.mintick)');\n }\n // mask: one '#' per decimal of the tick (\"#.##\" for 0.01, \"#\" for 1)\n const m = /^\\d+(?:\\.(\\d+))?(?:e-(\\d+))?$/.exec(String(mintick));\n const decimals = m ? (m[1]?.length ?? 0) + Number(m[2] ?? 0) : 0;\n const mask = decimals ? '#.' + '#'.repeat(decimals) : '#';\n return formatNumber(round_to_mintick(value, mintick), mask, 'halfUpShortest');\n }\n default:\n return formatNumber(value, format, 'halfUpShortest');\n }\n}\n\n/**\n * Converts a string to a number (float).\n *\n * @param str - The string to convert\n * @returns The numeric value, or null (na) if conversion fails\n *\n * @remarks\n * As in PineScript:\n * - The whole text must be a number: a sign, digits and one optional \".\" (\"0.5\", \".5\", \"5.\", \"-12\"); \"12abc\" is na\n * - Scientific notation (\"1e3\"), \"Infinity\", \"NaN\", hexadecimal, \"1,000\" give na\n * - The characters U+0000 to U+0020 (space, tab, line feed...) around the number are ignored; other spaces\n * (no-break space U+00A0, U+3000) give na\n *\n * @example\n * ```typescript\n * str.tonumber(\"123\") // Returns: 123\n * str.tonumber(\"45.67\") // Returns: 45.67\n * str.tonumber(\"abc\") // Returns: null\n * str.tonumber(\"12abc\") // Returns: null\n * str.tonumber(\"1e3\") // Returns: null\n * ```\n */\nexport function tonumber(str: simple_string): float | null {\n // remove the characters U+0000..U+0020 at both ends\n let start = 0;\n let end = str.length;\n while (start < end && str.charCodeAt(start) <= 0x20) start++;\n while (end > start && str.charCodeAt(end - 1) <= 0x20) end--;\n const text = str.slice(start, end);\n if (!/^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(text)) return null;\n return parseFloat(text);\n}\n\n/**\n * Extracts a substring from a string.\n *\n * @param str - The source string\n * @param begin - Starting index (0-based, inclusive)\n * @param end - Ending index (0-based, exclusive). If omitted, extracts to end of string\n * @returns The extracted substring\n *\n * @remarks\n * - An na `end` (NaN / null) is a runtime error in PineScript: it throws\n * - If begin > end, indices are swapped (JavaScript behavior)\n * - Negative indices are treated as 0\n * - Indices beyond string length return empty string\n *\n * @example\n * ```typescript\n * str.substring(\"hello world\", 0, 5) // Returns: \"hello\"\n * str.substring(\"hello world\", 6) // Returns: \"world\"\n * str.substring(\"test\", 1, 3) // Returns: \"es\"\n * ```\n */\nexport function substring(str: simple_string, begin: simple_int, end?: simple_int): string {\n if (end === null || Number.isNaN(end)) throw new Error('str.substring: the end position is na');\n return str.substring(begin, end);\n}\n\n/**\n * Converts all characters in a string to uppercase.\n *\n * @param str - The string to convert\n * @returns The uppercase string\n *\n * @example\n * ```typescript\n * str.upper(\"hello\") // Returns: \"HELLO\"\n * str.upper(\"Hello World\") // Returns: \"HELLO WORLD\"\n * str.upper(\"test123\") // Returns: \"TEST123\"\n * ```\n */\nexport function upper(str: simple_string): string {\n return str.toUpperCase();\n}\n\n/**\n * Converts all characters in a string to lowercase.\n *\n * @param str - The string to convert\n * @returns The lowercase string\n *\n * @example\n * ```typescript\n * str.lower(\"HELLO\") // Returns: \"hello\"\n * str.lower(\"Hello World\") // Returns: \"hello world\"\n * str.lower(\"TEST123\") // Returns: \"test123\"\n * ```\n */\nexport function lower(str: simple_string): string {\n return str.toLowerCase();\n}\n\n/**\n * Checks if a string contains a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns True if source contains str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string is contained in any string\n *\n * @example\n * ```typescript\n * str.contains(\"hello world\", \"world\") // Returns: true\n * str.contains(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.contains(\"test\", \"xyz\") // Returns: false\n * ```\n */\nexport function contains(source: simple_string, str: simple_string): bool {\n return source.includes(str);\n}\n\n/**\n * Returns the position of the first occurrence of a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns The 0-based index of the first occurrence, or na (NaN) if not found\n *\n * @remarks\n * - Search is case-sensitive\n * - Returns 0 for empty search string\n * - Returns na (NaN) if substring is not found, as in PineScript\n *\n * @example\n * ```typescript\n * str.pos(\"hello world\", \"world\") // Returns: 6\n * str.pos(\"hello world\", \"o\") // Returns: 4 (first occurrence)\n * str.pos(\"hello\", \"xyz\") // Returns: NaN (na)\n * ```\n */\nexport function pos(source: simple_string, str: simple_string): int {\n const index = source.indexOf(str);\n return index === -1 ? NaN : index;\n}\n\n/**\n * Replaces the Nth occurrence of a substring (PineScript `str.replace`).\n *\n * @param source - The source string\n * @param target - The substring to replace\n * @param replacement - The string to replace with\n * @param occurrence - 0-based occurrence to replace (default 0, the first one)\n * @returns The string with one occurrence replaced\n *\n * @remarks\n * PineScript rules:\n * - only the Nth occurrence is replaced; an na occurrence is 0 (`str.replace(\"aaa\", \"a\", \"-\", na)` is \"-aa\")\n * - occurrences overlap: \"aa\" occurs at 0 and 1 in \"aaa\" (`str.replace(\"aaa\", \"aa\", \"-\", 1)` is \"a-\")\n * - an empty target inserts the replacement at character N, at most at the end\n * (`str.replace(\"abc\", \"\", \"-\", 2)` is \"ab-c\")\n * - fewer than N + 1 occurrences (or a negative N) leave the source unchanged\n *\n * @example\n * ```typescript\n * str.replace(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hello\"\n * str.replace(\"hello world hello\", \"hello\", \"hi\", 1) // Returns: \"hello world hi\"\n * str.replace(\"test\", \"xyz\", \"abc\") // Returns: \"test\"\n * ```\n */\nexport function replace(source: simple_string, target: simple_string, replacement: simple_string, occurrence: simple_int = 0): string {\n const n = occurrence === null || Number.isNaN(occurrence) ? 0 : Math.trunc(occurrence);\n if (n < 0) return source;\n let index: number;\n if (target === '') {\n index = Math.min(n, source.length);\n } else {\n index = -1;\n for (let k = 0; k <= n; k++) {\n index = source.indexOf(target, index + 1);\n if (index === -1) return source;\n }\n }\n return source.slice(0, index) + replacement + source.slice(index + target.length);\n}\n\n/**\n * Replaces all occurrences of a target string with a replacement string.\n *\n * @param source - The source string\n * @param target - The substring to find\n * @param replacement - The replacement string\n * @returns String with all occurrences replaced\n *\n * @remarks\n * - Replaces all occurrences of the target string\n * - Case sensitive\n * - If target is empty string, returns original string\n * - This matches PineScript's `str.replace_all()` behavior\n *\n * @example\n * ```typescript\n * str.replace_all(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hi\"\n * str.replace_all(\"a-b-c\", \"-\", \"_\") // Returns: \"a_b_c\"\n * str.replace_all(\"test\", \"x\", \"y\") // Returns: \"test\" (no match)\n * ```\n */\nexport function replace_all(source: simple_string, target: simple_string, replacement: simple_string): string {\n return source.replaceAll(target, replacement);\n}\n\n/**\n * Splits a string into an array of substrings using a separator.\n *\n * @param str - The string to split\n * @param separator - The separator string\n * @returns Array of substrings\n *\n * @remarks\n * - Empty separator splits into individual characters; an empty string gives [\"\"] (also with \"\")\n * - If separator is not found, returns array with original string\n * - Consecutive separators create empty strings in result\n *\n * @example\n * ```typescript\n * str.split(\"a,b,c\", \",\") // Returns: [\"a\", \"b\", \"c\"]\n * str.split(\"hello\", \"\") // Returns: [\"h\", \"e\", \"l\", \"l\", \"o\"]\n * str.split(\"a,,b\", \",\") // Returns: [\"a\", \"\", \"b\"]\n * ```\n */\nexport function split(str: simple_string, separator: simple_string): string[] {\n // JavaScript gives [] for \"\".split(\"\"); PineScript gives [\"\"]\n return str === '' ? [''] : str.split(separator);\n}\n\n/**\n * Concatenates multiple strings into one.\n *\n * @param strings - Variable number of strings to concatenate\n * @returns The concatenated string\n *\n * @example\n * ```typescript\n * str.concat(\"hello\", \" \", \"world\") // Returns: \"hello world\"\n * str.concat(\"a\", \"b\", \"c\") // Returns: \"abc\"\n * str.concat(\"test\") // Returns: \"test\"\n * ```\n */\nexport function concat(...strings: simple_string[]): string {\n return strings.join('');\n}\n\n/**\n * Formats a string with placeholders (PineScript `str.format`, Java MessageFormat syntax).\n *\n * @param formatStr - Pattern with `{0}`, `{1,number,#.##}`, `{0,number,percent}`, `{0,date,yyyy-MM-dd}`...\n * @param args - Values for the placeholders\n * @returns Formatted string\n *\n * @remarks\n * As in PineScript: numbers in `{n}` use\n * \"#,##0.###\" (1234.5678 gives \"1,234.568\"); number patterns round half to even on the exact\n * value (16 decimals at most from 1e-3 up); an array gives \"[a, b]\"; `'...'` is literal text and `''` a quote;\n * a missing argument leaves `{n}` in the text.\n * Dates are formatted in UTC here; PineScript uses the exchange time zone.\n *\n * @example\n * ```typescript\n * str.format('Close: {0,number,#.##}', 101.456) // \"Close: 101.46\"\n * str.format('{0} and {1}', 'a', 2) // \"a and 2\"\n * ```\n */\nexport function format(formatStr: simple_string, ...args: any[]): string {\n return formatMessage(formatStr, args, 'Etc/UTC');\n}\n\n/**\n * Checks if a string starts with a specific prefix.\n *\n * @param source - The string to check\n * @param str - The prefix to look for\n * @returns True if source starts with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string prefix always returns true\n *\n * @example\n * ```typescript\n * str.startswith(\"hello world\", \"hello\") // Returns: true\n * str.startswith(\"hello world\", \"Hello\") // Returns: false (case-sensitive)\n * str.startswith(\"test\", \"testing\") // Returns: false\n * ```\n */\nexport function startswith(source: simple_string, str: simple_string): bool {\n return source.startsWith(str);\n}\n\n/**\n * Checks if a string ends with a specific suffix.\n *\n * @param source - The string to check\n * @param str - The suffix to look for\n * @returns True if source ends with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string suffix always returns true\n *\n * @example\n * ```typescript\n * str.endswith(\"hello world\", \"world\") // Returns: true\n * str.endswith(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.endswith(\"testing\", \"test\") // Returns: false\n * ```\n */\nexport function endswith(source: simple_string, str: simple_string): bool {\n return source.endsWith(str);\n}\n\n/**\n * Returns the character at a specific position in a string.\n *\n * @param str - The source string\n * @param pos - The 0-based position\n * @returns The character at the position, or empty string if out of bounds\n *\n * @remarks\n * - Returns empty string for negative or out-of-bounds positions\n * - Unicode characters may span multiple code units\n *\n * @example\n * ```typescript\n * str.charAt(\"hello\", 0) // Returns: \"h\"\n * str.charAt(\"hello\", 4) // Returns: \"o\"\n * str.charAt(\"hello\", 10) // Returns: \"\"\n * ```\n */\nexport function charAt(str: simple_string, pos: simple_int): string {\n return str.charAt(pos);\n}\n\n/**\n * Removes whitespace from both ends of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Removes spaces, tabs, newlines, and other whitespace characters\n * - Preserves internal whitespace\n *\n * @example\n * ```typescript\n * str.trim(\" hello \") // Returns: \"hello\"\n * str.trim(\" hello world \") // Returns: \"hello world\"\n * str.trim(\"\\thello\\n\") // Returns: \"hello\"\n * ```\n */\nexport function trim(str: simple_string): string {\n return str.trim();\n}\n\n/**\n * Removes whitespace from the left (start) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes leading whitespace\n * - Preserves trailing and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimLeft(\" hello \") // Returns: \"hello \"\n * str.trimLeft(\" hello\") // Returns: \"hello\"\n * str.trimLeft(\"\\thello\") // Returns: \"hello\"\n * ```\n */\nexport function trimLeft(str: simple_string): string {\n return str.trimStart();\n}\n\n/**\n * Removes whitespace from the right (end) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes trailing whitespace\n * - Preserves leading and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimRight(\" hello \") // Returns: \" hello\"\n * str.trimRight(\"hello \") // Returns: \"hello\"\n * str.trimRight(\"hello\\t\") // Returns: \"hello\"\n * ```\n */\nexport function trimRight(str: simple_string): string {\n return str.trimEnd();\n}\n\n/**\n * Returns the first substring of `source` that matches the `regex` (PineScript `str.match`).\n *\n * @param source - Source string\n * @param regex - Regular expression\n * @returns The first match (searched anywhere in the string), or na (null) when nothing matches or the match is empty\n *\n * @remarks\n * - As PineScript: \"Hello World!\" with \"o\" gives \"o\", with \"[a-z]+\" gives \"ello\"; no match gives na\n * - Uses JavaScript regex syntax\n *\n * @example\n * ```typescript\n * str.match(\"abc123def\", \"[0-9]+\") // Returns: \"123\"\n * str.match(\"abc\", \"[0-9]+\") // Returns: null (na)\n * ```\n */\nexport function match(source: simple_string, regex: simple_string): simple_string | null {\n const found = new RegExp(regex).exec(source);\n return found && found[0] !== '' ? found[0] : null;\n}\n\n/**\n * Formats a timestamp as a string according to the specified format.\n *\n * @param time - Unix timestamp in milliseconds\n * @param format - Format string using PineScript format specifiers\n * @returns Formatted date/time string\n *\n * @remarks\n * Supported format specifiers (case-sensitive):\n * - `yyyy` - 4-digit year (e.g., \"2024\")\n * - `yy` - 2-digit year (e.g., \"24\")\n * - `MMMM` - Full month name (e.g., \"January\")\n * - `MMM` - Abbreviated month name (e.g., \"Jan\")\n * - `MM` - 2-digit month (01-12)\n * - `M` - Month (1-12)\n * - `dd` - 2-digit day (01-31)\n * - `d` - Day (1-31)\n * - `HH` - 2-digit hour (00-23)\n * - `H` - Hour (0-23)\n * - `hh` - 2-digit hour (01-12)\n * - `h` - Hour (1-12)\n * - `mm` - 2-digit minute (00-59)\n * - `m` - Minute (0-59)\n * - `ss` - 2-digit second (00-59)\n * - `s` - Second (0-59)\n * - `a` - AM/PM\n *\n * @example\n * ```typescript\n * const timestamp = 1609459200000; // Jan 1, 2021 00:00:00 UTC\n * str.format_time(timestamp, \"dd MMM yyyy\") // Returns: \"01 Jan 2021\"\n * str.format_time(timestamp, \"yyyy-MM-dd HH:mm:ss\") // Returns: \"2021-01-01 00:00:00\"\n * str.format_time(timestamp, \"MMM d, yyyy\") // Returns: \"Jan 1, 2021\"\n * str.format_time(timestamp, \"hh:mm a\") // Returns: \"12:00 AM\"\n * ```\n */\n/**\n * Constructs a new string containing the source string repeated N times with an optional separator.\n *\n * @param source - The string to repeat\n * @param count - Number of times to repeat\n * @param separator - String to inject between repeated instances. Optional, defaults to empty string.\n * @returns The repeated string, or null (na) if source is na or count is 0 / negative / na, as in PineScript\n *\n * @example\n * ```typescript\n * str.repeat(\"ab\", 3) // Returns: \"ababab\"\n * str.repeat(\"?\", 3, \",\") // Returns: \"?,?,?\"\n * str.repeat(\"hello\", 0) // Returns: null (na)\n * str.repeat(\"x\", 1) // Returns: \"x\"\n * ```\n */\nexport function repeat(source: simple_string, count: simple_int, separator: simple_string = ''): string | null {\n if (source == null) return null;\n if (!(count > 0)) return null; // 0, negative or na: na\n return Array(count).fill(source).join(separator);\n}\n\n/**\n * Formats a UNIX time (ms) with a date pattern (PineScript `str.format_time`).\n *\n * @param time - UNIX time in milliseconds\n * @param format - Java SimpleDateFormat pattern, e.g. \"yyyy-MM-dd HH:mm:ss\", \"dd MMM yyyy\", \"hh:mm a\"\n * @param timezone - UTC/GMT offset (\"UTC-5\") or IANA name (\"America/New_York\"). PineScript's default is\n * the exchange time zone, which is not known here: the default is UTC\n * @returns Formatted date string\n *\n * @remarks\n * Letters: y, M, d, E, u, D, w,\n * H, k, K, h, a, m, s, S, Z, z; 'text' is literal text.\n */\nexport function format_time(time: simple_int, format: simple_string = \"yyyy-MM-dd'T'HH:mm:ssZ\", timezone: simple_string = 'Etc/UTC'): string {\n return formatDate(time, format, timezone);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["format", "pos"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `str.format` patterns as PineScript formats them (Java MessageFormat):
|
|
3
3
|
*
|
|
4
|
-
* - `{n}` inserts argument n; numbers use "#,##0.###" (1234.5678 gives "1,234.568")
|
|
4
|
+
* - `{n}` inserts argument n; numbers use "#,##0.###" (1234.5678 gives "1,234.568"); an array gives
|
|
5
|
+
* "[Hello, World!]"
|
|
5
6
|
* - `{n,number}`, `{n,number,integer}` ("#,##0"), `{n,number,percent}` ("#,##0%"),
|
|
6
7
|
* `{n,number,currency}` ("$1,234.57", "-$7.00"), `{n,number,<pattern>}`
|
|
7
8
|
* - `{n,date}`, `{n,time}`, `{n,date,<pattern>}`, `{n,time,<pattern>}` (styles short, medium, long, full)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messageformat.d.ts","sourceRoot":"","sources":["../../../src/str/messageformat.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"messageformat.d.ts","sourceRoot":"","sources":["../../../src/str/messageformat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA+DH,6HAA6H;AAC7H,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,CAsCzG"}
|
|
@@ -51,7 +51,10 @@ function choice(value, spec) {
|
|
|
51
51
|
}
|
|
52
52
|
function formatArgument(value, type, style, timezone) {
|
|
53
53
|
if (value === void 0) return "";
|
|
54
|
-
if (!type)
|
|
54
|
+
if (!type) {
|
|
55
|
+
if (Array.isArray(value)) return "[" + value.map((v) => v == null ? "NaN" : String(v)).join(", ") + "]";
|
|
56
|
+
return typeof value === "number" ? number(value, "#,##0.###") : String(value);
|
|
57
|
+
}
|
|
55
58
|
switch (type.trim()) {
|
|
56
59
|
case "number": {
|
|
57
60
|
const s = style?.trim();
|