gloomberb 0.18.4 → 0.19.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 (600) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +169 -5
  5. package/src/account/auth-model.ts +7 -3
  6. package/src/api-client/auth.ts +9 -4
  7. package/src/api-client/catalysts.ts +2 -1
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +31 -6
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/index.ts +171 -11
  14. package/src/api-client/live-scan.ts +91 -0
  15. package/src/api-client/market-discovery.ts +10 -0
  16. package/src/api-client/market-movers.ts +7 -0
  17. package/src/api-client/members.ts +13 -1
  18. package/src/api-client/perps.ts +43 -0
  19. package/src/api-client/request.ts +222 -41
  20. package/src/api-client/research-activity.ts +6 -12
  21. package/src/api-client/types.ts +74 -0
  22. package/src/app/keybindings/index.ts +3 -0
  23. package/src/app/keybindings/labels.ts +18 -1
  24. package/src/app/keybindings/resolve.ts +14 -0
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +42 -27
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +1 -1
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/fred-series.ts +5 -0
  35. package/src/cli/commands/keybindings.ts +23 -0
  36. package/src/cli/commands/market.ts +138 -46
  37. package/src/cli/commands/options-chain.ts +107 -10
  38. package/src/cli/commands/options-leaps.ts +9 -3
  39. package/src/cli/commands/overview.ts +176 -39
  40. package/src/cli/commands/search.ts +39 -3
  41. package/src/cli/commands/themes.ts +11 -2
  42. package/src/cli/commands/ticker.ts +57 -35
  43. package/src/cli/company-name-hint.ts +41 -0
  44. package/src/cli/earnings-calendar.ts +1 -1
  45. package/src/cli/errors.ts +10 -2
  46. package/src/cli/export-source.ts +29 -1
  47. package/src/cli/glossary.ts +73 -1
  48. package/src/cli/history-rows.ts +87 -16
  49. package/src/cli/index.ts +2 -0
  50. package/src/cli/known-gaps.ts +5 -2
  51. package/src/cli/listing-arg.ts +195 -56
  52. package/src/cli/output-file.ts +68 -0
  53. package/src/cli/pane-functions/capabilities.ts +3 -0
  54. package/src/cli/pane-functions/catalog.ts +80 -7
  55. package/src/cli/pane-functions/dom.ts +13 -3
  56. package/src/cli/pane-functions/freshness.ts +111 -16
  57. package/src/cli/pane-functions/function-help.ts +23 -13
  58. package/src/cli/pane-functions/headless.ts +56 -12
  59. package/src/cli/pane-functions/index.ts +58 -16
  60. package/src/cli/pane-functions/market-session.ts +81 -9
  61. package/src/cli/pane-functions/options.ts +19 -1
  62. package/src/cli/pane-functions/report.ts +5 -1
  63. package/src/cli/pane-functions/resolver.ts +35 -8
  64. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  65. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  66. package/src/cli/quote-answer.ts +30 -13
  67. package/src/cli/report-tables.ts +18 -7
  68. package/src/cli/restore-plugins.ts +13 -1
  69. package/src/cli/result.ts +57 -5
  70. package/src/cli/ticker-resolution.ts +3 -2
  71. package/src/components/chart/composite/composite-chart.tsx +26 -4
  72. package/src/components/chart/composite/format.ts +27 -0
  73. package/src/components/chart/composite/interactions.ts +6 -0
  74. package/src/components/chart/composite/legend.tsx +45 -7
  75. package/src/components/chart/composite/levels.ts +2 -0
  76. package/src/components/chart/composite/panel-gestures.ts +33 -0
  77. package/src/components/chart/composite/panel-series.ts +1 -0
  78. package/src/components/chart/composite/panel-surface.tsx +61 -5
  79. package/src/components/chart/composite/range-statistics.ts +62 -0
  80. package/src/components/chart/composite/rasterizer.ts +24 -13
  81. package/src/components/chart/composite/scene.ts +46 -7
  82. package/src/components/chart/composite/series-axis-tags.tsx +16 -9
  83. package/src/components/chart/composite/series-cues.ts +14 -0
  84. package/src/components/chart/composite/text-renderer.ts +9 -2
  85. package/src/components/chart/composite/tool-catalog.ts +36 -0
  86. package/src/components/chart/composite/tools.ts +203 -44
  87. package/src/components/chart/composite/types.ts +4 -0
  88. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  89. package/src/components/command-bar/assist/inventory.ts +2 -1
  90. package/src/components/command-bar/commands/direct/index.ts +28 -13
  91. package/src/components/command-bar/pane-templates/items.ts +3 -2
  92. package/src/components/command-bar/panel/prompt-binding.ts +0 -8
  93. package/src/components/command-bar/workflow/alert.ts +1 -1
  94. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  95. package/src/components/layout/header.tsx +26 -40
  96. package/src/components/layout/pane/footer/index.tsx +1 -0
  97. package/src/components/layout/pane/footer/model.ts +27 -0
  98. package/src/components/onboarding/onboarding-frame.tsx +1 -1
  99. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  100. package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
  101. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  102. package/src/components/onboarding/wizard-positions.ts +3 -3
  103. package/src/components/ticker/list-table-view.tsx +4 -1
  104. package/src/components/ui/choice-dialog.tsx +23 -18
  105. package/src/components/ui/choice-groups.ts +18 -0
  106. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  107. package/src/components/ui/data-table/opentui/model.ts +26 -1
  108. package/src/components/ui/data-table/types.ts +2 -0
  109. package/src/components/ui/display.tsx +5 -1
  110. package/src/components/ui/list-view.tsx +8 -4
  111. package/src/components/ui/query-bar-keys.tsx +2 -0
  112. package/src/components/ui/query-bar.tsx +1 -0
  113. package/src/components/ui/select-button.tsx +5 -1
  114. package/src/components/ui/select-field.tsx +1 -0
  115. package/src/components/ui/table-layout.ts +8 -5
  116. package/src/core/app-runtime.ts +2 -0
  117. package/src/core/app-services.ts +2 -2
  118. package/src/core/state/app/layout.ts +2 -0
  119. package/src/core/state/app/state.ts +9 -0
  120. package/src/core/state/app/types.ts +3 -0
  121. package/src/data/config/chart-settings.ts +1 -1
  122. package/src/data/json-ticker-repository.ts +17 -8
  123. package/src/data/saved-copies.ts +55 -16
  124. package/src/data/ticker-repository.ts +16 -9
  125. package/src/data/ticker-writes.ts +81 -0
  126. package/src/data/ttl-cache.ts +165 -0
  127. package/src/docs/snapshot.json +427 -216
  128. package/src/i18n/es.ts +19 -3
  129. package/src/i18n/ja.ts +19 -3
  130. package/src/i18n/ko.ts +19 -3
  131. package/src/i18n/zh-cn.ts +19 -3
  132. package/src/i18n/zh-tw.ts +19 -3
  133. package/src/market-data/coordinator/financials.ts +10 -2
  134. package/src/market-data/coordinator/index.ts +3 -1
  135. package/src/market-data/coordinator/quotes.ts +4 -2
  136. package/src/market-data/depositary-pair.ts +2 -2
  137. package/src/market-data/fund-facts.ts +50 -0
  138. package/src/market-data/market/change-basis.ts +48 -7
  139. package/src/market-data/market/dated-previous-close.ts +203 -0
  140. package/src/market-data/market/format.ts +25 -0
  141. package/src/market-data/market/freshness.ts +2 -1
  142. package/src/market-data/market/futures-week.ts +68 -0
  143. package/src/market-data/market/status.ts +16 -4
  144. package/src/market-data/options-alternatives.ts +141 -4
  145. package/src/market-data/quotes/contributions.ts +20 -2
  146. package/src/market-data/quotes/resolution.ts +3 -0
  147. package/src/market-data/selectors.ts +3 -0
  148. package/src/news/aggregator.ts +113 -9
  149. package/src/news/cloud-session.ts +21 -0
  150. package/src/news/hooks.ts +1 -0
  151. package/src/news/news-model.ts +1 -0
  152. package/src/news/types.ts +11 -0
  153. package/src/notifications/notification-log.ts +6 -4
  154. package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
  155. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  156. package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
  157. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  158. package/src/plugins/builtin/alerts/cli.ts +380 -82
  159. package/src/plugins/builtin/alerts/command.ts +31 -0
  160. package/src/plugins/builtin/alerts/constants.ts +1 -1
  161. package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
  162. package/src/plugins/builtin/alerts/fired-log.ts +113 -8
  163. package/src/plugins/builtin/alerts/format.ts +80 -4
  164. package/src/plugins/builtin/alerts/headless.ts +30 -7
  165. package/src/plugins/builtin/alerts/history-rows.ts +7 -3
  166. package/src/plugins/builtin/alerts/history.ts +2 -0
  167. package/src/plugins/builtin/alerts/index.tsx +135 -22
  168. package/src/plugins/builtin/alerts/live.ts +3 -3
  169. package/src/plugins/builtin/alerts/metric-command.ts +134 -0
  170. package/src/plugins/builtin/alerts/metric-data.ts +111 -0
  171. package/src/plugins/builtin/alerts/metric-model.ts +324 -0
  172. package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
  173. package/src/plugins/builtin/alerts/pane.tsx +90 -39
  174. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  175. package/src/plugins/builtin/alerts/storage.ts +37 -10
  176. package/src/plugins/builtin/alerts/types.ts +7 -0
  177. package/src/plugins/builtin/analytics/index.tsx +2 -1
  178. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  179. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  180. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  181. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  182. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  183. package/src/plugins/builtin/attention/index.tsx +2 -1
  184. package/src/plugins/builtin/attention/listing.ts +14 -0
  185. package/src/plugins/builtin/backtest/headless.ts +10 -5
  186. package/src/plugins/builtin/backtest/pane.tsx +9 -4
  187. package/src/plugins/builtin/backtest/presets.ts +29 -3
  188. package/src/plugins/builtin/broker-manager/index.tsx +10 -5
  189. package/src/plugins/builtin/broker-manager/model.ts +6 -1
  190. package/src/plugins/builtin/broker-trading/controller.ts +22 -12
  191. package/src/plugins/builtin/broker-trading/index.tsx +13 -1
  192. package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
  193. package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
  194. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  195. package/src/plugins/builtin/buildout/format.ts +38 -0
  196. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  197. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  198. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  199. package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
  200. package/src/plugins/builtin/catalysts/model.ts +2 -1
  201. package/src/plugins/builtin/changelog/index.tsx +9 -6
  202. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
  203. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
  204. package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
  205. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
  206. package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
  207. package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
  208. package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
  209. package/src/plugins/builtin/chart-composer/formula.ts +71 -18
  210. package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
  211. package/src/plugins/builtin/chart-composer/headless.ts +19 -3
  212. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  213. package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
  214. package/src/plugins/builtin/chart-composer/presets.ts +50 -15
  215. package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
  216. package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
  217. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  218. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
  219. package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
  220. package/src/plugins/builtin/chat/content/composer.tsx +13 -6
  221. package/src/plugins/builtin/chat/content/footer.tsx +6 -3
  222. package/src/plugins/builtin/chat/content/index.tsx +50 -21
  223. package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
  224. package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
  225. package/src/plugins/builtin/chat/index.tsx +23 -0
  226. package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
  227. package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
  228. package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
  229. package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
  230. package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
  231. package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
  232. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
  233. package/src/plugins/builtin/clinical-trials/model.ts +0 -9
  234. package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
  235. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
  236. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  237. package/src/plugins/builtin/cloud/plugin.tsx +1 -1
  238. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  239. package/src/plugins/builtin/company-attention/client.ts +8 -9
  240. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  241. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  242. package/src/plugins/builtin/company-attention/pane.tsx +9 -2
  243. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  244. package/src/plugins/builtin/correlation/headless.ts +13 -5
  245. package/src/plugins/builtin/correlation/history.ts +22 -0
  246. package/src/plugins/builtin/cot/client.ts +22 -18
  247. package/src/plugins/builtin/cot/headless.ts +15 -10
  248. package/src/plugins/builtin/cot/model.ts +14 -1
  249. package/src/plugins/builtin/cot/pane.tsx +22 -14
  250. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  251. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  252. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  253. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  254. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  255. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  256. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  257. package/src/plugins/builtin/credit-documents/headless.ts +7 -7
  258. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  259. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  260. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  261. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  262. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  263. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  264. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  265. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  266. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  267. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  268. package/src/plugins/builtin/docs/targets.ts +5 -1
  269. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  270. package/src/plugins/builtin/earnings/board.tsx +62 -71
  271. package/src/plugins/builtin/earnings/headless.ts +40 -4
  272. package/src/plugins/builtin/earnings/scope.ts +31 -43
  273. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  274. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  275. package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
  276. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  277. package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
  278. package/src/plugins/builtin/econ/calendar-model.ts +14 -9
  279. package/src/plugins/builtin/econ/figures.ts +72 -0
  280. package/src/plugins/builtin/econ/index.tsx +33 -3
  281. package/src/plugins/builtin/econ/types.ts +4 -0
  282. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  283. package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
  284. package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
  285. package/src/plugins/builtin/econ-statistics/view.ts +3 -0
  286. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  287. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  288. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  289. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  290. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  291. package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
  292. package/src/plugins/builtin/exposure/charts.tsx +2 -2
  293. package/src/plugins/builtin/exposure/model.ts +13 -1
  294. package/src/plugins/builtin/exposure/pane.tsx +4 -4
  295. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  296. package/src/plugins/builtin/futures/index.tsx +1 -0
  297. package/src/plugins/builtin/futures/model.ts +2 -1
  298. package/src/plugins/builtin/futures/table.tsx +2 -2
  299. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  300. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  301. package/src/plugins/builtin/fx-matrix/client.ts +11 -3
  302. package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
  303. package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
  304. package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
  305. package/src/plugins/builtin/gpu/headless.ts +8 -2
  306. package/src/plugins/builtin/help/function-index.ts +4 -1
  307. package/src/plugins/builtin/help/index.tsx +1 -0
  308. package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
  309. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  310. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  311. package/src/plugins/builtin/holders/headless.ts +18 -1
  312. package/src/plugins/builtin/iv-history/client.ts +52 -11
  313. package/src/plugins/builtin/iv-history/format.ts +36 -23
  314. package/src/plugins/builtin/iv-history/headless.ts +36 -18
  315. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  316. package/src/plugins/builtin/iv-history/model.ts +163 -27
  317. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  318. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  319. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  320. package/src/plugins/builtin/jobs/client.ts +25 -29
  321. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  322. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  323. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  324. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  325. package/src/plugins/builtin/macro-day/pane.tsx +7 -5
  326. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  327. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  328. package/src/plugins/builtin/market-movers/client.ts +25 -7
  329. package/src/plugins/builtin/market-movers/footer.ts +2 -2
  330. package/src/plugins/builtin/market-movers/headless.ts +32 -12
  331. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  332. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  333. package/src/plugins/builtin/market-movers/model.ts +4 -1
  334. package/src/plugins/builtin/market-movers/screener.ts +14 -0
  335. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  336. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  337. package/src/plugins/builtin/market-movers/table.ts +17 -5
  338. package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
  339. package/src/plugins/builtin/members/client.ts +19 -4
  340. package/src/plugins/builtin/members/headless.ts +20 -7
  341. package/src/plugins/builtin/members/index.tsx +9 -9
  342. package/src/plugins/builtin/members/model.ts +23 -4
  343. package/src/plugins/builtin/members/pane.tsx +25 -15
  344. package/src/plugins/builtin/news/index.tsx +1 -0
  345. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  346. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  347. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  348. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  349. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  350. package/src/plugins/builtin/openfda/client.ts +14 -2
  351. package/src/plugins/builtin/openfda/index.tsx +3 -2
  352. package/src/plugins/builtin/openfda/model.ts +9 -0
  353. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  354. package/src/plugins/builtin/options/analytics.ts +54 -6
  355. package/src/plugins/builtin/options/enrichment.ts +17 -5
  356. package/src/plugins/builtin/options/model-spot.ts +5 -2
  357. package/src/plugins/builtin/options/strike-window.ts +2 -1
  358. package/src/plugins/builtin/options/table.ts +6 -9
  359. package/src/plugins/builtin/options/types.ts +2 -0
  360. package/src/plugins/builtin/options/view.tsx +42 -12
  361. package/src/plugins/builtin/options-positioning/client.ts +30 -0
  362. package/src/plugins/builtin/options-positioning/headless.ts +74 -21
  363. package/src/plugins/builtin/options-positioning/index.tsx +4 -4
  364. package/src/plugins/builtin/options-positioning/model.ts +122 -14
  365. package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
  366. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  367. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  368. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  369. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  370. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  371. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  372. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  373. package/src/plugins/builtin/pe-band/model.ts +12 -49
  374. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  375. package/src/plugins/builtin/perps/cascade.ts +236 -0
  376. package/src/plugins/builtin/perps/client.ts +56 -2
  377. package/src/plugins/builtin/perps/freshness.ts +12 -10
  378. package/src/plugins/builtin/perps/headless.ts +65 -9
  379. package/src/plugins/builtin/perps/index.tsx +7 -5
  380. package/src/plugins/builtin/perps/live-rows.ts +10 -3
  381. package/src/plugins/builtin/perps/model.ts +18 -4
  382. package/src/plugins/builtin/perps/pane.tsx +32 -31
  383. package/src/plugins/builtin/perps/status-line.ts +25 -0
  384. package/src/plugins/builtin/plugin-module.ts +5 -1
  385. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  386. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  387. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  388. package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
  389. package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
  390. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  391. package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
  392. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  393. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  394. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
  395. package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
  396. package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
  397. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  398. package/src/plugins/builtin/rate-path/pane.tsx +2 -2
  399. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  400. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  401. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  402. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  403. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  404. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  405. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  406. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  407. package/src/plugins/builtin/research/event-model.ts +10 -8
  408. package/src/plugins/builtin/research/headless.ts +1 -1
  409. package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
  410. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  411. package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
  412. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  413. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  414. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  415. package/src/plugins/builtin/scanner/feed.ts +2 -2
  416. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  417. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  418. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  419. package/src/plugins/builtin/scanner/headless.ts +204 -35
  420. package/src/plugins/builtin/scanner/index.ts +40 -6
  421. package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
  422. package/src/plugins/builtin/scanner/live-feed.ts +168 -0
  423. package/src/plugins/builtin/scanner/live-headless.ts +138 -0
  424. package/src/plugins/builtin/scanner/live-model.ts +362 -0
  425. package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
  426. package/src/plugins/builtin/scanner/live-track.ts +186 -0
  427. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  428. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  429. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  430. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  431. package/src/plugins/builtin/sec/headless.ts +2 -1
  432. package/src/plugins/builtin/series-inventory.ts +3 -0
  433. package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
  434. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  435. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  436. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  437. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  438. package/src/plugins/builtin/shared/macro-releases.ts +65 -0
  439. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  440. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  441. package/src/plugins/builtin/shared/report-freshness.ts +3 -2
  442. package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
  443. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  444. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  445. package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
  446. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  447. package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
  448. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  449. package/src/plugins/builtin/themes/headless.ts +6 -4
  450. package/src/plugins/builtin/themes/model.ts +17 -8
  451. package/src/plugins/builtin/themes/pane.tsx +7 -7
  452. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  453. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  454. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  455. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  456. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  457. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  458. package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
  459. package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
  460. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  461. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  462. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  463. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  464. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  465. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  466. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  467. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  468. package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
  469. package/src/plugins/builtin/vol-surface/client.ts +9 -17
  470. package/src/plugins/builtin/vol-surface/headless.ts +63 -14
  471. package/src/plugins/builtin/vol-surface/index.tsx +2 -2
  472. package/src/plugins/builtin/vol-surface/model.ts +22 -7
  473. package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
  474. package/src/plugins/builtin/vol-surface/tables.ts +31 -3
  475. package/src/plugins/builtin/volatility/dvol.ts +46 -0
  476. package/src/plugins/builtin/volatility/index.tsx +2 -2
  477. package/src/plugins/builtin/volatility/pane.tsx +4 -4
  478. package/src/plugins/builtin/volatility/series.ts +26 -0
  479. package/src/plugins/builtin/world-indices/footer.ts +1 -1
  480. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  481. package/src/plugins/catalog-browser.ts +13 -8
  482. package/src/plugins/catalog-manifest.ts +2 -1
  483. package/src/plugins/marketplace/model.ts +18 -5
  484. package/src/portfolio/column-values.ts +5 -5
  485. package/src/portfolio/futures-positions.ts +6 -3
  486. package/src/portfolio/position-quantity.ts +15 -9
  487. package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
  488. package/src/remote/resources.ts +2 -1
  489. package/src/renderers/browser/app-services.ts +1 -7
  490. package/src/renderers/browser/config-host.ts +2 -55
  491. package/src/renderers/browser/main.tsx +7 -57
  492. package/src/renderers/dom/data-table/row.tsx +26 -0
  493. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  494. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  495. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  496. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  497. package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
  498. package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
  499. package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
  500. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  501. package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
  502. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
  503. package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
  504. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  505. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
  506. package/src/renderers/electrobun/bun/index.ts +25 -1
  507. package/src/renderers/electrobun/shared/app-name.ts +30 -0
  508. package/src/renderers/electrobun/shared/protocol.ts +10 -0
  509. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  510. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  511. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
  512. package/src/renderers/electrobun/view/main.tsx +24 -1
  513. package/src/renderers/electrobun/view/news-source.ts +3 -1
  514. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  515. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  516. package/src/renderers/opentui/start.tsx +5 -0
  517. package/src/renderers/opentui/toast-host.tsx +71 -43
  518. package/src/series/registry.ts +1 -0
  519. package/src/series/types.ts +6 -0
  520. package/src/series/validate.ts +1 -0
  521. package/src/sources/gloomberb-cloud/index.ts +1 -0
  522. package/src/sources/gloomberb-cloud/news.ts +47 -7
  523. package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
  524. package/src/sources/provider-router/batches.ts +10 -2
  525. package/src/sources/provider-router/financial-routes.ts +3 -3
  526. package/src/sources/provider-router/financials.ts +15 -3
  527. package/src/sources/provider-router/history.ts +28 -7
  528. package/src/sources/provider-router/index.ts +149 -10
  529. package/src/sources/provider-router/search.ts +13 -2
  530. package/src/state/app/context/index.tsx +3 -0
  531. package/src/state/ticker-cleanup.ts +50 -0
  532. package/src/sync/auth-transition.ts +19 -9
  533. package/src/sync/baseline.ts +10 -4
  534. package/src/sync/controller.ts +70 -19
  535. package/src/sync/core-contributors.ts +90 -31
  536. package/src/sync/react.ts +4 -1
  537. package/src/sync/types.ts +3 -0
  538. package/src/telemetry/stall-reports-dom.ts +38 -0
  539. package/src/telemetry/stall-reports-node.ts +75 -0
  540. package/src/telemetry/stall-reports.ts +214 -0
  541. package/src/telemetry/usage-counts.ts +0 -18
  542. package/src/test-support/preload.ts +14 -0
  543. package/src/theme/colors.ts +8 -0
  544. package/src/theme/themes.ts +10 -0
  545. package/src/tickers/company-name.ts +103 -3
  546. package/src/tickers/listing-venues.ts +38 -3
  547. package/src/tickers/search/bare-symbol.ts +36 -0
  548. package/src/tickers/search/index.ts +36 -7
  549. package/src/tickers/search/ranking.ts +10 -1
  550. package/src/tickers/search/types.ts +5 -0
  551. package/src/tickers/terminal-codes.ts +19 -0
  552. package/src/time-series/alignment.ts +1 -1
  553. package/src/time-series/base-series.ts +13 -5
  554. package/src/time-series/daily-valuation.ts +62 -0
  555. package/src/time-series/economic.ts +18 -4
  556. package/src/time-series/field-catalog.ts +2 -4
  557. package/src/time-series/filed-trailing-eps.ts +197 -0
  558. package/src/time-series/forward-valuation.ts +32 -0
  559. package/src/time-series/fundamentals.ts +198 -7
  560. package/src/time-series/hooks.ts +1 -0
  561. package/src/time-series/ice-credit.ts +3 -0
  562. package/src/time-series/price-comparison.ts +30 -2
  563. package/src/time-series/price-units.ts +72 -2
  564. package/src/time-series/resolve-context.ts +7 -1
  565. package/src/time-series/resolve.ts +116 -15
  566. package/src/time-series/series-presentation.ts +40 -6
  567. package/src/time-series/session-bar.ts +11 -0
  568. package/src/time-series/spec.ts +5 -3
  569. package/src/time-series/studies.ts +278 -74
  570. package/src/time-series/total-return.ts +160 -0
  571. package/src/time-series/transforms.ts +3 -3
  572. package/src/time-series/types.ts +27 -1
  573. package/src/time-series/valuation-releases.ts +39 -0
  574. package/src/types/data-provider.ts +9 -1
  575. package/src/types/desktop-window.ts +2 -0
  576. package/src/types/financials.ts +73 -2
  577. package/src/types/headless.ts +61 -5
  578. package/src/types/instrument.ts +3 -0
  579. package/src/types/news-source.ts +1 -0
  580. package/src/types/plugin.ts +10 -1
  581. package/src/ui/host.tsx +3 -0
  582. package/src/updater/command-names.ts +12 -7
  583. package/src/updater/failure.ts +4 -1
  584. package/src/utils/cli-output.ts +42 -1
  585. package/src/utils/crypto-pair.ts +44 -0
  586. package/src/utils/financial-statements.ts +6 -1
  587. package/src/utils/forward-dividend.ts +2 -0
  588. package/src/utils/fundamentals.ts +24 -3
  589. package/src/utils/fx-market-hours.ts +26 -3
  590. package/src/utils/main-thread-stall.ts +35 -2
  591. package/src/utils/market-capitalization.ts +24 -1
  592. package/src/utils/network-failure.ts +9 -6
  593. package/src/utils/option-expiry.ts +48 -1
  594. package/src/utils/options.ts +10 -0
  595. package/src/utils/plugin-proxy-hosts.json +1 -0
  596. package/src/utils/price-history.ts +12 -1
  597. package/src/utils/shell-word.ts +8 -0
  598. package/src/utils/trailing-dividend.ts +53 -4
  599. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
  600. package/src/renderers/browser/first-screen.ts +0 -193
