gloomberb 0.17.6 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (768) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/package.json +6 -1
  4. package/patches/electrobun@1.18.1.patch +74 -0
  5. package/src/account/auth-model.ts +3 -2
  6. package/src/account/device-signin-dialog.tsx +38 -13
  7. package/src/account/device-signin.ts +2 -1
  8. package/src/account/upgrade-dialog.tsx +6 -2
  9. package/src/api-client/askg.ts +5 -3
  10. package/src/api-client/bank-figures.ts +73 -0
  11. package/src/api-client/credit-boards.ts +21 -1
  12. package/src/api-client/crypto-markets.ts +88 -1
  13. package/src/api-client/data.ts +104 -4
  14. package/src/api-client/debt-maturities.ts +18 -0
  15. package/src/api-client/equity-screener.ts +3 -0
  16. package/src/api-client/errors.ts +13 -0
  17. package/src/api-client/fund-facts.ts +72 -0
  18. package/src/api-client/futures-curve.ts +26 -3
  19. package/src/api-client/geo.ts +11 -1
  20. package/src/api-client/index.ts +25 -2
  21. package/src/api-client/insider-signals.ts +95 -11
  22. package/src/api-client/normalizers.ts +1 -0
  23. package/src/api-client/paths.ts +20 -1
  24. package/src/api-client/request.ts +11 -3
  25. package/src/api-client/research-activity.ts +15 -8
  26. package/src/api-client/socket.ts +6 -3
  27. package/src/api-client/supply-chain-graph.ts +2 -2
  28. package/src/api-client/supply-chain.ts +8 -0
  29. package/src/api-client/tape.ts +12 -0
  30. package/src/api-client/types.ts +66 -2
  31. package/src/api-client/upgrade-experiment.ts +4 -3
  32. package/src/api-client/wall-experiments.ts +5 -3
  33. package/src/api-client/wall-summary.ts +3 -1
  34. package/src/api-client/web-experiments.ts +5 -2
  35. package/src/api-client/yield-curves.ts +17 -0
  36. package/src/app/command-bar-intro.ts +38 -0
  37. package/src/app/keybindings/actions.ts +1 -1
  38. package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
  39. package/src/app/pane-runtime/plugin-bindings.ts +4 -0
  40. package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
  41. package/src/app/pane-runtime/ticker-open-target.ts +31 -0
  42. package/src/app/runtime/desktop-deeplink.ts +3 -4
  43. package/src/app/runtime/plugin-bindings.ts +3 -2
  44. package/src/app/runtime/startup.ts +3 -4
  45. package/src/app/runtime/ticker-refresh.ts +13 -12
  46. package/src/app/runtime/update.ts +3 -2
  47. package/src/architecture/core-builtin-allowlist.json +0 -3
  48. package/src/architecture/silent-catch-allowlist.json +3 -0
  49. package/src/architecture/ui-literals-allowlist.json +186 -0
  50. package/src/architecture/ui-tokens-allowlist.json +40 -0
  51. package/src/ask-gloom/pane-context.ts +3 -1
  52. package/src/brokers/signed-in/catalog.ts +2 -1
  53. package/src/brokers/signed-in/client.ts +7 -2
  54. package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
  55. package/src/brokers/signed-in/sign-in.ts +22 -20
  56. package/src/brokers/sync-broker-instance.ts +2 -1
  57. package/src/capabilities/chart-series.ts +2 -0
  58. package/src/capabilities/registry.ts +1 -3
  59. package/src/cli/bank-section.ts +147 -0
  60. package/src/cli/base-converter.ts +5 -2
  61. package/src/cli/board-coins.ts +70 -0
  62. package/src/cli/commands/fred-series.ts +90 -0
  63. package/src/cli/commands/market.ts +340 -69
  64. package/src/cli/commands/news-search.ts +35 -2
  65. package/src/cli/commands/options-chain.ts +7 -5
  66. package/src/cli/commands/options-leaps.ts +3 -3
  67. package/src/cli/commands/overview.ts +78 -29
  68. package/src/cli/commands/plugins.ts +1 -0
  69. package/src/cli/commands/search.ts +87 -24
  70. package/src/cli/commands/system.ts +5 -2
  71. package/src/cli/commands/ticker.ts +337 -60
  72. package/src/cli/context.ts +2 -1
  73. package/src/cli/desktop-pane-shot.ts +11 -3
  74. package/src/cli/earnings-calendar.ts +2 -1
  75. package/src/cli/errors.ts +10 -0
  76. package/src/cli/export-source.ts +18 -3
  77. package/src/cli/glossary.ts +196 -14
  78. package/src/cli/history-rows.ts +60 -9
  79. package/src/cli/index-roots.ts +26 -0
  80. package/src/cli/index.ts +34 -9
  81. package/src/cli/known-gaps.ts +156 -22
  82. package/src/cli/listing-arg.ts +156 -12
  83. package/src/cli/listing-suggestion.ts +4 -2
  84. package/src/cli/name-entries.ts +76 -0
  85. package/src/cli/not-a-ticker.ts +15 -6
  86. package/src/cli/pane-functions/access-gate.ts +22 -0
  87. package/src/cli/pane-functions/capabilities.ts +62 -4
  88. package/src/cli/pane-functions/catalog.ts +94 -39
  89. package/src/cli/pane-functions/data.ts +6 -5
  90. package/src/cli/pane-functions/discovery.ts +1 -1
  91. package/src/cli/pane-functions/dom.ts +3 -3
  92. package/src/cli/pane-functions/freshness.ts +49 -8
  93. package/src/cli/pane-functions/function-help.ts +13 -10
  94. package/src/cli/pane-functions/headless.ts +177 -42
  95. package/src/cli/pane-functions/index.ts +64 -8
  96. package/src/cli/pane-functions/options.ts +17 -3
  97. package/src/cli/pane-functions/report-notices.ts +17 -3
  98. package/src/cli/pane-functions/resolver.ts +48 -13
  99. package/src/cli/pane-functions/screenshot.ts +124 -23
  100. package/src/cli/pane-functions/shot-options.ts +68 -0
  101. package/src/cli/perpetual-contract.ts +3 -1
  102. package/src/cli/report-tables.ts +10 -2
  103. package/src/cli/staged-tickers.ts +5 -0
  104. package/src/cli/statement-filings.ts +261 -0
  105. package/src/cli/symbol-args.ts +47 -8
  106. package/src/cli/symbol-list.ts +9 -2
  107. package/src/cli/symbol-rows.ts +269 -29
  108. package/src/cli/ticker-resolution.ts +148 -34
  109. package/src/cli/treasury-yields.ts +86 -0
  110. package/src/cli/types.ts +3 -0
  111. package/src/cli/uncovered-instrument.ts +20 -0
  112. package/src/components/chart/composite/axis-overlays.tsx +16 -9
  113. package/src/components/chart/composite/composite-chart.tsx +85 -6
  114. package/src/components/chart/composite/downsample.ts +19 -3
  115. package/src/components/chart/composite/format.ts +3 -0
  116. package/src/components/chart/composite/legend.tsx +155 -76
  117. package/src/components/chart/composite/panel-bitmap.ts +8 -2
  118. package/src/components/chart/composite/panel-surface.tsx +3 -3
  119. package/src/components/chart/composite/toolbar.tsx +12 -7
  120. package/src/components/chart/composite/types.ts +6 -0
  121. package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
  122. package/src/components/chart/curve/curve-surface.tsx +1 -1
  123. package/src/components/chart/static/series.ts +39 -0
  124. package/src/components/chart/static/trail-chart-surface.tsx +2 -1
  125. package/src/components/command-bar/commands/collection-list.ts +45 -15
  126. package/src/components/command-bar/commands/collection.ts +10 -3
  127. package/src/components/command-bar/commands/registry.ts +2 -2
  128. package/src/components/command-bar/list/badge.ts +4 -3
  129. package/src/components/command-bar/list/model.ts +5 -0
  130. package/src/components/command-bar/list/view.tsx +1 -1
  131. package/src/components/command-bar/panel/index.tsx +19 -8
  132. package/src/components/command-bar/panel/native-surface.ts +6 -4
  133. package/src/components/command-bar/panel/prompt-example.ts +22 -0
  134. package/src/components/command-bar/routes/root/provider-search.ts +3 -9
  135. package/src/components/command-bar/routes/root/results.ts +13 -0
  136. package/src/components/command-bar/routes/root/start-here.ts +90 -0
  137. package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
  138. package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
  139. package/src/components/command-bar/theme-picker.tsx +3 -1
  140. package/src/components/command-bar/workflow/builtin.ts +5 -1
  141. package/src/components/command-bar/workflow/collection-actions.ts +21 -12
  142. package/src/components/command-bar/workflow/ops.ts +21 -1
  143. package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
  144. package/src/components/command-bar/workflow/tickers.ts +6 -4
  145. package/src/components/feedback-dialog.tsx +1 -1
  146. package/src/components/form-modal/broker-step.tsx +7 -63
  147. package/src/components/form-modal/content.tsx +7 -2
  148. package/src/components/form-modal/field-row.tsx +6 -8
  149. package/src/components/form-modal/host.tsx +3 -3
  150. package/src/components/form-modal/model.ts +3 -0
  151. package/src/components/form-modal/routes.ts +17 -2
  152. package/src/components/index.ts +6 -6
  153. package/src/components/input-search-bar.tsx +43 -6
  154. package/src/components/layout/detached-pane-shell.tsx +0 -1
  155. package/src/components/layout/floating-pane.tsx +0 -1
  156. package/src/components/layout/header.tsx +8 -7
  157. package/src/components/layout/pane/footer/index.tsx +7 -4
  158. package/src/components/layout/pane/footer/model.ts +28 -2
  159. package/src/components/layout/pane/header.tsx +9 -19
  160. package/src/components/layout/pane/index.tsx +0 -1
  161. package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
  162. package/src/components/layout/pane/pane-tabs.tsx +4 -3
  163. package/src/components/layout/pane/sizing.ts +9 -0
  164. package/src/components/layout/pane/ticker-fate-gate.tsx +8 -2
  165. package/src/components/layout/shell/pane/capture.ts +44 -20
  166. package/src/components/layout/status-bar.tsx +3 -6
  167. package/src/components/markdown-text.tsx +17 -2
  168. package/src/components/market-board/index.tsx +19 -8
  169. package/src/components/metric-treemap/heat-surface.tsx +2 -7
  170. package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
  171. package/src/components/onboarding/onboarding-frame.tsx +24 -21
  172. package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
  173. package/src/components/onboarding/wizard-positions.ts +40 -11
  174. package/src/components/pane-settings-dialog/desktop.tsx +12 -50
  175. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
  176. package/src/components/price-performance.tsx +18 -2
  177. package/src/components/reader/article-reader.tsx +11 -10
  178. package/src/components/reader/detail-reader.tsx +9 -5
  179. package/src/components/reader/reader-text.tsx +51 -24
  180. package/src/components/sign-in-code-panel.tsx +129 -75
  181. package/src/components/ticker/badge/index.tsx +2 -1
  182. package/src/components/ui/action-row.tsx +5 -2
  183. package/src/components/ui/audio-player.tsx +70 -0
  184. package/src/components/ui/card.tsx +75 -0
  185. package/src/components/ui/data-table/index.tsx +4 -2
  186. package/src/components/ui/data-table/types.ts +11 -0
  187. package/src/components/ui/detail-scroll-body.tsx +1 -1
  188. package/src/components/ui/dialog-actions.tsx +1 -21
  189. package/src/components/ui/display.tsx +13 -5
  190. package/src/components/ui/external-link.tsx +4 -2
  191. package/src/components/ui/fields.tsx +12 -0
  192. package/src/components/ui/figure.tsx +5 -3
  193. package/src/components/ui/frame.tsx +40 -6
  194. package/src/components/ui/icon.tsx +4 -0
  195. package/src/components/ui/index.ts +4 -1
  196. package/src/components/ui/list-view.tsx +8 -0
  197. package/src/components/ui/prose.tsx +97 -25
  198. package/src/components/ui/query-bar-keys.tsx +3 -1
  199. package/src/components/ui/query-bar.tsx +6 -0
  200. package/src/components/ui/quote.ts +42 -0
  201. package/src/components/ui/range-track.tsx +15 -2
  202. package/src/components/ui/reveal-in-scroll-box.ts +21 -3
  203. package/src/components/ui/select-button.tsx +38 -3
  204. package/src/components/ui/status.tsx +73 -4
  205. package/src/components/ui/table-layout.ts +20 -0
  206. package/src/components/use-pinned-moment.ts +19 -0
  207. package/src/core/app-runtime.ts +2 -2
  208. package/src/core/state/app/instrument.ts +18 -1
  209. package/src/core/state/app/layout.ts +6 -0
  210. package/src/core/state/app/state.ts +14 -2
  211. package/src/data/cached-query.ts +1 -1
  212. package/src/data/config/chart-settings.ts +3 -1
  213. package/src/data/config/home.ts +4 -1
  214. package/src/data/config/store/node.ts +9 -4
  215. package/src/data/config/store/normalize.ts +9 -0
  216. package/src/data/json-storage.ts +6 -3
  217. package/src/data/json-ticker-repository.ts +3 -1
  218. package/src/data/plugin-cache.ts +5 -1
  219. package/src/data/sqlite/json.ts +3 -1
  220. package/src/data/ticker-repository.ts +5 -2
  221. package/src/feedback/diagnostics.ts +2 -6
  222. package/src/i18n/es.ts +86 -6
  223. package/src/i18n/index.ts +3 -9
  224. package/src/i18n/ja.ts +86 -6
  225. package/src/i18n/ko.ts +86 -6
  226. package/src/i18n/zh-cn.ts +86 -6
  227. package/src/i18n/zh-tw.ts +86 -6
  228. package/src/layout-marketplace/gallery-desktop.tsx +8 -4
  229. package/src/market-data/bars-per-year.ts +43 -0
  230. package/src/market-data/bond-funds.ts +85 -0
  231. package/src/market-data/coordinator/auxiliary.ts +5 -2
  232. package/src/market-data/coordinator/index.ts +6 -5
  233. package/src/market-data/futures-contract.ts +102 -5
  234. package/src/market-data/hooks.tsx +20 -11
  235. package/src/market-data/market/bar-period.ts +36 -0
  236. package/src/market-data/market/format.ts +5 -13
  237. package/src/market-data/market/freshness.ts +1 -3
  238. package/src/market-data/market/index-level.ts +51 -0
  239. package/src/market-data/model-dividend-yield.ts +57 -16
  240. package/src/market-data/quotes/live-valuation.ts +48 -2
  241. package/src/market-data/realized-volatility.ts +16 -7
  242. package/src/market-data/tax-equivalent.ts +344 -0
  243. package/src/news/aggregator.ts +4 -2
  244. package/src/notifications/notification-log.ts +5 -3
  245. package/src/plugins/auto-update-node.ts +1 -0
  246. package/src/plugins/builtin/account-management/footer.ts +1 -1
  247. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  248. package/src/plugins/builtin/account-management/model.ts +1 -0
  249. package/src/plugins/builtin/account-management/pane.tsx +71 -19
  250. package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
  251. package/src/plugins/builtin/alerts/cli.ts +7 -18
  252. package/src/plugins/builtin/alerts/constants.ts +8 -0
  253. package/src/plugins/builtin/alerts/event-rows.ts +16 -0
  254. package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
  255. package/src/plugins/builtin/alerts/headless.ts +182 -0
  256. package/src/plugins/builtin/alerts/index.tsx +3 -0
  257. package/src/plugins/builtin/alerts/pane.tsx +1 -7
  258. package/src/plugins/builtin/alerts/storage.ts +17 -1
  259. package/src/plugins/builtin/analytics/index.tsx +6 -1
  260. package/src/plugins/builtin/analytics/metrics.ts +47 -9
  261. package/src/plugins/builtin/analytics/risk-headless.ts +14 -2
  262. package/src/plugins/builtin/analytics/risk-math.ts +83 -0
  263. package/src/plugins/builtin/analytics/risk-model.ts +97 -4
  264. package/src/plugins/builtin/analytics/risk-pane.tsx +11 -6
  265. package/src/plugins/builtin/analytics/sector-model.ts +14 -3
  266. package/src/plugins/builtin/awards/headless.ts +6 -5
  267. package/src/plugins/builtin/awards/index.tsx +1 -1
  268. package/src/plugins/builtin/backtest/client.ts +38 -8
  269. package/src/plugins/builtin/backtest/engine.ts +25 -13
  270. package/src/plugins/builtin/backtest/export.ts +27 -0
  271. package/src/plugins/builtin/backtest/format.ts +19 -3
  272. package/src/plugins/builtin/backtest/headless.ts +42 -28
  273. package/src/plugins/builtin/backtest/index.tsx +3 -0
  274. package/src/plugins/builtin/backtest/pane.tsx +39 -10
  275. package/src/plugins/builtin/backtest/report.ts +160 -0
  276. package/src/plugins/builtin/backtest/rules.ts +2 -0
  277. package/src/plugins/builtin/backtest/window.ts +112 -0
  278. package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
  279. package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
  280. package/src/plugins/builtin/catalysts/headless.ts +24 -13
  281. package/src/plugins/builtin/catalysts/index.tsx +4 -2
  282. package/src/plugins/builtin/catalysts/model.ts +18 -1
  283. package/src/plugins/builtin/catalysts/pane.tsx +2 -2
  284. package/src/plugins/builtin/cds/client.ts +28 -5
  285. package/src/plugins/builtin/cds/headless.ts +183 -31
  286. package/src/plugins/builtin/cds/index.tsx +1 -1
  287. package/src/plugins/builtin/cds/model.ts +229 -53
  288. package/src/plugins/builtin/cds/pane.tsx +82 -26
  289. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
  290. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
  291. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
  292. package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
  293. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
  294. package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
  295. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
  296. package/src/plugins/builtin/chart-composer/headless.ts +99 -21
  297. package/src/plugins/builtin/chart-composer/index.tsx +70 -13
  298. package/src/plugins/builtin/chart-composer/pane.tsx +65 -15
  299. package/src/plugins/builtin/chart-composer/presets.ts +79 -11
  300. package/src/plugins/builtin/chart-composer/series-catalog.ts +50 -14
  301. package/src/plugins/builtin/chart-composer/series-expression.ts +9 -7
  302. package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
  303. package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
  304. package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
  305. package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
  306. package/src/plugins/builtin/chat/content/composer.tsx +22 -1
  307. package/src/plugins/builtin/chat/content/index.tsx +35 -5
  308. package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
  309. package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
  310. package/src/plugins/builtin/chat/content/snapshot.ts +29 -6
  311. package/src/plugins/builtin/chat/ghost-user.ts +11 -3
  312. package/src/plugins/builtin/chat/layout.ts +2 -0
  313. package/src/plugins/builtin/chat/message/desktop.tsx +13 -5
  314. package/src/plugins/builtin/chat/message/header.tsx +26 -4
  315. package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
  316. package/src/plugins/builtin/chat/message/render-state.ts +28 -1
  317. package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
  318. package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
  319. package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
  320. package/src/plugins/builtin/chat/share/flow.tsx +9 -73
  321. package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
  322. package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
  323. package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
  324. package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
  325. package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
  326. package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
  327. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
  328. package/src/plugins/builtin/cloud/plugin.tsx +2 -0
  329. package/src/plugins/builtin/cloud/team/module.tsx +5 -1
  330. package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
  331. package/src/plugins/builtin/cloud/team/views.ts +11 -4
  332. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
  333. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
  334. package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
  335. package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
  336. package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
  337. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
  338. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
  339. package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
  340. package/src/plugins/builtin/commodities-energy/index.tsx +8 -5
  341. package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
  342. package/src/plugins/builtin/company-attention/headless.ts +15 -7
  343. package/src/plugins/builtin/company-attention/index.tsx +1 -1
  344. package/src/plugins/builtin/company-attention/model.ts +49 -4
  345. package/src/plugins/builtin/company-attention/pane.tsx +11 -9
  346. package/src/plugins/builtin/company-kpis/headless.ts +4 -3
  347. package/src/plugins/builtin/company-kpis/index.tsx +2 -2
  348. package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
  349. package/src/plugins/builtin/congress-trades/headless.ts +23 -2
  350. package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
  351. package/src/plugins/builtin/cot/headless.ts +5 -2
  352. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
  353. package/src/plugins/builtin/credit-boards/client.ts +9 -2
  354. package/src/plugins/builtin/credit-boards/headless.ts +93 -27
  355. package/src/plugins/builtin/credit-boards/index.tsx +15 -4
  356. package/src/plugins/builtin/credit-boards/model.ts +105 -6
  357. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
  358. package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
  359. package/src/plugins/builtin/credit-documents/index.tsx +3 -0
  360. package/src/plugins/builtin/credit-documents/model.ts +3 -10
  361. package/src/plugins/builtin/crypto-board/client.ts +152 -2
  362. package/src/plugins/builtin/crypto-board/headless.ts +138 -19
  363. package/src/plugins/builtin/crypto-board/index.tsx +5 -3
  364. package/src/plugins/builtin/crypto-board/model.ts +210 -12
  365. package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
  366. package/src/plugins/builtin/crypto-board/series.ts +52 -1
  367. package/src/plugins/builtin/crypto-board/supply.ts +123 -0
  368. package/src/plugins/builtin/debt-maturities/client.ts +54 -1
  369. package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
  370. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  371. package/src/plugins/builtin/debt-maturities/model.ts +58 -13
  372. package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
  373. package/src/plugins/builtin/dividend-yield/client.ts +42 -36
  374. package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
  375. package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
  376. package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
  377. package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
  378. package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
  379. package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
  380. package/src/plugins/builtin/dividend-yield/types.ts +6 -1
  381. package/src/plugins/builtin/dividend-yield/view.ts +1 -1
  382. package/src/plugins/builtin/docs/blocks.tsx +54 -8
  383. package/src/plugins/builtin/docs/pane.tsx +8 -13
  384. package/src/plugins/builtin/earnings/headless.ts +3 -2
  385. package/src/plugins/builtin/earnings-calls/data.ts +51 -0
  386. package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
  387. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  388. package/src/plugins/builtin/earnings-calls/model.ts +97 -0
  389. package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
  390. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
  391. package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
  392. package/src/plugins/builtin/econ/calendar-model.ts +22 -0
  393. package/src/plugins/builtin/econ/calendar-source.ts +35 -0
  394. package/src/plugins/builtin/econ/index.tsx +40 -8
  395. package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
  396. package/src/plugins/builtin/econ-statistics/detail.tsx +8 -1
  397. package/src/plugins/builtin/econ-statistics/headless.ts +26 -5
  398. package/src/plugins/builtin/econ-statistics/pane.tsx +4 -2
  399. package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
  400. package/src/plugins/builtin/econ-statistics/view.ts +19 -0
  401. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  402. package/src/plugins/builtin/equity-screener/model.ts +46 -4
  403. package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
  404. package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
  405. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  406. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
  407. package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
  408. package/src/plugins/builtin/executives/index.tsx +1 -0
  409. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  410. package/src/plugins/builtin/fear-greed/index.tsx +9 -0
  411. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  412. package/src/plugins/builtin/futures/index.tsx +2 -2
  413. package/src/plugins/builtin/futures/model.ts +34 -21
  414. package/src/plugins/builtin/futures/table.tsx +1 -1
  415. package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
  416. package/src/plugins/builtin/futures-curve/headless.ts +4 -2
  417. package/src/plugins/builtin/futures-curve/model.ts +5 -7
  418. package/src/plugins/builtin/help/index.tsx +55 -8
  419. package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
  420. package/src/plugins/builtin/holders/headless.ts +12 -0
  421. package/src/plugins/builtin/insider/headless.ts +311 -41
  422. package/src/plugins/builtin/insider/index.tsx +280 -106
  423. package/src/plugins/builtin/insider/insiders.tsx +224 -0
  424. package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
  425. package/src/plugins/builtin/insider/signals-client.ts +135 -11
  426. package/src/plugins/builtin/insider/signals-model.ts +628 -56
  427. package/src/plugins/builtin/insider/signals.tsx +283 -83
  428. package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
  429. package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
  430. package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
  431. package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
  432. package/src/plugins/builtin/iv-history/headless.ts +2 -0
  433. package/src/plugins/builtin/jobs/headless.ts +1 -0
  434. package/src/plugins/builtin/jobs/index.tsx +1 -0
  435. package/src/plugins/builtin/jodi/headless.ts +2 -0
  436. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  437. package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
  438. package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
  439. package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
  440. package/src/plugins/builtin/market-valuation/pane.tsx +1 -1
  441. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
  442. package/src/plugins/builtin/mna/distress/headless.ts +2 -1
  443. package/src/plugins/builtin/mna/headless.ts +14 -2
  444. package/src/plugins/builtin/mna/index.tsx +1 -0
  445. package/src/plugins/builtin/news/headless.ts +4 -0
  446. package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
  447. package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
  448. package/src/plugins/builtin/notes/cli.ts +8 -4
  449. package/src/plugins/builtin/notes/files.ts +66 -20
  450. package/src/plugins/builtin/notes/migration.ts +18 -6
  451. package/src/plugins/builtin/notes/owner.tsx +3 -2
  452. package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
  453. package/src/plugins/builtin/options-scenario/client.ts +7 -4
  454. package/src/plugins/builtin/options-scenario/headless.ts +5 -3
  455. package/src/plugins/builtin/pe-band/index.tsx +1 -1
  456. package/src/plugins/builtin/pe-band/model.ts +1 -15
  457. package/src/plugins/builtin/perps/headless.ts +30 -13
  458. package/src/plugins/builtin/perps/model.ts +45 -3
  459. package/src/plugins/builtin/perps/pane.tsx +7 -7
  460. package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
  461. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
  462. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +108 -46
  463. package/src/plugins/builtin/portfolio-list/cli/render.ts +368 -45
  464. package/src/plugins/builtin/portfolio-list/cli/shared.ts +8 -2
  465. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +24 -34
  466. package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
  467. package/src/plugins/builtin/portfolio-list/headless.ts +151 -29
  468. package/src/plugins/builtin/portfolio-list/income.ts +296 -0
  469. package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
  470. package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
  471. package/src/plugins/builtin/portfolio-list/pane/index.tsx +27 -10
  472. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
  473. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +3 -2
  474. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +10 -3
  475. package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
  476. package/src/plugins/builtin/portfolio-list/summary/totals.ts +31 -3
  477. package/src/plugins/builtin/portfolio-list/table.tsx +25 -5
  478. package/src/plugins/builtin/power/prices-headless.ts +2 -1
  479. package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
  480. package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
  481. package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
  482. package/src/plugins/builtin/realized-vol/headless.ts +6 -4
  483. package/src/plugins/builtin/realized-vol/model.ts +5 -1
  484. package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
  485. package/src/plugins/builtin/realized-vol/settings.ts +1 -1
  486. package/src/plugins/builtin/research/analyst-headless.ts +4 -0
  487. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -0
  488. package/src/plugins/builtin/research/event-model.ts +22 -2
  489. package/src/plugins/builtin/research/headless-schema.ts +1 -0
  490. package/src/plugins/builtin/research/headless.ts +3 -0
  491. package/src/plugins/builtin/research/index.tsx +3 -0
  492. package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
  493. package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
  494. package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
  495. package/src/plugins/builtin/reverse-dcf/headless.ts +9 -6
  496. package/src/plugins/builtin/reverse-dcf/index.tsx +2 -2
  497. package/src/plugins/builtin/reverse-dcf/model.ts +51 -4
  498. package/src/plugins/builtin/reverse-dcf/pane.tsx +23 -10
  499. package/src/plugins/builtin/risk-factors/headless.ts +7 -0
  500. package/src/plugins/builtin/risk-factors/index.tsx +1 -0
  501. package/src/plugins/builtin/scanner/feed.ts +5 -3
  502. package/src/plugins/builtin/scanner/headless.ts +9 -57
  503. package/src/plugins/builtin/scanner/index.ts +1 -2
  504. package/src/plugins/builtin/sec/forms.ts +2 -6
  505. package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
  506. package/src/plugins/builtin/sec/fund-facts.ts +328 -0
  507. package/src/plugins/builtin/sec/headless.ts +140 -44
  508. package/src/plugins/builtin/sec/index.tsx +95 -30
  509. package/src/plugins/builtin/sec/model.ts +2 -44
  510. package/src/plugins/builtin/shared/headless-options.ts +10 -0
  511. package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
  512. package/src/plugins/builtin/shared/move-rank.ts +108 -0
  513. package/src/plugins/builtin/shared/report-freshness.ts +25 -9
  514. package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
  515. package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
  516. package/src/plugins/builtin/shared/use-quote-board.ts +1 -0
  517. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
  518. package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
  519. package/src/plugins/builtin/short-interest/headless.ts +3 -0
  520. package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
  521. package/src/plugins/builtin/social-mentions/client.ts +8 -1
  522. package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
  523. package/src/plugins/builtin/supply-chain/client.ts +12 -3
  524. package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
  525. package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
  526. package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
  527. package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
  528. package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
  529. package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
  530. package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
  531. package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
  532. package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
  533. package/src/plugins/builtin/supply-chain/headless.ts +69 -27
  534. package/src/plugins/builtin/supply-chain/index.tsx +1 -1
  535. package/src/plugins/builtin/supply-chain/model.ts +76 -15
  536. package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
  537. package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
  538. package/src/plugins/builtin/themes/model.ts +2 -5
  539. package/src/plugins/builtin/thirteenf/api.ts +50 -2
  540. package/src/plugins/builtin/thirteenf/data.ts +250 -11
  541. package/src/plugins/builtin/thirteenf/headless.ts +311 -28
  542. package/src/plugins/builtin/thirteenf/index.tsx +4 -1
  543. package/src/plugins/builtin/thirteenf/model.ts +244 -19
  544. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
  545. package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
  546. package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
  547. package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
  548. package/src/plugins/builtin/thirteenf/types.ts +49 -0
  549. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -8
  550. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +17 -1
  551. package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
  552. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
  553. package/src/plugins/builtin/ticker-detail/headless.ts +23 -10
  554. package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
  555. package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
  556. package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
  557. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
  558. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +36 -3
  559. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
  560. package/src/plugins/builtin/time-sales/client.ts +7 -4
  561. package/src/plugins/builtin/time-sales/headless.ts +71 -22
  562. package/src/plugins/builtin/time-sales/model.ts +12 -0
  563. package/src/plugins/builtin/time-sales/pane.tsx +31 -7
  564. package/src/plugins/builtin/time-sales/window.ts +128 -0
  565. package/src/plugins/builtin/trace-bonds/client.ts +351 -33
  566. package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
  567. package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
  568. package/src/plugins/builtin/trace-bonds/model.ts +27 -25
  569. package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
  570. package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
  571. package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
  572. package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
  573. package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
  574. package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
  575. package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
  576. package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
  577. package/src/plugins/builtin/vol-surface/headless.ts +3 -2
  578. package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
  579. package/src/plugins/builtin/vol-surface/stored.ts +4 -7
  580. package/src/plugins/builtin/volatility/chart-model.ts +16 -15
  581. package/src/plugins/builtin/volatility/charts.tsx +5 -5
  582. package/src/plugins/builtin/volatility/client.ts +73 -2
  583. package/src/plugins/builtin/volatility/evidence.ts +13 -14
  584. package/src/plugins/builtin/volatility/headless.ts +13 -7
  585. package/src/plugins/builtin/volatility/index.tsx +1 -1
  586. package/src/plugins/builtin/volatility/model.ts +104 -38
  587. package/src/plugins/builtin/volatility/pane.tsx +42 -24
  588. package/src/plugins/builtin/volatility/series.ts +22 -13
  589. package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
  590. package/src/plugins/builtin/world-venue-map/headless.ts +8 -5
  591. package/src/plugins/builtin/world-venue-map/index.ts +16 -2
  592. package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
  593. package/src/plugins/builtin/yield-curve/curves.ts +29 -5
  594. package/src/plugins/builtin/yield-curve/headless.ts +41 -8
  595. package/src/plugins/builtin/yield-curve/history.ts +5 -5
  596. package/src/plugins/builtin/yield-curve/index.tsx +30 -13
  597. package/src/plugins/compat.ts +3 -1
  598. package/src/plugins/dependencies.ts +8 -2
  599. package/src/plugins/host-link.ts +12 -5
  600. package/src/plugins/installer.ts +16 -7
  601. package/src/plugins/loader.ts +9 -2
  602. package/src/plugins/marketplace/activation.ts +10 -4
  603. package/src/plugins/registry/host-actions.ts +3 -0
  604. package/src/plugins/registry/index.ts +3 -1
  605. package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +74 -12
  606. package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +140 -53
  607. package/src/portfolio/futures-positions.ts +213 -0
  608. package/src/portfolio/futures-terms.ts +75 -0
  609. package/src/portfolio/manual-lots.ts +78 -0
  610. package/src/portfolio/mutations.ts +54 -10
  611. package/src/portfolio/position-metrics.ts +44 -10
  612. package/src/portfolio/valuation-basis.ts +31 -4
  613. package/src/public/__snapshots__/public-api.test.ts.snap +4 -0
  614. package/src/public/time-series.ts +2 -1
  615. package/src/remote/confirm-change.tsx +2 -1
  616. package/src/remote/server.ts +1 -1
  617. package/src/remote/watchlist.ts +9 -1
  618. package/src/renderers/dom/data-table/row.tsx +1 -1
  619. package/src/renderers/dom/desktop/audio-player.tsx +312 -0
  620. package/src/renderers/dom/desktop/control-styles.ts +17 -82
  621. package/src/renderers/dom/desktop/controls.tsx +39 -63
  622. package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
  623. package/src/renderers/dom/desktop/icons.tsx +8 -0
  624. package/src/renderers/dom/desktop/list-view.tsx +6 -6
  625. package/src/renderers/dom/desktop/popover.tsx +0 -3
  626. package/src/renderers/dom/desktop/query-bar.tsx +6 -4
  627. package/src/renderers/dom/desktop/select-field.tsx +9 -8
  628. package/src/renderers/dom/desktop/stack-header.ts +3 -0
  629. package/src/renderers/dom/dom-ui-host.tsx +3 -2
  630. package/src/renderers/dom/host/chart-surface.tsx +3 -3
  631. package/src/renderers/dom/host/size-observer.ts +33 -4
  632. package/src/renderers/dom/host/surface-3d.tsx +3 -2
  633. package/src/renderers/dom/host/surface-fit.ts +4 -1
  634. package/src/renderers/dom/host/tabs.tsx +22 -132
  635. package/src/renderers/dom/styles.css +358 -160
  636. package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
  638. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  639. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
  640. package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
  641. package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
  642. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
  643. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  644. package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
  645. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
  646. package/src/series/capability.ts +1 -0
  647. package/src/series/files.ts +9 -3
  648. package/src/series/registry.ts +40 -14
  649. package/src/series/resolve.ts +121 -18
  650. package/src/series/standalone.ts +3 -2
  651. package/src/series/types.ts +55 -1
  652. package/src/series/validate.ts +39 -0
  653. package/src/shares/api.ts +26 -7
  654. package/src/shares/bug-report-dialog.tsx +2 -2
  655. package/src/shares/bug-report-flow.tsx +4 -7
  656. package/src/shares/capture-flow.ts +5 -4
  657. package/src/shares/capture-preview.tsx +2 -2
  658. package/src/shares/chat.ts +7 -27
  659. package/src/shares/flow.tsx +86 -40
  660. package/src/shares/location.ts +1 -3
  661. package/src/shares/pane-image.ts +11 -9
  662. package/src/shares/pane.ts +5 -2
  663. package/src/shares/payload.ts +1 -1
  664. package/src/shares/personal-pane.ts +3 -3
  665. package/src/shares/portable-layout.ts +9 -2
  666. package/src/shares/ready.ts +42 -0
  667. package/src/shares/test-harness.ts +62 -0
  668. package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
  669. package/src/sources/gloomberb-cloud/index.ts +64 -12
  670. package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
  671. package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
  672. package/src/sources/history-omitted.ts +34 -0
  673. package/src/sources/history-result.ts +3 -0
  674. package/src/sources/history-retention.ts +1 -1
  675. package/src/sources/provider-errors.ts +43 -1
  676. package/src/sources/provider-router/batches.ts +3 -2
  677. package/src/sources/provider-router/cache.ts +2 -1
  678. package/src/sources/provider-router/cached-routes.ts +9 -3
  679. package/src/sources/provider-router/history.ts +9 -8
  680. package/src/sources/provider-router/index.ts +18 -3
  681. package/src/sources/provider-router/primary.ts +5 -4
  682. package/src/sources/provider-router/quote-route.ts +3 -1
  683. package/src/sources/provider-router/routing.ts +2 -1
  684. package/src/sources/provider-router/search.ts +47 -13
  685. package/src/sources/provider-router/streaming.ts +7 -4
  686. package/src/sources/sec-edgar.ts +1 -3
  687. package/src/state/app/context/index.tsx +9 -13
  688. package/src/state/app/tab-cursor.ts +149 -12
  689. package/src/state/hooks/quote-streaming.ts +3 -2
  690. package/src/state/hooks/session-snapshot.ts +3 -2
  691. package/src/state/persist-scheduler.ts +12 -4
  692. package/src/sync/controller.ts +22 -1
  693. package/src/sync/core-contributors.ts +58 -15
  694. package/src/team/collections-sync.ts +14 -4
  695. package/src/team/collections.ts +40 -11
  696. package/src/team/store.ts +5 -4
  697. package/src/telemetry/attention-counts.ts +8 -2
  698. package/src/telemetry/crash-reports-dom.ts +3 -0
  699. package/src/telemetry/crash-reports-node.ts +7 -4
  700. package/src/telemetry/crash-reports.ts +11 -11
  701. package/src/telemetry/usage-counts.ts +16 -12
  702. package/src/theme/colors.ts +2 -0
  703. package/src/theme/font-scale.ts +2 -0
  704. package/src/theme/ui-tokens.ts +71 -0
  705. package/src/tickers/batched-lookups.ts +186 -0
  706. package/src/tickers/company-name-listing.ts +97 -0
  707. package/src/tickers/company-name.ts +13 -0
  708. package/src/tickers/instrument-coverage.ts +112 -0
  709. package/src/tickers/listing-fate.ts +49 -9
  710. package/src/tickers/listing-venues.ts +24 -5
  711. package/src/tickers/open-target.ts +13 -5
  712. package/src/tickers/search/index.ts +112 -35
  713. package/src/tickers/search/ranking.ts +49 -1
  714. package/src/tickers/terminal-codes.ts +79 -0
  715. package/src/tickers/ticker-changes.ts +74 -3
  716. package/src/tickers/us-listing.ts +2 -1
  717. package/src/time-series/chart-data.ts +79 -8
  718. package/src/time-series/fundamentals.ts +55 -7
  719. package/src/time-series/ice-credit.ts +17 -2
  720. package/src/time-series/live-bars.ts +21 -8
  721. package/src/time-series/live-quotes.ts +2 -3
  722. package/src/time-series/price-comparison.ts +6 -1
  723. package/src/time-series/resolve.ts +65 -10
  724. package/src/time-series/spec.ts +2 -1
  725. package/src/time-series/studies.ts +3 -3
  726. package/src/time-series/types.ts +22 -0
  727. package/src/time-series/use-chart-resolution.ts +2 -1
  728. package/src/types/config.ts +7 -0
  729. package/src/types/data-provider.ts +20 -0
  730. package/src/types/headless.ts +142 -1
  731. package/src/types/instrument.ts +6 -0
  732. package/src/types/plugin.ts +32 -9
  733. package/src/types/price-history.ts +5 -0
  734. package/src/types/ticker-changes.ts +19 -0
  735. package/src/types/ticker.ts +10 -2
  736. package/src/ui/context-menu.tsx +2 -0
  737. package/src/ui/host.tsx +40 -4
  738. package/src/ui/measured-cells.ts +13 -7
  739. package/src/ui/qr.ts +7 -5
  740. package/src/updater/index.ts +5 -5
  741. package/src/utils/calendar-date.ts +86 -27
  742. package/src/utils/currency-units.ts +17 -0
  743. package/src/utils/debug-log.ts +1 -1
  744. package/src/utils/dividend-cash.ts +48 -0
  745. package/src/utils/dom-screenshot-rasters.ts +3 -1
  746. package/src/utils/dom-screenshot.ts +14 -10
  747. package/src/utils/exchange-rate-snapshot.ts +9 -4
  748. package/src/utils/external-url.ts +1 -0
  749. package/src/utils/fiscal-period.ts +47 -0
  750. package/src/utils/format.ts +5 -2
  751. package/src/utils/forward-dividend.ts +160 -11
  752. package/src/utils/futures-generic.ts +34 -8
  753. package/src/utils/http-proxy-response.ts +2 -1
  754. package/src/utils/local-utc-offset.ts +2 -0
  755. package/src/utils/log-failure.ts +31 -0
  756. package/src/utils/option-expiry.ts +12 -12
  757. package/src/utils/price-history-integrity.ts +56 -0
  758. package/src/utils/price-history.ts +13 -1
  759. package/src/utils/sec-filings.ts +245 -0
  760. package/src/utils/selection-clipboard.ts +5 -2
  761. package/src/utils/throttled-fetch.ts +1 -0
  762. package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
  763. package/src/utils/url.ts +1 -0
  764. package/src/utils/utc-time.ts +11 -0
  765. package/src/plugins/builtin/chat/share/targets.ts +0 -27
  766. package/src/renderers/dom/host/top-surface.ts +0 -119
  767. package/src/shares/dialog.tsx +0 -221
  768. package/src/shares/live.ts +0 -19
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../../utils/log-failure";
1
2
  import type { CliCommandDef } from "../../types/plugin";
