oakscriptjs 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/cjs/array/index.d.ts +8 -2
  2. package/dist/cjs/array/index.d.ts.map +1 -1
  3. package/dist/cjs/array/index.js +2 -1
  4. package/dist/cjs/array/index.js.map +2 -2
  5. package/dist/cjs/box/index.js +6 -6
  6. package/dist/cjs/box/index.js.map +2 -2
  7. package/dist/cjs/callsite/index.js +1 -1
  8. package/dist/cjs/callsite/index.js.map +2 -2
  9. package/dist/cjs/drawing/registry.d.ts +15 -6
  10. package/dist/cjs/drawing/registry.d.ts.map +1 -1
  11. package/dist/cjs/drawing/registry.js +16 -2
  12. package/dist/cjs/drawing/registry.js.map +2 -2
  13. package/dist/cjs/index.d.ts +3 -2
  14. package/dist/cjs/index.d.ts.map +1 -1
  15. package/dist/cjs/index.js +6 -1
  16. package/dist/cjs/index.js.map +2 -2
  17. package/dist/cjs/input.d.ts +9 -1
  18. package/dist/cjs/input.d.ts.map +1 -1
  19. package/dist/cjs/input.js +16 -0
  20. package/dist/cjs/input.js.map +2 -2
  21. package/dist/cjs/label/index.js +3 -3
  22. package/dist/cjs/label/index.js.map +2 -2
  23. package/dist/cjs/line/index.d.ts +10 -27
  24. package/dist/cjs/line/index.d.ts.map +1 -1
  25. package/dist/cjs/line/index.js +10 -27
  26. package/dist/cjs/line/index.js.map +2 -2
  27. package/dist/cjs/linefill/index.d.ts +3 -0
  28. package/dist/cjs/linefill/index.d.ts.map +1 -1
  29. package/dist/cjs/linefill/index.js +15 -1
  30. package/dist/cjs/linefill/index.js.map +3 -3
  31. package/dist/cjs/runtime/adapters/SimpleInputAdapter.js +1 -1
  32. package/dist/cjs/runtime/adapters/SimpleInputAdapter.js.map +2 -2
  33. package/dist/cjs/runtime/index.d.ts +1 -1
  34. package/dist/cjs/runtime/index.d.ts.map +1 -1
  35. package/dist/cjs/runtime/index.js +1 -0
  36. package/dist/cjs/runtime/index.js.map +2 -2
  37. package/dist/cjs/runtime/inputs.d.ts +9 -0
  38. package/dist/cjs/runtime/inputs.d.ts.map +1 -1
  39. package/dist/cjs/runtime/inputs.js +8 -0
  40. package/dist/cjs/runtime/inputs.js.map +2 -2
  41. package/dist/cjs/runtime/types.d.ts +1 -1
  42. package/dist/cjs/runtime/types.d.ts.map +1 -1
  43. package/dist/cjs/runtime/types.js.map +1 -1
  44. package/dist/cjs/script/index.d.ts +65 -12
  45. package/dist/cjs/script/index.d.ts.map +1 -1
  46. package/dist/cjs/script/index.js +168 -19
  47. package/dist/cjs/script/index.js.map +3 -3
  48. package/dist/cjs/security/other.d.ts +92 -0
  49. package/dist/cjs/security/other.d.ts.map +1 -0
  50. package/dist/cjs/security/other.js +164 -0
  51. package/dist/cjs/security/other.js.map +7 -0
  52. package/dist/cjs/session/bars.d.ts +10 -1
  53. package/dist/cjs/session/bars.d.ts.map +1 -1
  54. package/dist/cjs/session/bars.js +16 -2
  55. package/dist/cjs/session/bars.js.map +2 -2
  56. package/dist/cjs/session/calendar.d.ts +4 -1
  57. package/dist/cjs/session/calendar.d.ts.map +1 -1
  58. package/dist/cjs/session/calendar.js +11 -7
  59. package/dist/cjs/session/calendar.js.map +2 -2
  60. package/dist/cjs/session/context.d.ts +2 -1
  61. package/dist/cjs/session/context.d.ts.map +1 -1
  62. package/dist/cjs/session/context.js +2 -1
  63. package/dist/cjs/session/context.js.map +2 -2
  64. package/dist/cjs/str/floatingdecimal.d.ts +31 -0
  65. package/dist/cjs/str/floatingdecimal.d.ts.map +1 -0
  66. package/dist/cjs/str/floatingdecimal.js +298 -0
  67. package/dist/cjs/str/floatingdecimal.js.map +7 -0
  68. package/dist/cjs/str/index.d.ts +4 -3
  69. package/dist/cjs/str/index.d.ts.map +1 -1
  70. package/dist/cjs/str/index.js +1 -1
  71. package/dist/cjs/str/index.js.map +2 -2
  72. package/dist/cjs/str/numberformat.d.ts +6 -4
  73. package/dist/cjs/str/numberformat.d.ts.map +1 -1
  74. package/dist/cjs/str/numberformat.js +3 -2
  75. package/dist/cjs/str/numberformat.js.map +2 -2
  76. package/dist/cjs/ta/index.d.ts +34 -17
  77. package/dist/cjs/ta/index.d.ts.map +1 -1
  78. package/dist/cjs/ta/index.js +132 -116
  79. package/dist/cjs/ta/index.js.map +2 -2
  80. package/dist/cjs/ta/limits.d.ts +11 -0
  81. package/dist/cjs/ta/limits.d.ts.map +1 -1
  82. package/dist/cjs/ta/limits.js +15 -1
  83. package/dist/cjs/ta/limits.js.map +2 -2
  84. package/dist/cjs/ta/running-sum.js +1 -1
  85. package/dist/cjs/ta/running-sum.js.map +2 -2
  86. package/dist/cjs/ta-series.d.ts +1 -1
  87. package/dist/cjs/ta-series.d.ts.map +1 -1
  88. package/dist/cjs/ta-series.js +1 -1
  89. package/dist/cjs/ta-series.js.map +2 -2
  90. package/dist/cjs/table/index.d.ts +3 -1
  91. package/dist/cjs/table/index.d.ts.map +1 -1
  92. package/dist/cjs/table/index.js +1 -0
  93. package/dist/cjs/table/index.js.map +3 -3
  94. package/dist/cjs/time/session.d.ts +9 -0
  95. package/dist/cjs/time/session.d.ts.map +1 -1
  96. package/dist/cjs/time/session.js +42 -14
  97. package/dist/cjs/time/session.js.map +3 -3
  98. package/dist/cjs/time/timezone.d.ts +6 -1
  99. package/dist/cjs/time/timezone.d.ts.map +1 -1
  100. package/dist/cjs/time/timezone.js +2 -2
  101. package/dist/cjs/time/timezone.js.map +2 -2
  102. package/dist/cjs/types/metadata.d.ts +1 -1
  103. package/dist/cjs/types/metadata.d.ts.map +1 -1
  104. package/dist/cjs/types/metadata.js.map +1 -1
  105. package/dist/esm/array/index.d.ts +8 -2
  106. package/dist/esm/array/index.d.ts.map +1 -1
  107. package/dist/esm/array/index.js +12 -3
  108. package/dist/esm/array/index.js.map +1 -1
  109. package/dist/esm/box/index.js +6 -6
  110. package/dist/esm/box/index.js.map +1 -1
  111. package/dist/esm/callsite/index.js +1 -1
  112. package/dist/esm/callsite/index.js.map +1 -1
  113. package/dist/esm/drawing/registry.d.ts +15 -6
  114. package/dist/esm/drawing/registry.d.ts.map +1 -1
  115. package/dist/esm/drawing/registry.js +30 -7
  116. package/dist/esm/drawing/registry.js.map +1 -1
  117. package/dist/esm/index.d.ts +3 -2
  118. package/dist/esm/index.d.ts.map +1 -1
  119. package/dist/esm/index.js +4 -2
  120. package/dist/esm/index.js.map +1 -1
  121. package/dist/esm/input.d.ts +9 -1
  122. package/dist/esm/input.d.ts.map +1 -1
  123. package/dist/esm/input.js +16 -0
  124. package/dist/esm/input.js.map +1 -1
  125. package/dist/esm/label/index.js +3 -3
  126. package/dist/esm/label/index.js.map +1 -1
  127. package/dist/esm/line/index.d.ts +10 -27
  128. package/dist/esm/line/index.d.ts.map +1 -1
  129. package/dist/esm/line/index.js +20 -64
  130. package/dist/esm/line/index.js.map +1 -1
  131. package/dist/esm/linefill/index.d.ts +3 -0
  132. package/dist/esm/linefill/index.d.ts.map +1 -1
  133. package/dist/esm/linefill/index.js +20 -1
  134. package/dist/esm/linefill/index.js.map +1 -1
  135. package/dist/esm/runtime/adapters/SimpleInputAdapter.js +1 -1
  136. package/dist/esm/runtime/adapters/SimpleInputAdapter.js.map +1 -1
  137. package/dist/esm/runtime/index.d.ts +1 -1
  138. package/dist/esm/runtime/index.d.ts.map +1 -1
  139. package/dist/esm/runtime/index.js +1 -1
  140. package/dist/esm/runtime/index.js.map +1 -1
  141. package/dist/esm/runtime/inputs.d.ts +9 -0
  142. package/dist/esm/runtime/inputs.d.ts.map +1 -1
  143. package/dist/esm/runtime/inputs.js +15 -0
  144. package/dist/esm/runtime/inputs.js.map +1 -1
  145. package/dist/esm/runtime/types.d.ts +1 -1
  146. package/dist/esm/runtime/types.d.ts.map +1 -1
  147. package/dist/esm/script/index.d.ts +65 -12
  148. package/dist/esm/script/index.d.ts.map +1 -1
  149. package/dist/esm/script/index.js +217 -29
  150. package/dist/esm/script/index.js.map +1 -1
  151. package/dist/esm/security/other.d.ts +92 -0
  152. package/dist/esm/security/other.d.ts.map +1 -0
  153. package/dist/esm/security/other.js +211 -0
  154. package/dist/esm/security/other.js.map +1 -0
  155. package/dist/esm/session/bars.d.ts +10 -1
  156. package/dist/esm/session/bars.d.ts.map +1 -1
  157. package/dist/esm/session/bars.js +26 -1
  158. package/dist/esm/session/bars.js.map +1 -1
  159. package/dist/esm/session/calendar.d.ts +4 -1
  160. package/dist/esm/session/calendar.d.ts.map +1 -1
  161. package/dist/esm/session/calendar.js +19 -7
  162. package/dist/esm/session/calendar.js.map +1 -1
  163. package/dist/esm/session/context.d.ts +2 -1
  164. package/dist/esm/session/context.d.ts.map +1 -1
  165. package/dist/esm/session/context.js +5 -3
  166. package/dist/esm/session/context.js.map +1 -1
  167. package/dist/esm/str/floatingdecimal.d.ts +31 -0
  168. package/dist/esm/str/floatingdecimal.d.ts.map +1 -0
  169. package/dist/esm/str/floatingdecimal.js +272 -0
  170. package/dist/esm/str/floatingdecimal.js.map +1 -0
  171. package/dist/esm/str/index.d.ts +4 -3
  172. package/dist/esm/str/index.d.ts.map +1 -1
  173. package/dist/esm/str/index.js +6 -5
  174. package/dist/esm/str/index.js.map +1 -1
  175. package/dist/esm/str/numberformat.d.ts +6 -4
  176. package/dist/esm/str/numberformat.d.ts.map +1 -1
  177. package/dist/esm/str/numberformat.js +9 -5
  178. package/dist/esm/str/numberformat.js.map +1 -1
  179. package/dist/esm/ta/index.d.ts +34 -17
  180. package/dist/esm/ta/index.d.ts.map +1 -1
  181. package/dist/esm/ta/index.js +155 -185
  182. package/dist/esm/ta/index.js.map +1 -1
  183. package/dist/esm/ta/limits.d.ts +11 -0
  184. package/dist/esm/ta/limits.d.ts.map +1 -1
  185. package/dist/esm/ta/limits.js +27 -2
  186. package/dist/esm/ta/limits.js.map +1 -1
  187. package/dist/esm/ta/running-sum.js +1 -1
  188. package/dist/esm/ta/running-sum.js.map +1 -1
  189. package/dist/esm/ta-series.d.ts +1 -1
  190. package/dist/esm/ta-series.d.ts.map +1 -1
  191. package/dist/esm/ta-series.js +2 -1
  192. package/dist/esm/ta-series.js.map +1 -1
  193. package/dist/esm/table/index.d.ts +3 -1
  194. package/dist/esm/table/index.d.ts.map +1 -1
  195. package/dist/esm/table/index.js +6 -1
  196. package/dist/esm/table/index.js.map +1 -1
  197. package/dist/esm/time/session.d.ts +9 -0
  198. package/dist/esm/time/session.d.ts.map +1 -1
  199. package/dist/esm/time/session.js +53 -15
  200. package/dist/esm/time/session.js.map +1 -1
  201. package/dist/esm/time/timezone.d.ts +6 -1
  202. package/dist/esm/time/timezone.d.ts.map +1 -1
  203. package/dist/esm/time/timezone.js +8 -2
  204. package/dist/esm/time/timezone.js.map +1 -1
  205. package/dist/esm/types/metadata.d.ts +1 -1
  206. package/dist/esm/types/metadata.d.ts.map +1 -1
  207. package/package.json +1 -1
  208. package/src/array/index.ts +11 -3
  209. package/src/box/index.ts +6 -6
  210. package/src/callsite/index.ts +1 -1
  211. package/src/drawing/registry.ts +30 -7
  212. package/src/index.ts +11 -1
  213. package/src/input.ts +18 -1
  214. package/src/label/index.ts +3 -3
  215. package/src/line/index.ts +20 -68
  216. package/src/linefill/index.ts +20 -1
  217. package/src/runtime/adapters/SimpleInputAdapter.ts +1 -1
  218. package/src/runtime/index.ts +1 -0
  219. package/src/runtime/inputs.ts +16 -0
  220. package/src/runtime/types.ts +1 -1
  221. package/src/script/index.ts +292 -30
  222. package/src/security/other.ts +269 -0
  223. package/src/session/bars.ts +24 -1
  224. package/src/session/calendar.ts +23 -7
  225. package/src/session/context.ts +5 -3
  226. package/src/str/floatingdecimal.ts +268 -0
  227. package/src/str/index.ts +6 -5
  228. package/src/str/numberformat.ts +11 -6
  229. package/src/ta/index.ts +157 -183
  230. package/src/ta/limits.ts +28 -1
  231. package/src/ta/running-sum.ts +1 -1
  232. package/src/ta-series.ts +3 -2
  233. package/src/table/index.ts +4 -1
  234. package/src/time/session.ts +58 -16
  235. package/src/time/timezone.ts +9 -2
  236. package/src/types/metadata.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/session/calendar.ts"],
