gloomberb 0.16.5 → 0.16.6

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 (478) hide show
  1. package/package.json +2 -1
  2. package/src/api-client/central-bank-rates.ts +6 -1
  3. package/src/api-client/chat.ts +26 -0
  4. package/src/api-client/credit-boards.ts +2 -0
  5. package/src/api-client/data.ts +49 -1
  6. package/src/api-client/europe-energy.ts +106 -0
  7. package/src/api-client/index.ts +9 -0
  8. package/src/api-client/market-discovery.ts +3 -0
  9. package/src/api-client/normalizers.ts +32 -0
  10. package/src/api-client/perps.ts +8 -0
  11. package/src/api-client/request.ts +9 -2
  12. package/src/api-client/socket.ts +29 -0
  13. package/src/api-client/themes.ts +22 -1
  14. package/src/api-client/types.ts +61 -1
  15. package/src/app/global-shortcuts.ts +35 -1
  16. package/src/app/keybindings/actions.ts +12 -1
  17. package/src/app/pane-runtime/plugin-bindings.ts +2 -2
  18. package/src/app/pane-runtime/ticker-open-runtime.ts +9 -3
  19. package/src/app/runtime/desktop-deeplink.ts +10 -5
  20. package/src/app/runtime/startup.ts +1 -1
  21. package/src/app.tsx +6 -0
  22. package/src/architecture/core-builtin-allowlist.json +4 -13
  23. package/src/ask-gloom/pane-context.ts +114 -0
  24. package/src/ask-gloom/pane-request.ts +70 -0
  25. package/src/ask-gloom/pane-text.ts +201 -0
  26. package/src/capabilities/chart-series.ts +8 -1
  27. package/src/capabilities/factories.ts +1 -0
  28. package/src/cli/central-bank-events.ts +23 -0
  29. package/src/cli/command-options.ts +17 -5
  30. package/src/cli/commands/crypto-hints.ts +28 -2
  31. package/src/cli/commands/fred-series.ts +89 -4
  32. package/src/cli/commands/market.ts +101 -18
  33. package/src/cli/commands/overview.ts +47 -14
  34. package/src/cli/commands/search.ts +254 -23
  35. package/src/cli/commands/ticker.ts +73 -13
  36. package/src/cli/desktop-pane-shot.ts +23 -6
  37. package/src/cli/errors.ts +7 -5
  38. package/src/cli/exit-codes.ts +8 -0
  39. package/src/cli/export-source.ts +58 -0
  40. package/src/cli/glossary.ts +62 -8
  41. package/src/cli/help.ts +10 -0
  42. package/src/cli/helpers.ts +2 -5
  43. package/src/cli/history-rows.ts +70 -0
  44. package/src/cli/index.ts +9 -3
  45. package/src/cli/known-gaps.ts +65 -8
  46. package/src/cli/listing-arg.ts +182 -89
  47. package/src/cli/not-a-ticker.ts +12 -4
  48. package/src/cli/pane-functions/access-gate.ts +8 -2
  49. package/src/cli/pane-functions/capabilities.ts +1 -1
  50. package/src/cli/pane-functions/catalog.ts +183 -39
  51. package/src/cli/pane-functions/data.ts +30 -0
  52. package/src/cli/pane-functions/dom.ts +17 -2
  53. package/src/cli/pane-functions/freshness.ts +48 -24
  54. package/src/cli/pane-functions/function-help.ts +19 -10
  55. package/src/cli/pane-functions/headless.ts +55 -15
  56. package/src/cli/pane-functions/index.ts +45 -16
  57. package/src/cli/pane-functions/options.ts +8 -0
  58. package/src/cli/pane-functions/resolver.ts +25 -7
  59. package/src/cli/pane-functions/screenshot.ts +145 -9
  60. package/src/cli/perpetual-contract.ts +60 -0
  61. package/src/cli/registry.ts +3 -1
  62. package/src/cli/report-tables.ts +23 -7
  63. package/src/cli/result.ts +41 -10
  64. package/src/cli/series-search.ts +111 -0
  65. package/src/cli/staged-tickers.ts +1 -1
  66. package/src/cli/symbol-args.ts +18 -0
  67. package/src/cli/symbol-list.ts +1 -19
  68. package/src/cli/symbol-rows.ts +154 -0
  69. package/src/cli/ticker-resolution.ts +99 -8
  70. package/src/components/chart/composite/axis-overlays.tsx +8 -1
  71. package/src/components/chart/composite/composite-chart.tsx +41 -15
  72. package/src/components/chart/composite/crosshair-readout.tsx +150 -0
  73. package/src/components/chart/composite/downsample.ts +1 -1
  74. package/src/components/chart/composite/format.ts +29 -0
  75. package/src/components/chart/composite/interactions.ts +9 -2
  76. package/src/components/chart/composite/legend.tsx +52 -32
  77. package/src/components/chart/composite/panel-bitmap.ts +70 -35
  78. package/src/components/chart/composite/panel-labels.ts +18 -17
  79. package/src/components/chart/composite/panel-series.ts +1 -0
  80. package/src/components/chart/composite/panel-surface.tsx +6 -8
  81. package/src/components/chart/composite/rasterizer.ts +50 -8
  82. package/src/components/chart/composite/scene.ts +2 -1
  83. package/src/components/chart/composite/time-scale.ts +26 -9
  84. package/src/components/chart/composite/types.ts +18 -0
  85. package/src/components/chart/composite/use-composite-viewport.ts +30 -2
  86. package/src/components/chart/native/raster/crosshair.ts +8 -3
  87. package/src/components/chart/native/raster/primitives.ts +85 -21
  88. package/src/components/chart/native/surface/manager.ts +33 -8
  89. package/src/components/chart/surface3d/chart.tsx +1 -1
  90. package/src/components/command-bar/action-runtime.ts +1 -1
  91. package/src/components/command-bar/commands/collection-list.ts +210 -0
  92. package/src/components/command-bar/commands/collection.ts +40 -1
  93. package/src/components/command-bar/commands/direct/index.ts +4 -0
  94. package/src/components/command-bar/commands/registry.ts +8 -0
  95. package/src/components/command-bar/routes/root/plugin-install.ts +4 -4
  96. package/src/components/command-bar/routing/actions.ts +1 -1
  97. package/src/components/command-bar/workflow/tickers.ts +35 -29
  98. package/src/components/layout/detached-pane-shell.tsx +59 -39
  99. package/src/components/layout/floating-pane.tsx +7 -1
  100. package/src/components/layout/header.tsx +5 -0
  101. package/src/components/layout/missing-pane.tsx +3 -3
  102. package/src/components/layout/pane/capture-stamp.ts +41 -0
  103. package/src/components/layout/pane/capture-status.ts +12 -0
  104. package/src/components/layout/pane/footer/keyboard.ts +3 -0
  105. package/src/components/layout/pane/footer/model.ts +7 -1
  106. package/src/components/layout/pane/header.tsx +26 -0
  107. package/src/components/layout/pane/index.tsx +9 -1
  108. package/src/components/layout/pane/sidebar.tsx +10 -11
  109. package/src/components/layout/shell/cursor-occlusion.ts +2 -1
  110. package/src/components/layout/shell/index.tsx +61 -60
  111. package/src/components/layout/shell/menu.ts +9 -1
  112. package/src/components/layout/shell/pane/actions.ts +32 -10
  113. package/src/components/layout/shell/pane/ask.ts +131 -0
  114. package/src/components/layout/shell/pane/capture.ts +162 -0
  115. package/src/components/layout/shell/pane/layers.tsx +26 -5
  116. package/src/components/layout/shell/pane/management-shortcuts.ts +46 -1
  117. package/src/components/layout/shell/shortcuts.ts +21 -4
  118. package/src/components/metric-treemap/heat-surface.tsx +1 -3
  119. package/src/components/reader/article-reader.tsx +642 -0
  120. package/src/components/reader/article-text.ts +527 -0
  121. package/src/components/ticker/list-table-view.tsx +19 -1
  122. package/src/components/ui/data-table/index.tsx +1 -0
  123. package/src/components/ui/icon.tsx +4 -1
  124. package/src/components/ui/query-bar.tsx +10 -1
  125. package/src/data/config/store/migrations.ts +7 -0
  126. package/src/data/config/store/portfolio-as-of-column.ts +57 -0
  127. package/src/docs/snapshot.json +162 -23
  128. package/src/i18n/es.ts +4 -1
  129. package/src/i18n/ja.ts +4 -1
  130. package/src/i18n/ko.ts +4 -1
  131. package/src/i18n/zh-cn.ts +4 -1
  132. package/src/i18n/zh-tw.ts +4 -1
  133. package/src/layout-marketplace/gallery.tsx +1 -1
  134. package/src/market-data/market/freshness.ts +3 -2
  135. package/src/market-data/market/session-close-note.ts +3 -2
  136. package/src/market-data/quotes/contributions.ts +2 -0
  137. package/src/market-data/quotes/metadata.ts +12 -0
  138. package/src/market-data/quotes/resolution.ts +3 -0
  139. package/src/notifications/app-notifier.ts +17 -1
  140. package/src/notifications/notification-log.ts +180 -0
  141. package/src/plugins/auto-update-node.ts +2 -2
  142. package/src/plugins/auto-update.ts +2 -2
  143. package/src/plugins/builtin/account-management/index.tsx +1 -0
  144. package/src/plugins/builtin/alerts/catalysts.ts +1 -1
  145. package/src/plugins/builtin/alerts/focus.ts +27 -0
  146. package/src/plugins/builtin/alerts/index.tsx +6 -2
  147. package/src/plugins/builtin/alerts/pane.tsx +19 -1
  148. package/src/plugins/builtin/analytics/index.tsx +4 -1
  149. package/src/plugins/builtin/analytics/risk-headless.ts +30 -15
  150. package/src/plugins/builtin/attention/headless.ts +1 -1
  151. package/src/plugins/builtin/awards/headless.ts +88 -18
  152. package/src/plugins/builtin/awards/model.ts +41 -1
  153. package/src/plugins/builtin/awards/pane.tsx +19 -12
  154. package/src/plugins/builtin/backtest/headless.ts +2 -2
  155. package/src/plugins/builtin/broker-manager/index.tsx +1 -0
  156. package/src/plugins/builtin/catalysts/headless.ts +1 -1
  157. package/src/plugins/builtin/cds/client.ts +11 -31
  158. package/src/plugins/builtin/cds/headless.ts +5 -2
  159. package/src/plugins/builtin/cds/model.ts +21 -1
  160. package/src/plugins/builtin/cds/pane.tsx +6 -4
  161. package/src/plugins/builtin/central-bank-rates/client.ts +4 -0
  162. package/src/plugins/builtin/central-bank-rates/headless.ts +46 -16
  163. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -1
  164. package/src/plugins/builtin/central-bank-rates/model.ts +99 -0
  165. package/src/plugins/builtin/central-bank-rates/pane.tsx +1 -1
  166. package/src/plugins/builtin/central-bank-rates/series.ts +19 -25
  167. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  168. package/src/plugins/builtin/chart-composer/headless-schema.ts +1 -1
  169. package/src/plugins/builtin/chart-composer/headless.ts +65 -5
  170. package/src/plugins/builtin/chart-composer/index.tsx +6 -1
  171. package/src/plugins/builtin/chart-composer/pane.tsx +81 -38
  172. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  173. package/src/plugins/builtin/chat/content/composer.tsx +5 -1
  174. package/src/plugins/builtin/chat/content/footer.tsx +12 -0
  175. package/src/plugins/builtin/chat/content/index.tsx +89 -5
  176. package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
  177. package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
  178. package/src/plugins/builtin/chat/content/scroll.ts +127 -27
  179. package/src/plugins/builtin/chat/content/shortcuts.ts +2 -2
  180. package/src/plugins/builtin/chat/content/transcript.tsx +145 -78
  181. package/src/plugins/builtin/chat/content/types.ts +1 -0
  182. package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
  183. package/src/plugins/builtin/chat/controller/index.ts +121 -1
  184. package/src/plugins/builtin/chat/controller/message-loading.ts +3 -0
  185. package/src/plugins/builtin/chat/controller/messages.ts +7 -2
  186. package/src/plugins/builtin/chat/controller/realtime.ts +10 -1
  187. package/src/plugins/builtin/chat/controller/send.ts +61 -1
  188. package/src/plugins/builtin/chat/controller/state.ts +8 -0
  189. package/src/plugins/builtin/chat/ghost-user.ts +20 -0
  190. package/src/plugins/builtin/chat/index.tsx +23 -5
  191. package/src/plugins/builtin/chat/layout.ts +78 -5
  192. package/src/plugins/builtin/chat/message/ai-badge.tsx +18 -0
  193. package/src/plugins/builtin/chat/message/desktop.tsx +55 -6
  194. package/src/plugins/builtin/chat/message/header.tsx +16 -4
  195. package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
  196. package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
  197. package/src/plugins/builtin/chat/message/render-state.ts +18 -2
  198. package/src/plugins/builtin/chat/message/terminal.tsx +43 -4
  199. package/src/plugins/builtin/chat/message/types.ts +12 -0
  200. package/src/plugins/builtin/chat/reactions.ts +197 -0
  201. package/src/plugins/builtin/chat/share/flow.tsx +87 -0
  202. package/src/plugins/builtin/chat/share/targets.ts +27 -0
  203. package/src/plugins/builtin/chat/status-widget.tsx +6 -3
  204. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +148 -27
  205. package/src/plugins/builtin/chat/unread-inbox.ts +22 -0
  206. package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
  207. package/src/plugins/builtin/cloud/askg/pane-attachment.ts +94 -0
  208. package/src/plugins/builtin/cloud/askg/pane.tsx +141 -12
  209. package/src/plugins/builtin/cloud/askg/pending-question.ts +11 -0
  210. package/src/plugins/builtin/cloud/plugin.tsx +19 -2
  211. package/src/plugins/builtin/cloud/team/model.ts +5 -0
  212. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  213. package/src/plugins/builtin/cloud/team/store.ts +8 -3
  214. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  215. package/src/plugins/builtin/company-attention/headless.ts +2 -2
  216. package/src/plugins/builtin/company-kpis/headless.ts +97 -10
  217. package/src/plugins/builtin/company-kpis/model.ts +24 -2
  218. package/src/plugins/builtin/company-kpis/pane.tsx +13 -14
  219. package/src/plugins/builtin/connections/index.ts +1 -0
  220. package/src/plugins/builtin/cot/headless.ts +1 -1
  221. package/src/plugins/builtin/cpi/headless.ts +3 -3
  222. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +10 -1
  223. package/src/plugins/builtin/credit-boards/client.ts +6 -2
  224. package/src/plugins/builtin/credit-boards/headless.ts +108 -31
  225. package/src/plugins/builtin/credit-boards/model.ts +11 -1
  226. package/src/plugins/builtin/credit-conditions/client.ts +12 -9
  227. package/src/plugins/builtin/credit-conditions/headless.ts +13 -2
  228. package/src/plugins/builtin/credit-conditions/index.tsx +16 -9
  229. package/src/plugins/builtin/credit-conditions/model.ts +19 -11
  230. package/src/plugins/builtin/credit-conditions/series.ts +8 -18
  231. package/src/plugins/builtin/credit-documents/headless.ts +2 -2
  232. package/src/plugins/builtin/debt-maturities/headless.ts +17 -11
  233. package/src/plugins/builtin/dividend-yield/client.ts +3 -0
  234. package/src/plugins/builtin/dividend-yield/headless.ts +5 -2
  235. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  236. package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
  237. package/src/plugins/builtin/dividend-yield/types.ts +2 -0
  238. package/src/plugins/builtin/doe/headless.ts +12 -8
  239. package/src/plugins/builtin/earnings/index.tsx +9 -0
  240. package/src/plugins/builtin/earnings-calls/headless.ts +28 -2
  241. package/src/plugins/builtin/earnings-calls/index.tsx +1 -1
  242. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -0
  243. package/src/plugins/builtin/econ/fred-catalog.ts +9 -0
  244. package/src/plugins/builtin/equity-screener/headless.ts +3 -3
  245. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -3
  246. package/src/plugins/builtin/exposure/drivers.ts +13 -0
  247. package/src/plugins/builtin/exposure/headless.ts +205 -13
  248. package/src/plugins/builtin/exposure/index.tsx +1 -0
  249. package/src/plugins/builtin/exposure/model.ts +23 -3
  250. package/src/plugins/builtin/exposure/pane.tsx +11 -11
  251. package/src/plugins/builtin/fear-greed/index.tsx +14 -10
  252. package/src/plugins/builtin/futures-curve/headless.ts +1 -1
  253. package/src/plugins/builtin/futures-curve/index.tsx +1 -1
  254. package/src/plugins/builtin/fx-matrix/client.ts +63 -0
  255. package/src/plugins/builtin/fx-matrix/export.ts +2 -1
  256. package/src/plugins/builtin/fx-matrix/headless.ts +154 -0
  257. package/src/plugins/builtin/fx-matrix/index.tsx +3 -3
  258. package/src/plugins/builtin/gpu/headless.ts +4 -4
  259. package/src/plugins/builtin/help/function-index.ts +2 -0
  260. package/src/plugins/builtin/help/index.tsx +14 -1
  261. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -1
  262. package/src/plugins/builtin/iv-history/client.ts +6 -2
  263. package/src/plugins/builtin/iv-history/format.ts +9 -0
  264. package/src/plugins/builtin/iv-history/headless.ts +50 -18
  265. package/src/plugins/builtin/iv-history/model.ts +35 -4
  266. package/src/plugins/builtin/jobs/headless.ts +6 -8
  267. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  268. package/src/plugins/builtin/market-heatmap/headless.ts +20 -5
  269. package/src/plugins/builtin/market-heatmap/index.tsx +6 -0
  270. package/src/plugins/builtin/members/headless.ts +1 -1
  271. package/src/plugins/builtin/members/pane.tsx +3 -2
  272. package/src/plugins/builtin/mna/distress/headless.ts +4 -4
  273. package/src/plugins/builtin/mna/headless.ts +3 -3
  274. package/src/plugins/builtin/money-markets/headless.ts +5 -4
  275. package/src/plugins/builtin/news/index.tsx +0 -2
  276. package/src/plugins/builtin/news/wire/news/detail-view.tsx +53 -104
  277. package/src/plugins/builtin/news/wire/news/footer.ts +2 -25
  278. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +0 -2
  279. package/src/plugins/builtin/notes/index.tsx +1 -0
  280. package/src/plugins/builtin/options/index.tsx +3 -0
  281. package/src/plugins/builtin/options-positioning/headless.ts +3 -3
  282. package/src/plugins/builtin/perps/client.ts +50 -10
  283. package/src/plugins/builtin/perps/equity-row.tsx +29 -16
  284. package/src/plugins/builtin/perps/headless.ts +2 -2
  285. package/src/plugins/builtin/perps/index.tsx +5 -3
  286. package/src/plugins/builtin/perps/list-perpetuals.ts +118 -0
  287. package/src/plugins/builtin/perps/open-market.ts +15 -0
  288. package/src/plugins/builtin/plugin-marketplace/index.tsx +1 -1
  289. package/src/plugins/builtin/plugin-marketplace/pane.tsx +6 -6
  290. package/src/plugins/builtin/portfolio-list/allocation.ts +5 -4
  291. package/src/plugins/builtin/portfolio-list/cell-version.ts +8 -1
  292. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +23 -135
  293. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +59 -15
  294. package/src/plugins/builtin/portfolio-list/cli/render.ts +248 -51
  295. package/src/plugins/builtin/portfolio-list/cli/shared.ts +5 -12
  296. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +3 -2
  297. package/src/plugins/builtin/portfolio-list/column-values.ts +103 -25
  298. package/src/plugins/builtin/portfolio-list/headless.ts +51 -15
  299. package/src/plugins/builtin/portfolio-list/index.tsx +5 -1
  300. package/src/plugins/builtin/portfolio-list/mutations.ts +2 -9
  301. package/src/plugins/builtin/portfolio-list/pane/data.ts +5 -1
  302. package/src/plugins/builtin/portfolio-list/pane/index.tsx +46 -8
  303. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +16 -10
  304. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +21 -1
  305. package/src/plugins/builtin/portfolio-list/settings.ts +17 -0
  306. package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
  307. package/src/plugins/builtin/portfolio-list/summary/totals.ts +8 -3
  308. package/src/plugins/builtin/portfolio-list/table.tsx +30 -6
  309. package/src/plugins/builtin/power/evidence.ts +57 -8
  310. package/src/plugins/builtin/power/headless.ts +6 -2
  311. package/src/plugins/builtin/power/index.tsx +24 -2
  312. package/src/plugins/builtin/power/model.ts +2 -2
  313. package/src/plugins/builtin/power/pane.tsx +5 -0
  314. package/src/plugins/builtin/power/prices-client.ts +139 -0
  315. package/src/plugins/builtin/power/prices-headless.ts +137 -0
  316. package/src/plugins/builtin/power/prices-model.ts +103 -0
  317. package/src/plugins/builtin/power/prices.tsx +179 -0
  318. package/src/plugins/builtin/rate-path/headless.ts +38 -18
  319. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  320. package/src/plugins/builtin/research/index.tsx +2 -0
  321. package/src/plugins/builtin/research-search/article.ts +65 -0
  322. package/src/plugins/builtin/research-search/document-view.tsx +33 -215
  323. package/src/plugins/builtin/research-search/model.ts +0 -9
  324. package/src/plugins/builtin/research-search/pane.tsx +41 -30
  325. package/src/plugins/builtin/research-search/snippet.ts +0 -29
  326. package/src/plugins/builtin/revenue-breakdown/headless.ts +18 -6
  327. package/src/plugins/builtin/sec/headless.ts +25 -3
  328. package/src/plugins/builtin/sec/index.tsx +2 -1
  329. package/src/plugins/builtin/series-inventory.ts +2 -0
  330. package/src/plugins/builtin/shared/cloud-denied.ts +15 -0
  331. package/src/plugins/builtin/shared/free-preview.ts +47 -0
  332. package/src/plugins/builtin/shared/report-freshness.ts +3 -1
  333. package/src/plugins/builtin/shared/volatility/options.ts +4 -1
  334. package/src/plugins/builtin/short-interest/watch-headless.ts +2 -2
  335. package/src/plugins/builtin/short-volume/headless.ts +1 -1
  336. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  337. package/src/plugins/builtin/supply-chain/headless.ts +43 -11
  338. package/src/plugins/builtin/supply-chain/model.ts +13 -0
  339. package/src/plugins/builtin/supply-chain/pane.tsx +5 -5
  340. package/src/plugins/builtin/themes/headless.ts +74 -10
  341. package/src/plugins/builtin/themes/model.ts +71 -4
  342. package/src/plugins/builtin/themes/pane.tsx +22 -5
  343. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +114 -0
  344. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +57 -2
  345. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +19 -55
  346. package/src/plugins/builtin/ticker-detail/headless-schema.ts +30 -0
  347. package/src/plugins/builtin/ticker-detail/headless.ts +86 -0
  348. package/src/plugins/builtin/ticker-detail/index.tsx +23 -11
  349. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  350. package/src/plugins/builtin/time-sales/headless.ts +1 -1
  351. package/src/plugins/builtin/time-sales/index.tsx +2 -1
  352. package/src/plugins/builtin/vol-surface/evidence.ts +7 -1
  353. package/src/plugins/builtin/vol-surface/headless.ts +50 -17
  354. package/src/plugins/builtin/vol-surface/heat-grid.tsx +113 -0
  355. package/src/plugins/builtin/vol-surface/model.ts +39 -6
  356. package/src/plugins/builtin/vol-surface/pane.tsx +14 -13
  357. package/src/plugins/builtin/vol-surface/stored.ts +63 -3
  358. package/src/plugins/builtin/vol-surface/surface.tsx +30 -2
  359. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +6 -5
  360. package/src/plugins/builtin/world-venue-map/basemap.ts +6 -3
  361. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +2 -1
  362. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +6 -6
  363. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +16 -9
  364. package/src/plugins/builtin/world-venue-map/map.tsx +144 -13
  365. package/src/plugins/builtin/world-venue-map/model.ts +56 -19
  366. package/src/plugins/builtin/world-venue-map/pane.tsx +5 -2
  367. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  368. package/src/plugins/installer.ts +2 -2
  369. package/src/plugins/loader.ts +15 -0
  370. package/src/plugins/manager-node.ts +1 -1
  371. package/src/plugins/{builtin/plugin-marketplace → marketplace}/feed.ts +2 -2
  372. package/src/plugins/{builtin/plugin-marketplace → marketplace}/model.ts +6 -6
  373. package/src/plugins/{builtin/plugin-marketplace → marketplace}/store.ts +1 -1
  374. package/src/plugins/registry/host-actions.ts +11 -0
  375. package/src/portfolio/collection-name.ts +31 -0
  376. package/src/portfolio/valuation-basis.ts +79 -0
  377. package/src/public/time-series.ts +1 -1
  378. package/src/react/input.ts +33 -1
  379. package/src/react/key-repeat.ts +86 -0
  380. package/src/remote/controller.ts +21 -1
  381. package/src/remote/schema.ts +3 -2
  382. package/src/renderers/browser/main.tsx +1 -1
  383. package/src/renderers/browser/research-entry.ts +4 -2
  384. package/src/renderers/dom/data-table/row.tsx +3 -0
  385. package/src/renderers/dom/desktop/icons.tsx +9 -0
  386. package/src/renderers/dom/host/block-glyph-canvas.tsx +6 -3
  387. package/src/renderers/dom/host/box.tsx +7 -2
  388. package/src/renderers/dom/host/chart-surface.tsx +27 -0
  389. package/src/renderers/dom/host/scroll-box.tsx +32 -0
  390. package/src/renderers/dom/host/size-observer.ts +24 -9
  391. package/src/renderers/dom/host/sticky-scroll.ts +128 -0
  392. package/src/renderers/dom/host/style.ts +1 -1
  393. package/src/renderers/dom/host/surface-3d.tsx +13 -6
  394. package/src/renderers/dom/host/surface-fit.ts +406 -0
  395. package/src/renderers/dom/input-host.tsx +1 -0
  396. package/src/renderers/dom/styles.css +48 -12
  397. package/src/renderers/electrobun/bun/application-menu/click.ts +18 -0
  398. package/src/renderers/electrobun/bun/index.ts +3 -2
  399. package/src/renderers/electrobun/shared/protocol.ts +1 -1
  400. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +12 -0
  401. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +59 -24
  402. package/src/renderers/electrobun/view/main.tsx +3 -3
  403. package/src/renderers/electrobun/view/remote/asset-data-client.ts +2 -0
  404. package/src/renderers/opentui/chart-surface.tsx +1 -0
  405. package/src/renderers/opentui/frame-capture.ts +33 -0
  406. package/src/renderers/opentui/host.tsx +10 -13
  407. package/src/renderers/opentui/pane-image/glyphs-license.txt +107 -0
  408. package/src/renderers/opentui/pane-image/glyphs.json +1 -0
  409. package/src/renderers/opentui/pane-image/index.ts +107 -0
  410. package/src/renderers/opentui/pane-image/raster.ts +428 -0
  411. package/src/renderers/opentui/start.tsx +3 -3
  412. package/src/renderers/opentui/test-utils.tsx +3 -0
  413. package/src/renderers/opentui/ui-host.tsx +29 -1
  414. package/src/series/resolve.ts +3 -2
  415. package/src/series/types.ts +8 -0
  416. package/src/series/validate.ts +1 -1
  417. package/src/shares/chat.ts +34 -0
  418. package/src/shares/dialog.tsx +295 -0
  419. package/src/shares/flow.tsx +56 -0
  420. package/src/shares/live.ts +14 -22
  421. package/src/shares/pane-image.ts +152 -0
  422. package/src/shares/personal-pane.ts +64 -0
  423. package/src/shares/portable-layout.ts +4 -2
  424. package/src/shares/prepare.ts +20 -0
  425. package/src/sources/gloomberb-cloud/index.ts +102 -3
  426. package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
  427. package/src/sources/gloomberb-cloud/perpetuals.ts +249 -0
  428. package/src/sources/history-retention.ts +11 -0
  429. package/src/sources/listing-target.ts +4 -0
  430. package/src/sources/provider-router/index.ts +14 -0
  431. package/src/state/hooks/inline-tickers.ts +49 -11
  432. package/src/test-support/shell-pane-keys.tsx +1 -0
  433. package/src/theme/theme-context.tsx +15 -1
  434. package/src/tickers/instrument-kind.ts +9 -1
  435. package/src/tickers/listing-name.ts +34 -0
  436. package/src/tickers/listing-venues.ts +132 -0
  437. package/src/tickers/open-target.ts +11 -3
  438. package/src/tickers/perpetual-listing.ts +151 -0
  439. package/src/tickers/prediction-listing.ts +26 -0
  440. package/src/tickers/search/index.ts +27 -4
  441. package/src/tickers/search/ranking.ts +66 -4
  442. package/src/tickers/ticker-changes.ts +213 -0
  443. package/src/time-series/economic.ts +2 -6
  444. package/src/time-series/ice-credit.ts +39 -0
  445. package/src/time-series/resolve.ts +116 -13
  446. package/src/time-series/studies.ts +1 -0
  447. package/src/time-series/types.ts +8 -0
  448. package/src/types/config.ts +4 -2
  449. package/src/types/data-provider.ts +6 -0
  450. package/src/types/financials.ts +10 -0
  451. package/src/types/headless.ts +52 -0
  452. package/src/types/instrument.ts +4 -0
  453. package/src/types/plugin.ts +33 -2
  454. package/src/types/ticker-changes.ts +26 -0
  455. package/src/ui/context-menu.tsx +13 -1
  456. package/src/ui/host.tsx +43 -7
  457. package/src/ui/live-resize.ts +10 -0
  458. package/src/ui/live-stretch.ts +51 -19
  459. package/src/ui/sticky-scroll.ts +7 -0
  460. package/src/utils/central-banks.ts +76 -0
  461. package/src/utils/cli-output.ts +126 -16
  462. package/src/utils/currency-units.ts +11 -0
  463. package/src/utils/datetime-format.ts +27 -6
  464. package/src/utils/dom-screenshot.ts +3 -1
  465. package/src/utils/double-escape-close.ts +15 -1
  466. package/src/utils/exchanges.ts +36 -0
  467. package/src/utils/format.ts +19 -2
  468. package/src/utils/forward-dividend.ts +14 -0
  469. package/src/utils/fx-status.ts +6 -1
  470. package/src/utils/inline-content-tokenizer.ts +22 -3
  471. package/src/utils/market-capitalization.ts +8 -0
  472. package/src/utils/one-shot.ts +73 -0
  473. package/src/utils/options.ts +37 -8
  474. package/src/utils/utc-time.ts +12 -0
  475. package/src/ui/pane-fill.ts +0 -11
  476. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/activation.ts +0 -0
  477. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/ids.ts +0 -0
  478. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/packs.ts +0 -0