2
3
  import { TIME_RANGES, type TimeRange } from "../../time-series/range";
3
4
  import type { EarningsEvent, QuoteBatchResult, QuoteSubscriptionTarget, SecFilingItem } from "../../types/data-provider";
@@ -10,7 +11,8 @@ import type {
10
11
  Quote,
11
12
  TickerFinancials,
12
13
  } from "../../types/financials";
13
- import { currencyPriceMinDigits, formatMarketPrice, formatMarketPriceWithCurrency, quoteFormatOptions } from "../../market-data/market/format";
14
+ import { currencyPriceMinDigits, formatMarketPrice, formatMarketPriceWithCurrency, quoteFormatOptions, resolveAssetDisplayKind } from "../../market-data/market/format";
15
+ import { INDEX_POINTS_EXPORT_UNIT, isIndexLevel, reportedQuoteVolume } from "../../market-data/market/index-level";
14
16
  import {
15
17
  EXTENDED_SESSION_LABELS,
16
18
  getExtendedSessionDisplay,
@@ -48,10 +50,14 @@ import { getPublishedUsEquityCalendarYears, getPublishedUsEquitySession } from "
48
50
  import { isQuoteStaleForCurrentSession } from "../../market-data/quotes/freshness";
49
51
  import { isOptionsUnavailableError, listedOptionsAlternative, optionsUnavailableTitle } from "../../market-data/options-alternatives";
50
52
  import {
51
- cliFuturesContract, futuresContractMetrics, fundamentalsReportTables, loadForwardYieldDeclarations, renderFundamentalsReport,
53
+ cliFuturesContract, futuresContractMetrics, exportedDividendFundamentals, fundamentalsReportTables, loadDividendDistributions, loadReportBondFacts,
54
+ renderFundamentalsReport,
52
55
  } from "./ticker";
56
+ import { loadStatementFilings } from "../statement-filings";
57
+ import { loadBankFigures } from "../bank-section";
58
+ import { latestFinancialPeriod } from "../../utils/latest-financial-period";
53
59
  import type { FuturesContractTermsPayload } from "../../api-client/futures-curve";
54
- import { futuresSettleText, isFuturesInstrument } from "../../market-data/futures-contract";
60
+ import { futuresContractLines, futuresRollLines, futuresSettleText, isFuturesInstrument } from "../../market-data/futures-contract";
55
61
  import {
56
62
  chainHasExpiry,
57
63
  formatExpiryList,
@@ -70,7 +76,8 @@ import {
70
76
  valueChainAtModelSpot,
71
77
  type OptionsChainBasis,
72
78
  } from "./options-chain";
73
- import { missingExpiryMessage } from "../../utils/option-expiry";
79
+ import { missingExpiryMessage, OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER } from "../../utils/option-expiry";
80
+ import { DATE_OPTION_EXAMPLES } from "../../utils/calendar-date";
74
81
  import { declaredDividends } from "../../utils/declared-dividends";
75
82
  import {
76
83
  DEFAULT_LEAPS_CRITERIA, LEAPS_EXTRA_COLUMNS, LEAPS_USAGE, parseLeapsCriteria, runLeapsScreen, takeLeapsOptions,
@@ -84,6 +91,7 @@ import {
84
91
  type CliReportTables,
85
92
  } from "../report-tables";
86
93
  import {
94
+ closedPeriodBarDay,
87
95
  formingPeriodBar,
88
96
  historyBarStatusNote,
89
97
  historyCoverageWarning,
@@ -94,17 +102,20 @@ import {
94
102
  historyUnbrokenFrom,
95
103
  historyAllRangeYears,
96
104
  historyIntervalsByRange,
105
+ historyMissingVolumeNote,
97
106
  historyNotes,
107
+ historyReportsVolume,
98
108
  historyPriceDecimals,
99
109
  historyRows,
100
110
  historyUnit,
101
111
  historyVolume,
102
112
  historyVolumeText,
103
113
  } from "../history-rows";
114
+ import { MISSING_VOLUME_TEXT } from "../../utils/price-history-integrity";
104
115
  import { CRYPTO_BOARD_HINT, quoteNotes } from "./crypto-hints";
105
116
  import { bareFuturesCode } from "../../market-data/futures-board";
106
117
  import { newsEmptyMessage } from "./news-hints";
107
- import { newsFallbackText, newsSearchHeading, newsSearchText, newsSearchTextProblem, searchNewsStories } from "./news-search";
118
+ import { commonWordCandidate, newsFallbackText, newsSearchHeading, newsSearchText, newsSearchTextProblem, quotedSearchCommand, searchNewsStories, wordMeansMore } from "./news-search";
108
119
  import { formatUtcTime } from "../../utils/utc-time";
109
120
  import { DAY_SO_FAR_TEXT, withSessionBar } from "../../time-series/session-bar";
110
121
  import { isFiniteNumber } from "../../utils/guards";
@@ -155,20 +166,32 @@ import {
155
166
  } from "../listing-arg";
156
167
  import { bareListingNote, listingFateNotes, noLongerTradingNote, tradingListing } from "../../tickers/listing-fate";
157
168
  import { indexRootAlsoLine, indexRootFor, indexRootTryLine, isYieldIndexSymbol } from "../index-roots";
169
+ import { treasuryYieldLine, treasuryYieldTenor } from "../treasury-yields";
170
+ import {
171
+ analyzeTaxEquivalent, distributionBasis, loadTreasuryTenors, parseTaxRates, TAX_RATE_HELP_OPTIONS, taxEquivalentJson, type TaxRates,
172
+ } from "../../market-data/tax-equivalent";
173
+ import { CBOE_YIELD_INDEX_CLOSE } from "../../plugins/builtin/shared/treasury-cutoffs";
158
174
  import { earningsCalendarFailure, earningsChoiceNote, earningsDateLabel, isListingAbroad, noListingCalendarNote } from "../earnings-calendar";
159
175
  import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
160
176
  import { notATickerSymbol, providerMissReason } from "../../sources/provider-errors";
161
177
  import { isNotATickerMessage, withListingSuggestion } from "../not-a-ticker";
178
+ import { primeBoardCoins } from "../board-coins";
162
179
  import { describeSuggestion, suggestListing, suggestUsTicker, type ListingSuggestion } from "../listing-suggestion";
163
- import { splitSymbolArgs, uniqueSecuritySymbols } from "../symbol-args";
180
+ import { quotedPhrase, splitSymbolArgs, uniqueSecuritySymbols } from "../symbol-args";
181
+ import { isCompanyNameInput } from "../../tickers/company-name-listing";
182
+ import { readNameEntries, readPhraseArgument } from "../name-entries";
183
+ import { readSymbolList, SymbolListError } from "../symbol-list";
164
184
  import { cliPerpetualContract, perpetualContractStats, type PerpetualContractPayload } from "../perpetual-contract";
165
- import { subMinuteDataMessage } from "../known-gaps";
185
+ import { knownGapForTicker, knownGapMessage, subMinuteDataMessage } from "../known-gaps";
186
+ import { phraseSearchReport } from "./search";
166
187
  import { secRegistrantMismatchMessage, SecRegistrantMismatchError, areDifferentCompanies } from "../../sources/sec-registrant";
167
188
  import { isCryptoPairSymbol } from "../../utils/crypto-pair";
168
189
  import { looksLikeCurrencyPair } from "../../utils/currency-pair";
169
190
  import { canonicalExchange, isUsListingExchange } from "../../utils/exchanges";
170
- import { describeTickerChange, movedAwayChange, startTickerChangeLookup, tickerChangeLine, type TickerChange } from "../../tickers/ticker-changes";
171
- import { noSecReportsNotice, nonUsSecListingVenue } from "../../utils/sec";
191
+ import {
192
+ describeFormerIssuer, describeTickerChange, movedAwayChange, startTickerChangeLookup, tickerChangeLine, type FormerIssuer, type TickerChange,
193
+ } from "../../tickers/ticker-changes";
194
+ import { noSecReportsNotice, nonUsSecListingVenue, secFilingItemCodes } from "../../utils/sec";
172
195
  import type { MarketContext } from "../types";
173
196
  import { windowRows } from "../row-window";
174
197
  import { crossRate, describeFxConversion, describeFxRate, FX_USAGE, isUsableRate, parseFxArgs } from "../fx-pair";
@@ -195,6 +218,7 @@ import { fetchBeneficialOwners } from "../../plugins/builtin/holders/beneficial-
195
218
  import { centralBankEventsMessage } from "../central-bank-events";
196
219
  import { classSharesToday } from "../../plugins/builtin/holders/client";
197
220
  import { filingFormMatches, SEC_FILING_FETCH_LIMIT } from "../../plugins/builtin/sec/forms";
221
+ import { currentReportItemsLabel } from "../../utils/sec-filings";
198
222
  import { loadParsedInsiderFilings } from "../../plugins/builtin/insider/client";
199
223
  import { insiderEmptyNotice } from "../../plugins/builtin/insider/issuer";
200
224
  import {
@@ -220,10 +244,14 @@ type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & {
220
244
  listing?: { name: string | null };
221
245
  /** A futures row's contract, terms and latest confirmed settlement, as `ticker` reads them. */
222
246
  futuresContract?: FuturesContractTermsPayload;
247
+ /** The quote of the root's front contract, for a futures row that prices another one (NGF27, NG2). */
248
+ futuresFront?: Quote;
223
249
  /** A perpetual row's market, funding per eight hours and open interest. */
224
250
  perpetualContract?: PerpetualContractPayload;
225
251
  /** The symbol changed: its issuer moved to another symbol, or its holder had another before. */
226
252
  tickerChange?: TickerChange;
253
+ /** The company that last held the symbol merged into another, which trades under its own symbol now (GOGL, CMBT). */
254
+ formerIssuer?: FormerIssuer;
227
255
  /** A bare symbol read on a listing abroad that is one slip from a US ticker: the US ticker it was most likely typed for. */
228
256
  usSuggestion?: ListingSuggestion;
229
257
  };
@@ -244,14 +272,59 @@ function parseNewsFeed(value: string | undefined): NewsQuery["feed"] | undefined
244
272
  }
245
273
 
246
274
  /**
247
- * The symbols of a command's arguments, one each however they were separated
248
- * (spaces, commas or pasted lines), and each security once however its case or
249
- * share class was written: `AAPL aapl BRK.B BRK-B` is AAPL and BRK.B.
275
+ * The entries of a command's arguments (`splitSymbolArgs`), each once however
276
+ * its case or share class was written: `AAPL aapl BRK.B BRK-B` is AAPL and
277
+ * BRK.B. An entry with a space, from a comma list or pasted lines, keeps its
278
+ * case: it may be a company's name, which `readSymbols` says.
250
279
  */
251
280
  function normalizeSymbols(args: string[]): string[] {
252
- return uniqueSecuritySymbols(splitSymbolArgs(args).map((symbol) => symbol.toUpperCase()));
281
+ return uniqueEntries(splitSymbolArgs(args));
282
+ }
283
+
284
+ /** Each entry once, a symbol upper-cased and a company's name in its typed case. */
285
+ function uniqueEntries(entries: readonly string[]): string[] {
286
+ return uniqueSecuritySymbols(entries.map((entry) => isCompanyNameInput(entry) ? entry : entry.toUpperCase()));
287
+ }
288
+
289
+ /**
290
+ * The symbols to resolve: each entry with a space read as a company's name
291
+ * when search finds one, else as its words, each a symbol (`readNameEntries`),
292
+ * with the repeats that leaves dropped. A name found stays whole.
293
+ */
294
+ async function readSymbols(symbols: readonly string[], dataProvider: Parameters<typeof readNameEntries>[1]): Promise<string[]> {
295
+ return uniqueEntries(await readNameEntries(symbols, dataProvider));
296
+ }
297
+
298
+ /**
299
+ * The symbols of `--file <path>` or standard input (`-`), with any typed
300
+ * beside them, read by the parser `watchlist add` uses. Standard input is read
301
+ * only when asked for, so a script that pipes in nothing never waits on it.
302
+ */
303
+ async function readListedSymbols(
304
+ args: readonly string[],
305
+ file: string | undefined,
306
+ market: MarketContext,
307
+ ctx: Parameters<CliCommandDef["execute"]>[1],
308
+ usage: string,
309
+ ): Promise<string[]> {
310
+ try {
311
+ const list = await readSymbolList({
312
+ args, file, stdinPiped: false, readEntries: (entries) => readNameEntries(entries, market.dataProvider),
313
+ });
314
+ if (list.symbols.length === 0) ctx.fail(`No symbols in ${file && file !== "-" ? file : "standard input"}.`, usage);
315
+ return uniqueEntries(list.symbols);
316
+ } catch (error) {
317
+ if (error instanceof SymbolListError) ctx.fail(error.message, usage);
318
+ throw error;
319
+ }
253
320
  }
254
321
 
322
+ /** `--file` of quote and compare, as `watchlist add` takes it. */
323
+ const LIST_FILE_OPTION = {
324
+ flags: "--file <path>",
325
+ description: "Read the symbols from a CSV or text file, as watchlist add does (- reads standard input)",
326
+ };
327
+
255
328
  /** What JSON metadata says about the listing a command resolved. */
256
329
  function listingMetadata(identity: ListingIdentity) {
257
330
  return { symbol: identity.symbol, exchange: identity.exchange || null, name: identity.name };
@@ -366,6 +439,21 @@ function quoteFeedState(quote: QuoteCliRecord["quote"], now: number): "stale" |
366
439
  return quote.dataSource === "live" || quote.dataSource === "delayed" ? quote.dataSource : "";
367
440
  }
368
441
 
442
+ /** A hundredth of a cent, one basis point of a dollar peg: the decimals CRYP prints a stablecoin to. */
443
+ const STABLECOIN_PRICE_DIGITS = 4;
444
+
445
+ /**
446
+ * A coin trading near a dollar, a dollar stablecoin, prints to four decimals
447
+ * either side of par (0.9987, 1.0003), as CRYP does, whatever digits its
448
+ * source sent (0.99874, 0.99968725); below a dollar a coin otherwise keeps up to eight.
449
+ */
450
+ function nearParCoin(quote: QuoteCliRecord["quote"]): boolean {
451
+ if (!quote || resolveAssetDisplayKind({ assetCategory: quote.instrumentType }) !== "crypto") return false;
452
+ if ((quote.currency ?? "USD").trim().toUpperCase() !== "USD") return false;
453
+ const reference = [quote.previousClose, quote.price].find((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
454
+ return reference != null && reference >= 0.9 && reference < 1.1;
455
+ }
456
+
369
457
  function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
370
458
  return results.map((result) => {
371
459
  const quote = result.quote;
@@ -375,12 +463,13 @@ function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
375
463
  const extended = getExtendedSessionDisplay(quote);
376
464
  const yieldIndex = isYieldIndexSymbol(result.target.symbol);
377
465
  // An index level is in points, not in the currency its members trade in.
378
- const indexPoints = quote?.instrumentType?.trim().toUpperCase() === "INDEX";
466
+ const indexPoints = isIndexLevel(quote?.instrumentType);
379
467
  // Pad to two decimals so a column lines up, but never past the currency's minor unit (JPY has none).
380
468
  // Points have no minor unit, so a yen-listed index still pads to two.
381
469
  const options = {
382
470
  ...quoteFormatOptions(quote),
383
471
  minimumFractionDigits: indexPoints ? 2 : currencyPriceMinDigits(quote?.currency),
472
+ ...nearParCoin(quote) ? { fixedFractionDigits: STABLECOIN_PRICE_DIGITS } : {},
384
473
  };
385
474
  const price = (value: number | undefined) => (
386
475
  quote && value != null
@@ -413,8 +502,9 @@ function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
413
502
  dayRange: quote?.low != null && quote.high != null
414
503
  ? indexPoints ? `${price(quote.low)}-${price(quote.high)}` : formatPriceRange(quote.low, quote.high, quote.currency, options, "-")
415
504
  : "",
416
- volume: quote?.volume ?? null,
417
- currency: quote?.currency ?? "",
505
+ // An index's 0 is volume nobody counts, and its level has no currency.
506
+ volume: reportedQuoteVolume(quote),
507
+ currency: indexPoints ? "" : quote?.currency ?? "",
418
508
  providerId: quote?.providerId ?? "",
419
509
  source: quote?.dataSource ?? quote?.providerId ?? "",
420
510
  feed: quoteFeedState(quote, now),
@@ -460,9 +550,20 @@ function filingRows(filings: SecFilingItem[]) {
460
550
  companyName: filing.companyName ?? "",
461
551
  accessionNumber: filing.accessionNumber,
462
552
  url: filing.primaryDocumentUrl ?? filing.filingUrl,
553
+ items: secFilingItemCodes(filing.items) ?? "",
554
+ // A current report's items by number and name: the results 8-K reads apart from another filed that day.
555
+ itemsLabel: currentReportItemsLabel(filing.items) ?? "",
463
556
  }));
464
557
  }
465
558
 
559
+ /** The filings as JSON gives them, with a current report's items named beside their codes. */
560
+ function filingsWithItemLabels(filings: SecFilingItem[]) {
561
+ return filings.map((filing) => {
562
+ const itemsLabel = currentReportItemsLabel(filing.items);
563
+ return itemsLabel ? { ...filing, itemsLabel } : filing;
564
+ });
565
+ }
566
+
466
567
  function analystSummary(data: AnalystResearchData): string {
467
568
  const target = data.priceTarget;
468
569
  const currency = analystTargetCurrency(data);
@@ -596,18 +697,31 @@ const MAX_CHOICE_LOOKUPS = 10;
596
697
  * names the quote of its new symbol ("HFC became DINO on 2022-03-15:
597
698
  * `gloomberb quote DINO`"); its old line's last price stays in the row.
598
699
  */
599
- function quoteChangeLine(change: TickerChange | null | undefined): string | null {
700
+ function quoteChangeLine(change: TickerChange | null | undefined, formerIssuer?: FormerIssuer | null): string | null {
600
701
  const moved = movedAwayChange(change);
601
- return moved ? movedSymbolLine(moved, `gloomberb quote ${moved.tradesAs}`) : tickerChangeLine(change);
702
+ // One line per symbol: a company that left it, else the company that merged away from it (GOGL).
703
+ return (moved ? movedSymbolLine(moved, `gloomberb quote ${moved.tradesAs}`) : tickerChangeLine(change)) ?? formerIssuer?.note ?? null;
602
704
  }
603
705
 
604
706
  async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1], commandName: "quote" | "compare") {
605
707
  const args = [...rawArgs];
708
+ const usage = `Usage: gloomberb ${commandName} <symbol...> [--file <path>]`;
606
709
  const exchange = takeOption(args, "--exchange") ?? "";
607
- const symbols = normalizeSymbols(args);
608
- if (symbols.length === 0) ctx.fail(`Usage: gloomberb ${commandName} <symbol...>`);
710
+ // A list from a file or standard input (`-`), read as `watchlist add` reads one.
711
+ const file = args.some((arg) => arg === "--file" || arg.startsWith("--file=")) ? takeOption(args, "--file") ?? "" : undefined;
712
+ const listed = file != null || args.includes("-");
713
+ const entries = normalizeSymbols(args);
714
+ if (entries.length === 0 && !listed) ctx.fail(usage);
715
+ // One quoted argument with spaces is a company's name, a pasted line of tickers, or a phrase to search for.
716
+ const phrase = exchange || listed ? null : quotedPhrase(args);
609
717
 
610
718
  await withMarketData(ctx, async (market) => {
719
+ const read = phrase ? await readPhraseArgument(phrase, market.dataProvider) : null;
720
+ if (read?.kind === "phrase") return answerPhrase(read.phrase, market, ctx);
721
+ const symbols = read?.kind === "name" ? [read.name]
722
+ : read?.kind === "symbols" ? uniqueEntries(read.symbols)
723
+ : listed ? await readListedSymbols(args, file, market, ctx, usage)
724
+ : await readSymbols(entries, market.dataProvider);
611
725
  // With several symbols, --exchange is for the ones that name no exchange of their own.
612
726
  const listings = await Promise.all(symbols.map((symbol) => requireCliListing(
613
727
  symbol, exchange, market, ctx, { ownExchangeWins: symbols.length > 1 },
@@ -631,20 +745,26 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
631
745
  return answerQuote(result, listing, market, request, ctx.cliOptions.refresh, venues.get(listing) ?? null);
632
746
  }));
633
747
  const now = Date.now();
634
- const tickerChanges = await Promise.all(answers.map(async ({ result }, index) => {
748
+ const symbolChanges = await Promise.all(answers.map(async ({ result }, index) => {
635
749
  const listing = asked[index]!;
636
750
  // A bare symbol read on a listing abroad (SXR8 -> XETRA) is not the US symbol the changes are of.
637
- if (result.quote && bareListingNote(listing, result.quote)) return null;
638
- return describeTickerChange(listing.symbol, await changes.get(listing)?.settle() ?? null, tradingListing(result.quote, now));
751
+ if (result.quote && bareListingNote(listing, result.quote)) return { change: null, formerIssuer: null };
752
+ const payload = await changes.get(listing)?.settle() ?? null;
753
+ const current = tradingListing(result.quote, now);
754
+ return { change: describeTickerChange(listing.symbol, payload, current), formerIssuer: describeFormerIssuer(listing.symbol, payload, current) };
639
755
  }));
756
+ const tickerChanges = symbolChanges.map(({ change }) => change);
757
+ const formerIssuers = symbolChanges.map(({ formerIssuer }) => formerIssuer);
640
758
  const failed = answers[0]?.failure;
641
759
  if (listings.length === 1 && failed) {
642
- ctx.fail(failed.message, [quoteChangeLine(tickerChanges[0]), failed.details].filter(Boolean).join("\n") || undefined);
760
+ ctx.fail(failed.message, [quoteChangeLine(tickerChanges[0], formerIssuers[0]), failed.details].filter(Boolean).join("\n") || undefined);
643
761
  }
644
762
  // A futures quote adds its dated settlement and contract terms, as `ticker` prints them; `compare` stays a table.
645
763
  const contracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
646
764
  ? cliFuturesContract(result.quote.symbol || result.target.symbol, result.quote.instrumentType)
647
765
  : null));
766
+ // A contract that is not its root's front (NGF27, NG2) prints the front's own price beside it, read in one batch.
767
+ const fronts = await loadFuturesFrontQuotes(contracts, market.dataProvider);
648
768
  // A perpetual adds its funding per eight hours and open interest, the figures PERP lists beside its mark.
649
769
  const perpetualContracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
650
770
  ? cliPerpetualContract(result.quote)
@@ -662,16 +782,19 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
662
782
  ...getQuoteSessionFields(result.quote),
663
783
  quote: result.quote,
664
784
  ...contracts[index] ? { futuresContract: contracts[index] } : {},
785
+ ...fronts[index] ? { futuresFront: fronts[index] } : {},
665
786
  ...perpetualContracts[index] ? { perpetualContract: perpetualContracts[index] } : {},
666
787
  ...usSuggestions[index] ? { usSuggestion: usSuggestions[index] } : {},
667
788
  error: failure?.oneLine ?? errorMessage(result.error),
668
789
  ...yieldIndexFields(result),
669
790
  ...tickerChanges[index] ? { tickerChange: tickerChanges[index] } : {},
791
+ ...formerIssuers[index] ? { formerIssuer: formerIssuers[index] } : {},
670
792
  }));
671
793
  const notes = [
672
794
  // FB now lists another security; the company that was FB trades as META. A symbol a
673
795
  // company renamed into (META) is what was typed: its change is in the row, not here.
674
- ...tickerChanges.flatMap((change) => quoteChangeLine(change) ?? []),
796
+ // GOGL was Golden Ocean, which merged into CMB.Tech; a fund lists there now.
797
+ ...tickerChanges.flatMap((change, index) => quoteChangeLine(change, formerIssuers[index]) ?? []),
675
798
  // Which listing a bare symbol was read as, and the index a bare root is not, are in no row.
676
799
  ...answers.flatMap((answer) => answer.notes),
677
800
  // A row that stopped trading long ago reads as a quote that is merely stale.
@@ -682,14 +805,22 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
682
805
  ...ctx.cliOptions.format === "text" ? quoteNotes(data, { exchange }) : [],
683
806
  ];
684
807
  const columns = commandName === "compare" ? compareColumns() : quoteColumns();
808
+ if (ctx.cliOptions.format !== "json") await primeBoardCoins(notes);
685
809
  // Only yield indices: their change is in bp, so the text header drops the percent sign (CSV keeps the percent it holds).
686
810
  const yieldsOnly = data.length > 0 && data.every((row) => isYieldIndexSymbol(row.target.symbol));
687
811
  ctx.printResult({
688
812
  data,
689
813
  warnings: notes.length > 0 ? notes : undefined,
690
- freshness: quotesFreshness(data.map((row) => row.quote)),
814
+ // A Cboe yield index stops at its 15:00 ET close, before the Treasury's curve is taken: the line says so, so its
815
+ // close does not read as a contradiction of DGS10 or GC for the same day.
816
+ freshness: quotesFreshness(data.map((row) => row.quote), yieldsOnly ? { schedule: `Cboe close ${CBOE_YIELD_INDEX_CLOSE}` } : undefined),
691
817
  }, {
692
818
  rows: quoteRows,
819
+ // An index row's Cur is blank, its level having no currency; the export names what it is in instead.
820
+ exportIdentity: (row) => ({
821
+ symbolOrSeries: row.symbol,
822
+ currencyOrUnit: row.currency || (row.yieldIndex ? "pct" : isIndexLevel(row.instrumentType) ? INDEX_POINTS_EXPORT_UNIT : null),
823
+ }),
693
824
  details: quoteDetailsText,
694
825
  columns,
695
826
  ...(yieldsOnly ? { textColumns: columns.map((column) => column.key === "changePercent" ? { ...column, header: "Chg" } : column) } : {}),
@@ -699,6 +830,26 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
699
830
  });
700
831
  }
701
832
 
833
+ /**
834
+ * `quote "10 year yield"`: a phrase that is no ticker and no company's name
835
+ * is answered with what search finds for it and the catalog, never quoted
836
+ * word by word (10, YEAR, YIELD). A script asked for a quote, so structured
837
+ * output fails with where to look instead.
838
+ */
839
+ async function answerPhrase(phrase: string, market: MarketContext, ctx: Parameters<CliCommandDef["execute"]>[1]): Promise<void> {
840
+ const said = `"${phrase}" is not a ticker or a company's name.`;
841
+ if (ctx.cliOptions.format !== "text") {
842
+ ctx.fail(said, [knownGapMessage(phrase), `Search for it: gloomberb search "${phrase}"`, `Functions and data: gloomberb catalog "${phrase}"`]
843
+ .filter(Boolean).join("\n"));
844
+ }
845
+ process.exitCode = CLI_EXIT_CODES.error;
846
+ if (ctx.cliOptions.quiet) return;
847
+ // Searched first: a busy search fails on its own, without the line saying the phrase names nothing.
848
+ const report = await phraseSearchReport(phrase, market, ctx.plugins);
849
+ console.error(`${cliStyles.warning("warning:")} ${said} What search finds for it:`);
850
+ console.log(report);
851
+ }
852
+
702
853
  /**
703
854
  * Under the table: each futures row's Settle line and Contract block, then the
704
855
  * line for a symbol that is not a US ticker though it is one slip from one.
@@ -720,9 +871,24 @@ function notUsTickerLine({ target, quote, listing, usSuggestion }: QuoteCliRecor
720
871
  return cliStyles.warning(`${target.symbol} is not a US ticker: it is ${what}. Did you mean ${describeSuggestion(usSuggestion)}?`);
721
872
  }
722
873
 
874
+ /** Each futures row's front contract quote, when its terms name a front other than its own contract; null otherwise. */
875
+ async function loadFuturesFrontQuotes(
876
+ contracts: ReadonlyArray<FuturesContractTermsPayload | null>,
877
+ provider: Pick<MarketContext["dataProvider"], "getQuotesBatch">,
878
+ ): Promise<Array<Quote | null>> {
879
+ const symbols = [...new Set(contracts.flatMap((terms) => terms?.front?.contractSymbol ?? []))];
880
+ if (symbols.length === 0) return contracts.map(() => null);
881
+ const results = await provider.getQuotesBatch(symbols.map((symbol) => ({ symbol, exchange: "" })))
882
+ .catch(logFailure("cli.commands-market", "Read futures front quotes failed", []));
883
+ const quotes = new Map(results.flatMap((result) => result.quote ? [[result.target.symbol, result.quote] as const] : []));
884
+ return contracts.map((terms) => terms?.front ? quotes.get(terms.front.contractSymbol) ?? null : null);
885
+ }
886
+
723
887
  /**
724
- * Each futures row's Settle line and Contract block, worded
725
- * as `ticker` words them. Several symbols name theirs in the block's title.
888
+ * Each futures row's Settle line and Contract block, worded as `ticker`
889
+ * words them, with how a continuous symbol or a generic rolls, the front it
890
+ * is not and the day's most active contract after the contract it prices.
891
+ * Several symbols name theirs in the block's title.
726
892
  */
727
893
  function quoteFuturesContractText(data: QuoteCliRecord[]): string {
728
894
  return data.flatMap((row) => {
@@ -733,7 +899,10 @@ function quoteFuturesContractText(data: QuoteCliRecord[]): string {
733
899
  const terms = row.futuresContract;
734
900
  if (!terms || !row.quote) return [];
735
901
  const settle: [string, string] = ["Settle", futuresSettleText(terms, row.quote)];
736
- const contract = futuresContractMetrics(terms, row.quote.symbol || row.target.symbol);
902
+ const metrics = futuresContractMetrics(terms, row.quote.symbol || row.target.symbol);
903
+ const rolls = futuresRollLines(terms, row.futuresFront).map((line): [string, string] => [line.label, line.value]);
904
+ const at = futuresContractLines(terms, row.quote.symbol || row.target.symbol)[0]?.key === "contract" ? 1 : 0;
905
+ const contract = [...metrics.slice(0, at), ...rolls, ...metrics.slice(at)];
737
906
  if (data.length > 1) return [`${renderSection(`${row.target.symbol} Contract`)}\n${renderStats([settle, ...contract])}`];
738
907
  return [[renderStats([settle]), ...contract.length ? ["", renderSection("Contract"), renderStats(contract)] : []].join("\n")];
739
908
  }).join("\n\n");
@@ -801,9 +970,13 @@ async function answerQuote(
801
970
  };
802
971
  }
803
972
  // Other listings already name a futures board code's future (GC=F); with none, it is named here.
973
+ // A Treasury yield's root (TNX) names its FRED series and the curve beside its index.
974
+ const yieldTenor = root ? treasuryYieldTenor(listing.symbol) : null;
804
975
  const tryLines = [
805
- root ? indexRootTryLine(root, request.command) : null,
976
+ yieldTenor ? treasuryYieldLine(yieldTenor) : root ? indexRootTryLine(root, request.command) : null,
806
977
  other || listing.exchange ? null : futuresTryLine(listing.symbol, request),
978
+ // BDI asks which exchange (Black Diamond Group) and says nowhere that the Baltic Dry Index is not here.
979
+ listing.exchange ? null : knownGapForTicker(listing.symbol),
807
980
  ].filter((line): line is string => !!line);
808
981
  if (!other && tryLines.length === 0) return { result: await withCurrencyPairHint(result, market), listing, notes: [] };
809
982
  const message = other?.message ?? `${listing.symbol} has no ${request.noun}.`;
@@ -850,6 +1023,30 @@ function historySymbolArg(args: readonly string[]): string | undefined {
850
1023
  && !(index > 0 && args[index - 1]!.startsWith("--") && !args[index - 1]!.includes("=")));
851
1024
  }
852
1025
 
1026
+ /** What people type for quarterly figures, which `financials` (fiscal years) does not take. */
1027
+ const QUARTERLY_OPTIONS = new Set(["--quarterly", "--quarter", "--quarters", "--qtr", "--period", "--periods", "--frequency", "--freq", "--interval"]);
1028
+
1029
+ /** Options before the symbol whose next word is their value: `financials`'s own, and the quarterly ones that take one. */
1030
+ const FINANCIALS_VALUE_OPTIONS = new Set(["--exchange", "--section", "--period", "--periods", "--frequency", "--freq", "--interval"]);
1031
+
1032
+ /** The symbol `financials` was given, wherever it sits: `--quarterly WFC` is WFC, `--period quarterly WFC` too. */
1033
+ function financialsSymbolArg(args: readonly string[]): string | undefined {
1034
+ for (let index = 0; index < args.length; index += 1) {
1035
+ const arg = args[index]!;
1036
+ if (!arg.startsWith("-")) return arg;
1037
+ if (!arg.includes("=") && FINANCIALS_VALUE_OPTIONS.has(arg)) index += 1;
1038
+ }
1039
+ return undefined;
1040
+ }
1041
+
1042
+ /** `financials --quarterly` points at the functions that show quarters: GF a metric over time, FA the statements. */
1043
+ function financialsOptionHint(flag: string, args: readonly string[]): string | null {
1044
+ if (!QUARTERLY_OPTIONS.has(flag)) return null;
1045
+ const symbol = financialsSymbolArg(args) ?? "<symbol>";
1046
+ return `financials lists fiscal years. For quarters: gloomberb fn GF ${symbol} --period quarterly --periods 40 (one metric at a time), `
1047
+ + `or gloomberb fn FA ${symbol} --period quarterly (the statements).`;
1048
+ }
1049
+
853
1050
  function historyOptionHint(flag: string, args: readonly string[]): string | null {
854
1051
  if (DATE_WINDOW_OPTIONS.has(flag)) return `history takes a --range instead of dates: ${TIME_RANGES.join(", ")}. ${HISTORY_ALL_RANGE}.`;
855
1052
  if (BAR_SIZE_OPTIONS.has(flag)) return `history serves each --range in one bar size. ${subMinuteDataMessage(historySymbolArg(args))}`;
@@ -884,7 +1081,7 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
884
1081
  const fromQuote = points !== loaded.points;
885
1082
  const identity = listingIdentity(listing, quote);
886
1083
  // Without a quote currency, the listing metadata research reads (as `ticker` does) may still state it.
887
- const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(() => null);
1084
+ const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(logFailure("cli.commands-market", "Read quote metadata for history failed", null));
888
1085
  const unit = historyUnit(listing.key, listed ?? quote);
889
1086
  const traded = {
890
1087
  listing: listing.key, exchange: quote?.listingExchangeName || exchange || undefined, instrumentType: quote?.instrumentType,
@@ -896,6 +1093,8 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
896
1093
  const venue = quote?.listingExchangeName || exchange || undefined;
897
1094
  // October's monthly bar on 16 Oct runs through the 15th: marked partial, and the status line says how far.
898
1095
  const forming = formingPeriodBar(served, venue);
1096
+ // A complete one is dated by the day its last session closed, the day its close is from.
1097
+ const closedDay = forming ? null : closedPeriodBarDay(served, venue);
899
1098
  const data = forming?.rows ?? served;
900
1099
  const interval = data[0]?.interval ?? null;
901
1100
  const decimals = historyPriceDecimals(data, listing.saved?.metadata.assetCategory);
@@ -910,7 +1109,7 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
910
1109
  observedKey: "date",
911
1110
  oldest: null,
912
1111
  cadenceExchange: venue,
913
- ...(forming ? { periodToDate: forming.periodToDate } : {}),
1112
+ ...(forming ? { periodToDate: forming.periodToDate } : closedDay ? { asOf: closedDay } : {}),
914
1113
  ...(dayToDate ? { basis: "daily bars", ...(quote && Number.isFinite(quote.lastUpdated) ? { asOf: new Date(quote.lastUpdated).toISOString() } : {}) } : {}),
915
1114
  ...(volume || dayToDate ? { note: [volume ? historyVolumeText(volume, interval) : null, dayToDate ? `the newest bar is ${DAY_SO_FAR_TEXT}` : null].filter(Boolean).join("; ") } : {}),
916
1115
  });
@@ -921,7 +1120,8 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
921
1120
  ...await listingChoiceNotes(listing, market, quote, venues),
922
1121
  ...historyNotes(range, interval, data[0]?.date), historyCoverageWarning(range, loaded) ?? [],
923
1122
  // The status line says how far a bar still forming runs; the note is for the bars it does not name.
924
- gaps ?? [], historyJumpNote(data) ?? [], historyFlagNote(data) ?? [], historyBarStatusNote(forming ? served : data) ?? [],
1123
+ gaps ?? [], historyJumpNote(data) ?? [], historyFlagNote(data) ?? [], historyMissingVolumeNote(data) ?? [],
1124
+ historyBarStatusNote(forming ? served : data) ?? [],
925
1125
  ].flat();
926
1126
  const priceColumns: CliResultColumn[] = [
927
1127
  intraday
@@ -933,6 +1133,10 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
933
1133
  { key: "close", header: "Close", align: "right", format: price },
934
1134
  { key: "volume", header: "Volume", align: "right", format: formatCountCell },
935
1135
  ];
1136
+ // A bar without volume in a history that reports it prints as missing, never as 0 or blank beside the others.
1137
+ const missingVolume = historyReportsVolume(data) ? MISSING_VOLUME_TEXT : "";
1138
+ const textPriceColumns = priceColumns.map((column) => column.key !== "volume" ? column
1139
+ : { ...column, format: (value: unknown) => formatCountCell(value) || missingVolume });
936
1140
  // Text shows a flag only on a bar that has one; the column is dropped when none does.
937
1141
  const flagColumn: CliResultColumn = {
938
1142
  key: "flag", header: "Flag", format: (value) => typeof value === "string" ? cliStyles.warning(value) : "",
@@ -972,7 +1176,7 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
972
1176
  }, {
973
1177
  heading: `${listingHeading(identity)}\n${cliStyles.muted(historyFacts(unit, data).join(" · "))}`,
974
1178
  dateKey: "date",
975
- textColumns: [...priceColumns, flagTextColumn],
1179
+ textColumns: [...textPriceColumns, flagTextColumn],
976
1180
  columns: [
977
1181
  ...priceColumns,
978
1182
  flagColumn,
@@ -1004,6 +1208,21 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
1004
1208
  const sectionFlag = args.some((arg) => arg === "--section" || arg.startsWith("--section="));
1005
1209
  const section = takeOption(args, "--section");
1006
1210
  const commandName = view === "statements" ? "financials" : view;
1211
+ // `fundamentals --tax-rate 40.8 [--state-rate 9.3 --state CA]`: a muni fund's tax-equivalent yield and muni/Treasury ratio.
1212
+ let rates: TaxRates | null = null;
1213
+ if (view === "fundamentals") {
1214
+ const taken = (flag: string) => {
1215
+ const given = args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
1216
+ const value = takeOption(args, flag);
1217
+ if (given && !value?.trim()) ctx.fail(`${flag} needs a value, such as --tax-rate 40.8 --state-rate 9.3 --state CA.`);
1218
+ return value;
1219
+ };
1220
+ try {
1221
+ rates = parseTaxRates({ taxRate: taken("--tax-rate"), stateRate: taken("--state-rate"), state: taken("--state") });
1222
+ } catch (error) {
1223
+ ctx.fail(error instanceof Error ? error.message : String(error));
1224
+ }
1225
+ }
1007
1226
  const tabular = ctx.cliOptions.format === "csv" || ctx.cliOptions.format === "ndjson";
1008
1227
  if (sectionFlag && !tabular) ctx.fail("--section picks one table of --csv or --ndjson output.");
1009
1228
  if (sectionFlag && !section?.trim()) ctx.fail("--section needs a section title or number.");
@@ -1021,6 +1240,15 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
1021
1240
  const financials = await loadForListing(listing, market, ctx, () => market.dataProvider.getTickerFinancials(
1022
1241
  listing.request.symbol, listing.request.exchange, { cacheMode: ctx.cliOptions.refresh ? "refresh" : "default" },
1023
1242
  ), undefined, { command: commandName, noun: commandName === "valuation" ? "valuation data" : commandName });
1243
+ // A trailing yield is measured from the cash history as DVD measures it, a forward yield dated ahead names the
1244
+ // declared distribution it annualizes (not in JSON), and one whose dividend stopped says so; read beside the listing notes.
1245
+ const distributions = view === "statements" ? null : loadDividendDistributions(
1246
+ market.dataProvider, listing.request, financials, { declared: ctx.cliOptions.format !== "json" });
1247
+ // A bond fund's SEC yield, yield to worst and duration, beside its distribution yield.
1248
+ const bondFund = view === "statements" ? null : loadReportBondFacts(listing.request.symbol,
1249
+ financials.quote?.tradingExchangeName || listing.request.exchange || financials.quote?.listingExchangeName || financials.quote?.exchangeName,
1250
+ financials, listing.saved);
1251
+ const treasury = rates ? loadTreasuryTenors() : null;
1024
1252
  const identity = listingIdentity(listing, financials.quote);
1025
1253
  const choice = await listingChoiceNotes(listing, market, financials.quote, venues);
1026
1254
  const data: FinancialsCliData = {
@@ -1035,10 +1263,23 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
1035
1263
  // Market cap, the multiples and the yield at the regular session price, as the text gives them; the quote stays as sent.
1036
1264
  const priced = atRegularSessionPrice(financials);
1037
1265
  const capitalization = selectMarketCapitalization(priced.quote, priced.fundamentals);
1038
- // A forward yield dated ahead names the declared distribution it annualizes, in the text and tables.
1039
- const report = ctx.cliOptions.format === "json" ? data : {
1040
- ...data, declaredDividends: await loadForwardYieldDeclarations(market.dataProvider, listing.request, financials.fundamentals),
1041
- };
1266
+ // The filings behind the latest statements, and for a bank its deposits, uninsured share and CRE.
1267
+ const [statementFilings, bank] = view === "fundamentals"
1268
+ ? await Promise.all([
1269
+ loadStatementFilings(market.statementSources, listing.request, {
1270
+ quarter: latestFinancialPeriod(financials.quarterlyStatements, (row) => row.date),
1271
+ annual: latestFinancialPeriod(financials.annualStatements, (row) => row.date),
1272
+ }),
1273
+ loadBankFigures(market.statementSources, listing.request, financials),
1274
+ ])
1275
+ : [null, { bank: null, note: null }];
1276
+ const bond = await bondFund;
1277
+ // On the SEC yield when the sponsor publishes one, else the distribution yield the line above shows.
1278
+ const taxEquivalent = rates ? analyzeTaxEquivalent({
1279
+ symbol: identity.symbol || listing.key, bond, rates, treasury: await treasury,
1280
+ distribution: distributionBasis(priced.fundamentals?.dividendYield, priced.fundamentals?.dividendYieldBasis === "trailing" ? "trailing" : "forward"),
1281
+ }) : null;
1282
+ const report = { ...data, distributions: await distributions, statementFilings, bank: bank.bank, bankNote: bank.note, bondFund: bond, taxEquivalent };
1042
1283
  ctx.printResult({
1043
1284
  // JSON reads a multiple over a loss as null with its reason, never as a number.
1044
1285
  data: {
@@ -1048,7 +1289,11 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
1048
1289
  marketCap: capitalization?.value ?? null,
1049
1290
  marketCapCurrency: capitalization?.currency ?? null,
1050
1291
  } : {},
1051
- fundamentals: exportedFundamentals(priced.fundamentals),
1292
+ fundamentals: priced.fundamentals && exportedDividendFundamentals(exportedFundamentals(priced.fundamentals)!, report.distributions),
1293
+ // A bond fund's sponsor figures; fundamentals.dividendYield is then its distribution yield, not its SEC yield.
1294
+ ...bond ? { bondFund: bond } : {},
1295
+ ...view === "fundamentals" ? { statementFilings, bank: bank.bank, ...bank.note ? { bankNote: bank.note } : {} } : {},
1296
+ ...taxEquivalent ? { taxEquivalent: taxEquivalentJson(taxEquivalent) } : {},
1052
1297
  },
1053
1298
  freshness,
1054
1299
  ...(choice.length > 0 ? { warnings: choice } : {}),
@@ -1170,36 +1415,56 @@ async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
1170
1415
  : undefined;
1171
1416
  if (word && reason) {
1172
1417
  // The lookup that found no ticker already answered; a search that fails leaves that answer.
1173
- const found = await searchNewsStories(word, limit).catch(() => []);
1418
+ const found = await searchNewsStories(word, limit).catch(logFailure("cli.commands-market", "Search news stories failed", []));
1174
1419
  await printNewsSearch(word, found, { reason, empty: emptyMessage(true) }, ctx);
1175
1420
  return;
1176
1421
  }
1177
1422
  const empty = emptyMessage(false);
1178
1423
  const read = listing ? listingReadNotes(listing) : [];
1424
+ // A word that is also the company's name ("gold" for Gold.com Inc.) says once which ticker it found and how
1425
+ // to search the stories that mention it instead, when those stories are mostly about something else.
1426
+ const typed = args[0]?.trim() ?? "";
1427
+ const wordHint = listing && resolved && !listing.exchange && !feed && !exchangeOption && commonWordCandidate(typed, identity?.name)
1428
+ && wordMeansMore(await searchNewsStories(typed, 20).catch(logFailure("cli.commands-market", "Search news stories for a ticker word failed", [])), listing.symbol)
1429
+ ? `${typed} → ${identity?.name ?? listing.symbol} (ticker ${listing.symbol}). For stories that mention ${typed.toLowerCase()}, try: ${quotedSearchCommand(typed.toLowerCase())}`
1430
+ : null;
1179
1431
  ctx.printResult({
1180
1432
  data: articles,
1181
1433
  ...(read.length > 0 ? { warnings: read } : {}),
1182
- metadata: { ticker: listing?.key ?? null, ...(identity ? listingMetadata(identity) : {}), feed: feed ?? null },
1434
+ metadata: { ticker: listing?.key ?? null, ...(identity ? listingMetadata(identity) : {}), feed: feed ?? null, ...(wordHint ? { textSearch: quotedSearchCommand(typed.toLowerCase()) } : {}) },
1183
1435
  freshness: rowsFreshness(newsRows(articles), cloudNewsFreshness(await cloudRealtimeAccess())),
1184
1436
  }, {
1185
1437
  ...(identity ? { heading: listingHeading(identity) } : {}),
1186
1438
  rows: newsRows,
1187
1439
  columns: NEWS_COLUMNS,
1188
1440
  ...(empty ? { empty } : {}),
1441
+ ...(wordHint ? { footnote: wordHint } : {}),
1189
1442
  });
1190
1443
  });
1191
1444
  }
1192
1445
 
1446
+ // Exports keep the columns they had and add the items after them: codes, then their names.
1193
1447
  const FILING_COLUMNS = [
1194
1448
  { key: "filingDate", header: "Date" },
1195
1449
  { key: "form", header: "Form" },
1196
1450
  { key: "companyName", header: "Company", maxWidth: 24 },
1197
1451
  { key: "url", header: "URL", optional: true },
1452
+ { key: "items", header: "Items" },
1453
+ { key: "itemsLabel", header: "Item names" },
1454
+ ];
1455
+
1456
+ // The text table names the items before the URL, which a narrow terminal drops first.
1457
+ const FILING_TEXT_COLUMNS = [
1458
+ { key: "filingDate", header: "Date" },
1459
+ { key: "form", header: "Form" },
1460
+ { key: "companyName", header: "Company", maxWidth: 24 },
1461
+ { key: "itemsLabel", header: "Items" },
1462
+ { key: "url", header: "URL", optional: true },
1198
1463
  ];
1199
1464
 
1200
1465
  /** The US listing the SEC files a symbol under, from the symbol's venues: NYSE for SAN's Banco Santander. */
1201
1466
  async function registrantUsExchange(market: MarketContext, symbol: string, registrantName: string): Promise<string | null> {
1202
- const venues = (await listingVenues(symbol, market).catch(() => []))
1467
+ const venues = (await listingVenues(symbol, market).catch(logFailure("cli.commands-market", "Read listing venues for filings failed", [])))
1203
1468
  .filter((venue) => !nonUsSecListingVenue(symbol, venue.exchange) && !areDifferentCompanies(venue.name, registrantName));
1204
1469
  return (venues.find((venue) => isUsListingExchange(venue.exchange)) ?? venues[0])?.exchange ?? null;
1205
1470
  }
@@ -1232,7 +1497,7 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1232
1497
  const resolved = identity ?? listingIdentity(listing, quote);
1233
1498
  const choice = await listingChoiceNotes(listing, market, quote, venues);
1234
1499
  ctx.printResult({
1235
- data: filings,
1500
+ data: filingsWithItemLabels(filings),
1236
1501
  metadata: { ...listingMetadata(resolved), ...(form ? { form } : {}) },
1237
1502
  freshness: rowsFreshness(filingRows(filings), { ...SEC_FILINGS, observedKey: "filingDate" }),
1238
1503
  ...(choice.length > 0 ? { warnings: choice } : {}),
@@ -1240,6 +1505,7 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1240
1505
  heading: listingHeading(resolved),
1241
1506
  rows: filingRows,
1242
1507
  columns: FILING_COLUMNS,
1508
+ textColumns: FILING_TEXT_COLUMNS,
1243
1509
  // A listing abroad is looked up by its company: none found means the company files nothing with the SEC.
1244
1510
  empty: form ? `No ${form} filings found for ${listingTitle(resolved)}.`
1245
1511
  : identity ? noSecReportsNotice(resolved)
@@ -1256,7 +1522,7 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1256
1522
  data: [] as SecFilingItem[],
1257
1523
  metadata: { ...listingMetadata(resolved), secRegistrant: error.registrantName },
1258
1524
  warnings: ctx.cliOptions.format === "text" ? undefined : [message],
1259
- }, { rows: filingRows, columns: FILING_COLUMNS, empty: message });
1525
+ }, { rows: filingRows, columns: FILING_COLUMNS, textColumns: FILING_TEXT_COLUMNS, empty: message });
1260
1526
  }
1261
1527
  });
1262
1528
  }
@@ -1277,7 +1543,8 @@ async function loadForm4Insiders(market: MarketContext, listing: CliListing): Pr
1277
1543
  if (parsed.length > 0) return "available";
1278
1544
  if (noFilings && nonUsSecListingVenue(listing.request.symbol, listing.request.exchange)) return "no-sec-reports";
1279
1545
  return foreignPrivateIssuer ? "foreign-private-issuer" : "none";
1280
- } catch {
1546
+ } catch (error) {
1547
+ logFailure("cli.commands-market", "Read insider filing availability failed")(error);
1281
1548
  return null;
1282
1549
  }
