oakscriptjs 0.10.0 → 0.11.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.
Files changed (201) hide show
  1. package/dist/cjs/box/index.d.ts.map +1 -1
  2. package/dist/cjs/box/index.js +7 -1
  3. package/dist/cjs/box/index.js.map +2 -2
  4. package/dist/cjs/callsite/index.d.ts +73 -32
  5. package/dist/cjs/callsite/index.d.ts.map +1 -1
  6. package/dist/cjs/callsite/index.js +69 -65
  7. package/dist/cjs/callsite/index.js.map +2 -2
  8. package/dist/cjs/drawing/registry.d.ts +75 -0
  9. package/dist/cjs/drawing/registry.d.ts.map +1 -1
  10. package/dist/cjs/drawing/registry.js +139 -6
  11. package/dist/cjs/drawing/registry.js.map +2 -2
  12. package/dist/cjs/index.d.ts +8 -2
  13. package/dist/cjs/index.d.ts.map +1 -1
  14. package/dist/cjs/index.js +30 -1
  15. package/dist/cjs/index.js.map +2 -2
  16. package/dist/cjs/label/index.d.ts.map +1 -1
  17. package/dist/cjs/label/index.js +3 -0
  18. package/dist/cjs/label/index.js.map +2 -2
  19. package/dist/cjs/line/index.d.ts.map +1 -1
  20. package/dist/cjs/line/index.js +7 -1
  21. package/dist/cjs/line/index.js.map +2 -2
  22. package/dist/cjs/math/index.d.ts.map +1 -1
  23. package/dist/cjs/math/index.js +2 -0
  24. package/dist/cjs/math/index.js.map +2 -2
  25. package/dist/cjs/polyline/index.d.ts.map +1 -1
  26. package/dist/cjs/polyline/index.js +3 -0
  27. package/dist/cjs/polyline/index.js.map +2 -2
  28. package/dist/cjs/runtime/index.d.ts +2 -1
  29. package/dist/cjs/runtime/index.d.ts.map +1 -1
  30. package/dist/cjs/runtime/index.js +2 -0
  31. package/dist/cjs/runtime/index.js.map +2 -2
  32. package/dist/cjs/runtime/input-active.d.ts +27 -0
  33. package/dist/cjs/runtime/input-active.d.ts.map +1 -0
  34. package/dist/cjs/runtime/input-active.js +44 -0
  35. package/dist/cjs/runtime/input-active.js.map +7 -0
  36. package/dist/cjs/runtime/types.d.ts +29 -2
  37. package/dist/cjs/runtime/types.d.ts.map +1 -1
  38. package/dist/cjs/runtime/types.js.map +1 -1
  39. package/dist/cjs/script/index.d.ts +53 -17
  40. package/dist/cjs/script/index.d.ts.map +1 -1
  41. package/dist/cjs/script/index.js +70 -11
  42. package/dist/cjs/script/index.js.map +3 -3
  43. package/dist/cjs/session/bars.d.ts +15 -1
  44. package/dist/cjs/session/bars.d.ts.map +1 -1
  45. package/dist/cjs/session/bars.js +41 -6
  46. package/dist/cjs/session/bars.js.map +3 -3
  47. package/dist/cjs/session/calendar.d.ts +9 -1
  48. package/dist/cjs/session/calendar.d.ts.map +1 -1
  49. package/dist/cjs/session/calendar.js +28 -2
  50. package/dist/cjs/session/calendar.js.map +2 -2
  51. package/dist/cjs/session/context.d.ts +75 -0
  52. package/dist/cjs/session/context.d.ts.map +1 -0
  53. package/dist/cjs/session/context.js +113 -0
  54. package/dist/cjs/session/context.js.map +7 -0
  55. package/dist/cjs/str/index.d.ts +12 -4
  56. package/dist/cjs/str/index.d.ts.map +1 -1
  57. package/dist/cjs/str/index.js +13 -7
  58. package/dist/cjs/str/index.js.map +2 -2
  59. package/dist/cjs/str/numberformat.d.ts +10 -2
  60. package/dist/cjs/str/numberformat.d.ts.map +1 -1
  61. package/dist/cjs/str/numberformat.js +10 -2
  62. package/dist/cjs/str/numberformat.js.map +2 -2
  63. package/dist/cjs/ta/index.d.ts +33 -17
  64. package/dist/cjs/ta/index.d.ts.map +1 -1
  65. package/dist/cjs/ta/index.js +70 -29
  66. package/dist/cjs/ta/index.js.map +2 -2
  67. package/dist/cjs/ta/limits.d.ts +24 -0
  68. package/dist/cjs/ta/limits.d.ts.map +1 -0
  69. package/dist/cjs/ta/limits.js +50 -0
  70. package/dist/cjs/ta/limits.js.map +7 -0
  71. package/dist/cjs/ta/running-sum.d.ts +0 -16
  72. package/dist/cjs/ta/running-sum.d.ts.map +1 -1
  73. package/dist/cjs/ta/running-sum.js +2 -0
  74. package/dist/cjs/ta/running-sum.js.map +2 -2
  75. package/dist/cjs/ta-series.d.ts +7 -7
  76. package/dist/cjs/ta-series.d.ts.map +1 -1
  77. package/dist/cjs/ta-series.js +9 -7
  78. package/dist/cjs/ta-series.js.map +2 -2
  79. package/dist/cjs/time/index.d.ts +5 -2
  80. package/dist/cjs/time/index.d.ts.map +1 -1
  81. package/dist/cjs/time/index.js +2 -2
  82. package/dist/cjs/time/index.js.map +2 -2
  83. package/dist/cjs/time/session.d.ts +17 -1
  84. package/dist/cjs/time/session.d.ts.map +1 -1
  85. package/dist/cjs/time/session.js +61 -7
  86. package/dist/cjs/time/session.js.map +2 -2
  87. package/dist/cjs/types/metadata.d.ts +27 -3
  88. package/dist/cjs/types/metadata.d.ts.map +1 -1
  89. package/dist/cjs/types/metadata.js.map +1 -1
  90. package/dist/esm/box/index.d.ts.map +1 -1
  91. package/dist/esm/box/index.js +8 -2
  92. package/dist/esm/box/index.js.map +1 -1
  93. package/dist/esm/callsite/index.d.ts +73 -32
  94. package/dist/esm/callsite/index.d.ts.map +1 -1
  95. package/dist/esm/callsite/index.js +143 -106
  96. package/dist/esm/callsite/index.js.map +1 -1
  97. package/dist/esm/drawing/registry.d.ts +75 -0
  98. package/dist/esm/drawing/registry.d.ts.map +1 -1
  99. package/dist/esm/drawing/registry.js +237 -6
  100. package/dist/esm/drawing/registry.js.map +1 -1
  101. package/dist/esm/index.d.ts +8 -2
  102. package/dist/esm/index.d.ts.map +1 -1
  103. package/dist/esm/index.js +11 -1
  104. package/dist/esm/index.js.map +1 -1
  105. package/dist/esm/label/index.d.ts.map +1 -1
  106. package/dist/esm/label/index.js +4 -1
  107. package/dist/esm/label/index.js.map +1 -1
  108. package/dist/esm/line/index.d.ts.map +1 -1
  109. package/dist/esm/line/index.js +8 -2
  110. package/dist/esm/line/index.js.map +1 -1
  111. package/dist/esm/math/index.d.ts.map +1 -1
  112. package/dist/esm/math/index.js +3 -0
  113. package/dist/esm/math/index.js.map +1 -1
  114. package/dist/esm/polyline/index.d.ts.map +1 -1
  115. package/dist/esm/polyline/index.js +5 -1
  116. package/dist/esm/polyline/index.js.map +1 -1
  117. package/dist/esm/runtime/index.d.ts +2 -1
  118. package/dist/esm/runtime/index.d.ts.map +1 -1
  119. package/dist/esm/runtime/index.js +1 -0
  120. package/dist/esm/runtime/index.js.map +1 -1
  121. package/dist/esm/runtime/input-active.d.ts +27 -0
  122. package/dist/esm/runtime/input-active.d.ts.map +1 -0
  123. package/dist/esm/runtime/input-active.js +54 -0
  124. package/dist/esm/runtime/input-active.js.map +1 -0
  125. package/dist/esm/runtime/types.d.ts +29 -2
  126. package/dist/esm/runtime/types.d.ts.map +1 -1
  127. package/dist/esm/script/index.d.ts +53 -17
  128. package/dist/esm/script/index.d.ts.map +1 -1
  129. package/dist/esm/script/index.js +80 -13
  130. package/dist/esm/script/index.js.map +1 -1
  131. package/dist/esm/session/bars.d.ts +15 -1
  132. package/dist/esm/session/bars.d.ts.map +1 -1
  133. package/dist/esm/session/bars.js +50 -6
  134. package/dist/esm/session/bars.js.map +1 -1
  135. package/dist/esm/session/calendar.d.ts +9 -1
  136. package/dist/esm/session/calendar.d.ts.map +1 -1
  137. package/dist/esm/session/calendar.js +35 -3
  138. package/dist/esm/session/calendar.js.map +1 -1
  139. package/dist/esm/session/context.d.ts +75 -0
  140. package/dist/esm/session/context.d.ts.map +1 -0
  141. package/dist/esm/session/context.js +122 -0
  142. package/dist/esm/session/context.js.map +1 -0
  143. package/dist/esm/str/index.d.ts +12 -4
  144. package/dist/esm/str/index.d.ts.map +1 -1
  145. package/dist/esm/str/index.js +33 -12
  146. package/dist/esm/str/index.js.map +1 -1
  147. package/dist/esm/str/numberformat.d.ts +10 -2
  148. package/dist/esm/str/numberformat.d.ts.map +1 -1
  149. package/dist/esm/str/numberformat.js +16 -2
  150. package/dist/esm/str/numberformat.js.map +1 -1
  151. package/dist/esm/ta/index.d.ts +33 -17
  152. package/dist/esm/ta/index.d.ts.map +1 -1
  153. package/dist/esm/ta/index.js +130 -56
  154. package/dist/esm/ta/index.js.map +1 -1
  155. package/dist/esm/ta/limits.d.ts +24 -0
  156. package/dist/esm/ta/limits.d.ts.map +1 -0
  157. package/dist/esm/ta/limits.js +42 -0
  158. package/dist/esm/ta/limits.js.map +1 -0
  159. package/dist/esm/ta/running-sum.d.ts +0 -16
  160. package/dist/esm/ta/running-sum.d.ts.map +1 -1
  161. package/dist/esm/ta/running-sum.js +2 -0
  162. package/dist/esm/ta/running-sum.js.map +1 -1
  163. package/dist/esm/ta-series.d.ts +7 -7
  164. package/dist/esm/ta-series.d.ts.map +1 -1
  165. package/dist/esm/ta-series.js +9 -7
  166. package/dist/esm/ta-series.js.map +1 -1
  167. package/dist/esm/time/index.d.ts +5 -2
  168. package/dist/esm/time/index.d.ts.map +1 -1
  169. package/dist/esm/time/index.js +6 -3
  170. package/dist/esm/time/index.js.map +1 -1
  171. package/dist/esm/time/session.d.ts +17 -1
  172. package/dist/esm/time/session.d.ts.map +1 -1
  173. package/dist/esm/time/session.js +97 -10
  174. package/dist/esm/time/session.js.map +1 -1
  175. package/dist/esm/types/metadata.d.ts +27 -3
  176. package/dist/esm/types/metadata.d.ts.map +1 -1
  177. package/package.json +1 -1
  178. package/src/box/index.ts +8 -2
  179. package/src/callsite/index.ts +150 -100
  180. package/src/drawing/registry.ts +255 -5
  181. package/src/index.ts +36 -1
  182. package/src/label/index.ts +4 -1
  183. package/src/line/index.ts +8 -2
  184. package/src/math/index.ts +2 -0
  185. package/src/polyline/index.ts +3 -1
  186. package/src/runtime/index.ts +3 -0
  187. package/src/runtime/input-active.ts +50 -0
  188. package/src/runtime/types.ts +25 -2
  189. package/src/script/index.ts +123 -23
  190. package/src/session/bars.ts +47 -6
  191. package/src/session/calendar.ts +37 -3
  192. package/src/session/context.ts +160 -0
  193. package/src/str/index.ts +30 -12
  194. package/src/str/numberformat.ts +18 -3
  195. package/src/ta/index.ts +148 -66
  196. package/src/ta/limits.ts +47 -0
  197. package/src/ta/running-sum.ts +2 -0
  198. package/src/ta-series.ts +16 -14
  199. package/src/time/index.ts +6 -3
  200. package/src/time/session.ts +89 -9
  201. package/src/types/metadata.ts +27 -3
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/session/context.ts"],
4
+ "sourcesContent": ["/**\n * The session of a chart context, for code that does not run a whole script (a port with its own bar loop).\n *\n * The same values as the script API gives (`time(tf)`, `time_close(tf)`, `time_tradingday`, `session.*`, the periods\n * of `request.security`), from the session, corrections and holidays of the context. Bar times are in the unit of\n * the context (`timeUnit`, seconds by default, as `Bar.time`); PineScript time values are returned in ms.\n *\n * @module session/context\n */\n\nimport type { Bar } from '../types/index.js';\nimport { info, in_seconds } from '../timeframe/index.js';\nimport { afterCompletion, periodsOf, resample } from '../security/resample.js';\nimport { SessionBars, sessionFlags, type SessionFlags } from './bars.js';\nimport { TradingCalendar, type SessionSpec } from './calendar.js';\n\n/** The part of a chart context that the session functions read (a `ChartContext` of the script API fits). */\nexport interface SessionContext {\n /** Chart timeframe: \"5\", \"60\", \"1D\"... */\n timeframe?: string;\n /** Exchange time zone: \"America/New_York\", \"Etc/UTC\"... */\n timezone?: string;\n /** Session of the loaded bars: hours, corrections, holidays */\n session?: SessionSpec | string;\n /** Regular trading hours when the bars include extended hours (default: `session`) */\n regularSession?: SessionSpec | string;\n /** Unit of the bar times: 's' (default) or 'ms' */\n timeUnit?: 's' | 'ms';\n}\n\n/** The session calendars of a context: the loaded bars and the regular hours. */\nexport interface ChartSessions {\n bars: SessionBars;\n regular: SessionBars;\n /** ms per unit of the bar times */\n unit: number;\n}\n\nconst toSpec = (s: SessionSpec | string): SessionSpec => (typeof s === 'string' ? { session: s } : s);\nconst cache = new WeakMap<object, ChartSessions>();\n\n/**\n * Session calendars of a chart context (cached per context object).\n * @throws Error when the context has no timeframe, time zone or session\n */\nexport function chartSessions(chart: SessionContext): ChartSessions {\n const cached = cache.get(chart);\n if (cached) return cached;\n const { timeframe, timezone, session } = chart;\n if (!timeframe || !timezone || !session) {\n throw new Error('The chart context needs timeframe, timezone and session for the session functions.');\n }\n const loaded = toSpec(session);\n const regular = chart.regularSession ? toSpec(chart.regularSession) : loaded;\n const out: ChartSessions = {\n bars: new SessionBars(new TradingCalendar(timezone, loaded), timeframe),\n regular: new SessionBars(new TradingCalendar(timezone, { holidays: loaded.holidays, ...regular }), timeframe),\n unit: chart.timeUnit === 'ms' ? 1 : 1000,\n };\n cache.set(chart, out);\n return out;\n}\n\n/** PineScript `time(timeframe)` (ms) of a bar that opens at `time`: the start of its period; NaN without one. */\nexport function periodTime(time: number, timeframe: string, chart: SessionContext): number {\n const s = chartSessions(chart);\n return s.bars.timeOf(time * s.unit, timeframe);\n}\n\n/** PineScript `time_close(timeframe)` (ms) of a bar that opens at `time`. */\nexport function periodClose(time: number, timeframe: string, chart: SessionContext): number {\n const s = chartSessions(chart);\n return s.bars.closeOf(time * s.unit, timeframe);\n}\n\n/** PineScript `time_tradingday` (ms, 00:00 UTC of the trading day) of a bar that opens at `time`. */\nexport function tradingDayTime(time: number, chart: SessionContext): number {\n const s = chartSessions(chart);\n return s.bars.tradingDayTime(time * s.unit);\n}\n\n/** PineScript `session.*` flags of bars that open at `times` (in order). */\nexport function chartSessionFlags(times: ArrayLike<number>, chart: SessionContext): SessionFlags {\n const s = chartSessions(chart);\n return sessionFlags(Array.from(times, (t) => t * s.unit), s.bars, s.regular);\n}\n\n/** Higher-timeframe bars of the chart bars and what `mapToChart` needs to bring values back to the chart. */\nexport interface SessionResample {\n /** Bars of `timeframe`, built from the chart bars */\n bars: Bar[];\n /** Period index of each chart bar */\n group: number[];\n /** Latest completed period at each chart bar */\n latest: number[];\n}\n\n/**\n * Same-symbol `request.security` on a timeframe equal to or higher than the chart's, with the session of the context:\n * D / W / M periods follow the regular hours, corrections and holidays; intraday periods the loaded session.\n * Compute on `bars`, then bring each value series back with `mapToChart(values, group, latest, lookahead, gaps)`.\n * @throws Error when `timeframe` is lower than the chart timeframe\n */\nexport function resampleSession(bars: Bar[], timeframe: string, chart: SessionContext): SessionResample {\n const s = chartSessions(chart);\n const chartTf = s.bars.chart;\n const tf = info(timeframe || chartTf.period);\n if (tf.period === chartTf.period) {\n const group = bars.map((_, i) => i);\n return { bars, group, latest: group };\n }\n if (in_seconds(tf.period) < in_seconds(chartTf.period)) {\n throw new Error(`resampleSession: timeframe \"${tf.period}\" is lower than the chart timeframe \"${chartTf.period}\".`);\n }\n const times = bars.map((b) => (b.time as number) * s.unit);\n const calendar = tf.isdwm ? s.regular : s.bars;\n const starts = times.map((t) => calendar.timeOf(t, tf.period));\n const dayPeriods = times.map((t, i) => {\n const at = s.bars.calendar.locate(t);\n const periods = at ? calendar.calendar.periods(at.date) : [];\n return periods.length ? calendar.timeOf(periods[0]![0], tf.period) : starts[i]!;\n });\n const reaches = times.map((t) => s.bars.closeOf(t, chartTf.period) >= calendar.periodEnd(t, tf.period));\n const { group, latest } = periodsOf(starts, dayPeriods, reaches);\n // a higher-timeframe bar ends at the bar that completes it (no value from after the completion)\n const after = afterCompletion(group, latest);\n const kept = (_: unknown, i: number): boolean => !after[i];\n return { bars: resample(bars.filter(kept), starts.map((x) => x / s.unit).filter(kept)).bars, group, latest };\n}\n\n/**\n * Open time of the `count`-th bar after the bar that opens at `time`, on the trading calendar of the context: the\n * closed hours, weekends, holidays and days off are skipped. `count` 0 is the bar itself. For a drawing that ends\n * `count` bars after the last bar (PineScript `bar_index + count`).\n */\nexport function barTimeAfter(time: number, count: number, chart: SessionContext): number {\n const s = chartSessions(chart);\n let t = time * s.unit;\n for (let k = 0; k < count && !Number.isNaN(t); k++) t = s.bars.nextBarTime(t);\n return t / s.unit;\n}\n\n/**\n * Number of bars from the bar that opens at `from` to the bar that contains `to`, on the trading calendar of the\n * context (0 when `to` is in the bar of `from` or before it). For a drawing placed at a time after the last bar\n * (PineScript `time + n days` with `xloc.bar_time`): the chart shows it on the bar of that time, weekends and\n * holidays skipped. NaN when no bar is found within 100,000 bars.\n */\nexport function barsUntil(from: number, to: number, chart: SessionContext): number {\n const s = chartSessions(chart);\n const target = to * s.unit;\n let t = from * s.unit;\n for (let k = 0; k < 100_000; k++) {\n const next = s.bars.nextBarTime(t);\n if (Number.isNaN(next)) return NaN;\n if (next > target) return k;\n t = next;\n }\n return NaN;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["group"]
7
+ }
@@ -31,11 +31,15 @@ export declare function length(str: simple_string): int;
31
31
  * `format.price` / `format.inherit`