@@ -69,6 +69,7 @@ import {
69
69
  type CliReportTables,
70
70
  } from "../report-tables";
71
71
  import {
72
+ historyBarStatusNote,
72
73
  historyFacts,
73
74
  historyFlagNote,
74
75
  historyGapNote,
@@ -78,6 +79,8 @@ import {
78
79
  historyPriceDecimals,
79
80
  historyRows,
80
81
  historyUnit,
82
+ historyVolume,
83
+ historyVolumeText,
81
84
  } from "../history-rows";
82
85
  import { CRYPTO_BOARD_HINT, quoteNotes } from "./crypto-hints";
83
86
  import { bareFuturesCode } from "../../market-data/futures-board";
@@ -97,6 +100,7 @@ import {
97
100
  } from "../../plugins/builtin/shared/report-freshness";
98
101
  import {
99
102
  bareListingNote,
103
+ tradingBoardNote,
100
104
  EXCHANGE_OPTION,
101
105
  futuresTryLine,
102
106
  isNoProviderError,
@@ -110,10 +114,13 @@ import {
110
114
  type ListingVenue,
111
115
  loadForListing,
112
116
  loadListingQuote,
117
+ listingFateLoader,
118
+ listingFateNotes,
113
119
  noLongerTradingNote,
114
120
  notTradedMessage,
115
121
  quoteBareListing,
116
122
  requireCliListing,
123
+ tradingListing,
117
124
  type CliListing,
118
125
  type ListingDataRequest,
119
126
  type ListingIdentity,
@@ -124,9 +131,12 @@ import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
124
131
  import { providerMissReason } from "../../sources/provider-errors";
125
132
  import { isNotATickerMessage } from "../not-a-ticker";
126
133
  import { splitSymbolArgs } from "../symbol-args";
134
+ import { cliPerpetualContract, perpetualContractStats, type PerpetualContractPayload } from "../perpetual-contract";
135
+ import { subMinuteDataMessage } from "../known-gaps";
127
136
  import { secRegistrantMismatchMessage, SecRegistrantMismatchError, areDifferentCompanies } from "../../sources/sec-registrant";
128
137
  import { isCryptoPairSymbol } from "../../utils/crypto-pair";
129
138
  import { isUsListingExchange } from "../../utils/exchanges";
139
+ import { describeTickerChange, startTickerChangeLookup, tickerChangeLine, type TickerChange } from "../../tickers/ticker-changes";
130
140
  import { noSecReportsNotice, nonUsSecListingVenue } from "../../utils/sec";
131
141
  import type { MarketContext } from "../types";
132
142
  import { windowRows } from "../row-window";
@@ -151,6 +161,7 @@ import {
151
161
  unvaluedHoldersNote,
152
162
  } from "../../plugins/builtin/holders/report-rows";
153
163
  import { fetchBeneficialOwners } from "../../plugins/builtin/holders/beneficial-client";
164
+ import { centralBankEventsMessage } from "../central-bank-events";
154
165
  import { classSharesToday } from "../../plugins/builtin/holders/client";
155
166
  import { filingFormMatches, SEC_FILING_FETCH_LIMIT } from "../../plugins/builtin/sec/forms";
156
167
  import { loadParsedInsiderFilings } from "../../plugins/builtin/insider/client";
@@ -178,6 +189,10 @@ type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & {
178
189
  listing?: { name: string | null };
179
190
  /** A futures row's contract, terms and latest confirmed settlement, as `ticker` reads them. */
180
191
  futuresContract?: FuturesContractTermsPayload;
192
+ /** A perpetual row's market, funding per eight hours and open interest. */
193
+ perpetualContract?: PerpetualContractPayload;
194
+ /** The symbol changed: its issuer moved to another symbol, or its holder had another before. */
195
+ tickerChange?: TickerChange;
181
196
  };
182
197
  type FinancialsCliData = TickerFinancials & {
183
198
  symbol: string;
@@ -524,19 +539,36 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
524
539
  // Each costs a search, so a long list (a portfolio check, not a lookup) names none.
525
540
  const venues = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
526
541
  : listings.map((listing) => [listing, loadBareVenues(listing, market)] as const));
542
+ // So is whether a symbol changed (FB is now META), for the same lists.
543
+ const changes = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
544
+ : listings.map((listing) => [listing, startTickerChangeLookup(market.dataProvider, listing.symbol, listing.exchange)] as const));
527
545
  const results = await market.dataProvider.getQuotesBatch(targets, { forceRefresh: ctx.cliOptions.refresh });
528
546
  const listingOf = (result: QuoteBatchResult, index: number) => listings[targets.indexOf(result.target)] ?? listings[index]!;
547
+ const asked = results.map(listingOf);
529
548
  const request = { command: commandName, noun: "quote" };
530
549
  const answers = await Promise.all(results.map((result, index) => {
531
- const listing = listingOf(result, index);
550
+ const listing = asked[index]!;
532
551
  return answerQuote(result, listing, market, request, ctx.cliOptions.refresh, venues.get(listing) ?? null);
533
552
  }));
553
+ const now = Date.now();
554
+ const tickerChanges = await Promise.all(answers.map(async ({ result }, index) => {
555
+ const listing = asked[index]!;
556
+ // A bare symbol read on a listing abroad (SXR8 -> XETRA) is not the US symbol the changes are of.
557
+ if (result.quote && bareListingNote(listing, result.quote)) return null;
558
+ return describeTickerChange(listing.symbol, await changes.get(listing)?.settle() ?? null, tradingListing(result.quote, now));
559
+ }));
534
560
  const failed = answers[0]?.failure;
535
- if (listings.length === 1 && failed) ctx.fail(failed.message, failed.details);
561
+ if (listings.length === 1 && failed) {
562
+ ctx.fail(failed.message, [tickerChangeLine(tickerChanges[0]), failed.details].filter(Boolean).join("\n") || undefined);
563
+ }
536
564
  // A futures quote adds its dated settlement and contract terms, as `ticker` prints them; `compare` stays a table.
537
565
  const contracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
538
566
  ? cliFuturesContract(result.quote.symbol || result.target.symbol, result.quote.instrumentType)
539
567
  : null));