1283
1550
  }
@@ -1323,10 +1590,10 @@ async function printBeneficialOwners(
1323
1590
  const request = { form: beneficialRouteForm(form), history, listing: { exchange, name: abroad?.name ?? undefined } };
1324
1591
  const [payload, holders, quote, financials] = await Promise.all([
1325
1592
  loadForListing(listing, market, ctx, () => fetchBeneficialOwners(symbol, request)),
1326
- market.dataProvider.getHolders?.(symbol, exchange).catch(() => null) ?? null,
1593
+ market.dataProvider.getHolders?.(symbol, exchange).catch(logFailure("cli.commands-market", "Read company holders failed", null)) ?? null,
1327
1594
  quotePromise,
1328
1595
  // Today's share count, which the filed percents are set against.
1329
- market.dataProvider.getTickerFinancials(symbol, exchange).catch(() => null),
1596
+ market.dataProvider.getTickerFinancials(symbol, exchange).catch(logFailure("cli.commands-market", "Read company financials for holders failed", null)),
1330
1597
  ]);
1331
1598
  const identity = listingIdentity(listing, quote);
1332
1599
  const sharesOutstanding = classSharesToday(financials);
@@ -1557,9 +1824,9 @@ async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execu
1557
1824
  ctx.printResult({
1558
1825
  data,
1559
1826
  metadata: listingMetadata(identity),
1560
- freshness: rowsFreshness(corporateActionRows(data), {
1561
- ...REPORTED_DATA, basis: "corporate actions", observedKey: "date", oldest: null,
1562
- }),
1827
+ // Dated by when it was read, as EE and fn EVT date the same events: a row's
1828
+ // date is when the event happens, and the newest past one is no as-of.
1829
+ freshness: rowsFreshness([], { ...REPORTED_DATA, basis: "corporate actions", oldest: null }),
1563
1830
  ...(choice.length > 0 ? { warnings: choice } : {}),
1564
1831
  }, {
1565
1832
  heading: listingHeading(identity),
@@ -1583,9 +1850,9 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1583
1850
  const expirationGiven = expiration != null || rawArgs.some((arg) => arg === "--expiration" || arg.startsWith("--expiration="));
1584
1851
  const criteria = parseLeapsCriteria({ ...leapsOptions.raw, ...expirationGiven ? { "--expiration": expiration ?? "" } : {} });
1585
1852
  if ("error" in criteria) ctx.fail(criteria.error, `Usage: gloomberb ${LEAPS_USAGE}`);
1586
- const symbols = normalizeSymbols(args);
1587
- if (symbols.length === 0) ctx.fail("--leaps needs at least one symbol.", `Usage: gloomberb ${LEAPS_USAGE}`);
1588
- await withMarketData(ctx, (market) => runLeapsScreen(symbols, exchangeOption, criteria, market, ctx));
1853
+ const entries = normalizeSymbols(args);
1854
+ if (entries.length === 0) ctx.fail("--leaps needs at least one symbol.", `Usage: gloomberb ${LEAPS_USAGE}`);
1855
+ await withMarketData(ctx, async (market) => runLeapsScreen(await readSymbols(entries, market.dataProvider), exchangeOption, criteria, market, ctx));
1589
1856
  return;
1590
1857
  }
1591
1858
  if (leapsOptions.given.length > 0) {
@@ -1598,7 +1865,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1598
1865
  if (parsed == null) {
1599
1866
  ctx.fail(
1600
1867
  expiration == null ? "--expiration needs a date." : `Invalid --expiration "${expiration}".`,
1601
- `Use YYYY-MM-DD (2028-01-21) or Unix seconds (1832025600).\nUsage: gloomberb ${OPTIONS_USAGE}`,
1868
+ `Use ${OPTION_EXPIRATION_FORMAT}.\nUsage: gloomberb ${OPTIONS_USAGE}`,
1602
1869
  );
1603
1870
  }
1604
1871
  expirationDate = parsed;
@@ -1634,7 +1901,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
1634
1901
  // An unlisted date comes back as an empty chain; its own list of expiries, else the default chain's, says what to pick.
1635
1902
  const listed = loaded.expirationDates.length > 0 ? loaded.expirationDates : await market.dataProvider
1636
1903
  .getOptionsChain(symbol, exchange, undefined, { cacheMode: "default" })
1637
- .then((fallback) => fallback.expirationDates, () => []);
1904
+ .then((fallback) => fallback.expirationDates, logFailure("cli-market", "Read available option expirations failed", []));
1638
1905
  const miss = missingExpiryMessage(listing.key, expirationDate, listed);
1639
1906
  ctx.fail(miss.message, miss.details);
1640
1907
  }
@@ -1810,9 +2077,10 @@ async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]
1810
2077
  async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
1811
2078
  const args = [...rawArgs];
1812
2079
  const exchangeOption = takeOption(args, "--exchange");
1813
- const symbols = normalizeSymbols(args);
1814
- if (symbols.length === 0) ctx.fail("Usage: gloomberb earnings <symbol...>");
2080
+ const entries = normalizeSymbols(args);
2081
+ if (entries.length === 0) ctx.fail("Usage: gloomberb earnings <symbol...>");
1815
2082
  await withCliServices(ctx, async (services) => {
2083
+ const symbols = await readSymbols(entries, services.dataProvider);
1816
2084
  const listings = await Promise.all(symbols.map((symbol) => requireCliListing(
1817
2085
  symbol, exchangeOption, services, ctx, { ownExchangeWins: symbols.length > 1 },
1818
2086
  )));
@@ -1875,9 +2143,9 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
1875
2143
  description: "Show the latest price for one or more symbols",
1876
2144
  help: {
1877
2145
  group: CLI_COMMAND_GROUPS.research,
1878
- usage: ["quote <symbol...>"],
1879
- options: [EXCHANGE_OPTION],
1880
- examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote binance:BZUSDT", "quote AAPL --json"],
2146
+ usage: ["quote <symbol...>", "quote --file <path>"],
2147
+ options: [EXCHANGE_OPTION, LIST_FILE_OPTION],
2148
+ examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote binance:BZUSDT", "quote AAPL --json", "quote --file holdings.csv"],
1881
2149
  },
1882
2150
  execute: (args, ctx) => runQuote(args, ctx, "quote"),
1883
2151
  },
@@ -1886,9 +2154,9 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
1886
2154
  description: "Compare quotes for several symbols side by side",
1887
2155
  help: {
1888
2156
  group: CLI_COMMAND_GROUPS.research,
1889
- usage: ["compare <symbol...>"],
1890
- options: [EXCHANGE_OPTION],
1891
- examples: ["compare KO PEP", "compare SAN:EPA SAN:NYSE", "compare SPY QQQ IWM --csv"],
2157
+ usage: ["compare <symbol...>", "compare --file <path>"],
2158
+ options: [EXCHANGE_OPTION, LIST_FILE_OPTION],
2159
+ examples: ["compare KO PEP", "compare SAN:EPA SAN:NYSE", "compare SPY QQQ IWM --csv", "compare - < peers.txt"],
1892
2160
  },
1893
2161
  execute: (args, ctx) => runQuote(args, ctx, "compare"),
1894
2162
  },
@@ -1929,7 +2197,7 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
1929
2197
  usage: [OPTIONS_USAGE, LEAPS_USAGE],
1930
2198
  options: [
1931
2199
  {
1932
- flags: "--expiration <YYYY-MM-DD|unix>",
2200
+ flags: `--expiration <${OPTION_EXPIRATION_PLACEHOLDER}>`,
1933
2201
  description: "Expiration as a date (2028-01-21) or Unix seconds; defaults to the nearest one, and lists the others. With --leaps: rank that expiry alone",
1934
2202
  },
1935
2203
  EXCHANGE_OPTION,
@@ -1949,8 +2217,8 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
1949
2217
  description: `With --leaps: fewest calendar days to expiry (default ${DEFAULT_LEAPS_CRITERIA.minDays}); 180 reaches six months out`,
1950
2218
  },
1951
2219
  { flags: "--max-days <days>", description: "With --leaps: most calendar days to expiry; --min-days 365 --max-days 730 is 12 to 24 months" },
1952
- { flags: "--expiry-from <YYYY-MM-DD>", description: "With --leaps: first expiry date kept; replaces the one-year floor" },
1953
- { flags: "--expiry-to <YYYY-MM-DD>", description: "With --leaps: last expiry date kept" },
2220
+ { flags: "--expiry-from <date>", description: `With --leaps: first expiry date kept, as ${DATE_OPTION_EXAMPLES}; replaces the one-year floor` },
2221
+ { flags: "--expiry-to <date>", description: "With --leaps: last expiry date kept" },
1954
2222
  {
1955
2223
  flags: "--columns <list>",
1956
2224
  description: `With --leaps: add figures, comma-separated: ${LEAPS_EXTRA_COLUMNS.join(", ")}`,
@@ -2018,6 +2286,7 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
2018
2286
  options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
2019
2287
  examples: ["financials MSFT", "financials SAN:EPA", "financials MSFT --json", "financials MSFT --csv > msft.csv"],
2020
2288
  },
2289
+ unknownOptionHint: financialsOptionHint,
2021
2290
  execute: (args, ctx) => runFinancials(args, ctx, "statements"),
2022
2291
  },
