@gloom-sh/terminal 0.18.5 → 0.19.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 (570) 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 +163 -8
  5. package/src/api-client/app-attention.ts +3 -0
  6. package/src/api-client/beneficial-owners.ts +3 -0
  7. package/src/api-client/company-kpis.ts +3 -0
  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 +19 -7
  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/hiring.ts +3 -0
  14. package/src/api-client/index.ts +15 -0
  15. package/src/api-client/ipo.ts +24 -1
  16. package/src/api-client/market-discovery.ts +10 -0
  17. package/src/api-client/market-movers.ts +7 -0
  18. package/src/api-client/members.ts +1 -1
  19. package/src/api-client/mna.ts +3 -0
  20. package/src/api-client/paths.ts +9 -1
  21. package/src/api-client/perps.ts +43 -0
  22. package/src/api-client/research-activity.ts +3 -2
  23. package/src/api-client/roadmap.ts +85 -0
  24. package/src/api-client/types.ts +82 -2
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +0 -2
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +2 -2
  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/market.ts +107 -38
  35. package/src/cli/commands/news-search.ts +22 -0
  36. package/src/cli/commands/options-chain.ts +48 -7
  37. package/src/cli/commands/options-leaps.ts +9 -3
  38. package/src/cli/commands/overview.ts +147 -32
  39. package/src/cli/commands/search.ts +21 -1
  40. package/src/cli/commands/themes.ts +11 -2
  41. package/src/cli/commands/ticker.ts +87 -36
  42. package/src/cli/company-name-hint.ts +41 -0
  43. package/src/cli/earnings-calendar.ts +1 -1
  44. package/src/cli/errors.ts +25 -2
  45. package/src/cli/glossary.ts +31 -2
  46. package/src/cli/history-rows.ts +57 -9
  47. package/src/cli/index-roots.ts +0 -7
  48. package/src/cli/index.ts +2 -0
  49. package/src/cli/listing-arg.ts +235 -60
  50. package/src/cli/output-file.ts +68 -0
  51. package/src/cli/pane-functions/capabilities.ts +4 -0
  52. package/src/cli/pane-functions/catalog.ts +80 -7
  53. package/src/cli/pane-functions/discovery.ts +21 -1
  54. package/src/cli/pane-functions/dom.ts +13 -3
  55. package/src/cli/pane-functions/freshness.ts +93 -4
  56. package/src/cli/pane-functions/function-help.ts +20 -10
  57. package/src/cli/pane-functions/headless.ts +17 -7
  58. package/src/cli/pane-functions/index.ts +54 -16
  59. package/src/cli/pane-functions/market-session.ts +66 -4
  60. package/src/cli/pane-functions/options.ts +19 -1
  61. package/src/cli/pane-functions/resolver.ts +19 -8
  62. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  63. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  64. package/src/cli/quote-answer.ts +22 -11
  65. package/src/cli/saved-copy-persistence.ts +88 -0
  66. package/src/cli/ticker-resolution.ts +3 -2
  67. package/src/components/chart/composite/composite-chart.tsx +4 -0
  68. package/src/components/chart/composite/interactions.ts +6 -0
  69. package/src/components/chart/composite/legend.tsx +45 -7
  70. package/src/components/chart/composite/panel-gestures.ts +33 -0
  71. package/src/components/chart/composite/panel-surface.tsx +52 -3
  72. package/src/components/chart/composite/rasterizer.ts +24 -13
  73. package/src/components/chart/composite/scene.ts +39 -4
  74. package/src/components/chart/composite/series-cues.ts +14 -0
  75. package/src/components/chart/composite/text-renderer.ts +9 -2
  76. package/src/components/chart/composite/tool-catalog.ts +36 -0
  77. package/src/components/chart/composite/tools.ts +203 -44
  78. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  79. package/src/components/command-bar/assist/inventory.ts +2 -1
  80. package/src/components/command-bar/commands/direct/index.ts +28 -13
  81. package/src/components/command-bar/list/model.ts +5 -0
  82. package/src/components/command-bar/pane-templates/items.ts +3 -2
  83. package/src/components/command-bar/routes/root/runtime.ts +6 -7
  84. package/src/components/command-bar/selection-runtime.ts +52 -3
  85. package/src/components/command-bar/surface/index.tsx +2 -0
  86. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  87. package/src/components/layout/pane/capture-stamp.ts +2 -2
  88. package/src/components/layout/pane/footer/index.tsx +22 -6
  89. package/src/components/layout/pane/footer/model.ts +83 -15
  90. package/src/components/layout/pane/status-footer.ts +35 -2
  91. package/src/components/layout/status-widget-space.ts +9 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  93. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  94. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  95. package/src/components/onboarding/wizard-positions.ts +3 -3
  96. package/src/components/ticker/list-table-view.tsx +8 -21
  97. package/src/components/ui/choice-dialog.tsx +23 -18
  98. package/src/components/ui/choice-groups.ts +18 -0
  99. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  100. package/src/components/ui/data-table/opentui/model.ts +26 -1
  101. package/src/components/ui/data-table/types.ts +2 -0
  102. package/src/components/ui/display.tsx +18 -3
  103. package/src/components/ui/icon.tsx +4 -1
  104. package/src/components/ui/list-view.tsx +8 -4
  105. package/src/components/ui/query-bar-keys.tsx +2 -0
  106. package/src/components/ui/query-bar.tsx +1 -0
  107. package/src/components/ui/select-button.tsx +5 -1
  108. package/src/components/ui/select-field.tsx +1 -0
  109. package/src/components/ui/table-layout.ts +8 -5
  110. package/src/core/app-runtime.ts +2 -0
  111. package/src/core/app-services.ts +2 -2
  112. package/src/data/config/chart-settings.ts +1 -1
  113. package/src/data/json-ticker-repository.ts +17 -8
  114. package/src/data/plugin-cache.ts +5 -3
  115. package/src/data/saved-copies.ts +8 -0
  116. package/src/data/ticker-repository.ts +16 -9
  117. package/src/data/ticker-writes.ts +81 -0
  118. package/src/data/ttl-cache.ts +165 -0
  119. package/src/docs/snapshot.json +103 -142
  120. package/src/i18n/es.ts +20 -6
  121. package/src/i18n/ja.ts +20 -6
  122. package/src/i18n/ko.ts +20 -6
  123. package/src/i18n/zh-cn.ts +20 -6
  124. package/src/i18n/zh-tw.ts +20 -6
  125. package/src/market-data/depositary-pair.ts +27 -3
  126. package/src/market-data/fund-facts.ts +50 -0
  127. package/src/market-data/market/change-basis.ts +16 -3
  128. package/src/market-data/market/format.ts +25 -0
  129. package/src/market-data/market/freshness.ts +2 -1
  130. package/src/market-data/options-alternatives.ts +81 -4
  131. package/src/market-data/quotes/metadata.ts +7 -0
  132. package/src/news/aggregator.ts +119 -9
  133. package/src/news/cloud-session.ts +21 -0
  134. package/src/news/hooks.ts +1 -0
  135. package/src/news/news-model.ts +1 -0
  136. package/src/news/types.ts +17 -0
  137. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  138. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  139. package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
  140. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  141. package/src/plugins/builtin/alerts/cli.ts +362 -80
  142. package/src/plugins/builtin/alerts/command.ts +25 -1
  143. package/src/plugins/builtin/alerts/event-rows.ts +24 -3
  144. package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
  145. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  146. package/src/plugins/builtin/alerts/format.ts +75 -5
  147. package/src/plugins/builtin/alerts/headless.ts +34 -12
  148. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  149. package/src/plugins/builtin/alerts/history.ts +1 -1
  150. package/src/plugins/builtin/alerts/index.tsx +79 -16
  151. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  152. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  153. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  154. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  155. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  156. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  157. package/src/plugins/builtin/alerts/storage.ts +12 -8
  158. package/src/plugins/builtin/alerts/types.ts +4 -0
  159. package/src/plugins/builtin/analytics/index.tsx +2 -1
  160. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  161. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  162. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  163. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  164. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  165. package/src/plugins/builtin/attention/index.tsx +2 -1
  166. package/src/plugins/builtin/attention/listing.ts +14 -0
  167. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  168. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  169. package/src/plugins/builtin/buildout/format.ts +38 -0
  170. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  171. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  172. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  173. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  174. package/src/plugins/builtin/cds/client.ts +142 -30
  175. package/src/plugins/builtin/cds/headless.ts +35 -8
  176. package/src/plugins/builtin/cds/model.ts +145 -18
  177. package/src/plugins/builtin/cds/pane.tsx +16 -5
  178. package/src/plugins/builtin/changelog/index.tsx +9 -6
  179. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  180. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  181. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  182. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  183. package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
  184. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  185. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  186. package/src/plugins/builtin/chart-composer/index.tsx +5 -6
  187. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  188. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  189. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  190. package/src/plugins/builtin/chat/controller/index.ts +5 -1
  191. package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
  192. package/src/plugins/builtin/chat/controller/state.ts +25 -2
  193. package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
  194. package/src/plugins/builtin/chat/controller/utils.ts +7 -2
  195. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  196. package/src/plugins/builtin/chat/status-preview.ts +79 -0
  197. package/src/plugins/builtin/chat/status-widget.tsx +147 -10
  198. package/src/plugins/builtin/chat/unread-badge.ts +51 -10
  199. package/src/plugins/builtin/clinical-trials/client.ts +49 -4
  200. package/src/plugins/builtin/clinical-trials/model.ts +86 -31
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
  202. package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
  203. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
  204. package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
  205. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  206. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  207. package/src/plugins/builtin/company-attention/client.ts +8 -9
  208. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  209. package/src/plugins/builtin/company-attention/headless.ts +2 -1
  210. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  211. package/src/plugins/builtin/company-attention/pane.tsx +12 -2
  212. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  213. package/src/plugins/builtin/company-kpis/headless.ts +8 -3
  214. package/src/plugins/builtin/company-kpis/model.ts +73 -5
  215. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  216. package/src/plugins/builtin/composite-plugins.ts +2 -1
  217. package/src/plugins/builtin/correlation/headless.ts +13 -5
  218. package/src/plugins/builtin/correlation/history.ts +22 -0
  219. package/src/plugins/builtin/cot/client.ts +22 -18
  220. package/src/plugins/builtin/cot/headless.ts +15 -10
  221. package/src/plugins/builtin/cot/model.ts +14 -1
  222. package/src/plugins/builtin/cot/pane.tsx +22 -14
  223. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  224. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  225. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  226. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  227. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  228. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  229. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  230. package/src/plugins/builtin/credit-documents/headless.ts +10 -10
  231. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  232. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  233. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  234. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  235. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  236. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  237. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  238. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  239. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  240. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  241. package/src/plugins/builtin/docs/targets.ts +5 -1
  242. package/src/plugins/builtin/doe/headless.ts +3 -2
  243. package/src/plugins/builtin/doe/pane.tsx +13 -3
  244. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  245. package/src/plugins/builtin/earnings/board.tsx +62 -71
  246. package/src/plugins/builtin/earnings/headless.ts +40 -4
  247. package/src/plugins/builtin/earnings/scope.ts +31 -43
  248. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  249. package/src/plugins/builtin/earnings-calls/data.ts +18 -6
  250. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  251. package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
  252. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  253. package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
  254. package/src/plugins/builtin/econ/calendar-model.ts +48 -1
  255. package/src/plugins/builtin/econ/calendar-source.ts +4 -2
  256. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  257. package/src/plugins/builtin/econ/figures.ts +48 -0
  258. package/src/plugins/builtin/econ/index.tsx +14 -5
  259. package/src/plugins/builtin/econ/print-watch.ts +3 -1
  260. package/src/plugins/builtin/econ/types.ts +2 -0
  261. package/src/plugins/builtin/econ-statistics/client.ts +11 -0
  262. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  263. package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
  264. package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
  265. package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
  266. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  267. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  268. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  269. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  270. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  271. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  272. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  273. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  274. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  275. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  276. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  277. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  278. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  279. package/src/plugins/builtin/gpu/headless.ts +8 -2
  280. package/src/plugins/builtin/help/function-index.ts +4 -1
  281. package/src/plugins/builtin/help/index.tsx +62 -59
  282. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  283. package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
  284. package/src/plugins/builtin/help/table-cells.tsx +13 -2
  285. package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
  286. package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
  287. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  288. package/src/plugins/builtin/holders/headless.ts +22 -3
  289. package/src/plugins/builtin/holders/pane.tsx +3 -0
  290. package/src/plugins/builtin/holders/report-rows.ts +4 -1
  291. package/src/plugins/builtin/insider/headless.ts +2 -2
  292. package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
  293. package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
  294. package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
  295. package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
  296. package/src/plugins/builtin/iv-history/client.ts +52 -11
  297. package/src/plugins/builtin/iv-history/format.ts +36 -23
  298. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  299. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  300. package/src/plugins/builtin/iv-history/model.ts +163 -27
  301. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  302. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  303. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  304. package/src/plugins/builtin/jobs/client.ts +25 -29
  305. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  306. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  307. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  308. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  309. package/src/plugins/builtin/macro-day/headless.ts +12 -5
  310. package/src/plugins/builtin/macro-day/index.tsx +1 -1
  311. package/src/plugins/builtin/macro-day/model.ts +39 -5
  312. package/src/plugins/builtin/macro-day/pane.tsx +27 -19
  313. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  314. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  315. package/src/plugins/builtin/market-movers/client.ts +27 -7
  316. package/src/plugins/builtin/market-movers/headless.ts +34 -12
  317. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  318. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  319. package/src/plugins/builtin/market-movers/model.ts +4 -1
  320. package/src/plugins/builtin/market-movers/screener.ts +22 -4
  321. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  322. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  323. package/src/plugins/builtin/market-movers/table.ts +17 -5
  324. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  325. package/src/plugins/builtin/mna/headless.ts +2 -0
  326. package/src/plugins/builtin/mna/pane.tsx +3 -1
  327. package/src/plugins/builtin/news/headless.ts +3 -1
  328. package/src/plugins/builtin/news/index.tsx +4 -0
  329. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  330. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  331. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  332. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  333. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  334. package/src/plugins/builtin/openfda/client.ts +14 -2
  335. package/src/plugins/builtin/openfda/index.tsx +3 -2
  336. package/src/plugins/builtin/openfda/model.ts +9 -0
  337. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  338. package/src/plugins/builtin/options/analytics.ts +29 -5
  339. package/src/plugins/builtin/options/enrichment.ts +17 -5
  340. package/src/plugins/builtin/options/table.ts +5 -2
  341. package/src/plugins/builtin/options/types.ts +2 -0
  342. package/src/plugins/builtin/options/view.tsx +17 -4
  343. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  344. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  345. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  346. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  347. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  348. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  349. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  350. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  351. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  352. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  353. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  354. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  355. package/src/plugins/builtin/pe-band/model.ts +12 -49
  356. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  357. package/src/plugins/builtin/perps/cascade.ts +236 -0
  358. package/src/plugins/builtin/perps/client.ts +56 -2
  359. package/src/plugins/builtin/perps/headless.ts +65 -9
  360. package/src/plugins/builtin/perps/index.tsx +7 -5
  361. package/src/plugins/builtin/perps/model.ts +18 -4
  362. package/src/plugins/builtin/perps/pane.tsx +19 -22
  363. package/src/plugins/builtin/perps/status-line.ts +25 -0
  364. package/src/plugins/builtin/plugin-module.ts +5 -1
  365. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  366. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  367. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  368. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  369. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  370. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
  371. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  372. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  373. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
  374. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
  375. package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
  376. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  377. package/src/plugins/builtin/realized-vol/client.ts +13 -5
  378. package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
  379. package/src/plugins/builtin/realized-vol/headless.ts +8 -4
  380. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  381. package/src/plugins/builtin/realized-vol/model.ts +37 -6
  382. package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
  383. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  384. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  385. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  386. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  387. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  388. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  389. package/src/plugins/builtin/research/event-model.ts +40 -12
  390. package/src/plugins/builtin/research/headless.ts +1 -1
  391. package/src/plugins/builtin/research/index.tsx +7 -3
  392. package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
  393. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  394. package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
  395. package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
  396. package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
  397. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  398. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  399. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  400. package/src/plugins/builtin/risk-factors/data.ts +4 -2
  401. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  402. package/src/plugins/builtin/roadmap/index.tsx +28 -0
  403. package/src/plugins/builtin/roadmap/model.ts +67 -0
  404. package/src/plugins/builtin/roadmap/pane.tsx +244 -0
  405. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  406. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  407. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  408. package/src/plugins/builtin/scanner/headless.ts +204 -35
  409. package/src/plugins/builtin/scanner/index.ts +16 -5
  410. package/src/plugins/builtin/scanner/live-headless.ts +48 -5
  411. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  412. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  413. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  414. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  415. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  416. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  417. package/src/plugins/builtin/sec/headless.ts +4 -2
  418. package/src/plugins/builtin/sec/index.tsx +3 -0
  419. package/src/plugins/builtin/series-inventory.ts +2 -0
  420. package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
  421. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  422. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  423. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  424. package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
  425. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  426. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  427. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  428. package/src/plugins/builtin/shared/report-freshness.ts +11 -2
  429. package/src/plugins/builtin/shared/research-cells.tsx +1 -1
  430. package/src/plugins/builtin/shared/series-cache.ts +4 -4
  431. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  432. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  433. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  434. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  435. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  436. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  437. package/src/plugins/builtin/themes/model.ts +16 -7
  438. package/src/plugins/builtin/themes/pane.tsx +5 -5
  439. package/src/plugins/builtin/thirteenf/api.ts +12 -1
  440. package/src/plugins/builtin/thirteenf/data.ts +7 -7
  441. package/src/plugins/builtin/thirteenf/headless.ts +31 -4
  442. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  443. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  444. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  445. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  446. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  447. package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
  448. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
  449. package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
  450. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  451. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  452. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  453. package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
  454. package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
  455. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  456. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  457. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  458. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  459. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  460. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  461. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  462. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  463. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  464. package/src/plugins/builtin/volatility/index.tsx +2 -2
  465. package/src/plugins/builtin/volatility/pane.tsx +7 -4
  466. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  467. package/src/plugins/builtin/yield-curve/client.ts +61 -1
  468. package/src/plugins/builtin/yield-curve/headless.ts +9 -6
  469. package/src/plugins/builtin/yield-curve/index.tsx +34 -10
  470. package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
  471. package/src/plugins/catalog-browser.ts +2 -1
  472. package/src/plugins/catalog-manifest.ts +2 -1
  473. package/src/plugins/host-link.ts +1 -1
  474. package/src/plugins/marketplace/model.ts +18 -5
  475. package/src/portfolio/column-values.ts +11 -7
  476. package/src/portfolio/futures-positions.ts +6 -3
  477. package/src/portfolio/position-quantity.ts +15 -9
  478. package/src/remote/resources.ts +2 -1
  479. package/src/renderers/dom/data-table/row.tsx +26 -0
  480. package/src/renderers/dom/desktop/icons.tsx +7 -0
  481. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  482. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  483. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  484. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  485. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  486. package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
  487. package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
  488. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  489. package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
  490. package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
  491. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  492. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  493. package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
  494. package/src/renderers/electrobun/bun/index.ts +26 -2
  495. package/src/renderers/electrobun/bun/window/create.ts +43 -4
  496. package/src/renderers/electrobun/bun/window/events.ts +20 -0
  497. package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
  498. package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
  499. package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
  500. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  501. package/src/renderers/electrobun/shared/protocol.ts +12 -0
  502. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  503. package/src/renderers/electrobun/view/main.tsx +24 -1
  504. package/src/renderers/electrobun/view/news-source.ts +5 -1
  505. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  506. package/src/renderers/electrobun/view/ui-host.tsx +8 -0
  507. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  508. package/src/renderers/opentui/start.tsx +5 -0
  509. package/src/series/registry.ts +1 -0
  510. package/src/series/types.ts +6 -0
  511. package/src/series/validate.ts +1 -0
  512. package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
  513. package/src/sources/gloomberb-cloud/index.ts +43 -8
  514. package/src/sources/gloomberb-cloud/news.ts +47 -7
  515. package/src/sources/provider-router/financials.ts +15 -3
  516. package/src/sources/provider-router/index.ts +8 -5
  517. package/src/state/ticker-cleanup.ts +50 -0
  518. package/src/sync/controller.ts +4 -3
  519. package/src/sync/core-contributors.ts +90 -31
  520. package/src/telemetry/stall-reports-dom.ts +38 -0
  521. package/src/telemetry/stall-reports-node.ts +75 -0
  522. package/src/telemetry/stall-reports.ts +214 -0
  523. package/src/test-support/preload.ts +14 -0
  524. package/src/theme/themes.ts +10 -0
  525. package/src/tickers/company-name-listing.ts +53 -13
  526. package/src/tickers/company-name.ts +50 -4
  527. package/src/tickers/index-roots.ts +7 -0
  528. package/src/tickers/listing-fate.ts +2 -2
  529. package/src/tickers/listing-venues.ts +15 -3
  530. package/src/tickers/search/index.ts +6 -2
  531. package/src/tickers/search/ranking.ts +5 -1
  532. package/src/tickers/search/upsert.ts +13 -0
  533. package/src/tickers/terminal-codes.ts +19 -0
  534. package/src/tickers/ticker-changes.ts +8 -4
  535. package/src/time-series/alignment.ts +1 -1
  536. package/src/time-series/economic.ts +18 -4
  537. package/src/time-series/field-catalog.ts +1 -1
  538. package/src/time-series/filed-trailing-eps.ts +197 -0
  539. package/src/time-series/fundamentals.ts +44 -1
  540. package/src/time-series/ice-credit.ts +3 -0
  541. package/src/time-series/price-comparison.ts +30 -2
  542. package/src/time-series/resolve-context.ts +3 -1
  543. package/src/time-series/resolve.ts +56 -4
  544. package/src/time-series/session-bar.ts +11 -0
  545. package/src/time-series/spec.ts +3 -2
  546. package/src/time-series/studies.ts +8 -2
  547. package/src/time-series/total-return.ts +160 -0
  548. package/src/time-series/transforms.ts +2 -3
  549. package/src/time-series/types.ts +22 -1
  550. package/src/types/company-from.ts +40 -0
  551. package/src/types/data-provider.ts +7 -0
  552. package/src/types/financials.ts +59 -1
  553. package/src/types/headless.ts +27 -3
  554. package/src/types/news-source.ts +1 -0
  555. package/src/types/plugin.ts +9 -0
  556. package/src/ui/host.tsx +3 -0
  557. package/src/updater/failure.ts +4 -1
  558. package/src/utils/cli-output.ts +42 -1
  559. package/src/utils/crypto-pair.ts +165 -7
  560. package/src/utils/declared-dividends.ts +1 -1
  561. package/src/utils/forward-dividend.ts +2 -0
  562. package/src/utils/fundamentals.ts +60 -8
  563. package/src/utils/fx-market-hours.ts +26 -3
  564. package/src/utils/main-thread-stall.ts +35 -2
  565. package/src/utils/market-capitalization.ts +24 -1
  566. package/src/utils/price-history.ts +7 -1
  567. package/src/utils/shell-word.ts +8 -0
  568. package/src/utils/trailing-dividend.ts +53 -4
  569. package/src/version.ts +2 -2
  570. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