568
+ // A perpetual adds its funding per eight hours and open interest, the figures PERP lists beside its mark.
569
+ const perpetualContracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
570
+ ? cliPerpetualContract(result.quote)
571
+ : null));
540
572
  // Each row names its listing by key (SAN:EPA) and company, so the table needs no line above it.
541
573
  const data = answers.map(({ result, listing, failure }, index) => ({
542
574
  target: result.target,
@@ -545,11 +577,15 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
545
577
  ...getQuoteSessionFields(result.quote),
546
578
  quote: result.quote,
547
579
  ...contracts[index] ? { futuresContract: contracts[index] } : {},
580
+ ...perpetualContracts[index] ? { perpetualContract: perpetualContracts[index] } : {},
548
581
  error: failure?.oneLine ?? errorMessage(result.error),
549
582
  ...yieldIndexFields(result),
583
+ ...tickerChanges[index] ? { tickerChange: tickerChanges[index] } : {},
550
584
  }));
551
- const now = Date.now();
552
585
  const notes = [
586
+ // FB now lists another security; the company that was FB trades as META. A symbol a
587
+ // company renamed into (META) is what was typed: its change is in the row, not here.
588
+ ...tickerChanges.flatMap((change) => tickerChangeLine(change) ?? []),
553
589
  // Which listing a bare symbol was read as, and the index a bare root is not, are in no row.
554
590
  ...answers.flatMap((answer) => answer.notes),
555
591
  // A row that stopped trading long ago reads as a quote that is merely stale.
@@ -581,6 +617,10 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
581
617
  */
582
618
  function quoteFuturesContractText(data: QuoteCliRecord[]): string {
583
619
  return data.flatMap((row) => {
620
+ if (row.perpetualContract) {
621
+ const stats = perpetualContractStats(row.perpetualContract);
622
+ return [data.length > 1 ? `${renderSection(`${row.target.symbol} Contract`)}\n${renderStats(stats)}` : `${renderSection("Contract")}\n${renderStats(stats)}`];
623
+ }
584
624
  const terms = row.futuresContract;
585
625
  if (!terms || !row.quote) return [];
586
626
  const settle: [string, string] = ["Settle", futuresSettleText(terms, row.quote)];
@@ -636,7 +676,9 @@ async function answerQuote(
636
676
  // "CAR = Avis Budget Group, NASDAQ (also XETRA, ASX)" names the venue too, so it replaces "SXR8 -> XETRA".
637
677
  const choice = venues ? await listingChoiceNotes(listing, market, result.quote, venues) : listingReadNotes(listing);
638
678
  const venue = choice.length > 0 ? null : bareListingNote(listing, result.quote);
639
- return { result, listing, notes: [...choice, venue ?? [], also(result.quote)].flat() };
679
+ // A row saved as 247540:KRX is quoted from KOSDAQ, the board it trades on.
680
+ const board = tradingBoardNote(listing, result.quote);
681
+ return { result, listing, notes: [...choice, venue ?? [], board ?? [], also(result.quote)].flat() };
640
682
  }
641
683
  const notTraded = await notTradedMessage(listing, market);
642
684
  if (notTraded) return { result, listing, failure: { message: notTraded, oneLine: notTraded }, notes: [] };
@@ -675,6 +717,20 @@ const HISTORY_USAGE = "history <symbol> [--range <range>]";
675
717
  const HISTORY_ALL_RANGE = `ALL gives the full history the source has, up to ${historyAllRangeYears()} years`;
676
718
  /** Options people reach for to ask for dates, which history answers with a range. */
677
719
  const DATE_WINDOW_OPTIONS = new Set(["--from", "--to", "--start", "--end", "--since", "--until", "--date", "--start-date", "--end-date"]);
720
+ /** Options people reach for to pick a bar size, which history derives from the range. */
721
+ const BAR_SIZE_OPTIONS = new Set(["--interval", "--resolution", "--bar", "--bars", "--bar-size", "--granularity", "--timeframe", "--frequency", "--freq", "--tick", "--ticks"]);
722
+
723
+ /** The symbol `history` was given: the first word that is not an option or the value after one. */
724
+ function historySymbolArg(args: readonly string[]): string | undefined {
725
+ return args.find((arg, index) => !arg.startsWith("-")
726
+ && !(index > 0 && args[index - 1]!.startsWith("--") && !args[index - 1]!.includes("=")));
727
+ }
728
+
729
+ function historyOptionHint(flag: string, args: readonly string[]): string | null {
730
+ if (DATE_WINDOW_OPTIONS.has(flag)) return `history takes a --range instead of dates: ${TIME_RANGES.join(", ")}. ${HISTORY_ALL_RANGE}.`;
731
+ if (BAR_SIZE_OPTIONS.has(flag)) return `history serves each --range in one bar size. ${subMinuteDataMessage(historySymbolArg(args))}`;
732
+ return null;
733
+ }
678
734
 
679
735
  async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
680
736
  const args = [...rawArgs];
@@ -689,18 +745,25 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
689
745
  const load = () => market.dataProvider.getPriceHistoryWithMetadata
690
746
  ? market.dataProvider.getPriceHistoryWithMetadata(symbol, exchange, range, context)
691
747
  : market.dataProvider.getPriceHistory(symbol, exchange, range, context).then((points) => ({ points, resolution: null }));
748
+ const quoted = loadListingQuote(market.dataProvider, listing);
749
+ const fate = listingFateLoader(listing, market, quoted);
692
750
  const [{ points, resolution }, quote] = await Promise.all([
693
751
  loadForListing(listing, market, ctx, load, (loaded) => loaded.points.length === 0, {
694
- command: range === "1Y" ? "history" : `history --range ${range}`, noun: "history",
752
+ command: range === "1Y" ? "history" : `history --range ${range}`, noun: "history", fate,
695
753
  })
696
754
  .catch((error) => failHistory(error, listing.key, ctx)),
697
- loadListingQuote(market.dataProvider, listing),
755
+ quoted,
698
756
  ]);
699
757
  const identity = listingIdentity(listing, quote);
700
758
  // Without a quote currency, the listing metadata research reads (as `ticker` does) may still state it.
701
759
  const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(() => null);
702
760
  const unit = historyUnit(listing.key, listed ?? quote);
703
- const data = historyRows(points, resolution, unit.currency);
761
+ const traded = {
762
+ listing: listing.key, exchange: quote?.listingExchangeName || exchange || undefined, instrumentType: quote?.instrumentType,
763
+ };
764
+ // A coin's volume is traded value in its quote currency: said in every row and on the status line.
765
+ const volume = historyVolume(unit, traded);
766
+ const data = historyRows(points, resolution, unit.currency, unit.unit, volume);
704
767
  const interval = data[0]?.interval ?? null;
705
768
  const decimals = historyPriceDecimals(data, listing.saved?.metadata.assetCategory);
706
769
  const price = (value: unknown) => typeof value === "number" ? value.toFixed(decimals) : "";
@@ -713,13 +776,14 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
713
776
  oldest: null,
714
777
  // Bars stop arriving on the listing's own trading days, not New York's.
715
778
  cadenceExchange: quote?.listingExchangeName || exchange || undefined,
779
+ ...(volume ? { note: historyVolumeText(volume, interval) } : {}),
716
780
  });
717
- const gaps = historyGapNote(data, {
718
- listing: listing.key, exchange: quote?.listingExchangeName || exchange || undefined, instrumentType: quote?.instrumentType,
719
- });
781
+ const gaps = historyGapNote(data, traded);
720
782
  const notes = [
783
+ // FB's bars are the fund listed there now; META's from 2021 are from when it was FB.
784
+ ...listingFateNotes(await fate(), { served: data.length > 0, firstDate: data[0]?.date }),
721
785
  ...await listingChoiceNotes(listing, market, quote, venues),
722
- ...historyNotes(range, interval, data[0]?.date), gaps ?? [], historyFlagNote(data) ?? [],
786
+ ...historyNotes(range, interval, data[0]?.date), gaps ?? [], historyFlagNote(data) ?? [], historyBarStatusNote(data) ?? [],
723
787
  ].flat();
724
788
  const priceColumns: CliResultColumn[] = [
725
789
  intraday
@@ -735,6 +799,16 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
735
799
  const flagColumn: CliResultColumn = {
736
800
  key: "flag", header: "Flag", format: (value) => typeof value === "string" ? cliStyles.warning(value) : "",
737
801
  };
802
+ // Text also marks a bar still forming or rebuilt from hourly bars there; exports keep them as their own fields.
803
+ const flagTextColumn: CliResultColumn = {
804
+ ...flagColumn,
805
+ format: (value, row) => [
806
+ typeof value === "string" ? cliStyles.warning(value) : "",
807
+ row.partial ? cliStyles.muted("partial") : "",
808
+ row.filled ? cliStyles.muted("filled") : "",
809
+ ].filter(Boolean).join(" "),
810
+ };
811
+ const marked = data.some((row) => row.partial || row.filled);
738
812
  ctx.printResult({
739
813
  data,
740
814
  metadata: {
@@ -749,19 +823,23 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
749
823
  lastDate: data.at(-1)?.date ?? null,
750
824
  bars: data.length,
751
825
  asOf: freshness?.asOf ?? null,
826
+ ...(volume ? { volumeUnit: volume.unit, volumePeriod: interval === "1d" ? "UTC day" : interval } : {}),
752
827
  },
753
828
  ...(notes.length > 0 ? { warnings: notes } : {}),
754
829
  freshness,
755
830
  }, {
756
831
  heading: `${listingHeading(identity)}\n${cliStyles.muted(historyFacts(unit, data).join(" · "))}`,
757
832
  dateKey: "date",
758
- textColumns: [...priceColumns, flagColumn],
759
- // Every exported row says its currency and bar size, so it survives head and concatenation.
833
+ textColumns: [...priceColumns, flagTextColumn],
834
+ // Every exported row says its currency, what a price is in and its bar size, so it survives head and concatenation.
760
835
  columns: [
761
836
  ...priceColumns,
762
837
  { key: "currency", header: "Currency" },
838
+ { key: "unit", header: "Unit" },
763
839
  { key: "interval", header: "Interval" },
764
840
  flagColumn,
841
+ ...marked ? [{ key: "partial", header: "Partial" }, { key: "filled", header: "Filled" }] : [],
842
+ ...volume ? [{ key: "volumeUnit", header: "Volume unit" }, { key: "volumeNote", header: "Volume note" }] : [],
765
843
  ],
766
844
  });
767
845
  });
@@ -1295,6 +1373,9 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1295
1373
  async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
1296
1374
  const args = [...rawArgs];
1297
1375
  const exchangeOption = takeOption(args, "--exchange");
1376
+ // A central bank's name means the bank, not a listing that shares it; a venue still reaches the listing.
1377
+ const bank = exchangeOption ? null : centralBankEventsMessage(args.join(" "));
1378
+ if (bank) ctx.fail(bank);
1298
1379
  const raw = requireOneArg(args, "events <symbol>", "symbol", ctx);
1299
1380
  await withMarketData(ctx, async (market) => {
1300
1381
  const listing = await requireCliListing(raw, exchangeOption, market, ctx);
@@ -1364,6 +1445,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1364
1445
  const refresh = ctx.cliOptions.refresh;
1365
1446
  const venues = loadBareVenues(listing, market);
1366
1447
  const quotePromise = loadListingQuote(market.dataProvider, listing);
1448
+ const fate = listingFateLoader(listing, market, quotePromise);
1367
1449
  const inputsPromise = loadOptionModelInputs(market.dataProvider, listing, quotePromise, refresh);
1368
1450
  const { result, chain: loaded } = await loadForListing(listing, market, ctx, async () => {
1369
1451
  const cached = await market.dataProvider.getCachedQuery?.("getOptionsChain", [symbol, exchange, expirationDate, undefined])
@@ -1374,7 +1456,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1374
1456
  cacheMode: refresh ? "refresh" : "default",
1375
1457
  }),
1376
1458
  };
1377
- }, undefined, { command: "options", noun: "options chain" }).catch((error: unknown) => {
1459
+ }, undefined, { command: "options", noun: "options chain", fate }).catch((error: unknown) => {
1378
1460
  if (!isOptionsUnavailableError(error)) throw error;
1379
1461
  // A future or commodity with no chain of its own: name the listed fund whose options stand in.
1380
1462
  const alternative = listedOptionsAlternative(listing.key);
@@ -1403,7 +1485,10 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1403
1485
  const modelWarning = inputs.spot == null && (chain.calls.length > 0 || chain.puts.length > 0)
1404
1486
  ? `IV and delta need a current ${listing.key} quote; those columns are blank`
1405
1487
  : null;
1488
+ const listed = chain.expirationDates.length > 0 || chain.calls.length > 0 || chain.puts.length > 0;
1406
1489
  const warnings = [
1490
+ // FB's chain is the fund listed there now; a listing that stopped trading has none.
1491
+ ...listingFateNotes(await fate(), { served: listed }),
1407
1492
  ...await listingChoiceNotes(listing, market, quote, venues),
1408
1493
  ...[refreshWarning ?? sessionWarning, modelWarning].filter((warning): warning is string => warning != null),
1409
1494
  ];
@@ -1575,7 +1660,7 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
1575
1660
  group: CLI_COMMAND_GROUPS.research,
1576
1661
  usage: ["quote <symbol...>"],
1577
1662
  options: [EXCHANGE_OPTION],
1578
- examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote AAPL --json"],
1663
+ examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote binance:BZUSDT", "quote AAPL --json"],
1579
1664
  },
1580
1665
  execute: (args, ctx) => runQuote(args, ctx, "quote"),
1581
1666
  },