4
- "sourcesContent": ["/**\n * Trading calendar: the session periods of each trading day of a symbol, from the symbol session\n * information (issue #100).\n *\n * Session format (symbol session information, also PineScript session strings):\n * - periods \"HHMM-HHMM\", several separated by commas; \"24x7\" is the whole day, every day\n * - a period whose end is not after its start (\"1700-1600\", \"1700-1700\") starts the day before the\n * trading day: the session that opens Sunday 17:00 is Monday's\n * - \"HHMMF<n>\" moves a time <n> days before the trading day (\"1700F2-1200F1\"); \"F\" alone is one day (\"1700F-1900\":\n * 17:00 of the day before to 19:00)\n * - several sections joined by \"|\", each with its own days (\"1900-1900:3456|1700F-1900:2\": Monday opens on Sunday\n * 17:00, the other days at 19:00 of the day before)\n * - optional days after \":\" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to\n * Friday (\"24x7\": every day), and a PineScript session string follows PineScript's rules (one period:\n * every day; several periods: Monday to Friday)\n * - \"#YYYYMMDD/<session>\" changes the session from that date (\"0900-1746#20251201/0800-2200\")\n * - corrections (\"session-correction\"): \"<session>:<dates>;dayoff:<dates>\" replace the session of these\n * dates (early closes, days off); holidays (\"session_holidays\") are dates without session\n */\n\nimport { zonedFields, zonedToUnix } from '../time/timezone.js';\nimport { parsePineSession } from '../time/session.js';\n\n/** Session information of a symbol (hours, corrections, holidays). */\nexport interface SessionSpec {\n /** Session hours: \"0930-1600\", \"1700-1600\", \"24x7\", \"0400-2000\" ... */\n session: string;\n /** Exceptions by date (symbol info \"session-correction\"): \"0930-1300:20251128,20251224;dayoff:20250109\" */\n corrections?: string;\n /** Exchange holidays as \"YYYYMMDD\" (symbol info \"session_holidays\"), comma-separated or as an array */\n holidays?: string | string[];\n}\n\n/** Date as YYYYMMDD. */\nexport type DateNum = number;\n\ninterface Period {\n /** Minutes after midnight */\n start: number;\n end: number;\n /** Days before the trading day */\n startOffset: number;\n endOffset: number;\n}\n\ninterface Version {\n from: DateNum;\n periods: Period[];\n /** Index 1 (Sunday) to 7 (Saturday) */\n days: boolean[];\n /** PineScript session strings: the periods of each weekday (index 1 to 7), from the \"|\" sections */\n byDay?: Period[][];\n}\n\nexport const toDateNum = (y: number, m: number, d: number): DateNum => y * 10000 + m * 100 + d;\nconst utcOf = (date: DateNum): number => Date.UTC(Math.floor(date / 10000), Math.floor(date / 100) % 100 - 1, date % 100);\nconst fromUtc = (ms: number): DateNum => {\n const d = new Date(ms);\n return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n};\nexport const addDays = (date: DateNum, n: number): DateNum => fromUtc(utcOf(date) + n * 86_400_000);\n/** 1 = Sunday ... 7 = Saturday */\nexport const weekdayOf = (date: DateNum): number => new Date(utcOf(date)).getUTCDay() + 1;\n/** 00:00 UTC of the date, in ms (PineScript `time_tradingday`). */\nexport const dateUtcMs = utcOf;\n\nfunction parseTime(token: string, spec: string): { minutes: number; offset?: number } {\n const m = /^(\\d{2})(\\d{2})(?:F(\\d*))?$/.exec(token.trim());\n if (!m) throw new SyntaxError(`Invalid session \"${spec}\"`);\n // \"F\" without a number is one day before the trading day\n return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === undefined ? undefined : Number(m[3] || 1) };\n}\n\nfunction parsePeriods(text: string, spec: string): Period[] {\n if (text.trim() === '24x7') return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];\n return text.split(',').map((p) => {\n const [a = '', b = ''] = p.split('-');\n const s = parseTime(a, spec);\n const e = parseTime(b, spec);\n if (s.minutes === 0 && e.minutes === 0 && s.offset === undefined && e.offset === undefined) {\n return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };\n }\n return {\n start: s.minutes,\n end: e.minutes,\n startOffset: s.offset ?? (e.minutes <= s.minutes ? 1 : 0),\n endOffset: e.offset ?? 0,\n };\n });\n}\n\n/** A PineScript session string as periods per weekday (rules in time/session.ts). */\nfunction pineVersion(text: string, from: DateNum): Version {\n const ranges = parsePineSession(text);\n if (ranges === null) throw new SyntaxError(`Session \"${text}\" is the symbol session`);\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const { start, end, days } of ranges) {\n const period: Period =\n start === 0 && end === 0\n ? { start: 0, end: 1440, startOffset: 0, endOffset: 0 }\n : { start, end, startOffset: end <= start ? 1 : 0, endOffset: 0 };\n for (const d of days) byDay[d]!.push(period);\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n}\n\nfunction parseVersion(text: string, from: DateNum, spec: string, rule: 'symbol' | 'pine'): Version {\n if (rule === 'pine') return pineVersion(text, from);\n if (text.includes('|')) {\n // sections with their own days\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const section of text.split('|').filter((part) => part.trim())) {\n const one = parseVersion(section, from, spec, rule);\n one.days.forEach((on, d) => {\n if (on) byDay[d] = one.periods;\n });\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n }\n const [periodText = '', dayText] = text.trim().split(':');\n const periods = parsePeriods(periodText, spec);\n const allDays = periodText.trim() === '24x7';\n const digits = dayText ?? (allDays ? '1234567' : '23456');\n if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in \"${spec}\"`);\n const days = new Array<boolean>(8).fill(false);\n for (const d of digits) days[Number(d)] = true;\n return { from, periods, days };\n}\n\n/** Session periods of every trading day of a symbol, as UNIX times (ms). */\nexport class TradingCalendar {\n private readonly versions: Version[];\n private readonly corrections = new Map<DateNum, Period[] | null>();\n private readonly holidays: Set<DateNum>;\n private readonly cache = new Map<DateNum, Array<[number, number]>>();\n\n /**\n * @param timezone - Time zone of the session hours (the exchange time zone)\n * @param spec - Session information (hours, corrections, holidays)\n * @param rule - Days without \":days\": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)\n */\n constructor(\n readonly timezone: string,\n spec: SessionSpec,\n rule: 'symbol' | 'pine' = 'symbol'\n ) {\n const [first = '', ...changes] = spec.session.split('#');\n this.versions = [parseVersion(first, 0, spec.session, rule)];\n for (const change of changes) {\n const m = /^(\\d{8})\\/(.+)$/.exec(change.trim());\n if (!m) throw new SyntaxError(`Invalid session change in \"${spec.session}\"`);\n this.versions.push(parseVersion(m[2]!, Number(m[1]), spec.session, rule));\n }\n for (const part of (spec.corrections ?? '').split(';').filter((p) => p.trim())) {\n const i = part.lastIndexOf(':');\n const text = part.slice(0, i).trim();\n const periods = text === 'dayoff' ? null : parsePeriods(text, part);\n for (const date of part.slice(i + 1).split(',')) this.corrections.set(Number(date), periods);\n }\n const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? '').split(',');\n this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));\n }\n\n /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */\n periods(date: DateNum): Array<[number, number]> {\n let out = this.cache.get(date);\n if (out) return out;\n let periods: Period[] = [];\n const correction = this.corrections.get(date);\n if (correction !== undefined) {\n periods = correction ?? [];\n } else if (!this.holidays.has(date)) {\n const version = [...this.versions].reverse().find((v) => v.from <= date)!;\n if (version.byDay) periods = version.byDay[weekdayOf(date)]!;\n else if (version.days[weekdayOf(date)]) periods = version.periods;\n }\n const y = Math.floor(date / 10000);\n const m = Math.floor(date / 100) % 100;\n const d = date % 100;\n out = periods.map((p) => [\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),\n zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60),\n ]);\n this.cache.set(date, out);\n return out;\n }\n\n isTradingDay(date: DateNum): boolean {\n return this.periods(date).length > 0;\n }\n\n /**\n * True for a bar that opens after the session of its day was cut by a correction (an early close) but inside the\n * usual hours of that day: PineScript has no period for such a bar (`time(\"D\")` is na on it).\n */\n afterEarlyClose(time: number): boolean {\n const at = this.locate(time);\n if (!at || at.inside || !this.corrections.get(at.date)) return false;\n const version = [...this.versions].reverse().find((v) => v.from <= at.date)!;\n const usual = version.byDay ? version.byDay[weekdayOf(at.date)]! : version.days[weekdayOf(at.date)] ? version.periods : [];\n const y = Math.floor(at.date / 10000);\n const m = Math.floor(at.date / 100) % 100;\n const d = at.date % 100;\n return usual.some(\n (p) =>\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60) <= time &&\n time < zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)\n );\n }\n\n /**\n * Trading day and period of a time: the period that contains it, else the last period that started\n * before it. Null when no trading day is found near the time.\n */\n locate(time: number): { date: DateNum; index: number; inside: boolean } | null {\n const f = zonedFields(time, this.timezone);\n const local = toDateNum(f.year, f.month, f.day);\n for (let k = 0; k <= 5; k++) {\n const date = addDays(local, k);\n const index = this.periods(date).findIndex(([s, e]) => s <= time && time < e);\n if (index !== -1) return { date, index, inside: true };\n }\n for (let k = 5; k >= -7; k--) {\n const date = addDays(local, k);\n const periods = this.periods(date);\n let index = -1;\n periods.forEach(([s], i) => {\n if (s <= time) index = i;\n });\n if (index !== -1) return { date, index, inside: false };\n }\n return null;\n }\n\n /** First trading day in [from, to] (dates), or null. */\n firstTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n\n /** Last trading day in [from, to] (dates), or null. */\n lastTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n}\n\n/** Monday of the week of `date` (weeks run Monday to Sunday). */\nexport const weekStart = (date: DateNum): DateNum => addDays(date, -((weekdayOf(date) + 5) % 7));\n/** First day of the month of `date`. */\nexport const monthStart = (date: DateNum): DateNum => Math.floor(date / 100) * 100 + 1;\n/** Last day of the month of `date`. */\nexport const monthEnd = (date: DateNum): DateNum => addDays(monthStart(addDays(monthStart(date), 32)), -1);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Trading calendar: the session periods of each trading day of a symbol, from the symbol session\n * information (issue #100).\n *\n * Session format (symbol session information, also PineScript session strings):\n * - periods \"HHMM-HHMM\", several separated by commas; \"24x7\" is the whole day, every day\n * - a period whose end is not after its start (\"1700-1600\", \"1700-1700\") starts the day before the\n * trading day: the session that opens Sunday 17:00 is Monday's\n * - \"HHMMF<n>\" moves a time <n> days before the trading day (\"1700F2-1200F1\"); \"F\" alone is one day (\"1700F-1900\":\n * 17:00 of the day before to 19:00)\n * - several sections joined by \"|\", each with its own days (\"1900-1900:3456|1700F-1900:2\": Monday opens on Sunday\n * 17:00, the other days at 19:00 of the day before)\n * - optional days after \":\" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to\n * Friday (\"24x7\": every day), and a PineScript session string follows PineScript's rules (one period:\n * every day; several periods: Monday to Friday)\n * - \"#YYYYMMDD/<session>\" changes the session from that date (\"0900-1746#20251201/0800-2200\")\n * - corrections (\"session-correction\"): \"<session>:<dates>;dayoff:<dates>\" replace the session of these\n * dates (early closes, days off); holidays (\"session_holidays\") are dates without session\n */\n\nimport { zonedFields, zonedToUnix } from '../time/timezone.js';\nimport { parsePineSession } from '../time/session.js';\n\n/** Session information of a symbol (hours, corrections, holidays). */\nexport interface SessionSpec {\n /** Session hours: \"0930-1600\", \"1700-1600\", \"24x7\", \"0400-2000\" ... */\n session: string;\n /** Exceptions by date (symbol info \"session-correction\"): \"0930-1300:20251128,20251224;dayoff:20250109\" */\n corrections?: string;\n /** Exchange holidays as \"YYYYMMDD\" (symbol info \"session_holidays\"), comma-separated or as an array */\n holidays?: string | string[];\n}\n\n/** Date as YYYYMMDD. */\nexport type DateNum = number;\n\ninterface Period {\n /** Minutes after midnight */\n start: number;\n end: number;\n /** Days before the trading day */\n startOffset: number;\n endOffset: number;\n}\n\ninterface Version {\n from: DateNum;\n periods: Period[];\n /** Index 1 (Sunday) to 7 (Saturday) */\n days: boolean[];\n /** PineScript session strings: the periods of each weekday (index 1 to 7), from the \"|\" sections */\n byDay?: Period[][];\n}\n\nexport const toDateNum = (y: number, m: number, d: number): DateNum => y * 10000 + m * 100 + d;\nconst utcOf = (date: DateNum): number => Date.UTC(Math.floor(date / 10000), Math.floor(date / 100) % 100 - 1, date % 100);\nconst fromUtc = (ms: number): DateNum => {\n const d = new Date(ms);\n return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n};\nexport const addDays = (date: DateNum, n: number): DateNum => fromUtc(utcOf(date) + n * 86_400_000);\n/** 1 = Sunday ... 7 = Saturday */\nexport const weekdayOf = (date: DateNum): number => new Date(utcOf(date)).getUTCDay() + 1;\n/** 00:00 UTC of the date, in ms (PineScript `time_tradingday`). */\nexport const dateUtcMs = utcOf;\n\nfunction parseTime(token: string, spec: string): { minutes: number; offset?: number } {\n const m = /^(\\d{2})(\\d{2})(?:F(\\d*))?$/.exec(token.trim());\n if (!m) throw new SyntaxError(`Invalid session \"${spec}\"`);\n // \"F\" without a number is one day before the trading day\n return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === undefined ? undefined : Number(m[3] || 1) };\n}\n\nfunction parsePeriods(text: string, spec: string): Period[] {\n if (text.trim() === '24x7') return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];\n return text.split(',').map((p) => {\n const [a = '', b = ''] = p.split('-');\n const s = parseTime(a, spec);\n const e = parseTime(b, spec);\n if (s.minutes === 0 && e.minutes === 0 && s.offset === undefined && e.offset === undefined) {\n return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };\n }\n return {\n start: s.minutes,\n end: e.minutes,\n startOffset: s.offset ?? (e.minutes <= s.minutes ? 1 : 0),\n endOffset: e.offset ?? 0,\n };\n });\n}\n\n/** A PineScript session string as periods per weekday (rules in time/session.ts). */\nfunction pineVersion(text: string, from: DateNum): Version {\n const ranges = parsePineSession(text);\n if (ranges === null) throw new SyntaxError(`Session \"${text}\" is the symbol session`);\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const { start, end, days, startDays } of ranges) {\n // a run that ends at 0000 ends at 24:00 of the day it starts and belongs to that day (#173)\n const period: Period =\n start === 0 && end === 0\n ? { start: 0, end: 1440, startOffset: 0, endOffset: 0 }\n : startDays !== undefined\n ? { start, end, startOffset: startDays, endOffset: 0 }\n : end === 0\n ? { start, end: 1440, startOffset: 0, endOffset: 0 }\n : { start, end, startOffset: end <= start ? 1 : 0, endOffset: 0 };\n for (const d of days) byDay[d]!.push(period);\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n}\n\nfunction parseVersion(text: string, from: DateNum, spec: string, rule: 'symbol' | 'pine'): Version {\n if (rule === 'pine') return pineVersion(text, from);\n if (text.includes('|')) {\n // sections with their own days\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const section of text.split('|').filter((part) => part.trim())) {\n const one = parseVersion(section, from, spec, rule);\n one.days.forEach((on, d) => {\n if (on) byDay[d] = one.periods;\n });\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n }\n const [periodText = '', dayText] = text.trim().split(':');\n const periods = parsePeriods(periodText, spec);\n const allDays = periodText.trim() === '24x7';\n const digits = dayText ?? (allDays ? '1234567' : '23456');\n if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in \"${spec}\"`);\n const days = new Array<boolean>(8).fill(false);\n for (const d of digits) days[Number(d)] = true;\n return { from, periods, days };\n}\n\n/** Session periods of every trading day of a symbol, as UNIX times (ms). */\nexport class TradingCalendar {\n private readonly versions: Version[];\n private readonly corrections = new Map<DateNum, Period[] | null>();\n private readonly holidays: Set<DateNum>;\n private readonly cache = new Map<DateNum, Array<[number, number]>>();\n private readonly pine: boolean;\n\n /**\n * @param timezone - Time zone of the session hours (the exchange time zone)\n * @param spec - Session information (hours, corrections, holidays)\n * @param rule - Days without \":days\": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)\n * @param dayPlacement - PineScript session strings read for `time(\"D\", session)`: the wall times of a clock-change\n * day are placed as the JVM does (see `periods`); without it, as `time.inSession` places them\n */\n constructor(\n readonly timezone: string,\n spec: SessionSpec,\n rule: 'symbol' | 'pine' = 'symbol',\n dayPlacement = false\n ) {\n this.pine = rule === 'pine' && dayPlacement;\n const [first = '', ...changes] = spec.session.split('#');\n this.versions = [parseVersion(first, 0, spec.session, rule)];\n for (const change of changes) {\n const m = /^(\\d{8})\\/(.+)$/.exec(change.trim());\n if (!m) throw new SyntaxError(`Invalid session change in \"${spec.session}\"`);\n this.versions.push(parseVersion(m[2]!, Number(m[1]), spec.session, rule));\n }\n for (const part of (spec.corrections ?? '').split(';').filter((p) => p.trim())) {\n const i = part.lastIndexOf(':');\n const text = part.slice(0, i).trim();\n const periods = text === 'dayoff' ? null : parsePeriods(text, part);\n for (const date of part.slice(i + 1).split(',')) this.corrections.set(Number(date), periods);\n }\n const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? '').split(',');\n this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));\n }\n\n /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */\n periods(date: DateNum): Array<[number, number]> {\n let out = this.cache.get(date);\n if (out) return out;\n let periods: Period[] = [];\n const correction = this.corrections.get(date);\n if (correction !== undefined) {\n periods = correction ?? [];\n } else if (!this.holidays.has(date)) {\n const version = [...this.versions].reverse().find((v) => v.from <= date)!;\n if (version.byDay) periods = version.byDay[weekdayOf(date)]!;\n else if (version.days[weekdayOf(date)]) periods = version.periods;\n }\n const y = Math.floor(date / 10000);\n const m = Math.floor(date / 100) % 100;\n const d = date % 100;\n const at = (dd: number, minutes: number, earlier = false): number =>\n zonedToUnix(this.timezone, y, m, dd, Math.floor(minutes / 60), minutes % 60, 0, earlier);\n // for \"D\", PineScript session strings place the wall times of a clock-change day as the JVM does: a skipped time moves\n // past the gap, a repeated time is its first pass, and the end is the minute before it plus one minute (\"0000-0300\"\n // on a 02:00 -> 03:00 night ends at 03:00 of the old offset, \"0000-0400\" at 04:00 of the new one) (#173)\n out = periods.map((p) =>\n this.pine\n ? [at(d - p.startOffset, p.start, true), at(d - p.endOffset, p.end - 1, true) + 60_000]\n : [at(d - p.startOffset, p.start), at(d - p.endOffset, p.end)]\n );\n this.cache.set(date, out);\n return out;\n }\n\n isTradingDay(date: DateNum): boolean {\n return this.periods(date).length > 0;\n }\n\n /**\n * True for a bar that opens after the session of its day was cut by a correction (an early close) but inside the\n * usual hours of that day: PineScript has no period for such a bar (`time(\"D\")` is na on it).\n */\n afterEarlyClose(time: number): boolean {\n const at = this.locate(time);\n if (!at || at.inside || !this.corrections.get(at.date)) return false;\n const version = [...this.versions].reverse().find((v) => v.from <= at.date)!;\n const usual = version.byDay ? version.byDay[weekdayOf(at.date)]! : version.days[weekdayOf(at.date)] ? version.periods : [];\n const y = Math.floor(at.date / 10000);\n const m = Math.floor(at.date / 100) % 100;\n const d = at.date % 100;\n return usual.some(\n (p) =>\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60) <= time &&\n time < zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)\n );\n }\n\n /**\n * Trading day and period of a time: the period that contains it, else the last period that started\n * before it. Null when no trading day is found near the time.\n */\n locate(time: number): { date: DateNum; index: number; inside: boolean } | null {\n const f = zonedFields(time, this.timezone);\n const local = toDateNum(f.year, f.month, f.day);\n for (let k = 0; k <= 5; k++) {\n const date = addDays(local, k);\n const index = this.periods(date).findIndex(([s, e]) => s <= time && time < e);\n if (index !== -1) return { date, index, inside: true };\n }\n for (let k = 5; k >= -7; k--) {\n const date = addDays(local, k);\n const periods = this.periods(date);\n let index = -1;\n periods.forEach(([s], i) => {\n if (s <= time) index = i;\n });\n if (index !== -1) return { date, index, inside: false };\n }\n return null;\n }\n\n /** First trading day in [from, to] (dates), or null. */\n firstTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n\n /** Last trading day in [from, to] (dates), or null. */\n lastTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n}\n\n/** Monday of the week of `date` (weeks run Monday to Sunday). */\nexport const weekStart = (date: DateNum): DateNum => addDays(date, -((weekdayOf(date) + 5) % 7));\n/** First day of the month of `date`. */\nexport const monthStart = (date: DateNum): DateNum => Math.floor(date / 100) * 100 + 1;\n/** Last day of the month of `date`. */\nexport const monthEnd = (date: DateNum): DateNum => addDays(monthStart(addDays(monthStart(date), 32)), -1);\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -69,7 +69,8 @@ export declare function barTimeAfter(time: number, count: number, chart: Session
69
69
  * Number of bars from the bar that opens at `from` to the bar that contains `to`, on the trading calendar of the
70
70
  * context (0 when `to` is in the bar of `from` or before it). For a drawing placed at a time after the last bar
71
71
  * (PineScript `time + n days` with `xloc.bar_time`): the chart shows it on the bar of that time, weekends and
72
- * holidays skipped. NaN when no bar is found within 100,000 bars.
72
+ * holidays skipped. `to` is first rounded to the nearest second, a half second going up (#174: 15 ms before a bar
73
+ * opens is on that bar, 501 ms before is on the bar before). NaN when no bar is found within 100,000 bars.
73
74
  */
74
75
  export declare function barsUntil(from: number, to: number, chart: SessionContext): number;
75
76
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/session/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAgB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAElE,6GAA6G;AAC7G,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC/B,sFAAsF;IACtF,cAAc,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IACtC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CACvB;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAKD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAgBlE;AAED,iHAAiH;AACjH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAGzF;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAG1F;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAG1E;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,cAAc,GAAG,YAAY,CAG/F;AAED,6GAA6G;AAC7G,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,qCAAqC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,CAyBtG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAKvF;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAWjF"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/session/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAA8B,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAElE,6GAA6G;AAC7G,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC/B,sFAAsF;IACtF,cAAc,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IACtC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CACvB;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAKD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAgBlE;AAED,iHAAiH;AACjH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAGzF;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAG1F;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAG1E;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,cAAc,GAAG,YAAY,CAG/F;AAED,6GAA6G;AAC7G,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,qCAAqC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,CA0BtG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAKvF;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAWjF"}
@@ -86,6 +86,7 @@ function resampleSession(bars, timeframe, chart) {
86
86
  const periods = at ? calendar.calendar.periods(at.date) : [];
87
87
  return periods.length ? calendar.timeOf(periods[0][0], tf.period) : starts[i];
88
88
  });