32
32
  * @param mintick - Tick size for `format.mintick` (PineScript `syminfo.mintick`, implicit there). Not part of the
33
33
  * PineScript signature: the core function cannot see the chart, so pass it, as for `math.round_to_mintick`
34
+ * @param pricescale - Price scale of the symbol for `format.mintick` (PineScript `syminfo.pricescale`), needed for a
35
+ * symbol priced in fractions (32 for a 1/32 tick). Default: 10^(decimals of `mintick`)
34
36
  * @returns String representation of the value
35
37
  *
36
38
  * @remarks
37
39
  * As in PineScript:
38
- * - without format, numbers show up to 10 decimals ("0.3333333333"); 1e21 and more use "1E21"
40
+ * - without format, numbers show up to 10 decimals ("0.3333333333"), rounded on the exact value of the double
41
+ * (82325.10000000005 gives "82325.1"; with the format "#.##########" it gives "82325.1000000001"); 1e21 and more
42
+ * use "1E21"
39
43
  * - patterns round half away from zero on the shortest decimal form: 1.005 with "#.##" gives "1.01";
40
44
  * at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives "0.0012345678901235")
41
45
  * - "pos;neg" patterns: a negative number takes the text of the negative subpattern ("#.##;(#.##)": "(3.5)")
@@ -44,8 +48,12 @@ export declare function length(str: simple_string): int;
44
48
  * - `format.percent` shows 2 decimals and "%" without multiplying; `format.volume` uses K, M, B, T