@@ -1609,9 +1694,7 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
1609
1694
  }],
1610
1695
  examples: ["history AAPL", "history BHP:ASX --range 5Y", "history ZAR=X --range ALL", "history ZAR=X --tail 5", "history AAPL --range 5Y --csv > aapl.csv"],
1611
1696
  },
1612
- unknownOptionHint: (flag) => DATE_WINDOW_OPTIONS.has(flag)
1613
- ? `history takes a --range instead of dates: ${TIME_RANGES.join(", ")}. ${HISTORY_ALL_RANGE}.`
1614
- : null,
1697
+ unknownOptionHint: historyOptionHint,
1615
1698
  execute: runHistory,
1616
1699
  },
1617
1700
  {
@@ -38,12 +38,17 @@ import { quotesFreshness, rowsFreshness } from "../freshness";
38
38
  import { periodicityOf } from "../pane-functions/freshness";
39
39
  import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
40
40
  import {
41
+ basisPointsCell,
41
42
  DEFAULT_FRED_START,
42
43
  fredEmptyMessage,
44
+ fredExportValue,
43
45
  fredHistoryNotes,
46
+ fredProvenance,
47
+ fredPublicationTime,
48
+ fredValueHeader,
49
+ fredValueKey,
44
50
  isSpreadSeries,
45
51
  laggingSeriesNote,
46
- spreadBasisPointsCell,
47
52
  } from "./fred-series";
48
53
 
49
54
  // Batch quotes often omit names for indices and ETFs; these baskets are fixed, so name them here.
@@ -351,8 +356,9 @@ async function runFredList(filter: string, ctx: Parameters<CliCommandDef["execut
351
356
  ctx.printResult({ data: rows, metadata: { filter: filter || null, total: catalog.series.length } }, {
352
357
  columns: [
353
358
  { key: "id", header: "ID", shrink: false },
354
- { key: "title", header: "Title" },
355
- { key: "group", header: "Group" },
359
+ // A title too narrow to tell series apart goes on a line under its ID.
360
+ { key: "title", header: "Title", minWidth: 30, stackPriority: 1 },
361
+ { key: "group", header: "Group", shrink: false },
356
362
  ],
357
363
  empty: filter ? `No supported FRED series match "${filter}". Run gloomberb fred --list to see them all.` : "No FRED series are listed.",
358
364
  });
@@ -375,7 +381,15 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
375
381
  if (isUnsupportedSeries(error)) ctx.fail(`Unsupported FRED series ${seriesId}. List supported series with: gloomberb fred --list`);
376
382
  throw error;
377
383
  });
378
- const rows = data.observations;
384
+ // Each observation exports under its unit (`value_bp`, `value_pct`), a spread in basis points.
385
+ const valueKey = fredValueKey(seriesId, data.info?.units);
386
+ // A value FRED does not list yet carries its publisher and when it published, in every export.
387
+ const exportRow = (observation: { date: string; value: number | null; publisher?: string; publishedAt?: string }) => ({
388
+ date: observation.date,
389
+ [valueKey]: fredExportValue(seriesId, observation.value),
390
+ ...(observation.publisher?.trim() ? { publisher: observation.publisher.trim(), ...(observation.publishedAt ? { publishedAt: observation.publishedAt } : {}) } : {}),
391
+ });
392
+ const rows = data.observations.map(exportRow);
379
393
  // The default start hides older history and a start before the series begins is moved up silently: say where it really starts.
380
394
  const notes = [
381
395
  ...fredHistoryNotes({ startDate, startGiven: start != null, seriesStart: data.info?.observationStart }),
@@ -385,17 +399,25 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
385
399
  const latest = rows.length > 0 ? null : await apiClient.getCloudFredSeries(seriesId, { limit: 1, sortOrder: "desc" })
386
400
  .then((payload) => payload.observations[0] ?? null).catch(() => null);
387
401
  const periodicity = periodicityOf(data.info?.frequency);
402
+ const newestDate = rows.reduce<string | null>((newest, row) => (!newest || row.date > newest ? row.date : newest), null);
403
+ // A newest value FRED does not list yet names its publisher, Treasury for a yield; FRED's own stay "FRED".
404
+ const ahead = fredProvenance(data.observations.filter((observation) => observation.date === newestDate));
405
+ // FRED names the series' publisher when Gloom Cloud passes it on, and when it last updated the series.
406
+ const publisher = ahead?.publisher ?? data.info?.source?.trim();
407
+ const publishedAt = ahead ? ahead.publishedAt : fredPublicationTime(data.info, newestDate);
388
408
  ctx.printResult({
389
409
  data: rows,
390
410
  metadata: {
391
411
  info: data.info, seriesId, startDate, sortOrder,
392
412
  ...(notes.length ? { notes } : {}),
393
- ...(latest ? { latestObservation: latest } : {}),
413
+ ...(latest ? { latestObservation: exportRow(latest) } : {}),
394
414
  },
395
415
  freshness: rowsFreshness(rows, {
396
- source: "FRED", status: "not-a-feed", basis: "published statistics", observedKey: "date", oldest: null,
416
+ source: ahead?.source ?? "FRED", status: "not-a-feed", basis: "published statistics", observedKey: "date", oldest: null,
397
417
  // A monthly or quarterly observation is dated the first of its period, not a day it was seen.
398
418
  ...(periodicity ? { periodicity } : {}),
419
+ ...(publisher ? { publisher } : {}),
420
+ ...(publishedAt ? { publishedAt } : {}),
399
421
  }),
400
422
  }, {
401
423
  dateKey: "date",
@@ -404,9 +426,8 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
404
426
  textColumns: [
405
427
  { key: "date", header: "Date" },
406
428
  isSpreadSeries(seriesId)
407
- // A spread is read in basis points; JSON and CSV keep FRED's percentage points.
408
- ? { key: "value", header: "Value (bp)", align: "right", format: spreadBasisPointsCell }
409
- : { key: "value", header: data.info?.units ? `Value (${data.info.units})` : "Value", align: "right" },
429
+ ? { key: valueKey, header: "Value (bp)", align: "right", format: basisPointsCell }
430
+ : { key: valueKey, header: fredValueHeader(data.info), align: "right" },
410
431
  ],
411
432
  });
412
433
  }
@@ -424,22 +445,34 @@ async function runYieldCurve(rawArgs: string[], ctx: Parameters<CliCommandDef["e
424
445
  return {
425
446
  seriesId,
426
447
  date: latest?.date ?? "",
427
- value: latest?.value ?? null,
448
+ // Yields in percent, named so in every export, as `fred` names a percent series.
449
+ value_pct: latest?.value ?? null,
428
450
  title: data.info?.title ?? "",
429
451
  // The move since the previous observation FRED has, not calendar yesterday: a Monday reads against Friday.
430
452
  previousDate: previous?.date ?? null,
431
- previousValue: previous?.value ?? null,
453
+ previousValue_pct: previous?.value ?? null,
432
454
  changeBasisPoints: latest?.value != null && previous?.value != null ? toBasisPoints(latest.value - previous.value, 1) : null,
455
+ latest,
456
+ publishedAt: latest?.publisher ? null : fredPublicationTime(data.info, latest?.date),
433
457
  };
434
458
  }));