89
+ (0, import_bars.strayPeriods)(starts, dayPeriods, times, s.bars, tf.isdwm ? null : (0, import_timeframe.in_seconds)(tf.period) * 1e3);
89
90
  const reaches = times.map((t) => s.bars.closeOf(t, chartTf.period) >= calendar.periodEnd(t, tf.period));
90
91
  const { group, latest } = (0, import_resample.periodsOf)(starts, dayPeriods, reaches);
91
92
  const after = (0, import_resample.afterCompletion)(group, latest);
@@ -100,7 +101,7 @@ function barTimeAfter(time, count, chart) {
100
101
  }
101
102
  function barsUntil(from, to, chart) {
102
103
  const s = chartSessions(chart);
103
- const target = to * s.unit;
104
+ const target = Math.floor(to * s.unit / 1e3 + 0.5) * 1e3;
104
105
  let t = from * s.unit;
105
106
  for (let k = 0; k < 1e5; k++) {
106
107
  const next = s.bars.nextBarTime(t);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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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",
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, strayPeriods, 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 strayPeriods(starts, dayPeriods, times, s.bars, tf.isdwm ? null : in_seconds(tf.period) * 1000);\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. `to` is first rounded to the nearest second, a half second going up (#174: 15 ms before a bar\n * opens is on that bar, 501 ms before is on the bar before). 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 = Math.floor((to * s.unit) / 1000 + 0.5) * 1000;\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
6
  "names": ["group"]
7
7
  }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Decimal digits of a double as PineScript's plain `str.tostring` produces them.
3
+ *
4
+ * PineScript formats a number without a format with the digits of the classic binary to decimal conversion of the
5
+ * JVM (the one `Double.toString` used before Java 19), then rounds them to 10 decimals half to even, with two
6
+ * flags of the conversion deciding a tie on the last digit: digits that were rounded up are not rounded up again,
7
+ * digits that were cut on a '5' are. These digits are not always the shortest ones: 78195689105219552 keeps all
8
+ * its digits (the shortest form is 78195689105219550), and 1990736.000001111 has 16 digits where rounding the exact
9
+ * value would give 1990736.0000011111 (#174).
10
+ */
11
+ /** Digits d1d2d3... of |x| = 0.d1d2d3... × 10^decExponent, with the flags of the conversion. */
12
+ export interface ConvertedDigits {
13
+ digits: string;
14
+ decExponent: number;
15
+ /** The last digit was rounded up */
16
+ roundedUp: boolean;
17
+ /** The digits are the exact value of the double */
18
+ exact: boolean;
19
+ }
20
+ /** Digits of a positive finite double, as the JVM converter gives them. */
21
+ export declare function convertDouble(x: number): ConvertedDigits;
22
+ /**
23
+ * `|x|` with at most `maxFrac` decimals, as the JVM's decimal formatter rounds the converted digits (half to even;
24
+ * a tie on the last converted digit is decided by the conversion flags). Returns the digits and the position of the
25
+ * decimal point (value = 0.digits × 10^intLen); "" for zero.
26
+ */
27
+ export declare function javaFixedDigits(x: number, maxFrac: number): {
28
+ digits: string;
29
+ intLen: number;
30
+ };
31
+ //# sourceMappingURL=floatingdecimal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floatingdecimal.d.ts","sourceRoot":"","sources":["../../../src/str/floatingdecimal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gGAAgG;AAChG,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,KAAK,EAAE,OAAO,CAAC;CAChB;AAqCD,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,eAAe,CAuJxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAoD9F"}
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var floatingdecimal_exports = {};
20
+ __export(floatingdecimal_exports, {
21
+ convertDouble: () => convertDouble,
22
+ javaFixedDigits: () => javaFixedDigits
23
+ });
24
+ module.exports = __toCommonJS(floatingdecimal_exports);
25
+ const EXP_SHIFT = 52;
26
+ const MAX_SMALL_BIN_EXP = 62;
27
+ const MIN_SMALL_BIN_EXP = -21;
28
+ const N_5_BITS = [0, 3, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 56, 59, 61];
29
+ const INSIGNIFICANT_DIGITS = [
30
+ 0,
31
+ 0,
32
+ 0,
33
+ 0,
34
+ 1,
35
+ 1,
36
+ 1,
37
+ 2,
38
+ 2,
39
+ 2,
40
+ 3,
41
+ 3,
42
+ 3,
43
+ 3,
44
+ 4,
45
+ 4,
46
+ 4,
47
+ 5,
48
+ 5,
49
+ 5,
50
+ 6,
51
+ 6,
52
+ 6,
53
+ 6,
54
+ 7,
55
+ 7,
56
+ 7,
57
+ 8,
58
+ 8,
59
+ 8,
60
+ 9,
61
+ 9,
62
+ 9,
63
+ 9,
64
+ 10,
65
+ 10,
66
+ 10,
67
+ 11,
68
+ 11,
69
+ 11,
70
+ 12,
71
+ 12,
72
+ 12,
73
+ 12,
74
+ 13,
75
+ 13,
76
+ 13,
77
+ 14,
78
+ 14,
79
+ 14,
80
+ 15,
81
+ 15,
82
+ 15,
83
+ 15,
84
+ 16,
85
+ 16,
86
+ 16,
87
+ 17,
88
+ 17,
89
+ 17,
90
+ 18,
91
+ 18,
92
+ 18,
93
+ 19
94
+ ];
95
+ const LONG_5_POW_LENGTH = 27;
96
+ const view = new DataView(new ArrayBuffer(8));
97
+ const int32 = (v) => BigInt.asIntN(32, v);
98
+ const int64 = (v) => BigInt.asIntN(64, v);
99
+ function trailingZeros(v) {
100
+ let n = 0;
101
+ while ((v & 1n) === 0n) {
102
+ v >>= 1n;
103
+ n++;
104
+ }
105
+ return n;
106
+ }
107
+ function estimateDecExp(fractBits, binExp) {
108
+ view.setBigUint64(0, 1023n << 52n | fractBits & 0xfffffffffffffn);
109
+ const d2 = view.getFloat64(0);
110
+ const d = (d2 - 1.5) * 0.289529654 + 0.176091259 + binExp * 0.301029995663981;
111
+ const r = Math.trunc(d);
112
+ return d < 0 && r !== d ? r - 1 : r;
113
+ }
114
+ function convertDouble(x) {
115
+ view.setFloat64(0, Math.abs(x));
116
+ const bits = view.getBigUint64(0);
117
+ let fractBits = bits & 0xfffffffffffffn;
118
+ let binExp = Number(bits >> 52n & 0x7ffn);
119
+ if (binExp === 0 && fractBits === 0n) return { digits: "0", decExponent: 1, roundedUp: false, exact: false };
120
+ let nSignificantBits;
121
+ if (binExp === 0) {
122
+ const leadingZeros = 64 - fractBits.toString(2).length;
123
+ const shift = leadingZeros - (63 - EXP_SHIFT);
124
+ fractBits <<= BigInt(shift);
125
+ binExp = 1 - shift;
126
+ nSignificantBits = 64 - leadingZeros;
127
+ } else {
128
+ fractBits |= 1n << 52n;
129
+ nSignificantBits = EXP_SHIFT + 1;
130
+ }
131
+ binExp -= 1023;
132
+ const tailZeros = trailingZeros(fractBits);
133
+ const nFractBits = EXP_SHIFT + 1 - tailZeros;
134
+ const nTinyBits = Math.max(0, nFractBits - binExp - 1);
135
+ if (binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP && nTinyBits < LONG_5_POW_LENGTH && nFractBits + N_5_BITS[nTinyBits] < 64 && nTinyBits === 0) {
136
+ const p2 = binExp - nSignificantBits - 1;
137
+ const insignificant = binExp > nSignificantBits && p2 > 1 && p2 < INSIGNIFICANT_DIGITS.length ? INSIGNIFICANT_DIGITS[p2] : 0;
138
+ let lvalue = binExp >= EXP_SHIFT ? fractBits << BigInt(binExp - EXP_SHIFT) : fractBits >> BigInt(EXP_SHIFT - binExp);
139
+ if (insignificant !== 0) {
140
+ const pow10 = 10n ** BigInt(insignificant);
141
+ const residue = lvalue % pow10;
142
+ lvalue /= pow10;
143
+ if (residue >= pow10 >> 1n) lvalue++;
144
+ }
145
+ const s = lvalue.toString();
146
+ return { digits: s.replace(/0+$/, ""), decExponent: insignificant + s.length, roundedUp: false, exact: false };
147
+ }
148
+ let decExp = estimateDecExp(fractBits, binExp);
149
+ const B5 = Math.max(0, -decExp);
150
+ let B2 = B5 + nTinyBits + binExp;
151
+ const S5 = Math.max(0, decExp);
152
+ let S2 = S5 + nTinyBits;
153
+ const M5 = B5;
154
+ let M2 = B2 - nSignificantBits;
155
+ fractBits >>= BigInt(tailZeros);
156
+ B2 -= nFractBits - 1;
157
+ const common2factor = Math.min(B2, S2);
158
+ B2 -= common2factor;
159
+ S2 -= common2factor;
160
+ M2 -= common2factor;
161
+ if (nFractBits === 1) M2 -= 1;
162
+ if (M2 < 0) {
163
+ B2 -= M2;
164
+ S2 -= M2;
165
+ M2 = 0;
166
+ }
167
+ const digits = [];
168
+ let low;
169
+ let high;
170
+ let lowDigitDifference;
171
+ let exact;
172
+ const Bbits = nFractBits + B2 + (B5 < N_5_BITS.length ? N_5_BITS[B5] : B5 * 3);
173
+ const tenSbits = S2 + 1 + (S5 + 1 < N_5_BITS.length ? N_5_BITS[S5 + 1] : (S5 + 1) * 3);
174
+ const minTwo = decExp < -3 || decExp >= 8;
175
+ if (Bbits < 64 && tenSbits < 64) {
176
+ const wrap = Bbits < 32 && tenSbits < 32 ? int32 : int64;
177
+ let b = wrap(fractBits * 5n ** BigInt(B5) << BigInt(B2));
178
+ const s = wrap(5n ** BigInt(S5) << BigInt(S2));
179
+ let m = wrap(5n ** BigInt(M5) << BigInt(M2));
180
+ const tens = wrap(s * 10n);
181
+ let q = Number(b / s);
182
+ b = wrap(10n * (b % s));
183
+ m = wrap(m * 10n);
184
+ low = b < m;
185
+ high = wrap(b + m) > tens;
186
+ if (q === 0 && !high) decExp--;
187
+ else digits.push(q);
188
+ if (minTwo) high = low = false;
189
+ while (!low && !high) {
190
+ q = Number(b / s);
191
+ b = wrap(10n * (b % s));
192
+ m = wrap(m * 10n);
193
+ if (m > 0n) {
194
+ low = b < m;
195
+ high = wrap(b + m) > tens;
196
+ } else {
197
+ low = true;
198
+ high = true;
199
+ }
200
+ digits.push(q);
201
+ }
202
+ lowDigitDifference = wrap((b << 1n) - tens);
203
+ exact = b === 0n;
204
+ } else {
205
+ let b = fractBits * 5n ** BigInt(B5) << BigInt(B2);
206
+ const s = 5n ** BigInt(S5) << BigInt(S2);
207
+ let m = 5n ** BigInt(M5 + 1) << BigInt(M2 + 1);
208
+ const tens = s * 10n;
209
+ let q = Number(b / s);
210
+ b = b % s * 10n;
211
+ low = b < m;
212
+ high = b + m >= tens;
213
+ if (q === 0 && !high) decExp--;
214
+ else digits.push(q);
215
+ if (minTwo) high = low = false;
216
+ while (!low && !high) {
217
+ q = Number(b / s);
218
+ b = b % s * 10n;
219
+ m *= 10n;
220
+ low = b < m;
221
+ high = b + m >= tens;
222
+ digits.push(q);
223
+ }
224
+ lowDigitDifference = high && low ? (b << 1n) - tens : 0n;
225
+ exact = b === 0n;
226
+ }
227
+ let decExponent = decExp + 1;
228
+ let roundedUp = false;
229
+ const roundup = () => {
230
+ let i = digits.length - 1;
231
+ while (digits[i] === 9 && i > 0) digits[i--] = 0;
232
+ if (digits[i] === 9) {
233
+ decExponent += 1;
234
+ digits[0] = 1;
235
+ return;
236
+ }
237
+ digits[i] += 1;
238
+ roundedUp = true;
239
+ };
240
+ if (high) {
241
+ if (!low) roundup();
242
+ else if (lowDigitDifference === 0n) {
243
+ if (digits[digits.length - 1] % 2 !== 0) roundup();
244
+ } else if (lowDigitDifference > 0n) roundup();
245
+ }
246
+ const text = digits.join("").replace(/0+$/, "");
247
+ return { digits: text || "0", decExponent, roundedUp, exact };
248
+ }
249
+ function javaFixedDigits(x, maxFrac) {
250
+ if (x === 0) return { digits: "", intLen: 0 };
251
+ const c = convertDouble(x);
252
+ const digits = [...c.digits].map(Number);
253
+ let decimalAt = c.decExponent;
254
+ const n = digits.length;
255
+ if (decimalAt > 0 && decimalAt < 8) {
256
+ if (n <= decimalAt) digits.push(...Array(decimalAt - n + 1).fill(0));
257
+ } else if (decimalAt <= -3 || decimalAt >= 8) {
258
+ if (n === 1) digits.push(0);
259
+ }
260
+ let count = digits.length;
261
+ const shouldRoundUp = (keep2) => {
262
+ if (keep2 >= count) return false;
263
+ if (digits[keep2] > 5) return true;
264
+ if (digits[keep2] !== 5) return false;
265
+ if (keep2 === count - 1) {
266
+ if (c.exact) return keep2 > 0 && digits[keep2 - 1] % 2 !== 0;
267
+ return !c.roundedUp;
268
+ }
269
+ for (let i = keep2 + 1; i < count; i++) if (digits[i] !== 0) return true;
270
+ return false;
271
+ };
272
+ if (-decimalAt > maxFrac) return { digits: "", intLen: 0 };
273
+ if (-decimalAt === maxFrac) {
274
+ return shouldRoundUp(0) ? { digits: "1", intLen: decimalAt + 1 } : { digits: "", intLen: 0 };
275
+ }
276
+ while (count > 1 && digits[count - 1] === 0) count--;
277
+ let keep = maxFrac + decimalAt;
278
+ if (keep >= 0 && keep < count) {
279
+ if (shouldRoundUp(keep)) {
280
+ do {
281
+ keep--;
282
+ if (keep < 0) {
283
+ digits[0] = 1;
284
+ decimalAt++;
285
+ keep = 0;
286
+ break;
287
+ }
288
+ digits[keep] += 1;
289
+ } while (digits[keep] > 9);
290
+ keep++;
291
+ }
292
+ count = keep;
293
+ while (count > 1 && digits[count - 1] === 0) count--;
294
+ }
295
+ const text = digits.slice(0, count).join("").replace(/0+$/, "");
296
+ return { digits: text, intLen: text ? decimalAt : 0 };
297
+ }
298
+ //# sourceMappingURL=floatingdecimal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/str/floatingdecimal.ts"],
4
+ "sourcesContent": ["/**\n * Decimal digits of a double as PineScript's plain `str.tostring` produces them.\n *\n * PineScript formats a number without a format with the digits of the classic binary to decimal conversion of the\n * JVM (the one `Double.toString` used before Java 19), then rounds them to 10 decimals half to even, with two\n * flags of the conversion deciding a tie on the last digit: digits that were rounded up are not rounded up again,\n * digits that were cut on a '5' are. These digits are not always the shortest ones: 78195689105219552 keeps all\n * its digits (the shortest form is 78195689105219550), and 1990736.000001111 has 16 digits where rounding the exact\n * value would give 1990736.0000011111 (#174).\n */\n\n/** Digits d1d2d3... of |x| = 0.d1d2d3... \u00D7 10^decExponent, with the flags of the conversion. */\nexport interface ConvertedDigits {\n digits: string;\n decExponent: number;\n /** The last digit was rounded up */\n roundedUp: boolean;\n /** The digits are the exact value of the double */\n exact: boolean;\n}\n\nconst EXP_SHIFT = 52;\nconst MAX_SMALL_BIN_EXP = 62;\nconst MIN_SMALL_BIN_EXP = -21;\n// approximately ceil(log2(5^i))\nconst N_5_BITS = [0, 3, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 56, 59, 61];\n// number of decimal digits of 2^i minus one, for i < 64\nconst INSIGNIFICANT_DIGITS = [\n 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10,\n 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19,\n];\nconst LONG_5_POW_LENGTH = 27;\nconst view = new DataView(new ArrayBuffer(8));\n\nconst int32 = (v: bigint): bigint => BigInt.asIntN(32, v);\nconst int64 = (v: bigint): bigint => BigInt.asIntN(64, v);\n\nfunction trailingZeros(v: bigint): number {\n let n = 0;\n while ((v & 1n) === 0n) {\n v >>= 1n;\n n++;\n }\n return n;\n}\n\n/** floor(log10(x)) estimate of the conversion (it can be one too high). */\nfunction estimateDecExp(fractBits: bigint, binExp: number): number {\n view.setBigUint64(0, (1023n << 52n) | (fractBits & 0xfffffffffffffn));\n const d2 = view.getFloat64(0);\n const d = (d2 - 1.5) * 0.289529654 + 0.176091259 + binExp * 0.301029995663981;\n // the conversion truncates toward zero and corrects negative non-integers down\n const r = Math.trunc(d);\n return d < 0 && r !== d ? r - 1 : r;\n}\n\n/** Digits of a positive finite double, as the JVM converter gives them. */\nexport function convertDouble(x: number): ConvertedDigits {\n view.setFloat64(0, Math.abs(x));\n const bits = view.getBigUint64(0);\n let fractBits = bits & 0xfffffffffffffn;\n let binExp = Number((bits >> 52n) & 0x7ffn);\n if (binExp === 0 && fractBits === 0n) return { digits: '0', decExponent: 1, roundedUp: false, exact: false };\n let nSignificantBits: number;\n if (binExp === 0) {\n const leadingZeros = 64 - fractBits.toString(2).length;\n const shift = leadingZeros - (63 - EXP_SHIFT);\n fractBits <<= BigInt(shift);\n binExp = 1 - shift;\n nSignificantBits = 64 - leadingZeros;\n } else {\n fractBits |= 1n << 52n;\n nSignificantBits = EXP_SHIFT + 1;\n }\n binExp -= 1023;\n\n const tailZeros = trailingZeros(fractBits);\n const nFractBits = EXP_SHIFT + 1 - tailZeros;\n const nTinyBits = Math.max(0, nFractBits - binExp - 1);\n\n // an integer that fits in a long: its digits, the low ones rounded away when they are not significant\n if (\n binExp <= MAX_SMALL_BIN_EXP &&\n binExp >= MIN_SMALL_BIN_EXP &&\n nTinyBits < LONG_5_POW_LENGTH &&\n nFractBits + N_5_BITS[nTinyBits]! < 64 &&\n nTinyBits === 0\n ) {\n const p2 = binExp - nSignificantBits - 1;\n const insignificant = binExp > nSignificantBits && p2 > 1 && p2 < INSIGNIFICANT_DIGITS.length ? INSIGNIFICANT_DIGITS[p2]! : 0;\n let lvalue = binExp >= EXP_SHIFT ? fractBits << BigInt(binExp - EXP_SHIFT) : fractBits >> BigInt(EXP_SHIFT - binExp);\n if (insignificant !== 0) {\n const pow10 = 10n ** BigInt(insignificant);\n const residue = lvalue % pow10;\n lvalue /= pow10;\n if (residue >= pow10 >> 1n) lvalue++;\n }\n const s = lvalue.toString();\n return { digits: s.replace(/0+$/, ''), decExponent: insignificant + s.length, roundedUp: false, exact: false };\n }\n\n let decExp = estimateDecExp(fractBits, binExp);\n const B5 = Math.max(0, -decExp);\n let B2 = B5 + nTinyBits + binExp;\n const S5 = Math.max(0, decExp);\n let S2 = S5 + nTinyBits;\n const M5 = B5;\n let M2 = B2 - nSignificantBits;\n\n fractBits >>= BigInt(tailZeros);\n B2 -= nFractBits - 1;\n const common2factor = Math.min(B2, S2);\n B2 -= common2factor;\n S2 -= common2factor;\n M2 -= common2factor;\n // an exact power of two: the next smaller double is half as far\n if (nFractBits === 1) M2 -= 1;\n if (M2 < 0) {\n B2 -= M2;\n S2 -= M2;\n M2 = 0;\n }\n\n const digits: number[] = [];\n let low: boolean;\n let high: boolean;\n let lowDigitDifference: bigint;\n let exact: boolean;\n const Bbits = nFractBits + B2 + (B5 < N_5_BITS.length ? N_5_BITS[B5]! : B5 * 3);\n const tenSbits = S2 + 1 + (S5 + 1 < N_5_BITS.length ? N_5_BITS[S5 + 1]! : (S5 + 1) * 3);\n // two digits at least in the exponent form\n const minTwo = decExp < -3 || decExp >= 8;\n\n if (Bbits < 64 && tenSbits < 64) {\n // int or long arithmetic, with its overflows\n const wrap = Bbits < 32 && tenSbits < 32 ? int32 : int64;\n let b = wrap((fractBits * 5n ** BigInt(B5)) << BigInt(B2));\n const s = wrap(5n ** BigInt(S5) << BigInt(S2));\n let m = wrap(5n ** BigInt(M5) << BigInt(M2));\n const tens = wrap(s * 10n);\n let q = Number(b / s);\n b = wrap(10n * (b % s));\n m = wrap(m * 10n);\n low = b < m;\n high = wrap(b + m) > tens;\n if (q === 0 && !high) decExp--;\n else digits.push(q);\n if (minTwo) high = low = false;\n while (!low && !high) {\n q = Number(b / s);\n b = wrap(10n * (b % s));\n m = wrap(m * 10n);\n if (m > 0n) {\n low = b < m;\n high = wrap(b + m) > tens;\n } else {\n low = true;\n high = true;\n }\n digits.push(q);\n }\n lowDigitDifference = wrap((b << 1n) - tens);\n exact = b === 0n;\n } else {\n let b = (fractBits * 5n ** BigInt(B5)) << BigInt(B2);\n const s = 5n ** BigInt(S5) << BigInt(S2);\n let m = 5n ** BigInt(M5 + 1) << BigInt(M2 + 1);\n const tens = s * 10n;\n let q = Number(b / s);\n b = (b % s) * 10n;\n low = b < m;\n high = b + m >= tens;\n if (q === 0 && !high) decExp--;\n else digits.push(q);\n if (minTwo) high = low = false;\n while (!low && !high) {\n q = Number(b / s);\n b = (b % s) * 10n;\n m *= 10n;\n low = b < m;\n high = b + m >= tens;\n digits.push(q);\n }\n lowDigitDifference = high && low ? (b << 1n) - tens : 0n;\n exact = b === 0n;\n }\n\n let decExponent = decExp + 1;\n let roundedUp = false;\n const roundup = (): void => {\n let i = digits.length - 1;\n while (digits[i] === 9 && i > 0) digits[i--] = 0;\n if (digits[i] === 9) {\n decExponent += 1;\n digits[0] = 1;\n return;\n }\n digits[i]! += 1;\n roundedUp = true;\n };\n if (high) {\n if (!low) roundup();\n else if (lowDigitDifference === 0n) {\n if (digits[digits.length - 1]! % 2 !== 0) roundup();\n } else if (lowDigitDifference > 0n) roundup();\n }\n const text = digits.join('').replace(/0+$/, '');\n return { digits: text || '0', decExponent, roundedUp, exact };\n}\n\n/**\n * `|x|` with at most `maxFrac` decimals, as the JVM's decimal formatter rounds the converted digits (half to even;\n * a tie on the last converted digit is decided by the conversion flags). Returns the digits and the position of the\n * decimal point (value = 0.digits \u00D7 10^intLen); \"\" for zero.\n */\nexport function javaFixedDigits(x: number, maxFrac: number): { digits: string; intLen: number } {\n if (x === 0) return { digits: '', intLen: 0 };\n const c = convertDouble(x);\n const digits = [...c.digits].map(Number);\n let decimalAt = c.decExponent;\n // the formatter reads the conversion's text, which ends in \".0\" when no digit follows the point (\"5.0E-11\",\n // \"120.0\"): that zero is a digit for the tie test below (5e-11 to 10 decimals gives 0)\n const n = digits.length;\n if (decimalAt > 0 && decimalAt < 8) {\n if (n <= decimalAt) digits.push(...Array<number>(decimalAt - n + 1).fill(0));\n } else if (decimalAt <= -3 || decimalAt >= 8) {\n if (n === 1) digits.push(0);\n }\n let count = digits.length;\n\n const shouldRoundUp = (keep: number): boolean => {\n if (keep >= count) return false;\n if (digits[keep]! > 5) return true;\n if (digits[keep] !== 5) return false;\n if (keep === count - 1) {\n if (c.exact) return keep > 0 && digits[keep - 1]! % 2 !== 0;\n return !c.roundedUp;\n }\n for (let i = keep + 1; i < count; i++) if (digits[i] !== 0) return true;\n return false;\n };\n\n if (-decimalAt > maxFrac) return { digits: '', intLen: 0 };\n if (-decimalAt === maxFrac) {\n return shouldRoundUp(0) ? { digits: '1', intLen: decimalAt + 1 } : { digits: '', intLen: 0 };\n }\n while (count > 1 && digits[count - 1] === 0) count--;\n let keep = maxFrac + decimalAt;\n if (keep >= 0 && keep < count) {\n if (shouldRoundUp(keep)) {\n do {\n keep--;\n if (keep < 0) {\n digits[0] = 1;\n decimalAt++;\n keep = 0;\n break;\n }\n digits[keep]! += 1;\n } while (digits[keep]! > 9);\n keep++;\n }\n count = keep;\n while (count > 1 && digits[count - 1] === 0) count--;\n }\n const text = digits.slice(0, count).join('').replace(/0+$/, '');\n return { digits: text, intLen: text ? decimalAt : 0 };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["keep"]
7
+ }
@@ -37,9 +37,10 @@ export declare function length(str: simple_string): int;
37
37
  *
38
38
  * @remarks
39
39
  * As in PineScript:
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"
40
+ * - without format, numbers show up to 10 decimals ("0.3333333333"): the digits of the JVM's binary to decimal
41
+ * conversion, rounded half to even, a tie on the last converted digit decided by the conversion
42
+ * (82325.10000000005 gives "82325.1", with the format "#.##########" "82325.1000000001"; 1791662400001 / 900000
43
+ * gives "1990736.000001111"; 78195689105219552 keeps its 17 digits); 1e21 and more use "1E21"
43
44
  * - patterns round half away from zero on the shortest decimal form: 1.005 with "#.##" gives "1.01";
44
45
  * at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives "0.0012345678901235")
45
46
  * - "pos;neg" patterns: a negative number takes the text of the negative subpattern ("#.##;(#.##)": "(3.5)")
@@ -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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"}
@@ -60,7 +60,7 @@ function tostring(value, format2, mintick, pricescale) {
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, "#.##########", "halfUpExact");
63
+ return (0, import_numberformat.formatNumber)(value, "#.##########", "halfEvenConverted");
64
64
  }
65
65
  switch (format2) {
66
66
  case "percent":