@@ -20,6 +20,7 @@ export type {
20
20
  HeadlessPaneOptionType,
21
21
  HeadlessPaneOptionValue,
22
22
  HeadlessPaneOptionValues,
23
+ HeadlessPaneReference,
23
24
  HeadlessPaneResult,
24
25
  HeadlessPaneResultByShape,
25
26
  HeadlessPaneRow,
@@ -529,6 +530,14 @@ export interface PaneTemplateDef {
529
530
  description: string;
530
531
  keywords?: string[];
531
532
  shortcut?: PaneTemplateShortcut;
533
+ /**
534
+ * False leaves the function out of every list and search of functions
535
+ * (command-bar browse, search and assistant, Help, `gloom catalog` and
536
+ * search, the plugin directory, chat suggestions) while typing its code
537
+ * still opens it, `fn` and `shot` still run it and its help still answers.
538
+ * ATTN until it publishes (attention/listing.ts).
539
+ */
540
+ listed?: boolean;
532
541
  /** Template-specific headless model. Takes precedence over the pane-level model. */
533
542
  headless?: HeadlessPaneDefinition;
534
543
  /**
@@ -945,7 +954,7 @@ export interface AppNotificationRequest {
945
954
  /**
946
955
  * What a built-in notice is about, so the Unread list can keep it after the
947
956
  * toast is gone and open it again: `price:<alertId>`, `catalyst:<eventId>`,
948
- * `econ:<eventId>` or `team:<cardId>`. Other values are not kept.
957
+ * `econ:<eventId>`, `team:<cardId>` or `scan:<paneId>`. Other values are not kept.
949
958
  */
950
959
  refId?: string;
951
960
  /** Rendered next to `action`. Use for a dismissing counterpart such as snooze. */
package/src/ui/host.tsx CHANGED
@@ -82,6 +82,8 @@ export interface ChartVectorShape {
82
82
  gradient?: readonly string[];
83
83
  /** Opacity of the whole shape, so others can recede behind a highlighted one. */
84
84
  opacity?: number;
85
+ /** Text set just above the shape's first point, such as a retracement level's ratio and price. */
86
+ label?: string;
85
87
  }
86
88
 
87
89
  interface TextEditBuffer {
@@ -508,6 +510,7 @@ export interface HostCheckboxProps {
508
510
  interface HostQueryBarOption {
509
511
  value: string;
510
512
  label: string;
513
+ group?: string;
511
514
  description?: string;
512
515
  disabled?: boolean;
513
516
  selected: boolean;
@@ -3,7 +3,7 @@ import { lstat, readFile, readlink, stat, symlink, writeFile } from "fs/promises
3
3
  import { basename, dirname, join, resolve } from "path";
4
4
 
5
5
  /**
6
- * The terminal command is `gloom`. `gloomberb`, its name before 0.19, keeps
6
+ * The terminal command is `gloom`. `gloomberb`, its name before 0.18.4, keeps
7
7
  * working beside it, for scripts and habits.
8
8
  *
9
9
  * An install from before the rename has only `gloomberb`, and the ways it
@@ -58,17 +58,21 @@ export function linkStandaloneCommand(execPath: string): string | null {
58
58
  return addSymlink(name, path);
59
59
  }
60
60
 
61
+ /** Homebrew's prefixes on Apple Silicon and on Intel. */
62
+ export const HOMEBREW_PREFIXES = ["/opt/homebrew", "/usr/local"];
63
+
61
64
  /** Homebrew's command folders on Apple Silicon and on Intel. */
62
- const HOMEBREW_BINS = ["/opt/homebrew/bin", "/usr/local/bin"];
65
+ const HOMEBREW_BINS = HOMEBREW_PREFIXES.map((prefix) => join(prefix, "bin"));
63
66
 
64
67
  /**
65
- * Where a Mac keeps the desktop app's command: install.sh's folder, Homebrew's,
66
- * then the rest of PATH. A GUI app's PATH rarely has the first ones, so they
67
- * are named outright.
68
+ * Where a Mac keeps the desktop app's command: install.sh's folder, `~/bin`,
69
+ * Homebrew's, then the rest of PATH. A GUI app's PATH rarely has the first
70
+ * ones, so they are named outright.
68
71
  */
69
- function macCommandFolders(homeDir: string, pathEnv: string, homebrewBins: readonly string[]): string[] {
72
+ export function macCommandFolders(homeDir: string, pathEnv: string, homebrewBins: readonly string[] = HOMEBREW_BINS): string[] {
70
73
  return [...new Set([
71
74
  homeDir ? join(homeDir, ".local", "bin") : "",
75
+ homeDir ? join(homeDir, "bin") : "",
72
76
  ...homebrewBins,
73
77
  ...pathEnv.split(":"),
74
78
  ].filter(Boolean))];
@@ -141,7 +145,7 @@ interface DesktopCommandOptions {
141
145
  homeDir: string;
142
146
  /** PATH as the app sees it. */
143
147
  pathEnv: string;
144
- /** `%LOCALAPPDATA%`, where the Windows installer puts the app by default. */
148
+ /** `%LOCALAPPDATA%`, where the Windows installer puts the app by default (`Programs\Gloom`, `Programs\Gloomberb` before the rename). */
145
149
  localAppData?: string;
146
150
  /** Homebrew's command folders; tests pass none. */
147
151
  homebrewBins?: readonly string[];
@@ -166,6 +170,7 @@ export async function linkDesktopCommands({
166
170
  added = await Promise.all(macCommandFolders(homeDir, pathEnv, homebrewBins).map(linkMacCommand));
167
171
  } else if (platform === "win32") {
168
172
  const folders = new Set([
173
+ localAppData ? join(localAppData, "Programs", "Gloom", "bin") : "",
169
174
  localAppData ? join(localAppData, "Programs", "Gloomberb", "bin") : "",
170
175
  ...pathEnv.split(";"),
171
176
  ].filter(Boolean));
@@ -24,7 +24,7 @@ const UPDATE_FAILURE_CODES = [
24
24
  "no_build",
25
25
  /** The download stopped before the whole file arrived. */
26
26
  "interrupted",
27
- /** The file arrived whole but does not match its checksum, or does not unpack. */
27
+ /** The download does not match its checksum, or does not unpack (part of it is missing or damaged). */
28
28
  "corrupt",
29
29
  "permission_denied",
30
30
  /** The macOS app runs from where it cannot be replaced: a disk image, App Translocation, another volume. */
@@ -112,6 +112,9 @@ export function classifyUpdateError(error: unknown): UpdateFailureCode {
112
112
  if (status) return httpStatusFailureCode(Number(status));
113
113
  if (/checksum mismatch|hash mismatch/i.test(message)) return "corrupt";
114
114
  if (/zstd|decompress|gunzip|incorrect header|unexpected end of (?:file|data)/i.test(message)) return "corrupt";
115
+ // Bun.Archive on a tar that does not read: cut off, damaged, or none at all.
116
+ // Unpacking says only "ReadError", whatever stopped it.
117
+ if (/\bReadError\b|truncated tar|damaged tar|unrecognized archive format/i.test(message)) return "corrupt";
115
118
  if (/timed? ?out/i.test(message)) return "timeout";
116
119
  if (/rate limit/i.test(message)) return "rate_limited";
117
120
  if (/read-only file system/i.test(message)) return "read_only";
@@ -74,6 +74,47 @@ const PLAIN_EMPTY_CELL = "—";
74
74
  let colorEnabledOverride: boolean | null = null;
75
75
  let widthOverride: number | null = null;
76
76
  let plainOverride: boolean | null = null;
77
+ /** Output is being rendered for a file (`renderForFile`), which has no terminal width. */
78
+ let renderingForFile = false;
79
+
80
+ /** Renders for a file from here on; the function returned puts the terminal's rendering back. */
81
+ function enterFileRendering(): () => void {
82
+ const saved = { color: colorEnabledOverride, plain: plainOverride, file: renderingForFile };
83
+ colorEnabledOverride = false;
84
+ plainOverride = true;
85
+ renderingForFile = true;
86
+ return () => {
87
+ colorEnabledOverride = saved.color;
88
+ plainOverride = saved.plain;
89
+ renderingForFile = saved.file;
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Output rendered for a file (`fn --output`) as a redirect gets it: plain,
95
+ * without colour, and fitted to no width but `--width`'s.
96
+ */
97
+ export function renderForFile<T>(render: () => T): T {
98
+ const restore = enterFileRendering();
99
+ try {
100
+ return render();
101
+ } finally {
102
+ restore();
103
+ }
104
+ }
105
+
106
+ /**
107
+ * `renderForFile` around work that renders as it loads: a report whose text is
108
+ * drawn while it is built is drawn for the file, though stdout is a terminal.
109
+ */
110
+ export async function buildForFile<T>(build: () => Promise<T>): Promise<T> {
111
+ const restore = enterFileRendering();
112
+ try {
113
+ return await build();
114
+ } finally {
115
+ restore();
116
+ }
117
+ }
77
118
 
78
119
  function colorEnabled(): boolean {
79
120
  if (colorEnabledOverride != null) return colorEnabledOverride;
@@ -176,7 +217,7 @@ export function setCliWidthOverride(value: number | null): void {
176
217
  /** Columns the output is fitted to: `--width`, else the terminal's, or null when output is piped or redirected. */
177
218
  export function cliTerminalWidth(): number | null {
178
219
  if (widthOverride != null) return widthOverride;
179
- if (!process.stdout.isTTY) return null;
220
+ if (renderingForFile || !process.stdout.isTTY) return null;
180
221
  const columns = process.stdout.columns;
181
222
  return typeof columns === "number" && columns > 0 ? columns : null;
182
223
  }
@@ -91,6 +91,50 @@ export const MAJOR_COIN_NAMES: ReadonlyMap<string, string> = new Map([
91
91
  ["BNB", "BNB"],
92
92
  ]);
93
93
 
94
+ /**
95
+ * The largest coins by market value, stablecoins and gold tokens left out, with
96
+ * how a note names each: the coins whose bare ticker a listed company or fund
97
+ * may hold instead (SOL is a stale Emeren Group, LINK is Interlink Electronics).
98
+ * A hint reads this list; `MAJOR_COIN_NAMES` stays the shorter one stories and
99
+ * company-name lookups use, so a word such as "stellar" is still a company's.
100
+ */
101
+ export const TOP_COIN_NAMES: ReadonlyMap<string, string> = new Map([
102
+ ...MAJOR_COIN_NAMES,
103
+ ["TRX", "tron"],
104
+ ["ZEC", "zcash"],
105
+ ["HYPE", "hyperliquid"],
106
+ ["XMR", "monero"],
107
+ ["XLM", "stellar"],
108
+ ["NEAR", "near protocol"],
109
+ ["TON", "toncoin"],
110
+ ["UNI", "uniswap"],
111
+ ["SUI", "sui"],
112
+ ["HBAR", "hedera"],
113
+ ["TAO", "bittensor"],
114
+ ["SHIB", "shiba inu"],
115
+ ["ENA", "ethena"],
116
+ ["CRO", "cronos"],
117
+ ["AAVE", "aave"],
118
+ ["ONDO", "ondo"],
119
+ ["PEPE", "pepe"],
120
+ ["ICP", "internet computer"],
121
+ ["ETC", "ethereum classic"],
122
+ ["ARB", "arbitrum"],
123
+ ["ALGO", "algorand"],
124
+ ["ATOM", "cosmos"],
125
+ ["APT", "aptos"],
126
+ ["FIL", "filecoin"],
127
+ ["POL", "polygon"],
128
+ ["OP", "optimism"],
129
+ ["WLD", "worldcoin"],
130
+ ["MNT", "mantle"],
131
+ ["KAS", "kaspa"],
132
+ ["QNT", "quant"],
133
+ ["VET", "vechain"],
134
+ ["INJ", "injective"],
135
+ ["RENDER", "render"],
136
+ ]);
137
+
94
138
  const COIN_PAIR = /^([A-Z0-9]{1,15})[-/](?:USD|EUR|GBP|USDT|USDC|BTC)$/;
95
139
  // What a pair's listing name adds after the coin's: its quote currency, an id in brackets.
96
140
  const PAIR_NAME_SUFFIX = /(?:\s*\([^)]*\))+$|\s+(?:USD|EUR|GBP|USDT|USDC|BTC|US Dollars?)$/i;
@@ -116,11 +116,16 @@ export function statementFieldAvailability(row: FinancialStatement | undefined,
116
116
  return value && Number.isFinite(Date.parse(value)) ? value : undefined;
117
117
  }
118
118
 
119
+ /** A date without a publication time is only known to be available after that day. */
120
+ export function financialAvailabilityTime(value: string): number {
121
+ return Date.parse(value) + (/^\d{4}-\d{2}-\d{2}$/.test(value) ? 86_400_000 : 0);
122
+ }
123
+
119
124
  /** A derived value is dated only when every input's availability is known. */
120
125
  export function completeAvailability(values: readonly (string | null | undefined)[]): string | undefined {
121
126
  if (values.length === 0 || values.some((value) => !value || !Number.isFinite(Date.parse(value)))) return undefined;
122
127
  return values.reduce<string | undefined>((latest, value) => (
123
- !latest || Date.parse(value!) > Date.parse(latest) ? value! : latest
128
+ !latest || financialAvailabilityTime(value!) > financialAvailabilityTime(latest) ? value! : latest
124
129
  ), undefined);
125
130
  }
126
131
 
@@ -14,6 +14,8 @@ import { formatDistributionAmount } from "./format";
14
14
  * the rate divided back.
15
15
  */
16
16
  export const LATEST_DISTRIBUTION = "latest distribution annualized";
17
+ /** The basis of a forward rate that is the year's regular payments, which differ too widely for one to be annualized (unevenRegularPayments). */
18
+ export const UNEVEN_REGULAR_PAYMENTS = "trailing regular payments: uneven interims";
17
19
 
18
20
  /**
19
21
  * What a trailing dividend yield is everywhere it shows (DVD, a fund's in
@@ -6,16 +6,19 @@ import { formatCompact } from "./format";
6
6
  import { formatUtcTime } from "./utc-time";
7
7
 
8
8
  export const RETRACTABLE_VALUATION_FIELDS = ["enterpriseValue", "enterpriseToRevenue"] as const;
9
+ /** Every field a source can retract: the valuation fields, and a receipt's market cap (`marketCapUnavailable`). */
10
+ export const RETRACTED_FUNDAMENTALS_FIELDS = [...RETRACTABLE_VALUATION_FIELDS, "marketCap"] as const;
9
11
 
10
- /** A retraction survives serialization and overrides a contradictory cached number. */
12
+ /** A retraction survives serialization and overrides a contradictory cached number; its reason goes with it. */
11
13
  export function redactUnavailableFundamentals(value: Fundamentals | undefined): Fundamentals | undefined {
12
- if (!value || value.unavailableFields === undefined) return value;
14
+ if (!value || (value.unavailableFields === undefined && value.marketCapUnavailable === undefined)) return value;
13
15
  const unavailableFields = Array.isArray(value.unavailableFields)
14
- ? RETRACTABLE_VALUATION_FIELDS.filter((field) => value.unavailableFields!.includes(field)) : [];
16
+ ? RETRACTED_FUNDAMENTALS_FIELDS.filter((field) => value.unavailableFields!.includes(field)) : [];
15
17
  const result = { ...value };
16
18
  if (unavailableFields.length) result.unavailableFields = unavailableFields;
17
19
  else delete result.unavailableFields;
18
20
  for (const field of unavailableFields) delete result[field];
21
+ if (!unavailableFields.includes("marketCap")) delete result.marketCapUnavailable;
19
22
  return result;
20
23
  }
21
24
 
@@ -243,6 +246,24 @@ export function epsConversionNote(fundamentals: Pick<Fundamentals, "financialCur
243
246
  return eps && amounts && eps !== amounts ? `EPS (TTM) in ${eps}, converted from ${amounts} at report-date rates.` : undefined;
244
247
  }
245
248
 
249
+ /** What a trailing EPS that is not the filed quarters' is called where it is shown (EE's TTM row). */
250
+ export const SERVED_TRAILING_EPS = "Gloom Cloud's TTM";
251
+
252
+ /**
253
+ * "EPS (TTM) and P/E (TTM) are Gloom Cloud's trailing figures, not the last four filed quarters'.": the headline EPS
254
+ * and P/E are the filed quarters' (time-series/filed-trailing-eps), so served ones say so, naming those `shown`
255
+ * ("P/E (TTM) is Gloom Cloud's trailing figure, ..." for a fund's P/E with no EPS). Undefined otherwise.
256
+ */
257
+ export function trailingEpsSourceNote(
258
+ fundamentals: Pick<Fundamentals, "trailingEpsSource"> | undefined,
259
+ shown: { eps: boolean; pe: boolean },
260
+ ): string | undefined {
261
+ if (fundamentals?.trailingEpsSource !== "served" || (!shown.eps && !shown.pe)) return undefined;
262
+ const figures = shown.eps && shown.pe ? "EPS (TTM) and P/E (TTM) are Gloom Cloud's trailing figures"
263
+ : `${shown.eps ? "EPS (TTM)" : "P/E (TTM)"} is Gloom Cloud's trailing figure`;
264
+ return `${figures}, not the last four filed quarters'.`;
265
+ }
266
+
246
267
  type PriceEarningsBasis = Pick<Fundamentals, "financialCurrency" | "ttmConversion" | "epsCurrency" | "trailingPEConversion">;
247
268
 
248
269
  /** The trailing EPS in the price's currency: converted at the rate the served P/E used when it says so. */
@@ -30,12 +30,35 @@ export function isFxMarketOpen(time: number): boolean {
30
30
  }
31
31
 
32
32
  /**
33
- * The FX trading day an instant belongs to, as "YYYY-MM-DD": the day rolls
34
- * at 17:00 New York time, so Wednesday 18:00 New York is Thursday's.
33
+ * The FX trading day a print made at `time` belongs to, as "YYYY-MM-DD": the
34
+ * day rolls at 17:00 New York time, so Wednesday 18:00 New York is
35
+ * Thursday's. A print stamped exactly 17:00:00 is that day's close, so it is
36
+ * still that day's.
35
37
  */
36
38
  export function fxTradingDay(time: number): string {
37
39
  // The roll is 17:00, seven hours before New York's midnight, daylight saving or not.
38
- return zonedDateKey(time + 7 * HOUR_MS, NEW_YORK);
40
+ return zonedDateKey(time - 1 + 7 * HOUR_MS, NEW_YORK);
41
+ }
42
+
43
+ /**
44
+ * The FX trading day of a print made at `time` (fxTradingDay), or null for a
45
+ * print made while the market is shut: from just after Friday's 17:00 New
46
+ * York close, whose own print still counts, through Sunday's 17:00 open. A
47
+ * tick over the weekend belongs to no trading day.
48
+ */
49
+ export function fxPrintTradingDay(time: number): string | null {
50
+ return Number.isFinite(time) && isFxMarketOpen(time - 1) ? fxTradingDay(time) : null;
51
+ }
52
+
53
+ /**
54
+ * The FX trading day the market is on at `time`: the day in progress while it
55
+ * trades, or the Friday that closed it while it is shut.
56
+ */
57
+ export function fxTradingDayAt(time: number): string | null {
58
+ const day = fxPrintTradingDay(time);
59
+ if (day != null || !Number.isFinite(time)) return day;
60
+ const shut = currentFxClosureStart(time - 1);
61
+ return shut != null ? fxTradingDay(shut) : null;
39
62
  }
40
63
 
41
64
  /** The latest 17:00 New York on `weekday` (0 is Sunday) at or before `time`. */
@@ -9,13 +9,43 @@ const SAMPLE_INTERVAL_MS = 250;
9
9
  */
10
10
  const STALL_THRESHOLD_MS = 400;
11
11
 
12
+ export interface StallEvent {
13
+ blockedMs: number;
14
+ /** The last measured section before the stall (perf-marks), if any. */
15
+ lastMeasured?: string;
16
+ /** How long before the stall was noticed that section ended. */
17
+ lastMeasuredEndedMsAgo?: number;
18
+ }
19
+
20
+ let stallSink: ((event: StallEvent) => void) | null = null;
21
+
22
+ /**
23
+ * Where this process's stalls go besides the Debug Log: the stall log and its
24
+ * report (telemetry/stall-reports.ts), or, in the desktop window, the Bun
25
+ * process that keeps them.
26
+ */
27
+ export function setStallSink(next: ((event: StallEvent) => void) | null): () => void {
28
+ stallSink = next;
29
+ return () => {
30
+ if (stallSink === next) stallSink = null;
31
+ };
32
+ }
33
+
34
+ function noteStall(event: StallEvent): void {
35
+ try {
36
+ stallSink?.(event);
37
+ } catch {
38
+ // Intentional ignore: reporting a freeze must never become one.
39
+ }
40
+ }
41
+
12
42
  export interface MainThreadStallMonitorOptions {
13
43
  intervalMs?: number;
14
44
  thresholdMs?: number;
15
45
  now?: () => number;
16
46
  schedule?: (callback: () => void, delayMs: number) => unknown;
17
47
  cancel?: (handle: unknown) => void;
18
- report?: (blockedMs: number, context: Record<string, unknown>) => void;
48
+ report?: (blockedMs: number, context: Omit<StallEvent, "blockedMs">) => void;
19
49
  }
20
50
 
21
51
  function defaultNow(): number {
@@ -41,7 +71,10 @@ export function startMainThreadStallMonitor(
41
71
  const schedule = options.schedule ?? ((callback, delayMs) => setInterval(callback, delayMs));
42
72
  const cancel = options.cancel ?? ((handle) => clearInterval(handle as ReturnType<typeof setInterval>));
43
73
  const report = options.report
44
- ?? ((blockedMs, context) => recordPerfSample("runtime.stall", blockedMs, context));
74
+ ?? ((blockedMs, context) => {
75
+ recordPerfSample("runtime.stall", blockedMs, context);
76
+ noteStall({ blockedMs, ...context });
77
+ });
45
78
 
46
79
  let previous = now();
47
80
  const handle = schedule(() => {
@@ -20,11 +20,34 @@ function explicitCurrency(currency: string | undefined): currency is string {
20
20
  return currency != null && /^[A-Z]{3}$/.test(currency);
21
21
  }
22
22
 
23
- /** A lightweight quote can omit capitalization without invalidating the snapshot. */
23
+ /** What every view says for a cap Gloom Cloud withheld, in the words its screener uses. */
24
+ const WITHHELD_MARKET_CAP: Record<NonNullable<Fundamentals["marketCapUnavailable"]>, { gap: string; note: string }> = {
25
+ "receipt-priced-as-ordinary": {
26
+ gap: "receipt, no ratio",
27
+ note: "No market cap: a depositary receipt with no known ratio, whose source's cap is the receipt price x the company's ordinary shares.",
28
+ },
29
+ };
30
+
31
+ /**
32
+ * Why a listing has no market cap when Gloom Cloud withheld it: `gap` for a cell, `note` for a line under a
33
+ * report. Null when the cap was not withheld.
34
+ */
35
+ export function withheldMarketCap(fundamentals: Pick<Fundamentals, "unavailableFields" | "marketCapUnavailable"> | undefined):
36
+ { gap: string; note: string } | null {
37
+ if (!fundamentals?.unavailableFields?.includes("marketCap")) return null;
38
+ const reason = fundamentals.marketCapUnavailable ? WITHHELD_MARKET_CAP[fundamentals.marketCapUnavailable] : undefined;
39
+ return reason ?? { gap: "withheld", note: "No market cap: Gloom Cloud withheld it." };
40
+ }
41
+
42
+ /**
43
+ * A lightweight quote can omit capitalization without invalidating the snapshot. A cap Gloom Cloud withheld
44
+ * (withheldMarketCap) is none, the quote's included.
45
+ */
24
46
  export function selectMarketCapitalization(
25
47
  quote: Quote | undefined,
26
48
  fundamentals: Fundamentals | undefined,
27
49
  ): MarketCapitalization | null {
50
+ if (withheldMarketCap(fundamentals)) return null;
28
51
  if (usableValue(quote?.marketCap) && explicitCurrency(quote?.currency)) {
29
52
  return { value: quote.marketCap, currency: quote.currency,
30
53
  provenance: { kind: "quote", source: quote.providerId } };
@@ -3,9 +3,10 @@ export const DATA_SERVICE_UNREACHABLE_MESSAGE = "Gloom couldn't reach its data s
3
3
 
4
4
  /**
5
5
  * A request that never reached the data service: no connection, a proxy that
6
- * refused it, a name that did not resolve. The fetch's own error is the
7
- * `cause`, and its `code` (Bun's `ConnectionRefused`, Node's `ENOTFOUND`) is
8
- * kept for code that classifies failures by it.
6
+ * refused it, a name that did not resolve, or a connection that hung with
7
+ * nothing coming back before the request's deadline. The fetch's own error (or
8
+ * the deadline's) is the `cause`, and its `code` (Bun's `ConnectionRefused`,
9
+ * Node's `ENOTFOUND`) is kept for code that classifies failures by it.
9
10
  */
10
11
  export class DataServiceUnreachableError extends Error {
11
12
  readonly code?: string;
@@ -56,9 +57,11 @@ function cameFromServer(error: object): boolean {
56
57
 
57
58
  /**
58
59
  * Whether a request failed before any answer came back. A caller's abort and
59
- * a timeout are not: the first is on purpose, and the second reached a server
60
- * that did not answer in time. Nor is anything the server answered, whatever
61
- * its text or code says.
60
+ * a bare timeout are not: the first is on purpose, and the second may have
61
+ * reached a server that did not answer in time. A Cloud request whose deadline
62
+ * passed with nothing back from the service is one: the client throws it as
63
+ * `DataServiceUnreachableError`, as it does a refused connection. Nor is
64
+ * anything the server answered, whatever its text or code says.
62
65
  */
63
66
  export function isNetworkFailure(error: unknown): boolean {
64
67
  if (typeof error === "string") return NO_CONNECTION_MESSAGES.has(error.trim());
@@ -6,7 +6,9 @@
6
6
 
7
7
  import type { OptionsChain } from "../types/financials";
8
8
  import { hasRegularCloseHour, latestRegularSessionClose, sessionCalendarTimeZone } from "../market-data/market/freshness";
9
+ import { listedCoinOptions } from "../market-data/options-alternatives";
9
10
  import { dateOptionHint, readCalendarDate } from "./calendar-date";
11
+ import { isRoundTheClockCoin } from "./crypto-pair";
10
12
  import { canonicalExchange, isUsListingExchange } from "./exchanges";
11
13
  import { zonedWallClockToUtcMs } from "./zoned-date-time";
12
14
 
@@ -20,6 +22,23 @@ export const OPTION_EXPIRATION_FORMAT = "YYYY-MM-DD (2028-01-21), DD.MM.YYYY, Ja
20
22
  /** The value's name in a catalog flag, `--expiration <date|unix>`. */
21
23
  export const OPTION_EXPIRATION_PLACEHOLDER = "date|unix";
22
24
 
25
+ /** Seconds into its UTC day that Deribit stamps each expiry at: its 08:00 UTC settlement. */
26
+ const UTC_SETTLEMENT_SECONDS = 8 * 3600;
27
+
28
+ /**
29
+ * Whether an expiry stamp is the settlement instant itself, as Deribit stamps
30
+ * its BTC and ETH expiries (08:00 UTC), rather than a listed option's date. No
31
+ * US listing is stamped at that hour: they carry the date's midnight or its close.
32
+ */
33
+ export function isSettlementStamp(seconds: number): boolean {
34
+ return Number.isFinite(seconds) && ((seconds % 86_400) + 86_400) % 86_400 === UTC_SETTLEMENT_SECONDS;
35
+ }
36
+
37
+ /** `08:00 UTC` for a settlement stamp (`isSettlementStamp`); null for a dated one. */
38
+ export function settlementStampTime(seconds: number): string | null {
39
+ return isSettlementStamp(seconds) ? `${new Date(seconds * 1000).toISOString().slice(11, 16)} UTC` : null;
40
+ }
41
+
23
42
  /** The expiry's calendar date, `2028-01-21`; empty for a stamp that is not a time. */
24
43
  export function expiryIsoDate(seconds: number): string {
25
44
  const date = new Date(seconds * 1000);
@@ -89,6 +108,9 @@ export function formatDaysToExpiry(expiration: number, asOfMs: number, exchange?
89
108
  return isLiveOptionExpiry(expiration, asOfMs, exchange) ? `${Math.max(0, daysToExpiry(expiration, asOfMs))}d` : "expired";
90
109
  }
91
110
 
111
+ /** The coin board's venue code, which a coin pair's listing carries. */
112
+ const COIN_OPTIONS_VENUE = "CCC";
113
+
92
114
  /** Listed US options trade on the US equity calendar, NYSE's published one. */
93
115
  const US_OPTIONS_CALENDAR = "NYSE";
94
116
  const lastSessions = new Map<string, { date: string; close: number } | null>();
@@ -105,10 +127,33 @@ function expiryCalendarVenue(exchange: string | undefined): string {
105
127
  ? venue : US_OPTIONS_CALENDAR;
106
128
  }
107
129
 
130
+ /**
131
+ * The venue to read an expiry's close on for a listing: the coin board for a
132
+ * coin whose options are listed (BTC-USD, ETH-USD, even when it carries no
133
+ * venue), else the listing's own.
134
+ */
135
+ export function optionExpiryVenue(symbol: string | undefined, exchange: string | undefined): string | undefined {
136
+ return symbol && listedCoinOptions(symbol) ? COIN_OPTIONS_VENUE : exchange;
137
+ }
138
+
139
+ /**
140
+ * When a settlement-priced expiry settles (Deribit's BTC and ETH options): its
141
+ * stamp when it carries the 08:00 UTC settlement, or 08:00 UTC on a typed
142
+ * date's day for a coin listing. Any day of the week, weekends included: the
143
+ * US calendar does not apply. Null for a listed US option, whatever its venue.
144
+ */
145
+ function settlementCloseMs(expiration: number, exchange: string | undefined): number | null {
146
+ if (isSettlementStamp(expiration)) return expiration * 1000;
147
+ if (!isRoundTheClockCoin(undefined, exchange)) return null;
148
+ return Math.floor(expiration / 86_400) * 86_400_000 + UTC_SETTLEMENT_SECONDS * 1000;
149
+ }
150
+
108
151
  /** The expiry's last trading session, on or before its date, on its venue's calendar: the date and when it closed. */
109
152
  function expiryLastSession(expiration: number, exchange: string | undefined): { date: string; close: number } | null {
110
153
  const date = expiryIsoDate(expiration);
111
154
  if (!date) return null;
155
+ const settles = settlementCloseMs(expiration, exchange);
156
+ if (settles != null) return { date, close: settles };
112
157
  const venue = expiryCalendarVenue(exchange);
113
158
  const key = `${venue}:${date}`;
114
159
  const known = lastSessions.get(key);
@@ -128,7 +173,9 @@ function expiryLastSession(expiration: number, exchange: string | undefined): {
128
173
  * listing venue's last trading day on or before the expiry's date, on that
129
174
  * venue's clock and calendar. A US expiry closes at 16:00 New York, at 13:00
130
175
  * on a published early close, and one dated on a holiday or a weekend closed
131
- * with the session before it. Null for a stamp that is not a date.
176
+ * with the session before it. A Deribit expiry (BTC-USD, ETH-USD) settles at
177
+ * 08:00 UTC on its own date, a Saturday or Sunday too. Null for a stamp that
178
+ * is not a date.
132
179
  */
133
180
  export function optionExpiryCloseMs(expiration: number, exchange?: string): number | null {
134
181
  return expiryLastSession(expiration, exchange)?.close ?? null;
@@ -11,6 +11,16 @@ export function formatExpDate(ts: number): string {
11
11
  return `${month} ${d.getUTCDate()} '${yy}`;
12
12
  }
13
13
 
14
+ /**
15
+ * A strike as every options pane writes it: grouped, with every listed
16
+ * decimal (`82,000`, `82.5`). 15 significant digits keep every listed decimal
17
+ * but drop binary noise from computed strikes such as spot * 0.9.
18
+ */
19
+ export function formatStrikeLabel(strike: number): string {
20
+ return Number.isFinite(strike)
21
+ ? strike.toLocaleString("en-US", { maximumSignificantDigits: 15 }) : "\u2014";
22
+ }
23
+
14
24
  /**
15
25
  * Parse IBKR option symbol like "UBER 260821C00090000" into components.
16
26
  * Returns null if not a recognized option symbol.
@@ -12,6 +12,7 @@
12
12
  "data-api.polymarket.com",
13
13
  "data.pascal.trade",
14
14
  "efts.sec.gov",
15
+ "forms13f.com",
15
16
  "gamma-api.polymarket.com",
16
17
  "gateway.polymarket.us",
17
18
  "markets.newyorkfed.org",
@@ -2,7 +2,7 @@ import type { PricePoint, TickerFinancials } from "../types/financials";
2
2
  import { canonicalExchange } from "./exchanges";
3
3
  import { isRoundTheClockCoin } from "./crypto-pair";
4
4
  import { isCurrencyPairListing, isFxMarketOpen, latestFxWeekClose, latestFxWeekOpen } from "./fx-market-hours";
5
- import { hasPublishedSessionCalendar, historySourceWeekendDays, latestRegularSessionClose, latestRegularSessionOpen, sessionCalendarTimeZone } from "../market-data/market/freshness";
5
+ import { hasPublishedSessionCalendar, historySourceWeekendDays, isMiddayBreakPrint, latestRegularSessionClose, latestRegularSessionOpen, sessionCalendarTimeZone } from "../market-data/market/freshness";
6
6
  import { zonedDateTimeParts } from "./zoned-date-time";
7
7
  import { regularHistorySessionStaleness } from "../market-data/history-session";
8
8
  import type { HistorySession } from "../types/price-history";
@@ -234,6 +234,12 @@ export function isPriceHistoryStaleForCurrentWindow(
234
234
  const age = now - latestTime;
235
235
  const allowedLag = allowedBarLagMs(intervalMs);
236
236
  if (age <= allowedLag) return false;
237
+ // A venue paused at midday (SSE/SZSE/BSE, TKO, ...) holds the morning's last
238
+ // bar current through the break, as the quote freshness rule does. A bar is
239
+ // labelled by its start, so an hourly bar ending at the break is judged by
240
+ // when it ends.
241
+ const latestBarEnd = intervalMs != null ? Math.min(latestTime + intervalMs, now) : latestTime;
242
+ if (isMiddayBreakPrint(latestBarEnd, options.exchange, now, 0)) return false;
237
243
  const sessions = listingSessionsAt(now, options);
238
244
  if (!sessions) return age > UNSCHEDULED_HISTORY_MAX_AGE_MS;
239
245
  const fetchedAt = options.fetchedAt ?? Number.NEGATIVE_INFINITY;
@@ -354,6 +360,11 @@ function barDate(time: number, timeZone: string): string {
354
360
  return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
355
361
  }
356
362
 
363
+ /** The venue date a daily bar at `time` names on `exchange`, read as calendarHistoryLastBarDate reads it. */
364
+ export function dailyBarDate(time: number, exchange: string): string {
365
+ return barDate(time, sessionCalendarTimeZone(exchange) ?? "UTC");
366
+ }
367
+
357
368
  /**
358
369
  * The venue date of the latest bar, read as calendarHistoryFetchState reads
359
370
  * it, so copies whose sources label a session differently compare equal.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * One word of a command line as a shell reads it back: as written when it
3
+ * holds nothing a shell treats specially (`NVDA`, `SAN:EPA`, `BRK.B`), else
4
+ * in single quotes (`'Altria Group'`, `'$nvda'`, `'AT&T'`).
5
+ */
6
+ export function shellWord(word: string): string {
7
+ return /^[\p{L}\p{N}._:@%+,/][\p{L}\p{N}._:@%+,/=-]*$/u.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
8
+ }