459
+ // Tenors whose latest yield FRED does not list yet name the Treasury and when it published them.
460
+ const ahead = fredProvenance(results.flatMap((row) => row.latest ? [row.latest] : []));
461
+ // The curve shown was published with its last tenor: the newest of their FRED updates, when each has one.
462
+ const published = results.map((row) => row.publishedAt);
463
+ const publishedAt = ahead ? ahead.publishedAt : published.every((time) => time != null) ? published.sort().at(-1) : null;
435
464
  ctx.printResult({
436
- data: results,
465
+ data: results.map(({ publishedAt: _publishedAt, latest: _latest, ...row }) => row),
437
466
  metadata: { startDate },
438
- freshness: rowsFreshness(results, { source: "FRED", status: "not-a-feed", basis: "daily Treasury yields", observedKey: "date" }),
467
+ freshness: rowsFreshness(results, {
468
+ source: ahead?.source ?? "FRED", status: "not-a-feed", basis: "daily Treasury yields", observedKey: "date",
469
+ ...(ahead?.publisher ? { publisher: ahead.publisher } : {}),
470
+ ...(publishedAt ? { publishedAt } : {}),
471
+ }),
439
472
  }, {
440
473
  textColumns: [
441
474
  { key: "tenor", header: "Tenor", value: (row) => YIELD_TENORS[String(row.seriesId)] ?? row.seriesId },
442
- { key: "value", header: "Yield %", align: "right", format: (value) => typeof value === "number" ? value.toFixed(2) : "-" },
475
+ { key: "value_pct", header: "Yield %", align: "right", format: (value) => typeof value === "number" ? value.toFixed(2) : "-" },
443
476
  {
444
477
  key: "changeBasisPoints",
445
478
  header: "1D bp",