package/src/i18n/ja.ts CHANGED
@@ -507,10 +507,8 @@ export const ja: Record<string, string> = {
507
507
  "Pin Ticker": "銘柄を固定",
508
508
 
509
509
  // ── Help pane ────────────────────────────────────────────────
510
- "How To Use Gloom": "Gloom の使用方法",
511
510
  "Not sure of a code? Click the search bar at the top, or press {key}, and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "コードが分からない場合は、上部の検索バーをクリックするか {key} を押し、\"top news\" のように普段の言葉で入力してください。「関数」タブに全機能があります。",
512
511
  "Not sure of a code? Click the search bar at the top and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "コードが分からない場合は、上部の検索バーをクリックし、\"top news\" のように普段の言葉で入力してください。「関数」タブに全機能があります。",
513
- "Click anything to use it; the keys shown beside actions are shortcuts.": "どこでもクリックして操作できます。操作の横に表示されるキーはショートカットです。",
514
512
  "Open Help": "ヘルプを開く",
515
513
  "Command Prefixes": "コマンド接頭辞",
516
514
  "Window Templates": "ウィンドウ テンプレート",
@@ -520,7 +518,6 @@ export const ja: Record<string, string> = {
520
518
  "Scrolling": "スクロール",
521
519
  "Open Debug Log": "デバッグ ログを開く",
522
520
  "GitHub Issues": "GitHub Issues",
523
- "Docked panes stay in the saved layout.": "ドッキングされたペインは保存されたレイアウトに残ります。",
524
521
  "Floating panes can be dragged by the title bar and resized from the lower-right corner.": "フローティング ペインは、タイトル バーをドラッグして、右下隅からサイズを変更できます。",
525
522
  "Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets.": "レイアウト アクションを使用して、分割、移動、複製、すべてのフローティング ペインを閉じる、元に戻す、やり直し、およびレイアウト プリセットを実行します。",
526
523
  "Move through focused table and list rows.": "フォーカスされたテーブルとリストの行を移動します。",
@@ -565,7 +562,6 @@ export const ja: Record<string, string> = {
565
562
  "Go back from a nested command screen when the query is empty.": "クエリが空の場合、ネストされたコマンド画面から戻ります。",
566
563
  "Reorder ordered multi-select choices.": "順序付けされた複数選択の選択肢を並べ替えます。",
567
564
  "Submit multiline command forms.": "複数行のコマンド フォームを送信します。",
568
- "Layout Basics": "レイアウトの基本",
569
565
 
570
566
  // ── Onboarding ───────────────────────────────────────────────
571
567
  "The open terminal for modern finance.": "現代の金融に開かれたターミナル。",
@@ -1129,6 +1125,19 @@ export const ja: Record<string, string> = {
1129
1125
  "{symbol}'s figures are not collected yet; the next read stores them.": "{symbol} の数値はまだ収集していません。次回の読み込みで保存されます。",
1130
1126
  "{symbol} files with the SEC as a trust or pool, not as an open-end fund, and its sponsor's site is not one Gloom reads.": "{symbol} はオープンエンド型ファンドではなく、信託またはプールとして SEC に届け出ており、Gloom はそのスポンサーのサイトを読み込んでいません。",
1131
1127
  "No figures for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets list none for it yet.": "{symbol} の数値はありません。Gloom はスポンサーのサイトを読み込んでおらず、SEC の目論見書データセットと N-PORT データセットにもまだ掲載がありません。",
1128
+ "est. {range}": "推定 {range}",
1129
+ "Shares outstanding": "発行済口数",
1130
+ "1M flows": "1か月の資金流出入",
1131
+ "Structure": "形態",
1132
+ "Closed-end fund": "クローズドエンド型ファンド",
1133
+ "BDC": "BDC",
1134
+ "est.": "推定",
1135
+ "{count} shares": "{count} 口",
1136
+ "No figures for {symbol}: it files with the SEC as a closed-end fund or BDC, but neither the filings Gloom reads nor its sponsor's site state any for it yet.": "{symbol} の数値はありません。SEC にクローズドエンド型ファンドまたは BDC として届け出ていますが、Gloom が読み込む提出書類にもスポンサーのサイトにもまだ掲載がありません。",
1137
+ "No NAV for {symbol}: {publisher} publishes figures for it, but no NAV.": "{symbol} の基準価額はありません。{publisher} は数値を公表していますが、基準価額は公表していません。",
1138
+ "No NAV for {symbol}: its SEC filings state net assets but no NAV per share, and its sponsor's NAV is not one Gloom reads.": "{symbol} の基準価額はありません。SEC への提出書類には純資産が記載されていますが 1 株あたり基準価額はなく、Gloom はスポンサーの基準価額を読み込んでいません。",
1139
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and a trust files no NAV with the SEC's fund data sets.": "{symbol} の基準価額はありません。Gloom はスポンサーのサイトを読み込んでおらず、信託は SEC のファンドデータセットに基準価額を届け出ていません。",
1140
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets state no NAV.": "{symbol} の基準価額はありません。Gloom はスポンサーのサイトを読み込んでおらず、SEC の目論見書データセットと N-PORT データセットにも基準価額は記載されていません。",
1132
1141
  "No figures for {symbol}: it lists as a fund, but the SEC's lists do not name it yet and its sponsor's site is not one Gloom reads.": "{symbol} の数値はありません。ファンドとして上場していますが、SEC のリストにはまだ掲載されておらず、Gloom はスポンサーのサイトを読み込んでいません。",
1133
1142
  "No fund facts for {symbol}: it is not on the SEC's lists of US-listed funds.": "{symbol} のファンド概要はありません: SEC の米国上場ファンドの一覧に含まれていません。",
1134
1143
  "No fund facts for {symbol} yet.": "{symbol} のファンド概要はまだありません。",
@@ -1474,8 +1483,11 @@ export const ja: Record<string, string> = {
1474
1483
  "Answers cite filings, calls and news.": "回答は開示、決算説明会、ニュースを引用します。",
1475
1484
  "{ticker} is open. {shortcut}, then AP, adds more.": "{ticker} を開きました。{shortcut} のあと AP で追加できます。",
1476
1485
  "Later: {shortcut}, then AP.": "後で: {shortcut} のあと AP。",
1477
- "Enter: next field, then add.": "Enter: 次の項目へ、最後に追加。",
1478
- "Enter to add a ticker.": "Enter でティッカーを追加。",
1486
+ "Enter: next field, then add": "Enter: 次の項目へ、最後に追加",
1487
+ "Add one ticker to continue": "ティッカーを1つ追加すると続行できます",
1488
+ "Add one ticker to continue.": "ティッカーを1つ追加すると続行できます。",
1489
+ "Esc, then Enter: continue": "Esc のあと Enter: 続行",
1490
+ "Enter to add a ticker": "Enter でティッカーを追加",
1479
1491
  " already added": " 追加済み",
1480
1492
  "Credentials stay on this device.": "認証情報はこの端末にのみ保存されます。",
1481
1493
  "Ask Gloom": "Ask Gloom",
@@ -1652,6 +1664,8 @@ export const ja: Record<string, string> = {
1652
1664
  "Guides": "ガイド",
1653
1665
  "Academy": "アカデミー",
1654
1666
  "offline, saved copy": "オフライン、保存済みのコピー",
1667
+ "company data from {listing}": "{listing} の企業データ",
1668
+ "from {listing}": "{listing} から",
1655
1669
  "Open in Browser": "ブラウザで開く",
1656
1670
  "No docs match \"{query}\".": "「{query}」に一致するドキュメントはありません。",
1657
1671
  "No docs for {name} yet.": "{name} のドキュメントはまだありません。",
package/src/i18n/ko.ts CHANGED
@@ -505,10 +505,8 @@ export const ko: Record<string, string> = {
505
505
  "Pin Ticker": "종목 고정",
506
506
 
507
507
  // ── Help pane ────────────────────────────────────────────────
508
- "How To Use Gloom": "Gloom 사용 방법",
509
508
  "Not sure of a code? Click the search bar at the top, or press {key}, and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "코드를 모르겠다면 위쪽 검색창을 클릭하거나 {key}을 누르고 \"top news\"처럼 원하는 것을 평범한 말로 입력하세요. 기능 탭에 전체 목록이 있습니다.",
510
509
  "Not sure of a code? Click the search bar at the top and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "코드를 모르겠다면 위쪽 검색창을 클릭하고 \"top news\"처럼 원하는 것을 평범한 말로 입력하세요. 기능 탭에 전체 목록이 있습니다.",
511
- "Click anything to use it; the keys shown beside actions are shortcuts.": "무엇이든 클릭해서 사용하세요. 동작 옆에 표시된 키는 단축키입니다.",
512
510
  "Open Help": "도움말 열기",
513
511
  "Command Prefixes": "명령 접두사",
514
512
  "Window Templates": "창 템플릿",
@@ -518,7 +516,6 @@ export const ko: Record<string, string> = {
518
516
  "Scrolling": "스크롤",
519
517
  "Open Debug Log": "디버그 로그 열기",
520
518
  "GitHub Issues": "GitHub 이슈",
521
- "Docked panes stay in the saved layout.": "도킹된 패널은 저장된 레이아웃에 유지됩니다.",
522
519
  "Floating panes can be dragged by the title bar and resized from the lower-right corner.": "플로팅 패널은 제목 표시줄로 이동하고 오른쪽 아래에서 크기를 조절할 수 있습니다.",
523
520
  "Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets.": "레이아웃 작업에서 분할, 이동, 복제, 플로팅 패널 모두 닫기, 실행 취소, 다시 실행 및 프리셋을 관리합니다.",
524
521
  "Move through focused table and list rows.": "포커스된 표와 목록의 행을 이동합니다.",
@@ -563,7 +560,6 @@ export const ko: Record<string, string> = {
563
560
  "Go back from a nested command screen when the query is empty.": "쿼리가 비어 있으면 중첩된 명령 화면에서 돌아갑니다.",
564
561
  "Reorder ordered multi-select choices.": "순서가 있는 다중 선택 항목을 재정렬합니다.",
565
562
  "Submit multiline command forms.": "여러 줄 명령 양식을 제출합니다.",
566
- "Layout Basics": "레이아웃 기본 사항",
567
563
 
568
564
  // ── Onboarding ───────────────────────────────────────────────
569
565
  "The open terminal for modern finance.": "현대 금융을 위한 오픈 소스 터미널.",
@@ -1127,6 +1123,19 @@ export const ko: Record<string, string> = {
1127
1123
  "{symbol}'s figures are not collected yet; the next read stores them.": "{symbol}의 수치는 아직 수집되지 않았습니다. 다음 읽기에서 저장됩니다.",
1128
1124
  "{symbol} files with the SEC as a trust or pool, not as an open-end fund, and its sponsor's site is not one Gloom reads.": "{symbol}은(는) SEC에 개방형 펀드가 아닌 신탁 또는 풀로 등록되어 있으며, Gloom은 스폰서의 사이트를 읽지 않습니다.",
1129
1125
  "No figures for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets list none for it yet.": "{symbol}의 수치가 없습니다. Gloom은 스폰서의 사이트를 읽지 않으며, SEC의 투자설명서 및 N-PORT 데이터 세트에도 아직 없습니다.",
1126
+ "est. {range}": "추정 {range}",
1127
+ "Shares outstanding": "발행 주식 수",
1128
+ "1M flows": "1개월 자금 유출입",
1129
+ "Structure": "구조",
1130
+ "Closed-end fund": "폐쇄형 펀드",
1131
+ "BDC": "BDC",
1132
+ "est.": "추정",
1133
+ "{count} shares": "{count}주",
1134
+ "No figures for {symbol}: it files with the SEC as a closed-end fund or BDC, but neither the filings Gloom reads nor its sponsor's site state any for it yet.": "{symbol}의 수치가 없습니다. SEC에 폐쇄형 펀드 또는 BDC로 신고하지만, Gloom이 읽는 공시와 스폰서 사이트 어디에도 아직 수치가 없습니다.",
1135
+ "No NAV for {symbol}: {publisher} publishes figures for it, but no NAV.": "{symbol}의 순자산가치가 없습니다. {publisher}는 수치를 공시하지만 순자산가치는 공시하지 않습니다.",
1136
+ "No NAV for {symbol}: its SEC filings state net assets but no NAV per share, and its sponsor's NAV is not one Gloom reads.": "{symbol}의 순자산가치가 없습니다. SEC 공시에는 순자산은 있지만 주당 순자산가치는 없으며, Gloom은 스폰서의 순자산가치를 읽지 않습니다.",
1137
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and a trust files no NAV with the SEC's fund data sets.": "{symbol}의 순자산가치가 없습니다. Gloom은 스폰서의 사이트를 읽지 않으며, 신탁은 SEC 펀드 데이터 세트에 순자산가치를 신고하지 않습니다.",
1138
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets state no NAV.": "{symbol}의 순자산가치가 없습니다. Gloom은 스폰서의 사이트를 읽지 않으며, SEC의 투자설명서 및 N-PORT 데이터 세트에도 순자산가치가 없습니다.",
1130
1139
  "No figures for {symbol}: it lists as a fund, but the SEC's lists do not name it yet and its sponsor's site is not one Gloom reads.": "{symbol}의 수치가 없습니다. 펀드로 상장되어 있지만 SEC 목록에는 아직 없으며, Gloom은 스폰서의 사이트를 읽지 않습니다.",
1131
1140
  "No fund facts for {symbol}: it is not on the SEC's lists of US-listed funds.": "{symbol}의 펀드 정보가 없습니다: SEC의 미국 상장 펀드 목록에 없습니다.",
1132
1141
  "No fund facts for {symbol} yet.": "{symbol}의 펀드 정보가 아직 없습니다.",
@@ -1472,8 +1481,11 @@ export const ko: Record<string, string> = {
1472
1481
  "Answers cite filings, calls and news.": "답변은 공시, 실적 발표, 뉴스를 인용합니다.",
1473
1482
  "{ticker} is open. {shortcut}, then AP, adds more.": "{ticker}이(가) 열렸습니다. {shortcut} 다음 AP로 더 추가하세요.",
1474
1483
  "Later: {shortcut}, then AP.": "나중에: {shortcut} 다음 AP.",
1475
- "Enter: next field, then add.": "Enter: 다음 필드, 마지막에 추가.",
1476
- "Enter to add a ticker.": "Enter로 티커 추가.",
1484
+ "Enter: next field, then add": "Enter: 다음 필드, 마지막에 추가",
1485
+ "Add one ticker to continue": "티커를 하나 추가하면 계속할 수 있습니다",
1486
+ "Add one ticker to continue.": "티커를 하나 추가하면 계속할 수 있습니다.",
1487
+ "Esc, then Enter: continue": "Esc 후 Enter: 계속",
1488
+ "Enter to add a ticker": "Enter로 티커 추가",
1477
1489
  " already added": " 이미 추가됨",
1478
1490
  "Credentials stay on this device.": "자격 증명은 이 기기에만 저장됩니다.",
1479
1491
  "Ask Gloom": "Ask Gloom",
@@ -1650,6 +1662,8 @@ export const ko: Record<string, string> = {
1650
1662
  "Guides": "가이드",
1651
1663
  "Academy": "아카데미",
1652
1664
  "offline, saved copy": "오프라인, 저장된 사본",
1665
+ "company data from {listing}": "{listing}의 기업 데이터",
1666
+ "from {listing}": "{listing}에서",
1653
1667
  "Open in Browser": "브라우저에서 열기",
1654
1668
  "No docs match \"{query}\".": "\"{query}\"와 일치하는 문서가 없습니다.",
1655
1669
  "No docs for {name} yet.": "{name} 문서가 아직 없습니다.",
package/src/i18n/zh-cn.ts CHANGED
@@ -507,10 +507,8 @@ export const zhCN: Record<string, string> = {
507
507
  "Pin Ticker": "固定股票",
508
508
 
509
509
  // ── Help pane ────────────────────────────────────────────────
510
- "How To Use Gloom": "如何使用 Gloom",
511
510
  "Not sure of a code? Click the search bar at the top, or press {key}, and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "不知道代码?点击顶部的搜索栏,或按 {key},用日常语言输入你想要的,比如 \"top news\"。“功能”标签列出了全部功能。",
512
511
  "Not sure of a code? Click the search bar at the top and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "不知道代码?点击顶部的搜索栏,用日常语言输入你想要的,比如 \"top news\"。“功能”标签列出了全部功能。",
513
- "Click anything to use it; the keys shown beside actions are shortcuts.": "点击任意内容即可使用;操作旁显示的按键是快捷键。",
514
512
  "Open Help": "打开帮助",
515
513
  "Command Prefixes": "命令前缀",
516
514
  "Window Templates": "窗口模板",
@@ -520,7 +518,6 @@ export const zhCN: Record<string, string> = {
520
518
  "Scrolling": "滚动",
521
519
  "Open Debug Log": "打开调试日志",
522
520
  "GitHub Issues": "GitHub 问题反馈",
523
- "Docked panes stay in the saved layout.": "停靠面板会保留在已存布局中。",
524
521
  "Floating panes can be dragged by the title bar and resized from the lower-right corner.": "浮动面板可拖动标题栏移动,右下角调整大小。",
525
522
  "Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets.": "用布局操作完成拆分、移动、复制、关闭全部浮动面板、撤销、重做与布局预设。",
526
523
  "Move through focused table and list rows.": "在聚焦的表格与列表行间移动。",
@@ -565,7 +562,6 @@ export const zhCN: Record<string, string> = {
565
562
  "Go back from a nested command screen when the query is empty.": "查询为空时从嵌套命令界面返回。",
566
563
  "Reorder ordered multi-select choices.": "调整有序多选项的顺序。",
567
564
  "Submit multiline command forms.": "提交多行命令表单。",
568
- "Layout Basics": "布局基础",
569
565
 
570
566
  // ── Onboarding ───────────────────────────────────────────────
571
567
  "The open terminal for modern finance.": "面向现代金融的开源终端。",
@@ -1129,6 +1125,19 @@ export const zhCN: Record<string, string> = {
1129
1125
  "{symbol}'s figures are not collected yet; the next read stores them.": "尚未采集 {symbol} 的数据;下次读取时会保存。",
1130
1126
  "{symbol} files with the SEC as a trust or pool, not as an open-end fund, and its sponsor's site is not one Gloom reads.": "{symbol} 在 SEC 登记为信托或资金池,而非开放式基金,且 Gloom 不读取其发起人的网站。",
1131
1127
  "No figures for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets list none for it yet.": "暂无 {symbol} 的数据:Gloom 不读取其发起人的网站,SEC 的招募说明书和 N-PORT 数据集中也尚未列出。",
1128
+ "est. {range}": "估计 {range}",
1129
+ "Shares outstanding": "流通份额",
1130
+ "1M flows": "1个月资金流",
1131
+ "Structure": "结构",
1132
+ "Closed-end fund": "封闭式基金",
1133
+ "BDC": "BDC",
1134
+ "est.": "估计",
1135
+ "{count} shares": "{count} 份",
1136
+ "No figures for {symbol}: it files with the SEC as a closed-end fund or BDC, but neither the filings Gloom reads nor its sponsor's site state any for it yet.": "暂无 {symbol} 的数据:它以封闭式基金或 BDC 身份向 SEC 申报,但 Gloom 读取的申报文件和其发起人的网站均尚未列出任何数据。",
1137
+ "No NAV for {symbol}: {publisher} publishes figures for it, but no NAV.": "暂无 {symbol} 的资产净值:{publisher} 公布了其数据,但未公布资产净值。",
1138
+ "No NAV for {symbol}: its SEC filings state net assets but no NAV per share, and its sponsor's NAV is not one Gloom reads.": "暂无 {symbol} 的资产净值:其 SEC 申报文件列出了净资产,但未列出每股资产净值,Gloom 也不读取其发起人公布的资产净值。",
1139
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and a trust files no NAV with the SEC's fund data sets.": "暂无 {symbol} 的资产净值:Gloom 不读取其发起人的网站,信托也不在 SEC 的基金数据集中申报资产净值。",
1140
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets state no NAV.": "暂无 {symbol} 的资产净值:Gloom 不读取其发起人的网站,SEC 的招募说明书和 N-PORT 数据集中也没有资产净值。",
1132
1141
  "No figures for {symbol}: it lists as a fund, but the SEC's lists do not name it yet and its sponsor's site is not one Gloom reads.": "暂无 {symbol} 的数据:它以基金身份上市,但 SEC 的名单尚未收录,且 Gloom 不读取其发起人的网站。",
1133
1142
  "No fund facts for {symbol}: it is not on the SEC's lists of US-listed funds.": "{symbol} 没有基金概况:它不在 SEC 的美国上市基金名单中。",
1134
1143
  "No fund facts for {symbol} yet.": "{symbol} 暂无基金概况。",
@@ -1474,8 +1483,11 @@ export const zhCN: Record<string, string> = {
1474
1483
  "Answers cite filings, calls and news.": "回答引用公告、电话会议和新闻。",
1475
1484
  "{ticker} is open. {shortcut}, then AP, adds more.": "{ticker} 已打开。{shortcut} 然后输入 AP 可添加更多。",
1476
1485
  "Later: {shortcut}, then AP.": "之后:{shortcut},然后输入 AP。",
1477
- "Enter: next field, then add.": "Enter:下一字段,最后添加。",
1478
- "Enter to add a ticker.": "按 Enter 添加代码。",
1486
+ "Enter: next field, then add": "Enter:下一字段,最后添加",
1487
+ "Add one ticker to continue": "添加一个代码即可继续",
1488
+ "Add one ticker to continue.": "添加一个代码即可继续。",
1489
+ "Esc, then Enter: continue": "Esc,然后 Enter:继续",
1490
+ "Enter to add a ticker": "按 Enter 添加代码",
1479
1491
  " already added": " 已添加",
1480
1492
  "Credentials stay on this device.": "凭据仅保存在此设备上。",
1481
1493
  "Ask Gloom": "Ask Gloom",
@@ -1652,6 +1664,8 @@ export const zhCN: Record<string, string> = {
1652
1664
  "Guides": "指南",
1653
1665
  "Academy": "学院",
1654
1666
  "offline, saved copy": "离线,已保存的副本",
1667
+ "company data from {listing}": "公司数据来自 {listing}",
1668
+ "from {listing}": "来自 {listing}",
1655
1669
  "Open in Browser": "在浏览器中打开",
1656
1670
  "No docs match \"{query}\".": "没有与“{query}”匹配的文档。",
1657
1671
  "No docs for {name} yet.": "{name} 暂无文档。",
package/src/i18n/zh-tw.ts CHANGED
@@ -507,10 +507,8 @@ export const zhTW: Record<string, string> = {
507
507
  "Pin Ticker": "固定股票",
508
508
 
509
509
  // ── Help pane ────────────────────────────────────────────────
510
- "How To Use Gloom": "如何使用 Gloom",
511
510
  "Not sure of a code? Click the search bar at the top, or press {key}, and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "不知道代碼?點擊頂部的搜尋列,或按 {key},用日常用語輸入你想要的,例如 \"top news\"。「功能」標籤列出了全部功能。",
512
511
  "Not sure of a code? Click the search bar at the top and type what you want in plain words, like \"top news\". The Functions tab lists everything.": "不知道代碼?點擊頂部的搜尋列,用日常用語輸入你想要的,例如 \"top news\"。「功能」標籤列出了全部功能。",
513
- "Click anything to use it; the keys shown beside actions are shortcuts.": "點擊任何內容即可使用;動作旁顯示的按鍵是快捷鍵。",
514
512
  "Open Help": "開啟說明",
515
513
  "Command Prefixes": "命令字首",
516
514
  "Window Templates": "視窗範本",
@@ -520,7 +518,6 @@ export const zhTW: Record<string, string> = {
520
518
  "Scrolling": "捲動",
521
519
  "Open Debug Log": "開啟除錯日誌",
522
520
  "GitHub Issues": "GitHub 問題回報",
523
- "Docked panes stay in the saved layout.": "停靠面板會保留在已儲存的版面設定中。",
524
521
  "Floating panes can be dragged by the title bar and resized from the lower-right corner.": "拖曳標題列可移動浮動面板,從右下角可調整大小。",
525
522
  "Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets.": "用版面設定操作完成拆分、移動、複製、關閉全部浮動面板、撤銷、重做與版面設定預設。",
526
523
  "Move through focused table and list rows.": "在目前表格與清單的各列之間移動。",
@@ -565,7 +562,6 @@ export const zhTW: Record<string, string> = {
565
562
  "Go back from a nested command screen when the query is empty.": "查詢為空時,從巢狀命令畫面回到上一層。",
566
563
  "Reorder ordered multi-select choices.": "調整有序多選項的順序。",
567
564
  "Submit multiline command forms.": "提交多行命令表單。",
568
- "Layout Basics": "版面設定基礎",
569
565
 
570
566
  // ── Onboarding ───────────────────────────────────────────────
571
567
  "The open terminal for modern finance.": "現代金融的開源終端機。",
@@ -1129,6 +1125,19 @@ export const zhTW: Record<string, string> = {
1129
1125
  "{symbol}'s figures are not collected yet; the next read stores them.": "尚未蒐集 {symbol} 的數據;下次讀取時會儲存。",
1130
1126
  "{symbol} files with the SEC as a trust or pool, not as an open-end fund, and its sponsor's site is not one Gloom reads.": "{symbol} 在 SEC 登記為信託或資金池,而非開放式基金,且 Gloom 不讀取其發起人的網站。",
1131
1127
  "No figures for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets list none for it yet.": "暫無 {symbol} 的數據:Gloom 不讀取其發起人的網站,SEC 的公開說明書與 N-PORT 資料集中也尚未列出。",
1128
+ "est. {range}": "估計 {range}",
1129
+ "Shares outstanding": "流通單位數",
1130
+ "1M flows": "1個月資金流",
1131
+ "Structure": "結構",
1132
+ "Closed-end fund": "封閉式基金",
1133
+ "BDC": "BDC",
1134
+ "est.": "估計",
1135
+ "{count} shares": "{count} 單位",
1136
+ "No figures for {symbol}: it files with the SEC as a closed-end fund or BDC, but neither the filings Gloom reads nor its sponsor's site state any for it yet.": "暫無 {symbol} 的數據:它以封閉式基金或 BDC 身分向 SEC 申報,但 Gloom 讀取的申報文件與其發起人的網站均尚未列出任何數據。",
1137
+ "No NAV for {symbol}: {publisher} publishes figures for it, but no NAV.": "暫無 {symbol} 的資產淨值:{publisher} 公布了其數據,但未公布資產淨值。",
1138
+ "No NAV for {symbol}: its SEC filings state net assets but no NAV per share, and its sponsor's NAV is not one Gloom reads.": "暫無 {symbol} 的資產淨值:其 SEC 申報文件列出了淨資產,但未列出每股資產淨值,Gloom 也不讀取其發起人公布的資產淨值。",
1139
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and a trust files no NAV with the SEC's fund data sets.": "暫無 {symbol} 的資產淨值:Gloom 不讀取其發起人的網站,信託也不在 SEC 的基金資料集中申報資產淨值。",
1140
+ "No NAV for {symbol}: its sponsor's site is not one Gloom reads, and the SEC's prospectus and N-PORT data sets state no NAV.": "暫無 {symbol} 的資產淨值:Gloom 不讀取其發起人的網站,SEC 的公開說明書與 N-PORT 資料集中也沒有資產淨值。",
1132
1141
  "No figures for {symbol}: it lists as a fund, but the SEC's lists do not name it yet and its sponsor's site is not one Gloom reads.": "暫無 {symbol} 的數據:它以基金身分上市,但 SEC 的名單尚未收錄,且 Gloom 不讀取其發起人的網站。",
1133
1142
  "No fund facts for {symbol}: it is not on the SEC's lists of US-listed funds.": "{symbol} 沒有基金概況:它不在 SEC 的美國上市基金名單中。",
1134
1143
  "No fund facts for {symbol} yet.": "{symbol} 暫無基金概況。",
@@ -1474,8 +1483,11 @@ export const zhTW: Record<string, string> = {
1474
1483
  "Answers cite filings, calls and news.": "回答引用申報、法說會和新聞。",
1475
1484
  "{ticker} is open. {shortcut}, then AP, adds more.": "{ticker} 已開啟。{shortcut} 然後輸入 AP 可新增更多。",
1476
1485
  "Later: {shortcut}, then AP.": "之後:{shortcut},然後輸入 AP。",
1477
- "Enter: next field, then add.": "Enter:下一欄位,最後新增。",
1478
- "Enter to add a ticker.": "按 Enter 新增代號。",
1486
+ "Enter: next field, then add": "Enter:下一欄位,最後新增",
1487
+ "Add one ticker to continue": "新增一個代號即可繼續",
1488
+ "Add one ticker to continue.": "新增一個代號即可繼續。",
1489
+ "Esc, then Enter: continue": "Esc,然後 Enter:繼續",
1490
+ "Enter to add a ticker": "按 Enter 新增代號",
1479
1491
  " already added": " 已新增",
1480
1492
  "Credentials stay on this device.": "憑證僅保存在此裝置上。",
1481
1493
  "Ask Gloom": "Ask Gloom",
@@ -1652,6 +1664,8 @@ export const zhTW: Record<string, string> = {
1652
1664
  "Guides": "指南",
1653
1665
  "Academy": "學院",
1654
1666
  "offline, saved copy": "離線,已儲存的副本",
1667
+ "company data from {listing}": "公司資料來自 {listing}",
1668
+ "from {listing}": "來自 {listing}",
1655
1669
  "Open in Browser": "在瀏覽器中開啟",
1656
1670
  "No docs match \"{query}\".": "沒有符合「{query}」的文件。",
1657
1671
  "No docs for {name} yet.": "{name} 尚無文件。",
@@ -1,4 +1,4 @@
1
- import type { DepositaryPair, Fundamentals, Quote } from "../types/financials";
1
+ import type { CedearRatio, DepositaryPair, Fundamentals, Quote } from "../types/financials";
2
2
  import { canonicalExchange, parsePublicTickerKey } from "../utils/exchanges";
3
3
  import { formatNumber } from "../utils/format";
4
4
  import { formatUtcDayTime } from "../utils/utc-time";
@@ -41,9 +41,33 @@ export function ratioCount(ratio: number): string {
41
41
  return Number.isInteger(ratio) ? String(ratio) : String(Number(ratio.toPrecision(6)));
42
42
  }
43
43
 
44
- /** What a home line says of its receipt: `1 GGAL ADR = 10 of these shares`. */
44
+ type CedearRecords = { quote?: Pick<Quote, "cedearRatio"> | null; fundamentals?: Pick<Fundamentals, "cedearRatio"> | null };
45
+
46
+ const positiveCount = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value) && value > 0;
47
+
48
+ /** A served CEDEAR ratio, when it is whole. */
49
+ function readCedearRatio(value: unknown): CedearRatio | null {
50
+ if (!value || typeof value !== "object") return null;
51
+ const ratio = value as Partial<CedearRatio>;
52
+ if (typeof ratio.listing !== "string" || !ratio.listing.trim()) return null;
53
+ if (typeof ratio.company !== "string" || !ratio.company.trim()) return null;
54
+ if (!positiveCount(ratio.receipts) || !positiveCount(ratio.shares)) return null;
55
+ return ratio as CedearRatio;
56
+ }
57
+
58
+ /** The CEDEAR ratio a listing's records state: the quote's, else the fundamentals'. */
59
+ export function listingCedearRatio(records: CedearRecords | null | undefined): CedearRatio | null {
60
+ return readCedearRatio(records?.quote?.cedearRatio) ?? readCedearRatio(records?.fundamentals?.cedearRatio);
61
+ }
62
+
63
+ /** A CEDEAR's ratio in its company's shares: `20 AAPL:BYMA = 1 Apple Inc. share`. */
64
+ export function cedearRatioText(ratio: CedearRatio): string {
65
+ return `${ratioCount(ratio.receipts)} ${ratio.listing} = ${ratioCount(ratio.shares)} ${ratio.company.trim()} share${ratio.shares === 1 ? "" : "s"}`;
66
+ }
67
+
68
+ /** What a home line says of its receipt: `1 GGAL ADR = 10 of these shares`, and `1 of these shares` for one. */
45
69
  function homeLineRatioText(pair: DepositaryPair): string {
46
- return `1 ${pair.receipt} ADR = ${ratioCount(pair.ratio)} of these share${pair.ratio === 1 ? "" : "s"}`;
70
+ return `1 ${pair.receipt} ADR = ${ratioCount(pair.ratio)} of these shares`;
47
71
  }
48
72
 
49
73
  /**
@@ -0,0 +1,50 @@
1
+ import { apiClient } from "../api-client";
2
+ import type { FundFactsPayload, FundFlows } from "../api-client/fund-facts";
3
+ import { isBondFactsShape } from "./bond-funds";
4
+
5
+ /**
6
+ * A fund facts answer as every reader takes it (the ETF pane, DES, CMP,
7
+ * `ticker`, `fundamentals`): each figure dated and published, the parts a
8
+ * server before them leaves out still readable, anything malformed refused.
9
+ */
10
+
11
+ const fact = (value: unknown) => value === null || (typeof value === "object" && value !== null && "value" in value && "source" in value);
12
+ const finite = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value);
13
+
14
+ function isMetalHoldingsShape(value: FundFactsPayload["metalHoldings"]): boolean {
15
+ return value == null || ((value.metal === "gold" || value.metal === "silver") && typeof value.asOf === "string" && finite(value.ounces)
16
+ && (value.tonnes === null || finite(value.tonnes)) && typeof value.source?.publisher === "string"
17
+ && (value.change === null || (typeof value.change?.since === "string" && finite(value.change.ounces))));
18
+ }
19
+
20
+ /** Absent from servers before it; otherwise a dated factor with its reset period. */
21
+ const leverageShape = (value: FundFactsPayload["leverage"]) => value === undefined || value === null
22
+ || (fact(value) && typeof value.value === "number" && Number.isFinite(value.value) && (value.reset === "daily" || value.reset === "monthly"));
23
+
24
+ /** A NAV's estimate range, when the sponsor published one: absent, or two ends in order. */
25
+ const estimateShape = (nav: FundFactsPayload["nav"]) => !nav?.estimate
26
+ || (finite(nav.estimate.low) && finite(nav.estimate.high) && nav.estimate.low <= nav.estimate.high);
27
+
28
+ /** Absent from servers before them; otherwise a dated count, and a change between two dated counts. */
29
+ const sharesShape = (value: FundFactsPayload["sharesOutstanding"]) => value == null || (fact(value) && finite(value.value));
30
+ const flowsShape = (value: FundFlows | null | undefined) => value == null
31
+ || (finite(value.shares) && (value.value === null || finite(value.value)) && typeof value.from === "string" && typeof value.asOf === "string");
32
+
33
+ export function validateFundFacts(data: FundFactsPayload): FundFactsPayload {
34
+ if (!data || typeof data.ticker !== "string" || typeof data.covered !== "boolean"
35
+ || ![data.expenseRatio, data.nav, data.premiumDiscount, data.netAssets, data.sponsor, data.inception, data.index].every(fact)
36
+ || !estimateShape(data.nav) || !sharesShape(data.sharesOutstanding) || !flowsShape(data.flows)
37
+ || !isBondFactsShape(data.bond) || !leverageShape(data.leverage) || !isMetalHoldingsShape(data.metalHoldings)) {
38
+ throw new Error("Unreadable fund facts response");
39
+ }
40
+ return data;
41
+ }
42
+
43
+ /** `GET /cloud/fund-facts/:ticker`, read and checked. */
44
+ export async function readFundFacts(
45
+ symbol: string,
46
+ client: Pick<typeof apiClient, "getFundFacts"> = apiClient,
47
+ signal?: AbortSignal,
48
+ ): Promise<FundFactsPayload> {
49
+ return validateFundFacts(await (signal ? client.getFundFacts(symbol, { signal }) : client.getFundFacts(symbol)));
50
+ }
@@ -4,6 +4,7 @@ import type { Quote } from "../../types/financials";
4
4
  import { canonicalExchange } from "../../utils/exchanges";
5
5
  import { isFiniteNumber } from "../../utils/guards";
6
6
  import { formatUtcDayTime } from "../../utils/utc-time";
7
+ import { fxPrintTradingDay } from "../../utils/fx-market-hours";
7
8
  import { zonedDateKey } from "../../utils/zoned-date-time";
8
9
  import { futuresChangeBasis, futuresSettleText, isFuturesInstrument } from "../futures-contract";
9
10
  import { quoteTradingDay } from "../quotes/day-range";
@@ -94,6 +95,18 @@ function isCurrencyPair(quote: Quote): boolean {
94
95
  || kindFromListingSyntax(quote.symbol, quote.listingExchangeName || quote.exchangeName) === "currency";
95
96
  }
96
97
 
98
+ /**
99
+ * The trading day a quote describes (quoteTradingDay); for a currency pair,
100
+ * whose venue has no clock, the FX trading day of its print, which turns at
101
+ * 17:00 New York, so every pair on a board is dated alike. Null for a pair's
102
+ * print made while FX is shut, which belongs to no trading day.
103
+ */
104
+ function tradingDayOf(quote: Quote): string | null {
105
+ const day = quoteTradingDay(quote);
106
+ if (day || !isCurrencyPair(quote) || !Number.isFinite(quote.lastUpdated) || quote.lastUpdated <= 0) return day;
107
+ return fxPrintTradingDay(quote.lastUpdated);
108
+ }
109
+
97
110
  /** A coin on the spot crypto venue: its day is the UTC day. */
98
111
  function isSpotCoin(quote: Quote): boolean {
99
112
  return quoteVenue(quote) === "CCC";
@@ -154,7 +167,7 @@ export function isChangeBasisDatedWithoutCloses(quote: Quote, now = Date.now()):
154
167
  * nothing dates it.
155
168
  */
156
169
  export function changeBasisSessionDate(quote: Quote): string | null {
157
- const date = regularSessionDisplayDate(quote) ?? quoteTradingDay(quote);
170
+ const date = regularSessionDisplayDate(quote) ?? tradingDayOf(quote);
158
171
  return date && ISO_DATE.test(date) ? date : null;
159
172
  }
160
173
 
@@ -179,7 +192,7 @@ export function quoteChangeBasis(
179
192
  if (!display || !isFiniteNumber(display.change)) return null;
180
193
  if (quote.changeBasis === "24h" || isPerpetualExchange(quote.listingExchangeName || quote.exchangeName)) return { kind: "24h" };
181
194
  if (isSpotCoin(quote)) return { kind: "utc day" };
182
- const sessionDate = (session === "active" ? quote.changeSessionDate : regularSessionDisplayDate(quote)) ?? quoteTradingDay(quote);
195
+ const sessionDate = (session === "active" ? quote.changeSessionDate : regularSessionDisplayDate(quote)) ?? tradingDayOf(quote);
183
196
  if (isFuturesInstrument(quote.symbol, quote.instrumentType)) {
184
197
  const basis = futuresChangeBasis(terms ?? { settlement: null }, { ...display, sessionDate }, quoteVenue(quote));
185
198
  return basis ? { kind: basis.kind, date: basis.session } : { kind: "previous close" };
@@ -211,7 +224,7 @@ export function quoteSettleText(
211
224
  export function quotePreviousCloseDate(quote: Quote | null | undefined): string | null {
212
225
  if (!quote || !isFiniteNumber(quote.previousClose) || quote.changeBasis != null) return null;
213
226
  if (isPerpetualExchange(quote.listingExchangeName || quote.exchangeName)) return null;
214
- const sessionDate = quote.changeSessionDate ?? quoteTradingDay(quote);
227
+ const sessionDate = quote.changeSessionDate ?? tradingDayOf(quote);
215
228
  if (!sessionDate || !ISO_DATE.test(sessionDate)) return null;
216
229
  if (isSpotCoin(quote)) return dayBefore(sessionDate);
217
230
  if (isFuturesInstrument(quote.symbol, quote.instrumentType)) {
@@ -628,6 +628,31 @@ function currencyMarks(currency: string, normalizedCurrency: string, code = fals
628
628
  return code ? { prefix: "", suffix: ` ${normalizedCurrency}` } : { prefix: getCurrencySymbol(normalizedCurrency), suffix: "" };
629
629
  }
630
630
 
631
+ /**
632
+ * A London line shown in pounds and served in the pence it trades in. The
633
+ * code is compared in its own case: `GBp` (and `GBX`) is a price already in
634
+ * pence, which says so itself (630.8p) and needs no pence figure beside it.
635
+ */
636
+ export function isPenceQuotedLine(quote: Pick<Quote, "currency" | "providerPriceDivisor"> | null | undefined): boolean {
637
+ return quote?.currency?.trim() === "GBP" && quote.providerPriceDivisor === 100;
638
+ }
639
+
640
+ /**
641
+ * A London line's price in the pence it trades in, as the exchange and its
642
+ * announcements write it: `630.8p` for £6.308, with the decimals of a penny
643
+ * the pounds are shown to (`poundDecimals`) beyond two. Null for a price not
644
+ * quoted in pence.
645
+ */
646
+ export function penceFigure(
647
+ value: number | null | undefined,
648
+ quote: Pick<Quote, "currency" | "providerPriceDivisor"> | null | undefined,
649
+ poundDecimals: number,
650
+ ): string | null {
651
+ if (value == null || !Number.isFinite(value) || !isPenceQuotedLine(quote)) return null;
652
+ const digits = Math.max(0, Math.min(4, poundDecimals - 2));
653
+ return `${getNumberFormatter(digits, digits, true).format(value * 100)}${minorUnitSign("GBp")}`;
654
+ }
655
+
631
656
  export function formatMarketPriceWithCurrency(
632
657
  value: number | undefined,
633
658
  currency = "USD",
@@ -32,7 +32,8 @@ const REGULAR_OPEN_MINUTES: Record<string, number> = {
32
32
  TSXV: 9 * 60 + 30,
33
33
  CSE: 9 * 60 + 30,
34
34
  FWB2: 8 * 60,
35
- XETRA: 8 * 60,
35
+ // Continuous trading opens at 09:00 Berlin time, after the opening auction; before it is pre-trading.
36
+ XETRA: 9 * 60,
36
37
  LSE: 8 * 60,
37
38
  EPA: 9 * 60,
38
39
  AMS: 9 * 60,
@@ -1,4 +1,5 @@
1
1
  import { isRoundTheClockCoin } from "../utils/crypto-pair";
2
+ import { currencyPairCode, isCurrencyCode } from "../utils/currency-pair";
2
3
  import { parsePublicTickerKey } from "../utils/exchanges";
3
4
  import { parseFuturesGeneric } from "../utils/futures-generic";
4
5
 
@@ -34,23 +35,24 @@ const FUTURES_ALTERNATIVES: Readonly<Record<string, ListedOptionsAlternative>> =
34
35
  ZN: { symbol: "IEF", name: "iShares 7-10 Year Treasury Bond ETF" },
35
36
  "6E": { symbol: "FXE", name: "Invesco CurrencyShares Euro Trust" },
36
37
  "6J": { symbol: "FXY", name: "Invesco CurrencyShares Japanese Yen Trust" },
38
+ "6B": { symbol: "FXB", name: "Invesco CurrencyShares British Pound Sterling Trust" },
37
39
  BTC: { symbol: "IBIT", name: "iShares Bitcoin Trust" },
38
40
  ETH: { symbol: "ETHA", name: "iShares Ethereum Trust" },
39
41
  };
40
42
 
41
43
  const MONTH_CODE_CONTRACT = /^([A-Z0-9]{1,3}?)[FGHJKMNQUVXZ]\d{1,4}$/;
42
44
 
43
- /** The futures root a symbol names: GC=F, GCZ26 and the generic GC1 all give GC. */
44
- function futuresRoot(symbol: string): string | null {
45
+ /** The futures root a symbol names: GC=F, GCZ26 and the generic GC1 all give GC. `known` says which roots a bare contract may carry. */
46
+ function futuresRoot(symbol: string, known: (root: string) => boolean = (root) => !!FUTURES_ALTERNATIVES[root]): string | null {
45
47
  const ticker = symbol.trim().toUpperCase().replace(/[.:][A-Z]+$/, "");
46
48
  if (ticker.endsWith("=F")) {
47
49
  const body = ticker.slice(0, -2);
48
- return FUTURES_ALTERNATIVES[body] ? body : MONTH_CODE_CONTRACT.exec(body)?.[1] ?? body;
50
+ return known(body) ? body : MONTH_CODE_CONTRACT.exec(body)?.[1] ?? body;
49
51
  }
50
52
  const generic = parseFuturesGeneric(ticker);
51
53
  if (generic) return generic.root;
52
54
  const contract = MONTH_CODE_CONTRACT.exec(ticker)?.[1];
53
- return contract && FUTURES_ALTERNATIVES[contract] ? contract : null;
55
+ return contract && known(contract) ? contract : null;
54
56
  }
55
57
 
56
58
  /** The listed fund whose options stand in for an instrument with no chain, if there is one. */
@@ -127,3 +129,78 @@ export function coinOptionsAlias(code: string) {
127
129
  return pair ? { symbol: pair, note: `${key} read as ${pair}, the coin's options. For the US fund that trades as ${key}: gloomberb fn ${code} ${key}:ARCA` } : null;
128
130
  };
129
131
  }
132
+
133
+ /**
134
+ * The CME currency futures, each as the pair it settles on, in the order the
135
+ * pair is quoted: 6E is EUR/USD, 6J USD/JPY. No chain of options on any of
136
+ * them is served here, as none is on the spot pair.
137
+ */
138
+ const FX_FUTURES_PAIRS: Readonly<Record<string, string>> = {
139
+ "6E": "EURUSD", "6J": "USDJPY", "6B": "GBPUSD", "6A": "AUDUSD", "6C": "USDCAD", "6S": "USDCHF", "6N": "NZDUSD", "6M": "USDMXN",
140
+ };
141
+
142
+ /** The US-listed currency funds whose options stand in for an FX pair's volatility, by the currency they hold. */
143
+ const FX_VOL_FUNDS: Readonly<Record<string, string>> = { EUR: "FXE", JPY: "FXY", GBP: "FXB" };
144
+ /** The fund that holds the dollar against a basket, for a pair none of the single-currency funds holds. */
145
+ const DOLLAR_FUND = "UUP";
146
+ const FX_VOL_FUND_LIST = [...Object.values(FX_VOL_FUNDS), DOLLAR_FUND];
147
+
148
+ /** The pair a symbol is: a spot pair in any spelling (EURUSD=X, EUR/USD), or a currency future (6E=F, 6EZ26, 6E1) as the pair it settles on; null for anything else. */
149
+ export function fxVolatilityPair(symbol: string, exchange?: string): string | null {
150
+ const spot = currencyPairCode(symbol, exchange);
151
+ // Metals quoted against a currency (XAUUSD=X) are no currency pair.
152
+ if (spot) return isCurrencyCode(spot.slice(0, 3)) && isCurrencyCode(spot.slice(3)) ? spot : null;
153
+ const root = futuresRoot(symbol, (candidate) => !!FX_FUTURES_PAIRS[candidate]);
154
+ return root ? FX_FUTURES_PAIRS[root] ?? null : null;
155
+ }
156
+
157
+ /**
158
+ * The listed fund that stands in for an FX pair's volatility: the one that
159
+ * holds the pair's euro, yen or pound, else the dollar index fund.
160
+ */
161
+ function fxVolatilityFunds(pair: string): string[] {
162
+ const held = [pair.slice(0, 3), pair.slice(3)].map((code) => FX_VOL_FUNDS[code]).filter((fund): fund is string => !!fund);
163
+ return held.length > 0 ? held : FX_VOL_FUND_LIST;
164
+ }
165
+
166
+ /**
167
+ * What exists instead for the volatility of an FX pair or currency future,
168
+ * worded as one line for the CLI: OTC FX volatility is not served, the
169
+ * currency funds' options are the nearest listed thing (the one that holds
170
+ * the pair's euro, yen or pound, else any of them), and HVT reads the pair's
171
+ * realized volatility. Null for anything that is no FX pair. `code` is the
172
+ * function asked (OVDV), `symbol` as typed.
173
+ */
174
+ function fxVolatilityAnswer(code: string, symbol: string): string | null {
175
+ const pair = fxVolatilityPair(symbol);
176
+ if (!pair) return null;
177
+ const typed = symbol.trim().toUpperCase();
178
+ const funds = fxVolatilityFunds(pair);
179
+ // VCA screens every fund; the others read the pair's own, or the dollar's when it has none.
180
+ const read = code === "VCA" ? funds.join(",") : funds === FX_VOL_FUND_LIST ? DOLLAR_FUND : funds[0]!;
181
+ return `OTC FX volatility is not covered for ${typed}. Currency-ETF options are the listed proxy: `
182
+ + `${funds.join(", ")} (gloom fn ${code} ${read}). Realized volatility: gloom fn HVT ${typed}.`;
183
+ }
184
+
185
+ /**
186
+ * The same for a pane, which has no commands to give: what is not covered,
187
+ * the funds whose options stand in, and HVT for realized volatility.
188
+ */
189
+ export function fxVolatilityNote(symbol: string, exchange?: string): string | null {
190
+ const pair = fxVolatilityPair(symbol, exchange);
191
+ if (!pair) return null;
192
+ return `OTC FX volatility is not covered for ${symbol.trim().toUpperCase()}. Currency-ETF options are the listed proxy: `
193
+ + `${fxVolatilityFunds(pair).join(", ")}. HVT shows the pair's realized volatility.`;
194
+ }
195
+
196
+ /**
197
+ * A volatility function's `refuseSymbol` for FX (OVDV, VCA, HIVG): an FX pair
198
+ * or currency future is answered before any request, with one line saying
199
+ * OTC FX volatility is not covered and what to read instead.
200
+ */
201
+ export function fxVolatilityRefusal(code: string) {
202
+ return (key: string): { message: string; details?: string } | null => {
203
+ const message = fxVolatilityAnswer(code, key);
204
+ return message ? { message } : null;
205
+ };
206
+ }
@@ -2,6 +2,7 @@ import type { Quote, QuoteMetadata } from "../../types/financials";
2
2
  import { canonicalExchange, parsePublicTickerKey } from "../../utils/exchanges";
3
3
  import { getListingSymbolsToTry } from "../../sources/listing-symbols";
4
4
  import { currencyPairCode } from "../../utils/currency-pair";
5
+ import { exchangeLineCoinListing } from "../../utils/crypto-pair";
5
6
 
6
7
  export function quoteMetadataFromQuote(quote: Quote): QuoteMetadata {
7
8
  return {
@@ -44,6 +45,12 @@ export function mergeQuoteMetadata(known: QuoteMetadata | null | undefined, load
44
45
 
45
46
  /** Qualified listings must agree; provider suffix spellings use the existing exact-listing normalizer. */
46
47
  export function quoteMetadataMatchesTarget(metadata: QuoteMetadata, symbol: string, exchange?: string): boolean {
48
+ // A crypto exchange's line of a pair (BTC/GBP on COINBASE PRO) is priced as the crypto venue's listing, BTC-GBP on
49
+ // CCC, and a quote kept under the line's key is that listing's.
50
+ const line = exchangeLineCoinListing(symbol, exchange);
51
+ if (line) return quoteMetadataMatchesTarget(metadata, line.symbol, line.exchange);
52
+ const quotedLine = exchangeLineCoinListing(metadata.symbol, parsePublicTickerKey(metadata.symbol).exchange ? undefined : metadata.listingExchangeName);
53
+ if (quotedLine) metadata = { ...metadata, symbol: quotedLine.symbol, listingExchangeName: quotedLine.exchange };
47
54
  const target = parsePublicTickerKey(symbol);
48
55
  const actual = parsePublicTickerKey(metadata.symbol);
49
56
  // An index is one series whatever its venue is called: search files ^VIX under CXI and ^RUT under WCB, while