45
49
  * - `format.price` and `format.inherit` give "price1" / "inherit1" in PineScript: they are patterns
46
50
  * without digit characters, and oakscriptjs does the same
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`
51
+ * - `format.mintick` shows floor(log10(pricescale)) decimals, zeros included ("1.00", "0.40" for tick 0.01), and
52
+ * rounds in two steps, with mintick = minmove / pricescale: the double quotient `|x| / (1 / pricescale)` half away
53
+ * from zero (140.355 / 0.01 = 14035.499999999998 gives "140.35", where `math.round_to_mintick` gives 140.36),
54
+ * then `floor(units / minmove + 0.5)` ticks (tick 0.02: 0.005 gives "0.02", -0.01 gives "0.00"). With a
55
+ * pricescale that is no power of 10 the tick value is then rounded half even to those decimals (1/32: 25.25 gives
56
+ * "25.2", -0.02 gives "-0.0"). It throws without `mintick`
49
57
  * - na gives "NaN" ("NaN%" with `format.percent`); an infinity gives "NaN" too ("NaN%" for `format.percent`,
50
58
  * "NaNT" for `format.volume`), except `format.mintick`, which gives "Infinity" / "-Infinity"
51
59
  *
@@ -57,7 +65,7 @@ export declare function length(str: simple_string): int;
57
65
  * str.tostring(12345.678, format.volume) // "12.346K"
58
66
  * ```
59
67
  */