2023
2292
  {
@@ -2026,8 +2295,9 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
2026
2295
  help: {
2027
2296
  group: CLI_COMMAND_GROUPS.companyData,
2028
2297
  usage: ["fundamentals <symbol>"],
2029
- options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
2030
- examples: ["fundamentals NVDA", "fundamentals ASML:AMS", "fundamentals NVDA --csv --section fundamentals"],
2298
+ options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION, ...TAX_RATE_HELP_OPTIONS],
2299
+ examples: ["fundamentals NVDA", "fundamentals ASML:AMS", "fundamentals NVDA --csv --section fundamentals", "fundamentals MUB --tax-rate 40.8",
2300
+ "fundamentals CMF --tax-rate 40.8 --state-rate 13.3 --state CA"],
2031
2301
  },
2032
2302
  execute: (args, ctx) => runFinancials(args, ctx, "fundamentals"),
2033
2303
  },
@@ -2151,9 +2421,10 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
2151
2421
  title: "Text",
2152
2422
  lines: [
2153
2423
  "Several words, in quotes or not, search the stories that mention them. So does a single word of three letters or more that no listing answers to, and a futures board code such as TTF, a gas benchmark rather than a ticker. A real ticker always gives its own news. The best matches print newest first, 20 unless --limit says otherwise; a search takes neither --feed nor --exchange.",
2424
+ "A shell drops the quotes around one word, so news \"gold\" is the ticker GOLD. Keep them to search the text: news '\"gold\"'. When a word you type is also the name of the company it found, and the stories that mention it are mostly about something else, the listing says so under its stories with that command.",
2154
2425
  ],
2155
2426
  }],
2156
- examples: ["news", "news TSLA", "news SAN:EPA", "news TTF", "news \"european gas\"", "news --feed top --limit 10"],
2427
+ examples: ["news", "news TSLA", "news SAN:EPA", "news TTF", "news \"european gas\"", "news '\"gold\"'", "news --feed top --limit 10"],
2157
2428
  },
2158
2429
  execute: runNews,
2159
2430
  },