60
- export declare function tostring(value: any, format?: simple_string, mintick?: float): string;
68
+ export declare function tostring(value: any, format?: simple_string, mintick?: float, pricescale?: float): string;
61
69
  /**
62
70
  * Converts a string to a number (float).
63
71
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/str/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/str/index.ts"],"names":[],"mappings":"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"}
@@ -45,11 +45,11 @@ 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");
49
48
  function length(str) {
50
49
  return str.length;
51
50
  }
52
- function tostring(value, format2, mintick) {
51
+ function tostring(value, format2, mintick, pricescale) {
52
+ if (Array.isArray(value)) return "[" + value.map((v) => tostring(v, format2, mintick, pricescale)).join(", ") + "]";
53
53
  if (typeof value !== "number") return String(value);
54
54
  if (Number.isNaN(value)) return format2 === "percent" ? "NaN%" : "NaN";
55
55
  if (!Number.isFinite(value)) {
@@ -60,7 +60,7 @@ function tostring(value, format2, mintick) {
60
60
  if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
61
61
  return String(value).replace("e+", "E").replace(/\.0+E/, "E");
62
62
  }
63
- return (0, import_numberformat.formatNumber)(value, "#.##########", "halfUpShortest");
63
+ return (0, import_numberformat.formatNumber)(value, "#.##########", "halfUpExact");
64
64
  }
65
65
  switch (format2) {
66
66
  case "percent":
@@ -76,10 +76,16 @@ function tostring(value, format2, mintick) {
76
76
  if (mintick === void 0) {
77
77
  throw new Error("str.tostring(x, format.mintick) needs the tick size: pass it as str.tostring(x, format.mintick, syminfo.mintick)");
78
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");
79
+ const [minmove, scale] = (0, import_numberformat.tickScale)(mintick, pricescale);
80
+ const decimals = Math.floor(Math.log10(scale) + 1e-9);
81
+ const mask = decimals > 0 ? "0." + "0".repeat(decimals) : "0";
82
+ if (!(minmove >= 1)) return (0, import_numberformat.formatNumber)(value, mask, "halfUpShortest");
83
+ const scaled = Math.abs(value) / (1 / scale);
84
+ let units = Math.trunc(scaled);
85
+ if (scaled - units >= 0.5) units++;
86
+ const ticks = Math.floor((value < 0 ? -units : units) / minmove + 0.5);
87
+ const text = (0, import_numberformat.formatNumber)(Math.abs(ticks) * minmove / scale, mask, "halfEvenExact");
88
+ return ticks < 0 ? "-" + text : text;
83
89
  }
84
90
  default:
85
91
  return (0, import_numberformat.formatNumber)(value, format2, "halfUpShortest");
@@ -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 { 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": ";;;;;;;;;;;;;;;;;;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;AAC3B,kBAAiC;AAe1B,SAAS,OAAO,KAAyB;AAC9C,SAAO,IAAI;AACb;AAqCO,SAAS,SAAS,OAAYA,SAAwB,SAAyB;AACpF,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAClD,MAAI,OAAO,MAAM,KAAK,EAAG,QAAOA,YAAW,YAAY,SAAS;AAChE,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAE3B,QAAIA,YAAW,UAAW,QAAO,OAAO,KAAK;AAC7C,WAAOA,YAAW,YAAY,SAASA,YAAW,WAAW,SAAS;AAAA,EACxE;AACA,MAAIA,YAAW,UAAaA,YAAW,IAAI;AACzC,QAAI,KAAK,IAAI,KAAK,KAAK,QAAQ,OAAO,SAAS,KAAK,GAAG;AACrD,aAAO,OAAO,KAAK,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,SAAS,GAAG;AAAA,IAC9D;AACA,eAAO,kCAAa,OAAO,gBAAgB,gBAAgB;AAAA,EAC7D;AACA,UAAQA,SAAQ;AAAA,IACd,KAAK;AACH,iBAAO,kCAAa,OAAO,QAAQ,gBAAgB,IAAI;AAAA,IACzD,KAAK,UAAU;AACb,YAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,iBAAW,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAY;AACrF,YAAI,OAAO,KAAM,YAAO,kCAAa,QAAQ,MAAM,SAAS,gBAAgB,IAAI;AAAA,MAClF;AACA,iBAAO,kCAAa,OAAO,KAAK,gBAAgB;AAAA,IAClD;AAAA,IACA,KAAK,WAAW;AACd,UAAI,YAAY,QAAW;AACzB,cAAM,IAAI,MAAM,kHAAkH;AAAA,MACpI;AAEA,YAAM,IAAI,gCAAgC,KAAK,OAAO,OAAO,CAAC;AAC9D,YAAM,WAAW,KAAK,EAAE,CAAC,GAAG,UAAU,KAAK,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI;AAC/D,YAAM,OAAO,WAAW,OAAO,IAAI,OAAO,QAAQ,IAAI;AACtD,iBAAO,sCAAa,8BAAiB,OAAO,OAAO,GAAG,MAAM,gBAAgB;AAAA,IAC9E;AAAA,IACA;AACE,iBAAO,kCAAa,OAAOA,SAAQ,gBAAgB;AAAA,EACvD;AACF;AAwBO,SAAS,SAAS,KAAkC;AAEzD,MAAI,QAAQ;AACZ,MAAI,MAAM,IAAI;AACd,SAAO,QAAQ,OAAO,IAAI,WAAW,KAAK,KAAK,GAAM;AACrD,SAAO,MAAM,SAAS,IAAI,WAAW,MAAM,CAAC,KAAK,GAAM;AACvD,QAAM,OAAO,IAAI,MAAM,OAAO,GAAG;AACjC,MAAI,CAAC,6BAA6B,KAAK,IAAI,EAAG,QAAO;AACrD,SAAO,WAAW,IAAI;AACxB;AAuBO,SAAS,UAAU,KAAoB,OAAmB,KAA0B;AACzF,MAAI,QAAQ,QAAQ,OAAO,MAAM,GAAG,EAAG,OAAM,IAAI,MAAM,uCAAuC;AAC9F,SAAO,IAAI,UAAU,OAAO,GAAG;AACjC;AAeO,SAAS,MAAM,KAA4B;AAChD,SAAO,IAAI,YAAY;AACzB;AAeO,SAAS,MAAM,KAA4B;AAChD,SAAO,IAAI,YAAY;AACzB;AAoBO,SAAS,SAAS,QAAuB,KAA0B;AACxE,SAAO,OAAO,SAAS,GAAG;AAC5B;AAqBO,SAAS,IAAI,QAAuB,KAAyB;AAClE,QAAM,QAAQ,OAAO,QAAQ,GAAG;AAChC,SAAO,UAAU,KAAK,MAAM;AAC9B;AA0BO,SAAS,QAAQ,QAAuB,QAAuB,aAA4B,aAAyB,GAAW;AACpI,QAAM,IAAI,eAAe,QAAQ,OAAO,MAAM,UAAU,IAAI,IAAI,KAAK,MAAM,UAAU;AACrF,MAAI,IAAI,EAAG,QAAO;AAClB,MAAI;AACJ,MAAI,WAAW,IAAI;AACjB,YAAQ,KAAK,IAAI,GAAG,OAAO,MAAM;AAAA,EACnC,OAAO;AACL,YAAQ;AACR,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,cAAQ,OAAO,QAAQ,QAAQ,QAAQ,CAAC;AACxC,UAAI,UAAU,GAAI,QAAO;AAAA,IAC3B;AAAA,EACF;AACA,SAAO,OAAO,MAAM,GAAG,KAAK,IAAI,cAAc,OAAO,MAAM,QAAQ,OAAO,MAAM;AAClF;AAuBO,SAAS,YAAY,QAAuB,QAAuB,aAAoC;AAC5G,SAAO,OAAO,WAAW,QAAQ,WAAW;AAC9C;AAqBO,SAAS,MAAM,KAAoB,WAAoC;AAE5E,SAAO,QAAQ,KAAK,CAAC,EAAE,IAAI,IAAI,MAAM,SAAS;AAChD;AAeO,SAAS,UAAU,SAAkC;AAC1D,SAAO,QAAQ,KAAK,EAAE;AACxB;AAsBO,SAAS,OAAO,cAA6B,MAAqB;AACvE,aAAO,oCAAc,WAAW,MAAM,SAAS;AACjD;AAoBO,SAAS,WAAW,QAAuB,KAA0B;AAC1E,SAAO,OAAO,WAAW,GAAG;AAC9B;AAoBO,SAAS,SAAS,QAAuB,KAA0B;AACxE,SAAO,OAAO,SAAS,GAAG;AAC5B;AAoBO,SAAS,OAAO,KAAoBC,MAAyB;AAClE,SAAO,IAAI,OAAOA,IAAG;AACvB;AAmBO,SAAS,KAAK,KAA4B;AAC/C,SAAO,IAAI,KAAK;AAClB;AAmBO,SAAS,SAAS,KAA4B;AACnD,SAAO,IAAI,UAAU;AACvB;AAmBO,SAAS,UAAU,KAA4B;AACpD,SAAO,IAAI,QAAQ;AACrB;AAmBO,SAAS,MAAM,QAAuB,OAA4C;AACvF,QAAM,QAAQ,IAAI,OAAO,KAAK,EAAE,KAAK,MAAM;AAC3C,SAAO,SAAS,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI;AAC/C;AAsDO,SAAS,OAAO,QAAuB,OAAmB,YAA2B,IAAmB;AAC7G,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,EAAE,QAAQ,GAAI,QAAO;AACzB,SAAO,MAAM,KAAK,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS;AACjD;AAeO,SAAS,YAAY,MAAkBD,UAAwB,0BAA0B,WAA0B,WAAmB;AAC3I,aAAO,8BAAW,MAAMA,SAAQ,QAAQ;AAC1C;",
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, tickScale } 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.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 * @param pricescale - Price scale of the symbol for `format.mintick` (PineScript `syminfo.pricescale`), needed for a\n * symbol priced in fractions (32 for a 1/32 tick). Default: 10^(decimals of `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\"), rounded on the exact value of the double\n * (82325.10000000005 gives \"82325.1\"; with the format \"#.##########\" it gives \"82325.1000000001\"); 1e21 and more\n * 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` shows floor(log10(pricescale)) decimals, zeros included (\"1.00\", \"0.40\" for tick 0.01), and\n * rounds in two steps, with mintick = minmove / pricescale: the double quotient `|x| / (1 / pricescale)` half away\n * from zero (140.355 / 0.01 = 14035.499999999998 gives \"140.35\", where `math.round_to_mintick` gives 140.36),\n * then `floor(units / minmove + 0.5)` ticks (tick 0.02: 0.005 gives \"0.02\", -0.01 gives \"0.00\"). With a\n * pricescale that is no power of 10 the tick value is then rounded half even to those decimals (1/32: 25.25 gives\n * \"25.2\", -0.02 gives \"-0.0\"). 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, pricescale?: float): string {\n // an array: \"[a, b]\", each element as a single value\n if (Array.isArray(value)) return '[' + value.map((v) => tostring(v, format, mintick, pricescale)).join(', ') + ']';\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 // without a format the exact value is rounded (82325.10000000005 is 82325.10000000004947...: \"82325.1\")\n return formatNumber(value, '#.##########', 'halfUpExact');\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 const [minmove, scale] = tickScale(mintick, pricescale);\n const decimals = Math.floor(Math.log10(scale) + 1e-9);\n const mask = decimals > 0 ? '0.' + '0'.repeat(decimals) : '0';\n if (!(minmove >= 1)) return formatNumber(value, mask, 'halfUpShortest');\n // 1. the double quotient |x| / (1 / pricescale), half away from zero (not math.round_to_mintick's exact decimal)\n const scaled = Math.abs(value) / (1 / scale);\n let units = Math.trunc(scaled);\n if (scaled - units >= 0.5) units++;\n // 2. to the tick, a half going up (minmove 2: -0.01 gives \"0.00\", 0.01 gives \"0.02\")\n const ticks = Math.floor((value < 0 ? -units : units) / minmove + 0.5);\n // 3. the tick value at the scale's decimals; a negative one keeps its sign when it prints as zero (\"-0.0\")\n const text = formatNumber((Math.abs(ticks) * minmove) / scale, mask, 'halfEvenExact');\n return ticks < 0 ? '-' + text : text;\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
  }
@@ -4,8 +4,10 @@
4
4
  * The pattern syntax is Java's DecimalFormat: `0` required digit, `#` optional digit, `,` grouping,
5
5
  * `.` decimal point, `E0` exponent, `%` multiplies by 100, `'...'` quoted literal text.
6
6
  * PineScript rules:
7
- * - `str.tostring` rounds half away from zero on the shortest decimal form of the number
7
+ * - `str.tostring` with a format rounds half away from zero on the shortest decimal form of the number
8
8
  * (`1.005` with "#.##" gives "1.01", `2.5` with "#" gives "3")
9
+ * - `str.tostring` without a format rounds the exact binary value to 10 decimals (12.34500000005 is
10
+ * 12.34500000004999975...: "12.345", and "12.3450000001" with the format "#.##########")
9
11
  * - `str.format` rounds half to even on the exact binary value (`0.25` with "#.#" gives "0.2")
10
12
  * - a negative number that rounds to zero loses its sign; "#.00" writes `.50` (no leading zero),
11
13
  * but "#.##" writes `0.5` (a pattern without any '0' shows one integer digit)
@@ -17,7 +19,13 @@
17
19
  * - at most 16 decimals when |x| >= 1e-3 (the shortest digits of a double never have more there):
18
20
  * 0.1 with 20 '#' decimals gives "0.1" in both rounding modes
19
21
  */
20
- export type Rounding = 'halfUpShortest' | 'halfEvenExact';
22
+ export type Rounding = 'halfUpShortest' | 'halfUpExact' | 'halfEvenExact';
21
23
  /** Formats a number with a DecimalFormat pattern. */
22
24
  export declare function formatNumber(value: number, pattern: string, mode: Rounding): string;
25
+ /**
26
+ * `[minmove, pricescale]` of a tick (PineScript `syminfo.mintick = syminfo.minmove / syminfo.pricescale`).
27
+ * Without `pricescale` it is 10^(decimals of the tick): 0.02 is 2 / 100, 0.25 is 25 / 100. A symbol priced in
28
+ * fractions needs its own (1/32 is 1 / 32).
29
+ */
30
+ export declare function tickScale(mintick: number, pricescale?: number): [number, number];
23
31
  //# sourceMappingURL=numberformat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"numberformat.d.ts","sourceRoot":"","sources":["../../../src/str/numberformat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,QAAQ,GAAG,gBAAgB,GAAG,eAAe,CAAC;AA8K1D,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CA6BnF"}
1
+ {"version":3,"file":"numberformat.d.ts","sourceRoot":"","sources":["../../../src/str/numberformat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,QAAQ,GAAG,gBAAgB,GAAG,aAAa,GAAG,eAAe,CAAC;AA8K1E,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CA6BnF;AASD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAMhF"}
@@ -18,7 +18,8 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var numberformat_exports = {};
20
20
  __export(numberformat_exports, {
21
- formatNumber: () => formatNumber
21
+ formatNumber: () => formatNumber,
22
+ tickScale: () => tickScale
22
23
  });
23
24
  module.exports = __toCommonJS(numberformat_exports);
24
25
  const patterns = /* @__PURE__ */ new Map();
@@ -128,7 +129,7 @@ function roundDigits(d, frac, mode) {
128
129
  if (keep < 0) return { digits: "", intLen: 0 };
129
130
  const first = d.digits.charCodeAt(keep) - 48;
130
131
  let up;
131
- if (mode === "halfUpShortest") up = first >= 5;
132
+ if (mode !== "halfEvenExact") up = first >= 5;
132
133
  else if (first !== 5) up = first > 5;
133
134
  else if (d.digits.length > keep + 1) up = true;
134
135
  else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
@@ -189,4 +190,11 @@ function affixes(p, negative, body) {
189
190
  if (p.negPrefix === null) return "-" + p.prefix + body + p.suffix;
190
191
  return p.negPrefix + body + p.negSuffix;
191
192
  }
193
+ function tickScale(mintick, pricescale) {
194
+ if (pricescale === void 0) {
195
+ const m = /^\d+(?:\.(\d+))?(?:e-(\d+))?$/.exec(String(mintick));
196
+ pricescale = Number(`1e${m ? (m[1]?.length ?? 0) + Number(m[2] ?? 0) : 0}`);
197
+ }
198
+ return [Math.round(mintick * pricescale), pricescale];
199
+ }
192
200
  //# sourceMappingURL=numberformat.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/str/numberformat.ts"],
4
- "sourcesContent": ["/**\n * Number patterns (\"#.##\", \"#,##0.00\", \"0.00E0\", \"#.##%\", \"'#'#.##\") as PineScript formats them.\n *\n * The pattern syntax is Java's DecimalFormat: `0` required digit, `#` optional digit, `,` grouping,\n * `.` decimal point, `E0` exponent, `%` multiplies by 100, `'...'` quoted literal text.\n * PineScript rules:\n * - `str.tostring` rounds half away from zero on the shortest decimal form of the number\n * (`1.005` with \"#.##\" gives \"1.01\", `2.5` with \"#\" gives \"3\")\n * - `str.format` rounds half to even on the exact binary value (`0.25` with \"#.#\" gives \"0.2\")\n * - a negative number that rounds to zero loses its sign; \"#.00\" writes `.50` (no leading zero),\n * but \"#.##\" writes `0.5` (a pattern without any '0' shows one integer digit)\n * - with an exponent, the value is first rounded to the fraction digits (\"0.00E0\": 0.125 gives \"1.30E-1\")\n * - a pattern without digit characters (`format.price` is \"price\") formats like \"#\" after its text,\n * and keeps the sign: -0.004 gives \"-price0\"\n * - \"pos;neg\": a negative number uses the text around the digits of the negative subpattern instead of \"-\"\n * (\"#.##;(#.##)\" gives \"(3.5)\" for -3.5); the digits follow the positive subpattern, as in Java\n * - at most 16 decimals when |x| >= 1e-3 (the shortest digits of a double never have more there):\n * 0.1 with 20 '#' decimals gives \"0.1\" in both rounding modes\n */\n\nexport type Rounding = 'halfUpShortest' | 'halfEvenExact';\n\ninterface Pattern {\n prefix: string;\n suffix: string;\n /** Text around the digits of the negative subpattern (\"pos;neg\"), or null without one */\n negPrefix: string | null;\n negSuffix: string | null;\n minInt: number;\n minFrac: number;\n maxFrac: number;\n /** Digits per group, 0 without grouping */\n grouping: number;\n percent: boolean;\n /** Minimum exponent digits, or null without exponent */\n exponent: number | null;\n hasDigits: boolean;\n}\n\nconst patterns = new Map<string, Pattern>();\n\n/** Index of the first ';' outside quotes, or -1. */\nfunction subpatternSplit(pattern: string): number {\n let quoted = false;\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === \"'\") quoted = !quoted;\n else if (pattern[i] === ';' && !quoted) return i;\n }\n return -1;\n}\n\nfunction parsePattern(pattern: string): Pattern {\n const cached = patterns.get(pattern);\n if (cached) return cached;\n const semi = subpatternSplit(pattern);\n if (semi !== -1) {\n // only the text around the digits of the negative subpattern is used\n const neg = parsePattern(pattern.slice(semi + 1));\n const parsed = { ...parsePattern(pattern.slice(0, semi)), negPrefix: neg.prefix, negSuffix: neg.suffix };\n patterns.set(pattern, parsed);\n return parsed;\n }\n\n let prefix = '';\n let suffix = '';\n let number = '';\n let percent = false;\n let phase: 'prefix' | 'number' | 'suffix' = 'prefix';\n for (let i = 0; i < pattern.length; i++) {\n const ch = pattern[i]!;\n let literal: string;\n if (ch === \"'\") {\n const end = pattern.indexOf(\"'\", i + 1);\n if (end === i + 1) {\n literal = \"'\";\n i = end;\n } else {\n literal = pattern.slice(i + 1, end === -1 ? undefined : end);\n i = end === -1 ? pattern.length : end;\n }\n } else if (phase !== 'suffix' && ('#0,.'.includes(ch) || (phase === 'number' && ch === 'E'))) {\n phase = 'number';\n number += ch;\n continue;\n } else {\n if (ch === '%') percent = true;\n literal = ch;\n }\n if (phase === 'number') phase = 'suffix';\n if (phase === 'prefix') prefix += literal;\n else suffix += literal;\n }\n\n const [mantissa = '', exp] = number.split('E');\n const [intPart = '', fracPart = ''] = mantissa.split('.');\n const lastComma = intPart.lastIndexOf(',');\n const parsed: Pattern = {\n prefix,\n suffix,\n negPrefix: null,\n negSuffix: null,\n // as in Java, a pattern with a decimal point and no '0' at all (\"#.##\") shows one integer digit\n minInt: !mantissa.includes('0') && mantissa.includes('.') ? 1 : [...intPart].filter((c) => c === '0').length,\n minFrac: [...fracPart].filter((c) => c === '0').length,\n maxFrac: [...fracPart].filter((c) => c === '0' || c === '#').length,\n grouping: lastComma === -1 ? 0 : intPart.length - lastComma - 1,\n percent,\n exponent: exp === undefined ? null : exp.length,\n hasDigits: /[0#]/.test(mantissa),\n };\n patterns.set(pattern, parsed);\n return parsed;\n}\n\n/** Decimal digits of |x|: value = 0.d1d2d3... \u00D7 10^intLen (no leading zero digit; \"\" for 0). */\ninterface Digits {\n digits: string;\n intLen: number;\n}\n\nfunction shortestDigits(x: number): Digits {\n const m = /^(\\d+)(?:\\.(\\d+))?(?:e([+-]\\d+))?$/.exec(String(Math.abs(x)))!;\n const all = m[1]! + (m[2] ?? '');\n let intLen = m[1]!.length + Number(m[3] ?? 0);\n const lead = all.length - all.replace(/^0+/, '').length;\n intLen -= lead;\n const digits = all.slice(lead).replace(/0+$/, '');\n return { digits, intLen: digits ? intLen : 0 };\n}\n\n/** Exact decimal expansion of the double |x|. */\nfunction exactDigits(x: number): Digits {\n const abs = Math.abs(x);\n if (abs === 0) return { digits: '', intLen: 0 };\n const view = new DataView(new ArrayBuffer(8));\n view.setFloat64(0, abs);\n const bits = view.getBigUint64(0);\n const expBits = Number((bits >> 52n) & 0x7ffn);\n let mant = bits & 0xfffffffffffffn;\n const e2 = expBits === 0 ? -1074 : expBits - 1075;\n if (expBits !== 0) mant |= 1n << 52n;\n let str: string;\n let fracDigits: number;\n if (e2 >= 0) {\n str = (mant << BigInt(e2)).toString();\n fracDigits = 0;\n } else {\n str = (mant * 5n ** BigInt(-e2)).toString();\n fracDigits = -e2;\n }\n const intLen = str.length - fracDigits;\n const lead = str.length - str.replace(/^0+/, '').length;\n const digits = str.slice(lead).replace(/0+$/, '');\n return { digits, intLen: intLen - lead };\n}\n\n/** Rounds to `frac` digits after the point. */\nfunction roundDigits(d: Digits, frac: number, mode: Rounding): Digits {\n const keep = d.intLen + frac;\n if (keep >= d.digits.length) return d;\n if (keep < 0) return { digits: '', intLen: 0 };\n const first = d.digits.charCodeAt(keep) - 48;\n let up: boolean;\n if (mode === 'halfUpShortest') up = first >= 5;\n else if (first !== 5) up = first > 5;\n else if (d.digits.length > keep + 1) up = true;\n else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;\n let kept = d.digits.slice(0, keep);\n let intLen = d.intLen;\n if (up) {\n const n = (BigInt(kept || '0') + 1n).toString();\n if (n.length > kept.length) intLen += 1;\n kept = n;\n }\n kept = kept.replace(/0+$/, '');\n return { digits: kept, intLen: kept ? intLen : 0 };\n}\n\nfunction group(int: string, size: number): string {\n if (!size || int.length <= size) return int;\n const parts: string[] = [];\n for (let end = int.length; end > 0; end -= size) parts.unshift(int.slice(Math.max(0, end - size), end));\n return parts.join(',');\n}\n\nfunction layout(d: Digits, p: Pattern): string {\n let int = d.intLen > 0 ? d.digits.slice(0, d.intLen).padEnd(d.intLen, '0') : '';\n int = int.replace(/^0+/, '').padStart(p.minInt, '0');\n let frac = d.intLen < 0 ? '0'.repeat(-d.intLen) + d.digits : d.digits.slice(Math.max(0, d.intLen));\n frac = frac.slice(0, p.maxFrac).replace(/0+$/, '').padEnd(p.minFrac, '0');\n if (!int && !frac) int = '0';\n return group(int, p.grouping) + (frac ? '.' + frac : '');\n}\n\n/** Formats a number with a DecimalFormat pattern. */\nexport function formatNumber(value: number, pattern: string, mode: Rounding): string {\n if (Number.isNaN(value)) return 'NaN';\n const p = parsePattern(pattern);\n if (!Number.isFinite(value)) return affixes(p, value < 0, '\u221E');\n const x = p.percent ? value * 100 : value;\n const toDigits = mode === 'halfUpShortest' ? shortestDigits : exactDigits;\n\n let body: string;\n let isZero: boolean;\n if (p.exponent !== null) {\n const rounded = roundDigits(toDigits(x), p.maxFrac, mode);\n const intDigits = Math.max(p.minInt, 1);\n isZero = rounded.digits === '';\n let exponent = isZero ? 0 : rounded.intLen - intDigits;\n let mant = roundDigits({ digits: rounded.digits, intLen: isZero ? 0 : intDigits }, p.maxFrac, mode);\n if (mant.intLen > intDigits) {\n exponent += 1;\n mant = { digits: mant.digits, intLen: intDigits };\n }\n const e = String(Math.abs(exponent)).padStart(p.exponent, '0');\n body = layout(mant, p) + 'E' + (exponent < 0 ? '-' : '') + e;\n } else {\n // 16 decimals at most from 1e-3 up (the shortest digits of a double have no more there)\n const frac = !p.hasDigits ? 0 : Math.abs(x) >= 1e-3 ? Math.min(p.maxFrac, 16) : p.maxFrac;\n const rounded = roundDigits(toDigits(x), frac, mode);\n isZero = rounded.digits === '';\n body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });\n }\n return affixes(p, x < 0 && (!isZero || !p.hasDigits), body);\n}\n\n/** `body` with the text of the positive pattern, or of the negative one (\"-\" + prefix without \"pos;neg\"). */\nfunction affixes(p: Pattern, negative: boolean, body: string): string {\n if (!negative) return p.prefix + body + p.suffix;\n if (p.negPrefix === null) return '-' + p.prefix + body + p.suffix;\n return p.negPrefix + body + p.negSuffix!;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Number patterns (\"#.##\", \"#,##0.00\", \"0.00E0\", \"#.##%\", \"'#'#.##\") as PineScript formats them.\n *\n * The pattern syntax is Java's DecimalFormat: `0` required digit, `#` optional digit, `,` grouping,\n * `.` decimal point, `E0` exponent, `%` multiplies by 100, `'...'` quoted literal text.\n * PineScript rules:\n * - `str.tostring` with a format rounds half away from zero on the shortest decimal form of the number\n * (`1.005` with \"#.##\" gives \"1.01\", `2.5` with \"#\" gives \"3\")\n * - `str.tostring` without a format rounds the exact binary value to 10 decimals (12.34500000005 is\n * 12.34500000004999975...: \"12.345\", and \"12.3450000001\" with the format \"#.##########\")\n * - `str.format` rounds half to even on the exact binary value (`0.25` with \"#.#\" gives \"0.2\")\n * - a negative number that rounds to zero loses its sign; \"#.00\" writes `.50` (no leading zero),\n * but \"#.##\" writes `0.5` (a pattern without any '0' shows one integer digit)\n * - with an exponent, the value is first rounded to the fraction digits (\"0.00E0\": 0.125 gives \"1.30E-1\")\n * - a pattern without digit characters (`format.price` is \"price\") formats like \"#\" after its text,\n * and keeps the sign: -0.004 gives \"-price0\"\n * - \"pos;neg\": a negative number uses the text around the digits of the negative subpattern instead of \"-\"\n * (\"#.##;(#.##)\" gives \"(3.5)\" for -3.5); the digits follow the positive subpattern, as in Java\n * - at most 16 decimals when |x| >= 1e-3 (the shortest digits of a double never have more there):\n * 0.1 with 20 '#' decimals gives \"0.1\" in both rounding modes\n */\n\nexport type Rounding = 'halfUpShortest' | 'halfUpExact' | 'halfEvenExact';\n\ninterface Pattern {\n prefix: string;\n suffix: string;\n /** Text around the digits of the negative subpattern (\"pos;neg\"), or null without one */\n negPrefix: string | null;\n negSuffix: string | null;\n minInt: number;\n minFrac: number;\n maxFrac: number;\n /** Digits per group, 0 without grouping */\n grouping: number;\n percent: boolean;\n /** Minimum exponent digits, or null without exponent */\n exponent: number | null;\n hasDigits: boolean;\n}\n\nconst patterns = new Map<string, Pattern>();\n\n/** Index of the first ';' outside quotes, or -1. */\nfunction subpatternSplit(pattern: string): number {\n let quoted = false;\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === \"'\") quoted = !quoted;\n else if (pattern[i] === ';' && !quoted) return i;\n }\n return -1;\n}\n\nfunction parsePattern(pattern: string): Pattern {\n const cached = patterns.get(pattern);\n if (cached) return cached;\n const semi = subpatternSplit(pattern);\n if (semi !== -1) {\n // only the text around the digits of the negative subpattern is used\n const neg = parsePattern(pattern.slice(semi + 1));\n const parsed = { ...parsePattern(pattern.slice(0, semi)), negPrefix: neg.prefix, negSuffix: neg.suffix };\n patterns.set(pattern, parsed);\n return parsed;\n }\n\n let prefix = '';\n let suffix = '';\n let number = '';\n let percent = false;\n let phase: 'prefix' | 'number' | 'suffix' = 'prefix';\n for (let i = 0; i < pattern.length; i++) {\n const ch = pattern[i]!;\n let literal: string;\n if (ch === \"'\") {\n const end = pattern.indexOf(\"'\", i + 1);\n if (end === i + 1) {\n literal = \"'\";\n i = end;\n } else {\n literal = pattern.slice(i + 1, end === -1 ? undefined : end);\n i = end === -1 ? pattern.length : end;\n }\n } else if (phase !== 'suffix' && ('#0,.'.includes(ch) || (phase === 'number' && ch === 'E'))) {\n phase = 'number';\n number += ch;\n continue;\n } else {\n if (ch === '%') percent = true;\n literal = ch;\n }\n if (phase === 'number') phase = 'suffix';\n if (phase === 'prefix') prefix += literal;\n else suffix += literal;\n }\n\n const [mantissa = '', exp] = number.split('E');\n const [intPart = '', fracPart = ''] = mantissa.split('.');\n const lastComma = intPart.lastIndexOf(',');\n const parsed: Pattern = {\n prefix,\n suffix,\n negPrefix: null,\n negSuffix: null,\n // as in Java, a pattern with a decimal point and no '0' at all (\"#.##\") shows one integer digit\n minInt: !mantissa.includes('0') && mantissa.includes('.') ? 1 : [...intPart].filter((c) => c === '0').length,\n minFrac: [...fracPart].filter((c) => c === '0').length,\n maxFrac: [...fracPart].filter((c) => c === '0' || c === '#').length,\n grouping: lastComma === -1 ? 0 : intPart.length - lastComma - 1,\n percent,\n exponent: exp === undefined ? null : exp.length,\n hasDigits: /[0#]/.test(mantissa),\n };\n patterns.set(pattern, parsed);\n return parsed;\n}\n\n/** Decimal digits of |x|: value = 0.d1d2d3... \u00D7 10^intLen (no leading zero digit; \"\" for 0). */\ninterface Digits {\n digits: string;\n intLen: number;\n}\n\nfunction shortestDigits(x: number): Digits {\n const m = /^(\\d+)(?:\\.(\\d+))?(?:e([+-]\\d+))?$/.exec(String(Math.abs(x)))!;\n const all = m[1]! + (m[2] ?? '');\n let intLen = m[1]!.length + Number(m[3] ?? 0);\n const lead = all.length - all.replace(/^0+/, '').length;\n intLen -= lead;\n const digits = all.slice(lead).replace(/0+$/, '');\n return { digits, intLen: digits ? intLen : 0 };\n}\n\n/** Exact decimal expansion of the double |x|. */\nfunction exactDigits(x: number): Digits {\n const abs = Math.abs(x);\n if (abs === 0) return { digits: '', intLen: 0 };\n const view = new DataView(new ArrayBuffer(8));\n view.setFloat64(0, abs);\n const bits = view.getBigUint64(0);\n const expBits = Number((bits >> 52n) & 0x7ffn);\n let mant = bits & 0xfffffffffffffn;\n const e2 = expBits === 0 ? -1074 : expBits - 1075;\n if (expBits !== 0) mant |= 1n << 52n;\n let str: string;\n let fracDigits: number;\n if (e2 >= 0) {\n str = (mant << BigInt(e2)).toString();\n fracDigits = 0;\n } else {\n str = (mant * 5n ** BigInt(-e2)).toString();\n fracDigits = -e2;\n }\n const intLen = str.length - fracDigits;\n const lead = str.length - str.replace(/^0+/, '').length;\n const digits = str.slice(lead).replace(/0+$/, '');\n return { digits, intLen: intLen - lead };\n}\n\n/** Rounds to `frac` digits after the point. */\nfunction roundDigits(d: Digits, frac: number, mode: Rounding): Digits {\n const keep = d.intLen + frac;\n if (keep >= d.digits.length) return d;\n if (keep < 0) return { digits: '', intLen: 0 };\n const first = d.digits.charCodeAt(keep) - 48;\n let up: boolean;\n if (mode !== 'halfEvenExact') up = first >= 5;\n else if (first !== 5) up = first > 5;\n else if (d.digits.length > keep + 1) up = true;\n else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;\n let kept = d.digits.slice(0, keep);\n let intLen = d.intLen;\n if (up) {\n const n = (BigInt(kept || '0') + 1n).toString();\n if (n.length > kept.length) intLen += 1;\n kept = n;\n }\n kept = kept.replace(/0+$/, '');\n return { digits: kept, intLen: kept ? intLen : 0 };\n}\n\nfunction group(int: string, size: number): string {\n if (!size || int.length <= size) return int;\n const parts: string[] = [];\n for (let end = int.length; end > 0; end -= size) parts.unshift(int.slice(Math.max(0, end - size), end));\n return parts.join(',');\n}\n\nfunction layout(d: Digits, p: Pattern): string {\n let int = d.intLen > 0 ? d.digits.slice(0, d.intLen).padEnd(d.intLen, '0') : '';\n int = int.replace(/^0+/, '').padStart(p.minInt, '0');\n let frac = d.intLen < 0 ? '0'.repeat(-d.intLen) + d.digits : d.digits.slice(Math.max(0, d.intLen));\n frac = frac.slice(0, p.maxFrac).replace(/0+$/, '').padEnd(p.minFrac, '0');\n if (!int && !frac) int = '0';\n return group(int, p.grouping) + (frac ? '.' + frac : '');\n}\n\n/** Formats a number with a DecimalFormat pattern. */\nexport function formatNumber(value: number, pattern: string, mode: Rounding): string {\n if (Number.isNaN(value)) return 'NaN';\n const p = parsePattern(pattern);\n if (!Number.isFinite(value)) return affixes(p, value < 0, '\u221E');\n const x = p.percent ? value * 100 : value;\n const toDigits = mode === 'halfUpShortest' ? shortestDigits : exactDigits;\n\n let body: string;\n let isZero: boolean;\n if (p.exponent !== null) {\n const rounded = roundDigits(toDigits(x), p.maxFrac, mode);\n const intDigits = Math.max(p.minInt, 1);\n isZero = rounded.digits === '';\n let exponent = isZero ? 0 : rounded.intLen - intDigits;\n let mant = roundDigits({ digits: rounded.digits, intLen: isZero ? 0 : intDigits }, p.maxFrac, mode);\n if (mant.intLen > intDigits) {\n exponent += 1;\n mant = { digits: mant.digits, intLen: intDigits };\n }\n const e = String(Math.abs(exponent)).padStart(p.exponent, '0');\n body = layout(mant, p) + 'E' + (exponent < 0 ? '-' : '') + e;\n } else {\n // 16 decimals at most from 1e-3 up (the shortest digits of a double have no more there)\n const frac = !p.hasDigits ? 0 : Math.abs(x) >= 1e-3 ? Math.min(p.maxFrac, 16) : p.maxFrac;\n const rounded = roundDigits(toDigits(x), frac, mode);\n isZero = rounded.digits === '';\n body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });\n }\n return affixes(p, x < 0 && (!isZero || !p.hasDigits), body);\n}\n\n/** `body` with the text of the positive pattern, or of the negative one (\"-\" + prefix without \"pos;neg\"). */\nfunction affixes(p: Pattern, negative: boolean, body: string): string {\n if (!negative) return p.prefix + body + p.suffix;\n if (p.negPrefix === null) return '-' + p.prefix + body + p.suffix;\n return p.negPrefix + body + p.negSuffix!;\n}\n\n/**\n * `[minmove, pricescale]` of a tick (PineScript `syminfo.mintick = syminfo.minmove / syminfo.pricescale`).\n * Without `pricescale` it is 10^(decimals of the tick): 0.02 is 2 / 100, 0.25 is 25 / 100. A symbol priced in\n * fractions needs its own (1/32 is 1 / 32).\n */\nexport function tickScale(mintick: number, pricescale?: number): [number, number] {\n if (pricescale === undefined) {\n const m = /^\\d+(?:\\.(\\d+))?(?:e-(\\d+))?$/.exec(String(mintick));\n pricescale = Number(`1e${m ? (m[1]?.length ?? 0) + Number(m[2] ?? 0) : 0}`);\n }\n return [Math.round(mintick * pricescale), pricescale];\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["parsed"]
7
7
  }
@@ -328,6 +328,8 @@ export declare function rma(source: Source, length: simple_int): series_float;
328
328
  * - Summed from the oldest value to the newest; as PineScript, a partial sum `s + t` with
329
329
  * `|s + t| <= 1e-10 * max(1, |s| + |t|)` is 0 (a cancellation residue, or values below 1e-10)
330
330
  * - More responsive to recent price changes than SMA
331
+ * - `length` can be an array (a length per bar, PineScript series length): each bar gives the result of its own
332
+ * length (`na` for a length below 1)
331
333
  *
332
334
  * @example
333
335
  * ```typescript
@@ -335,7 +337,7 @@ export declare function rma(source: Source, length: simple_int): series_float;
335
337
  * // WMA gives more weight to recent prices
336
338
  * ```
337
339
  */
338
- export declare function wma(source: Source, length: simple_int): series_float;
340
+ export declare function wma(source: Source, length: simple_int | ArrayLike<number>): series_float;
339
341
  /**
340
342
  * Highest value over the last `length` bars.
341
343
  *
@@ -344,11 +346,15 @@ export declare function wma(source: Source, length: simple_int): series_float;
344
346
  * @returns Highest value series (na on the first `length - 1` bars)
345
347
  *
346
348
  * @remarks
347
- * As in PineScript: an `na` value ends the window, so only the values after the last `na`
348
- * count, and a bar whose own value is `na` gives `na`.
349
+ * As in PineScript: `na` values are skipped. The result is the highest of the last `length` non-na values (of all
350
+ * of them when there are fewer), also on a bar whose own value is `na`; `na` before bar `length - 1` and while no
351
+ * value was received. A fractional length is truncated (7.5 is 7 bars).
352
+ * `length` can be an array (a length per bar, PineScript series length), also for `ta.lowest`, `ta.highestbars`
353
+ * and `ta.lowestbars`: each bar uses its own length.
354
+ * A length above 15,000 throws the PineScript runtime error (also `ta.lowest`, `ta.highestbars`, `ta.lowestbars`).
349
355
  * PineScript `ta.highest(length)` (source = high) is `ta.highest(length)` in the script API.
350
356
  */
351
- export declare function highest(source: Source, length: simple_int): series_float;
357
+ export declare function highest(source: Source, length: simple_int | ArrayLike<number>): series_float;
352
358
  /**
353
359
  * Lowest value over the last `length` bars.
354
360
  *
@@ -357,10 +363,11 @@ export declare function highest(source: Source, length: simple_int): series_floa
357
363
  * @returns Lowest value series (na on the first `length - 1` bars)
358
364
  *
359
365
  * @remarks
360
- * As in PineScript: an `na` value ends the window, so only the values after the last `na`
361
- * count, and a bar whose own value is `na` gives `na`.
366
+ * As in PineScript: `na` values are skipped. The result is the lowest of the last `length` non-na values (of all
367
+ * of them when there are fewer), also on a bar whose own value is `na`; `na` before bar `length - 1` and while no
368
+ * value was received.
362
369
  */
363
- export declare function lowest(source: Source, length: simple_int): series_float;
370
+ export declare function lowest(source: Source, length: simple_int | ArrayLike<number>): series_float;
364
371
  /**
365
372
  * Cumulative Sum - returns the total sum of all elements from the beginning.
366
373
  *
@@ -819,6 +826,8 @@ export declare function hma(source: Source, length: simple_int): series_float;
819
826
  * ```
820
827
  */
821
828
  export declare function sar(start: simple_float, inc: simple_float, max: simple_float, high?: Source, low?: Source, close?: Source): series_float;
829
+ /** A pivot length: one value, or a value per bar (PineScript series length). */
830
+ type PivotLength = simple_int | ArrayLike<number>;
822
831
  /**
823
832
  * Pivot High - detects pivot high points in the price series.
824
833
  *
@@ -833,7 +842,10 @@ export declare function sar(start: simple_float, inc: simple_float, max: simple_
833
842
  * - As in PineScript, the value appears `rightbars` bars after the pivot bar: the pivot bar
834
843
  * index is `i - rightbars`
835
844
  * - A pivot high may equal values on its left but must be higher than all values on its right
836
- * - An `na` neighbour ends the check on its side
845
+ * - `na` values are skipped: the window is the last `leftbars + rightbars + 1` non-na values, and the highest of
846
+ * them must be on the bar `rightbars` bars back
847
+ * - `leftbars` / `rightbars` can be arrays (a length per bar, PineScript series length), also for `ta.pivotlow`:
848
+ * each bar uses its own lengths
837
849
  *
838
850
  * @example
839
851
  * ```typescript
@@ -841,7 +853,7 @@ export declare function sar(start: simple_float, inc: simple_float, max: simple_
841
853
  * // pivotHighs[i] is high[i - 2] when bar i - 2 is a pivot high
842
854
  * ```
843
855
  */
844
- export declare function pivothigh(sourceOrLeftbars: Source | simple_int, leftbarsOrRightbars: simple_int, rightbars?: simple_int, high?: Source): series_float;
856
+ export declare function pivothigh(sourceOrLeftbars: Source | simple_int, leftbarsOrRightbars: PivotLength, rightbars?: PivotLength, high?: Source): series_float;
845
857
  /**
846
858
  * Pivot Low - detects pivot low points in the price series.
847
859
  *
@@ -856,7 +868,8 @@ export declare function pivothigh(sourceOrLeftbars: Source | simple_int, leftbar
856
868
  * - As in PineScript, the value appears `rightbars` bars after the pivot bar: the pivot bar
857
869
  * index is `i - rightbars`
858
870
  * - A pivot low may equal values on its left but must be lower than all values on its right
859
- * - An `na` neighbour ends the check on its side
871
+ * - `na` values are skipped: the window is the last `leftbars + rightbars + 1` non-na values, and the lowest of
872
+ * them must be on the bar `rightbars` bars back
860
873
  *
861
874
  * @example
862
875
  * ```typescript
@@ -864,7 +877,7 @@ export declare function pivothigh(sourceOrLeftbars: Source | simple_int, leftbar
864
877
  * // pivotLows[i] is low[i - 2] when bar i - 2 is a pivot low
865
878
  * ```
866
879
  */
867
- export declare function pivotlow(sourceOrLeftbars: Source | simple_int, leftbarsOrRightbars: simple_int, rightbars?: simple_int, low?: Source): series_float;
880
+ export declare function pivotlow(sourceOrLeftbars: Source | simple_int, leftbarsOrRightbars: PivotLength, rightbars?: PivotLength, low?: Source): series_float;
868
881
  /**
869
882
  * Bars Since - returns number of bars since condition was true.
870
883
  *
@@ -1129,19 +1142,21 @@ export declare function range(source: Source, length: simple_int): series_float;
1129
1142
  * -1 for the previous bar, and so on (as in PineScript, the offset is negative).
1130
1143
  *
1131
1144
  * @remarks
1132
- * As in PineScript: on equal values the oldest bar wins; an `na` value ends the window;
1133
- * 0 when the window has no value.
1145
+ * As in PineScript: the best of the last `length` non-na values, the newest one on equal values; the offset is the
1146
+ * distance in bars to its bar, so it is below `-(length - 1)` when `na` values were skipped. `na` before bar
1147
+ * `length - 1` and while no value was received; a fractional length is truncated.
1134
1148
  */
1135
- export declare function highestbars(source: Source, length: simple_int): series_int;
1149
+ export declare function highestbars(source: Source, length: simple_int | ArrayLike<number>): series_int;
1136
1150
  /**
1137
1151
  * Offset to the bar with the lowest value over the last `length` bars: 0 for the current bar,
1138
1152
  * -1 for the previous bar, and so on (as in PineScript, the offset is negative).
1139
1153
  *
1140
1154
  * @remarks
1141
- * As in PineScript: on equal values the oldest bar wins; an `na` value ends the window;
1142
- * 0 when the window has no value.
1155
+ * As in PineScript: the best of the last `length` non-na values, the newest one on equal values; the offset is the
1156
+ * distance in bars to its bar, so it is below `-(length - 1)` when `na` values were skipped. `na` before bar
1157
+ * `length - 1` and while no value was received; a fractional length is truncated.
1143
1158
  */
1144
- export declare function lowestbars(source: Source, length: simple_int): series_int;
1159
+ export declare function lowestbars(source: Source, length: simple_int | ArrayLike<number>): series_int;
1145
1160
  /**
1146
1161
  * All-time highest value of `source` up to each bar (PineScript `ta.max(source)`).
1147
1162
  *
@@ -1448,4 +1463,5 @@ export declare function nvi(close: Source, volume: Source): series_float;
1448
1463
  * Starts at 1.
1449
1464
  */
1450
1465
  export declare function pvi(close: Source, volume: Source): series_float;
1466
+ export {};
1451
1467
  //# sourceMappingURL=index.d.ts.map