gloomberb 0.16.4 → 0.16.5

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 (554) hide show
  1. package/README.md +14 -91
  2. package/package.json +4 -1
  3. package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
  4. package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
  5. package/src/account/billing.ts +57 -0
  6. package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
  7. package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
  8. package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
  9. package/src/account/session.ts +16 -0
  10. package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
  11. package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
  12. package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
  13. package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
  14. package/src/api-client/data.ts +24 -2
  15. package/src/api-client/earnings.ts +6 -0
  16. package/src/api-client/futures-curve.ts +56 -0
  17. package/src/api-client/index.ts +4 -1
  18. package/src/api-client/market-discovery.ts +10 -1
  19. package/src/api-client/paths.ts +7 -0
  20. package/src/api-client/research-activity.ts +15 -10
  21. package/src/api-client/types.ts +17 -1
  22. package/src/api-client/yield-curves.ts +8 -0
  23. package/src/app/global-shortcuts.ts +15 -1
  24. package/src/app/pane-runtime/ticker-open-runtime.ts +7 -3
  25. package/src/architecture/core-builtin-allowlist.json +4 -13
  26. package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
  27. package/src/capabilities/chart-series.ts +11 -1
  28. package/src/cli/commands/crypto-hints.ts +7 -0
  29. package/src/cli/commands/fred-series.ts +61 -0
  30. package/src/cli/commands/market.ts +407 -104
  31. package/src/cli/commands/news-hints.ts +62 -0
  32. package/src/cli/commands/news-search.ts +53 -0
  33. package/src/cli/commands/options-leaps.ts +5 -2
  34. package/src/cli/commands/overview.ts +93 -20
  35. package/src/cli/commands/remote.ts +2 -1
  36. package/src/cli/commands/search.ts +82 -9
  37. package/src/cli/commands/system.ts +55 -8
  38. package/src/cli/commands/ticker.ts +88 -11
  39. package/src/cli/context.ts +45 -14
  40. package/src/cli/desktop-pane-shot.ts +59 -7
  41. package/src/cli/earnings-calendar.ts +71 -0
  42. package/src/cli/freshness.ts +18 -7
  43. package/src/cli/glossary.ts +429 -0
  44. package/src/cli/history-rows.ts +48 -7
  45. package/src/cli/index-roots.ts +7 -0
  46. package/src/cli/index.ts +21 -7
  47. package/src/cli/known-gaps.ts +27 -0
  48. package/src/cli/listing-arg.ts +417 -56
  49. package/src/cli/not-a-ticker.ts +12 -5
  50. package/src/cli/pane-functions/access-gate.ts +5 -0
  51. package/src/cli/pane-functions/capabilities.ts +3 -2
  52. package/src/cli/pane-functions/catalog.ts +35 -5
  53. package/src/cli/pane-functions/data.ts +12 -8
  54. package/src/cli/pane-functions/discovery.ts +1 -0
  55. package/src/cli/pane-functions/dom.ts +20 -6
  56. package/src/cli/pane-functions/freshness.ts +363 -43
  57. package/src/cli/pane-functions/function-help.ts +12 -5
  58. package/src/cli/pane-functions/headless.ts +45 -17
  59. package/src/cli/pane-functions/index.ts +63 -10
  60. package/src/cli/pane-functions/market-session.ts +22 -8
  61. package/src/cli/pane-functions/options.ts +63 -22
  62. package/src/cli/pane-functions/report-notices.ts +25 -7
  63. package/src/cli/pane-functions/report.ts +15 -8
  64. package/src/cli/pane-functions/resolver.ts +49 -12
  65. package/src/cli/pane-functions/screenshot.ts +110 -23
  66. package/src/cli/report-tables.ts +3 -0
  67. package/src/cli/result.ts +4 -1
  68. package/src/cli/staged-tickers.ts +52 -0
  69. package/src/cli/symbol-args.ts +9 -0
  70. package/src/cli/symbol-list.ts +172 -0
  71. package/src/cli/ticker-resolution.ts +223 -30
  72. package/src/cli/types.ts +5 -0
  73. package/src/components/chart/composite/axis-overlays.tsx +6 -2
  74. package/src/components/chart/composite/composite-chart.tsx +40 -6
  75. package/src/components/chart/composite/legend.tsx +49 -2
  76. package/src/components/chart/composite/panel-surface.tsx +44 -17
  77. package/src/components/chart/composite/price-axis-labels.tsx +3 -1
  78. package/src/components/command-bar/panel/index.tsx +11 -3
  79. package/src/components/command-bar/panel/runtime.ts +3 -0
  80. package/src/components/command-bar/panel/types.ts +2 -0
  81. package/src/components/command-bar/routes/root/provider-search.ts +4 -3
  82. package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
  83. package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
  84. package/src/components/command-bar/workflow/tickers.ts +23 -4
  85. package/src/components/layout/detached-pane-shell.tsx +9 -0
  86. package/src/components/layout/floating-pane.tsx +3 -0
  87. package/src/components/layout/header.tsx +20 -5
  88. package/src/components/layout/pane/content.tsx +7 -13
  89. package/src/components/layout/pane/footer/index.tsx +6 -2
  90. package/src/components/layout/pane/footer/model.ts +35 -1
  91. package/src/components/layout/pane/header.tsx +24 -1
  92. package/src/components/layout/pane/index.tsx +12 -2
  93. package/src/components/layout/pane/sidebar.tsx +78 -10
  94. package/src/components/layout/shell/active-drag.ts +21 -5
  95. package/src/components/layout/shell/drag/index.ts +39 -9
  96. package/src/components/layout/shell/drag/live.ts +61 -51
  97. package/src/components/layout/shell/drag/runtime.ts +54 -12
  98. package/src/components/layout/shell/index.tsx +25 -7
  99. package/src/components/layout/shell/menu.ts +5 -0
  100. package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
  101. package/src/components/layout/shell/pane/layers.tsx +192 -45
  102. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
  103. package/src/components/markdown-text.tsx +62 -20
  104. package/src/components/metric-treemap/heat-surface.tsx +10 -1
  105. package/src/components/onboarding/account-step/form-panel.tsx +1 -1
  106. package/src/components/onboarding/account-step/index.tsx +1 -1
  107. package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
  108. package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
  109. package/src/components/onboarding/onboarding-keyboard.ts +1 -1
  110. package/src/components/onboarding/onboarding-wizard.tsx +2 -2
  111. package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
  112. package/src/components/onboarding/wizard-account.ts +2 -2
  113. package/src/components/pane-template-wizard.tsx +9 -1
  114. package/src/components/price-selector-dialog.tsx +11 -2
  115. package/src/components/sign-in-gate.tsx +3 -3
  116. package/src/components/ticker/list-table-view.tsx +24 -1
  117. package/src/components/ui/action-row.tsx +2 -1
  118. package/src/components/ui/button.tsx +7 -1
  119. package/src/components/ui/choice-dialog.tsx +6 -4
  120. package/src/components/ui/confirm-dialog.tsx +28 -18
  121. package/src/components/ui/data-table/index.tsx +17 -6
  122. package/src/components/ui/data-table/opentui/index.tsx +37 -6
  123. package/src/components/ui/data-table/types.ts +12 -0
  124. package/src/components/ui/dialog-actions.tsx +64 -0
  125. package/src/components/ui/display.tsx +17 -3
  126. package/src/components/ui/frame.tsx +35 -2
  127. package/src/components/ui/icon.tsx +2 -0
  128. package/src/components/ui/text-prompt-dialog.tsx +11 -9
  129. package/src/data/cached-query.ts +2 -0
  130. package/src/data/config/store/normalize.ts +7 -2
  131. package/src/data/ticker-repository.ts +5 -0
  132. package/src/data/ticker-store.ts +15 -0
  133. package/src/docs/content.ts +215 -0
  134. package/src/docs/load.ts +139 -0
  135. package/src/docs/requests.ts +39 -0
  136. package/src/docs/snapshot.chunk.ts +8 -0
  137. package/src/docs/snapshot.json +7416 -0
  138. package/src/docs/topics.ts +216 -0
  139. package/src/i18n/es.ts +61 -4
  140. package/src/i18n/ja.ts +61 -4
  141. package/src/i18n/ko.ts +61 -4
  142. package/src/i18n/zh-cn.ts +61 -4
  143. package/src/i18n/zh-tw.ts +61 -4
  144. package/src/layout/desks.ts +1 -1
  145. package/src/layout/pane-manager/dock-tree.ts +18 -0
  146. package/src/layout/pane-manager/index.ts +1 -0
  147. package/src/market-data/coordinator/chart.ts +10 -4
  148. package/src/market-data/coordinator/index.ts +6 -3
  149. package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
  150. package/src/market-data/futures-contract.ts +151 -0
  151. package/src/market-data/hooks.tsx +10 -2
  152. package/src/market-data/market/format.ts +39 -21
  153. package/src/market-data/market/freshness.ts +81 -18
  154. package/src/market-data/market/status.ts +19 -23
  155. package/src/market-data/published-apac-sessions.ts +23 -1
  156. package/src/market-data/quotes/contributions.ts +3 -0
  157. package/src/market-data/quotes/metadata.ts +7 -24
  158. package/src/market-data/quotes/resolution.ts +2 -0
  159. package/src/market-data/quotes/screener-live-quotes.ts +5 -4
  160. package/src/market-data/result-types.ts +2 -0
  161. package/src/plugins/builtin/account-management/model.ts +1 -56
  162. package/src/plugins/builtin/account-management/pane.tsx +6 -4
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
  164. package/src/plugins/builtin/alerts/cli.ts +313 -47
  165. package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
  166. package/src/plugins/builtin/alerts/events.ts +23 -2
  167. package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
  168. package/src/plugins/builtin/alerts/history.ts +2 -0
  169. package/src/plugins/builtin/alerts/index.tsx +41 -9
  170. package/src/plugins/builtin/alerts/pane.tsx +8 -0
  171. package/src/plugins/builtin/alerts/research-builder.ts +8 -3
  172. package/src/plugins/builtin/alerts/research-rules.ts +18 -2
  173. package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
  174. package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
  175. package/src/plugins/builtin/alerts/scheduled.ts +254 -0
  176. package/src/plugins/builtin/alerts/targets.ts +92 -0
  177. package/src/plugins/builtin/analytics/index.tsx +4 -2
  178. package/src/plugins/builtin/analytics/risk-headless.ts +30 -12
  179. package/src/plugins/builtin/analytics/risk-math.ts +91 -27
  180. package/src/plugins/builtin/analytics/risk-model.ts +35 -5
  181. package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
  182. package/src/plugins/builtin/attention/index.tsx +2 -0
  183. package/src/plugins/builtin/attention/pane.tsx +3 -1
  184. package/src/plugins/builtin/attention/series.ts +26 -0
  185. package/src/plugins/builtin/awards/index.tsx +2 -0
  186. package/src/plugins/builtin/awards/pane.tsx +3 -1
  187. package/src/plugins/builtin/awards/series.ts +29 -0
  188. package/src/plugins/builtin/bond-calculator/model.ts +2 -1
  189. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  190. package/src/plugins/builtin/cds/client.ts +70 -13
  191. package/src/plugins/builtin/cds/headless.ts +116 -19
  192. package/src/plugins/builtin/cds/index.tsx +3 -1
  193. package/src/plugins/builtin/cds/model.ts +193 -31
  194. package/src/plugins/builtin/cds/pane.tsx +56 -14
  195. package/src/plugins/builtin/cds/series.ts +19 -0
  196. package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
  197. package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
  198. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
  199. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
  200. package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
  201. package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
  202. package/src/plugins/builtin/chart-composer/headless.ts +8 -3
  203. package/src/plugins/builtin/chart-composer/index.tsx +9 -2
  204. package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
  205. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
  206. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
  207. package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
  208. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
  209. package/src/plugins/builtin/chat/controller/index.ts +2 -0
  210. package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
  211. package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
  212. package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
  213. package/src/plugins/builtin/cloud/askg/model.ts +27 -8
  214. package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
  215. package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
  216. package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
  217. package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
  218. package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
  219. package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
  220. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  221. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
  222. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
  223. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
  224. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
  225. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
  226. package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
  227. package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
  228. package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
  229. package/src/plugins/builtin/company-attention/headless.ts +19 -5
  230. package/src/plugins/builtin/company-attention/index.tsx +2 -0
  231. package/src/plugins/builtin/company-attention/model.ts +26 -7
  232. package/src/plugins/builtin/company-attention/pane.tsx +12 -9
  233. package/src/plugins/builtin/company-attention/series.ts +52 -0
  234. package/src/plugins/builtin/company-kpis/pane.tsx +4 -2
  235. package/src/plugins/builtin/composite-plugins.ts +19 -13
  236. package/src/plugins/builtin/cot/index.tsx +3 -1
  237. package/src/plugins/builtin/cot/model.ts +40 -11
  238. package/src/plugins/builtin/cot/series.ts +27 -0
  239. package/src/plugins/builtin/credit-boards/index.tsx +2 -0
  240. package/src/plugins/builtin/credit-boards/series.ts +66 -0
  241. package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
  242. package/src/plugins/builtin/credit-conditions/series.ts +26 -0
  243. package/src/plugins/builtin/credit-documents/headless.ts +31 -1
  244. package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
  245. package/src/plugins/builtin/crypto-board/headless.ts +2 -3
  246. package/src/plugins/builtin/crypto-board/index.tsx +2 -0
  247. package/src/plugins/builtin/crypto-board/model.ts +12 -0
  248. package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
  249. package/src/plugins/builtin/crypto-board/series.ts +48 -0
  250. package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
  251. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  252. package/src/plugins/builtin/debt-maturities/series.ts +45 -0
  253. package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
  254. package/src/plugins/builtin/dividend-yield/index.tsx +2 -0
  255. package/src/plugins/builtin/dividend-yield/series.ts +20 -0
  256. package/src/plugins/builtin/docs/blocks.tsx +181 -0
  257. package/src/plugins/builtin/docs/index.tsx +68 -0
  258. package/src/plugins/builtin/docs/pane.tsx +426 -0
  259. package/src/plugins/builtin/docs/targets.ts +72 -0
  260. package/src/plugins/builtin/docs/use-docs.ts +60 -0
  261. package/src/plugins/builtin/doe/index.tsx +2 -0
  262. package/src/plugins/builtin/doe/series.ts +62 -0
  263. package/src/plugins/builtin/earnings/client.ts +3 -2
  264. package/src/plugins/builtin/earnings/format.ts +1 -5
  265. package/src/plugins/builtin/earnings/headless.ts +58 -6
  266. package/src/plugins/builtin/earnings/history-model.ts +15 -1
  267. package/src/plugins/builtin/earnings/history-table.ts +13 -2
  268. package/src/plugins/builtin/earnings/history.tsx +7 -3
  269. package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
  270. package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
  271. package/src/plugins/builtin/econ/index.tsx +2 -0
  272. package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
  273. package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
  274. package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
  275. package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
  276. package/src/plugins/builtin/exposure/holdings.ts +1 -1
  277. package/src/plugins/builtin/exposure/pane.tsx +3 -1
  278. package/src/plugins/builtin/fear-greed/index.tsx +4 -0
  279. package/src/plugins/builtin/fear-greed/series.ts +52 -0
  280. package/src/plugins/builtin/futures/index.tsx +43 -3
  281. package/src/plugins/builtin/futures/model.ts +1 -1
  282. package/src/plugins/builtin/futures/returns.ts +1 -1
  283. package/src/plugins/builtin/futures/series.ts +13 -0
  284. package/src/plugins/builtin/futures/table.tsx +1 -1
  285. package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
  286. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  287. package/src/plugins/builtin/futures-curve/headless.ts +70 -9
  288. package/src/plugins/builtin/futures-curve/index.tsx +1 -1
  289. package/src/plugins/builtin/futures-curve/model.ts +66 -11
  290. package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
  291. package/src/plugins/builtin/fx-matrix/index.tsx +1 -1
  292. package/src/plugins/builtin/gpu/board.tsx +13 -10
  293. package/src/plugins/builtin/gpu/changes.tsx +4 -4
  294. package/src/plugins/builtin/gpu/client.ts +19 -1
  295. package/src/plugins/builtin/gpu/equities.tsx +14 -10
  296. package/src/plugins/builtin/gpu/headless.ts +56 -25
  297. package/src/plugins/builtin/gpu/history.tsx +3 -3
  298. package/src/plugins/builtin/gpu/index.tsx +2 -0
  299. package/src/plugins/builtin/gpu/model.ts +101 -16
  300. package/src/plugins/builtin/gpu/pane.tsx +28 -12
  301. package/src/plugins/builtin/gpu/series.ts +38 -0
  302. package/src/plugins/builtin/help/function-card.tsx +25 -6
  303. package/src/plugins/builtin/help/index.tsx +17 -8
  304. package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
  305. package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
  306. package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
  307. package/src/plugins/builtin/holders/client.ts +22 -3
  308. package/src/plugins/builtin/holders/format.ts +18 -3
  309. package/src/plugins/builtin/holders/headless.ts +36 -133
  310. package/src/plugins/builtin/holders/pane.tsx +11 -4
  311. package/src/plugins/builtin/holders/report-header.ts +40 -5
  312. package/src/plugins/builtin/holders/report-rows.ts +240 -0
  313. package/src/plugins/builtin/insider/client.ts +35 -20
  314. package/src/plugins/builtin/insider/headless.ts +41 -11
  315. package/src/plugins/builtin/insider/index.tsx +26 -9
  316. package/src/plugins/builtin/insider/insider-data.ts +10 -0
  317. package/src/plugins/builtin/insider/issuer.ts +49 -0
  318. package/src/plugins/builtin/insider/model.ts +3 -0
  319. package/src/plugins/builtin/iv-history/client.ts +14 -4
  320. package/src/plugins/builtin/iv-history/headless.ts +7 -4
  321. package/src/plugins/builtin/iv-history/index.tsx +2 -0
  322. package/src/plugins/builtin/iv-history/model.ts +111 -7
  323. package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
  324. package/src/plugins/builtin/iv-history/series.ts +29 -0
  325. package/src/plugins/builtin/jobs/headless.ts +28 -3
  326. package/src/plugins/builtin/jobs/index.tsx +3 -1
  327. package/src/plugins/builtin/jobs/series.ts +19 -0
  328. package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
  329. package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
  330. package/src/plugins/builtin/macro-day/client.ts +44 -3
  331. package/src/plugins/builtin/macro-day/headless.ts +13 -10
  332. package/src/plugins/builtin/macro-day/model.ts +56 -10
  333. package/src/plugins/builtin/macro-day/pane.tsx +30 -20
  334. package/src/plugins/builtin/market-heatmap/model.ts +19 -11
  335. package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
  336. package/src/plugins/builtin/market-movers/client.ts +7 -5
  337. package/src/plugins/builtin/market-movers/index.tsx +3 -2
  338. package/src/plugins/builtin/market-movers/model.ts +30 -0
  339. package/src/plugins/builtin/market-movers/screener.ts +31 -0
  340. package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
  341. package/src/plugins/builtin/market-valuation/index.tsx +3 -3
  342. package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
  343. package/src/plugins/builtin/members/headless.ts +7 -2
  344. package/src/plugins/builtin/members/model.ts +31 -3
  345. package/src/plugins/builtin/members/pane.tsx +3 -6
  346. package/src/plugins/builtin/mna/headless.ts +15 -3
  347. package/src/plugins/builtin/money-markets/index.tsx +2 -0
  348. package/src/plugins/builtin/money-markets/series.ts +41 -0
  349. package/src/plugins/builtin/notes/cli.ts +12 -7
  350. package/src/plugins/builtin/options/view.tsx +1 -1
  351. package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
  352. package/src/plugins/builtin/options-positioning/client.ts +7 -3
  353. package/src/plugins/builtin/options-positioning/headless.ts +1 -0
  354. package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
  355. package/src/plugins/builtin/options-scenario/client.ts +2 -1
  356. package/src/plugins/builtin/options-scenario/headless.ts +1 -0
  357. package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
  358. package/src/plugins/builtin/perps/client.ts +40 -6
  359. package/src/plugins/builtin/perps/equity-row.tsx +33 -11
  360. package/src/plugins/builtin/perps/freshness.ts +66 -0
  361. package/src/plugins/builtin/perps/headless.ts +45 -30
  362. package/src/plugins/builtin/perps/index.tsx +18 -1
  363. package/src/plugins/builtin/perps/model.ts +20 -5
  364. package/src/plugins/builtin/perps/pane.tsx +76 -20
  365. package/src/plugins/builtin/perps/series.ts +76 -0
  366. package/src/plugins/builtin/plugin-marketplace/pane.tsx +67 -61
  367. package/src/plugins/builtin/plugin-module.ts +3 -0
  368. package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
  369. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
  370. package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
  371. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +331 -65
  372. package/src/plugins/builtin/portfolio-list/cli/render.ts +200 -31
  373. package/src/plugins/builtin/portfolio-list/cli/shared.ts +24 -0
  374. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
  375. package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
  376. package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
  377. package/src/plugins/builtin/portfolio-list/headless.ts +34 -4
  378. package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
  379. package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
  380. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
  381. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
  382. package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
  383. package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
  384. package/src/plugins/builtin/power/client.ts +16 -0
  385. package/src/plugins/builtin/power/evidence.ts +6 -2
  386. package/src/plugins/builtin/power/headless.ts +71 -7
  387. package/src/plugins/builtin/power/index.tsx +4 -1
  388. package/src/plugins/builtin/power/model.ts +72 -1
  389. package/src/plugins/builtin/power/pane.tsx +32 -9
  390. package/src/plugins/builtin/rate-path/headless.ts +11 -3
  391. package/src/plugins/builtin/realized-vol/headless.ts +5 -0
  392. package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
  393. package/src/plugins/builtin/research/event-model.ts +68 -24
  394. package/src/plugins/builtin/research/events-headless.ts +2 -1
  395. package/src/plugins/builtin/research/headless.ts +2 -1
  396. package/src/plugins/builtin/seasonality/client.ts +15 -7
  397. package/src/plugins/builtin/seasonality/headless.ts +36 -12
  398. package/src/plugins/builtin/seasonality/index.tsx +2 -2
  399. package/src/plugins/builtin/seasonality/model.ts +119 -0
  400. package/src/plugins/builtin/seasonality/pane.tsx +108 -37
  401. package/src/plugins/builtin/series-inventory.ts +91 -0
  402. package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
  403. package/src/plugins/builtin/shared/command-tab.ts +30 -12
  404. package/src/plugins/builtin/shared/earnings-report.ts +55 -0
  405. package/src/plugins/builtin/shared/fred-points.ts +20 -0
  406. package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
  407. package/src/plugins/builtin/shared/open-description.ts +29 -0
  408. package/src/plugins/builtin/shared/report-freshness.ts +9 -0
  409. package/src/plugins/builtin/shared/series-warning.ts +5 -0
  410. package/src/plugins/builtin/short-interest/headless.ts +1 -0
  411. package/src/plugins/builtin/short-interest/index.tsx +2 -0
  412. package/src/plugins/builtin/short-interest/series.ts +36 -0
  413. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  414. package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
  415. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  416. package/src/plugins/builtin/short-volume/index.tsx +2 -0
  417. package/src/plugins/builtin/short-volume/series.ts +23 -0
  418. package/src/plugins/builtin/social-mentions/index.tsx +2 -0
  419. package/src/plugins/builtin/social-mentions/series.ts +23 -0
  420. package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
  421. package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
  422. package/src/plugins/builtin/thirteenf/api.ts +1 -0
  423. package/src/plugins/builtin/thirteenf/data.ts +6 -4
  424. package/src/plugins/builtin/thirteenf/headless.ts +217 -36
  425. package/src/plugins/builtin/thirteenf/model.ts +39 -1
  426. package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
  427. package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
  428. package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
  429. package/src/plugins/builtin/thirteenf/types.ts +2 -0
  430. package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
  431. package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
  432. package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
  433. package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
  434. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
  435. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
  436. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
  437. package/src/plugins/builtin/time-sales/headless.ts +1 -0
  438. package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
  439. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  440. package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
  441. package/src/plugins/builtin/vol-surface/headless.ts +1 -0
  442. package/src/plugins/builtin/volatility/index.tsx +2 -0
  443. package/src/plugins/builtin/volatility/series.ts +84 -0
  444. package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
  445. package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
  446. package/src/plugins/builtin/world-venue-map/index.ts +2 -1
  447. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
  448. package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
  449. package/src/plugins/builtin/yield-curve/chart.ts +2 -2
  450. package/src/plugins/builtin/yield-curve/curves.ts +21 -8
  451. package/src/plugins/builtin/yield-curve/headless.ts +71 -18
  452. package/src/plugins/builtin/yield-curve/index.tsx +3 -1
  453. package/src/plugins/builtin/yield-curve/series.ts +86 -0
  454. package/src/plugins/catalog-browser.ts +2 -1
  455. package/src/plugins/registry/index.ts +89 -4
  456. package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
  457. package/src/portfolio/position-value.ts +40 -0
  458. package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
  459. package/src/public/time-series.ts +17 -0
  460. package/src/react/auto-refresh.ts +13 -3
  461. package/src/remote/app-host.tsx +20 -4
  462. package/src/remote/confirm-change.tsx +85 -0
  463. package/src/remote/controller-utils.ts +8 -1
  464. package/src/remote/controller.ts +123 -6
  465. package/src/remote/pane-screenshot.ts +47 -0
  466. package/src/remote/schema.ts +62 -0
  467. package/src/remote/types.ts +30 -0
  468. package/src/remote/watchlist.ts +306 -0
  469. package/src/renderers/browser/main.tsx +3 -2
  470. package/src/renderers/cloudflare/worker.ts +1 -1
  471. package/src/renderers/dom/data-table/frozen-column.ts +3 -1
  472. package/src/renderers/dom/data-table/index.tsx +33 -6
  473. package/src/renderers/dom/data-table/row.tsx +27 -1
  474. package/src/renderers/dom/desktop/control-styles.ts +63 -1
  475. package/src/renderers/dom/desktop/controls.tsx +30 -23
  476. package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
  477. package/src/renderers/dom/desktop/icons.tsx +7 -0
  478. package/src/renderers/dom/dom-ui-host.tsx +3 -0
  479. package/src/renderers/dom/host/box.tsx +3 -0
  480. package/src/renderers/dom/host/chart-surface.tsx +6 -2
  481. package/src/renderers/dom/host/live-frame.ts +6 -2
  482. package/src/renderers/dom/host/overflow-fade.ts +14 -1
  483. package/src/renderers/dom/host/size-observer.ts +26 -0
  484. package/src/renderers/dom/host/surface-3d.tsx +7 -2
  485. package/src/renderers/dom/host/text.tsx +1 -1
  486. package/src/renderers/dom/styles.css +61 -0
  487. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +17 -2
  488. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +19 -11
  489. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
  490. package/src/renderers/electrobun/view/main.tsx +2 -0
  491. package/src/series/capability.ts +75 -0
  492. package/src/series/csv.ts +160 -0
  493. package/src/series/files.ts +107 -0
  494. package/src/series/registry.ts +198 -0
  495. package/src/series/resolve.ts +107 -0
  496. package/src/series/standalone.ts +48 -0
  497. package/src/series/types.ts +145 -0
  498. package/src/series/validate.ts +171 -0
  499. package/src/sources/gloomberb-cloud/index.ts +7 -7
  500. package/src/sources/gloomberb-cloud/normalizers.ts +1 -0
  501. package/src/sources/history-result.ts +1 -0
  502. package/src/sources/listing-symbols.ts +4 -1
  503. package/src/sources/provider-router/batches.ts +4 -1
  504. package/src/sources/provider-router/cache.ts +4 -2
  505. package/src/sources/provider-router/cached-routes.ts +1 -1
  506. package/src/sources/provider-router/financial-routes.ts +1 -0
  507. package/src/sources/provider-router/financials.ts +13 -0
  508. package/src/sources/provider-router/history.ts +43 -25
  509. package/src/sources/provider-router/search.ts +1 -0
  510. package/src/sync/core-contributors.ts +1 -0
  511. package/src/tickers/company-name.ts +75 -0
  512. package/src/tickers/instrument-kind.ts +25 -0
  513. package/src/tickers/open-target.ts +3 -2
  514. package/src/tickers/search/index.ts +45 -7
  515. package/src/tickers/search/ranking.ts +33 -17
  516. package/src/tickers/search/types.ts +7 -1
  517. package/src/tickers/us-listing.ts +34 -0
  518. package/src/time-series/hooks.ts +9 -0
  519. package/src/time-series/resolve.ts +95 -16
  520. package/src/time-series/types.ts +4 -2
  521. package/src/time-series/use-chart-resolution.ts +32 -0
  522. package/src/types/config.ts +6 -0
  523. package/src/types/data-provider.ts +5 -0
  524. package/src/types/financials.ts +52 -1
  525. package/src/types/headless.ts +61 -0
  526. package/src/types/plugin.ts +19 -0
  527. package/src/types/price-history.ts +7 -0
  528. package/src/types/ticker.ts +14 -0
  529. package/src/ui/host.tsx +18 -0
  530. package/src/ui/live-resize.ts +81 -0
  531. package/src/ui/live-stretch.ts +121 -0
  532. package/src/ui/measured-cells.ts +58 -0
  533. package/src/ui/pane-fill.ts +11 -0
  534. package/src/utils/basis-points.ts +17 -0
  535. package/src/utils/cli-output.ts +7 -1
  536. package/src/utils/concurrency.ts +25 -0
  537. package/src/utils/crypto-pair.ts +20 -0
  538. package/src/utils/currency-pair.ts +19 -0
  539. package/src/utils/decimal-comma.ts +19 -0
  540. package/src/utils/dom-screenshot-css.ts +265 -0
  541. package/src/utils/dom-screenshot-rasters.ts +276 -0
  542. package/src/utils/dom-screenshot.ts +437 -52
  543. package/src/utils/exchange-rate-snapshot.ts +6 -2
  544. package/src/utils/exchanges.ts +107 -1
  545. package/src/utils/format.ts +16 -3
  546. package/src/utils/fundamentals.ts +66 -0
  547. package/src/utils/futures-generic.ts +6 -12
  548. package/src/utils/fx-market-hours.ts +15 -5
  549. package/src/utils/price-history.ts +99 -42
  550. package/src/utils/sec-eps-basis.ts +24 -11
  551. package/src/utils/sec.ts +61 -7
  552. package/src/utils/utc-time.ts +51 -4
  553. package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
  554. /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
@@ -0,0 +1,62 @@
1
+ import { futuresBoardContract, type FuturesContract, type FuturesSector } from "../../market-data/futures-board";
2
+ import { isFuturesInstrument } from "../../market-data/futures-contract";
3
+ import type { Quote } from "../../types/financials";
4
+
5
+ /**
6
+ * A company whose news follows a futures market, for the hint `news` gives a
7
+ * futures code: news is filed under company tickers, and funds such as UNG
8
+ * rarely carry stories of their own. Index, rates and currency futures have
9
+ * none; market headlines fit them better.
10
+ */
11
+ const CONTRACT_COMPANIES: Readonly<Record<string, string>> = {
12
+ NG: "EQT", TTF: "LNG", CL: "XOM", BZ: "XOM", RB: "XOM", HO: "XOM",
13
+ GC: "NEM", SI: "NEM", PL: "NEM", PA: "NEM", HG: "FCX", ALI: "AA",
14
+ };
15
+ const SECTOR_COMPANIES: Partial<Record<FuturesSector, string>> = {
16
+ energy: "XOM",
17
+ metals: "NEM",
18
+ agriculture: "DE",
19
+ livestock: "TSN",
20
+ };
21
+
22
+ /** "try news LNG", or market headlines where no company follows the contract. */
23
+ function contractSuggestion(contract: FuturesContract): string {
24
+ const company = CONTRACT_COMPANIES[contract.code] ?? SECTOR_COMPANIES[contract.sector];
25
+ return company ? `try news ${company}` : "for market headlines run news with no symbol";
26
+ }
27
+
28
+ /**
29
+ * What `news <symbol>` says when it finds no stories: news is filed under
30
+ * company tickers, so a futures board code (TTF), a futures contract (CL=F)
31
+ * or a symbol no listing answers to gets told so, with something to try.
32
+ * `searched` says a text search for the word found no story either.
33
+ */
34
+ export function newsEmptyMessage(
35
+ symbol: string,
36
+ title: string,
37
+ quote: Pick<Quote, "instrumentType"> | null,
38
+ resolved: boolean,
39
+ searched = false,
40
+ ): string {
41
+ const contract = futuresBoardContract(symbol);
42
+ const typed = symbol.trim().toUpperCase();
43
+ if (!resolved) {
44
+ if (searched) {
45
+ return contract
46
+ ? `No stories match ${typed}, the futures board's code for ${contract.name}. It is not a ticker: ${contractSuggestion(contract)}.`
47
+ : `No listing answers to ${typed} and no stories match it. Try \`gloomberb search ${typed}\`.`;
48
+ }
49
+ return contract
50
+ ? `${typed} is the futures board's code for ${contract.name}, not a ticker. news takes a company ticker: ${contractSuggestion(contract)}.`
51
+ : `No listing answers to ${typed}. news takes a company ticker, such as news AAPL. Try \`gloomberb search ${typed}\`.`;
52
+ }
53
+ if (isFuturesInstrument(symbol, quote?.instrumentType)) {
54
+ if (searched) {
55
+ return `No stories match ${typed}, and news is filed under company tickers, not futures${contract ? `; ${contractSuggestion(contract)}` : ""}.`;
56
+ }
57
+ return `No news for ${title}: news is filed under company tickers, not futures${contract ? `; ${contractSuggestion(contract)}` : ""}.`;
58
+ }
59
+ return contract
60
+ ? `No news for ${title}. ${typed} is also the futures board's code for ${contract.name}: ${contractSuggestion(contract)}.`
61
+ : `No news for ${title}.`;
62
+ }
@@ -0,0 +1,53 @@
1
+ import { apiClient } from "../../api-client";
2
+ import type { NewsArticle } from "../../news/types";
3
+ import { mapCloudNewsArticle } from "../../sources/gloomberb-cloud/news";
4
+ import { cliStyles } from "../../utils/cli-output";
5
+
6
+ /** What the service accepts of a search: two characters to two hundred, with a letter or a number in them. */
7
+ const MIN_TEXT_LENGTH = 2;
8
+ const MAX_TEXT_LENGTH = 200;
9
+ const MIN_FALLBACK_LENGTH = 3;
10
+
11
+ /**
12
+ * The text `news` was asked to search for rather than a symbol: several words,
13
+ * quoted or not (`news "european gas"`, `news european gas`). A single word
14
+ * is a symbol until nothing answers to it (`newsFallbackText`).
15
+ */
16
+ export function newsSearchText(args: readonly string[]): string | null {
17
+ const text = args.join(" ").trim().replace(/\s+/g, " ");
18
+ return args.length > 1 || /\s/.test(text) ? text : null;
19
+ }
20
+
21
+ /** Why the service would refuse the text, or null when it can search it. */
22
+ export function newsSearchTextProblem(text: string): string | null {
23
+ if (text.length < MIN_TEXT_LENGTH || !/[\p{L}\p{N}]/u.test(text)) return "Search text needs at least two characters, with a letter or a number.";
24
+ if (text.length > MAX_TEXT_LENGTH) return `Search text is limited to ${MAX_TEXT_LENGTH} characters.`;
25
+ return null;
26
+ }
27
+
28
+ /**
29
+ * The word a symbol lookup found no stories for, as text to search stories for.
30
+ * Null under three characters, where the fuzzy match finds names that merely
31
+ * contain it (ZN, Telomir-Zn) and the lookup's own answer is the better one.
32
+ */
33
+ export function newsFallbackText(symbol: string): string | null {
34
+ const text = symbol.trim().replace(/^\$/, "");
35
+ return text.length < MIN_FALLBACK_LENGTH || newsSearchTextProblem(text) ? null : text;
36
+ }
37
+
38
+ /**
39
+ * The stories matching the text, newest first. The service answers best match
40
+ * first and holds stories back from an account without real-time access, as
41
+ * every other news request.
42
+ */
43
+ export async function searchNewsStories(text: string, limit: number): Promise<NewsArticle[]> {
44
+ const response = await apiClient.searchCloudNews(text, limit);
45
+ return response.items
46
+ .map((item) => mapCloudNewsArticle(item))
47
+ .sort((a, b) => b.publishedAt.getTime() - a.publishedAt.getTime());
48
+ }
49
+
50
+ /** The line above the stories: "Stories matching "european gas"", with why a word typed as a symbol is searched. */
51
+ export function newsSearchHeading(text: string, reason?: string): string {
52
+ return `${cliStyles.bold(`Stories matching "${text}"`)}${reason ? cliStyles.muted(` · ${reason}`) : ""}`;
53
+ }
@@ -2,7 +2,7 @@ import type { OptionContract, OptionsChain } from "../../types/financials";
2
2
  import type { CliCommandContext } from "../../types/plugin";
3
3
  import type { MarketContext } from "../types";
4
4
  import type { CliResultColumn } from "../result";
5
- import { requireCliListing, loadListingQuote, type CliListing } from "../listing-arg";
5
+ import { listingReadNotes, requireCliListing, loadListingQuote, type CliListing } from "../listing-arg";
6
6
  import { rowsFreshness } from "../freshness";
7
7
  import { formatCountCell } from "../helpers";
8
8
  import { cliStyles } from "../../utils/cli-output";
@@ -388,7 +388,10 @@ export async function runLeapsScreen(
388
388
  ctx.fail(screened.map((entry) => entry.warning).filter(Boolean).join("\n"));
389
389
  }
390
390
  const rows = sortLeapsRows(screened.flatMap((entry) => entry.rows), criteria.sort);
391
- const warnings = screened.map((entry) => entry.warning).filter((warning): warning is string => warning != null);
391
+ const warnings = [
392
+ ...listings.flatMap(listingReadNotes),
393
+ ...screened.map((entry) => entry.warning).filter((warning): warning is string => warning != null),
394
+ ];
392
395
  const { format, limit: rowLimit, tail } = ctx.cliOptions;
393
396
  const capped = format === "text" && rowLimit == null && tail == null && rows.length > LEAPS_TEXT_ROWS;
394
397
  const describe = (data: readonly LeapsRow[]) => {
@@ -10,7 +10,7 @@ import {
10
10
  rankScreenerQuotes,
11
11
  type ScreenerCategory,
12
12
  } from "../../plugins/builtin/market-movers/screener";
13
- import { formatMoverPrice, moverReferencePrice } from "../../plugins/builtin/market-movers/model";
13
+ import { formatMoverPrice, moverReferencePrice, moverRegularSession } from "../../plugins/builtin/market-movers/model";
14
14
  import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, type ExtendedSession } from "../../market-data/market/status";
15
15
  import {
16
16
  COUNTRY_CYCLE,
@@ -27,14 +27,24 @@ import { buildCorrelationSeries } from "../../plugins/builtin/correlation/matrix
27
27
  import { alignDailyCloses, correlateDailyCloses } from "../../plugins/builtin/correlation/compute";
28
28
  import { mixedSessionCloseNote } from "../../market-data/market/session-close-note";
29
29
  import { CORRELATION_RETURN_BASIS, loadCorrelationHistory } from "../../plugins/builtin/correlation/history";
30
- import { EXCHANGE_OPTION, listingIdentity, loadForListing, loadListingQuote, requireCliListing, type CliListing } from "../listing-arg";
30
+ import { EXCHANGE_OPTION, listingIdentity, listingReadNotes, loadForListing, loadListingQuote, requireCliListing, type CliListing } from "../listing-arg";
31
31
  import { CLI_COMMAND_GROUPS } from "../help";
32
32
  import { formatChangePercentCell, formatCompactCell } from "../helpers";
33
33
  import { WORLD_INDICES } from "../../plugins/builtin/world-indices/indices";
34
34
  import { getSectorCollection, SECTOR_COLLECTIONS } from "../../plugins/builtin/sectors/sector-data";
35
35
  import { DAILY_CLOSES } from "../../plugins/builtin/shared/report-freshness";
36
- import { newestReportTime, oldestReportTime } from "../../utils/utc-time";
36
+ import { localTimeSuffix, newestReportTime, oldestReportTime } from "../../utils/utc-time";
37
37
  import { quotesFreshness, rowsFreshness } from "../freshness";
38
+ import { periodicityOf } from "../pane-functions/freshness";
39
+ import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
40
+ import {
41
+ DEFAULT_FRED_START,
42
+ fredEmptyMessage,
43
+ fredHistoryNotes,
44
+ isSpreadSeries,
45
+ laggingSeriesNote,
46
+ spreadBasisPointsCell,
47
+ } from "./fred-series";
38
48
 
39
49
  // Batch quotes often omit names for indices and ETFs; these baskets are fixed, so name them here.
40
50
  const BASKET_NAMES = new Map<string, string>([
@@ -80,7 +90,10 @@ const MOVER_COLUMNS = [
80
90
  { key: "volume", header: "Volume", align: "right" as const, format: formatCompactCell },
81
91
  { ...MARKET_CAP_CELL, format: formatCompactCell },
82
92
  ];
83
- const START_OPTION = { flags: "--start <yyyy-mm-dd>", description: "First observation date (default 2021-01-01)" };
93
+ const START_OPTION = {
94
+ flags: "--start <yyyy-mm-dd>",
95
+ description: `First observation date; yyyy or yyyy-mm start at the first of that year or month (default ${DEFAULT_FRED_START})`,
96
+ };
84
97
 
85
98
  const MOVER_LISTS: Record<string, ScreenerCategory | "trending"> = {
86
99
  gainers: "day_gainers",
@@ -150,13 +163,25 @@ async function runMoverCommand(args: string[], ctx: Parameters<CliCommandDef["ex
150
163
  }
151
164
 
152
165
  const screener = await fetchScreenerResult(category, limit, undefined, { forceRefresh: ctx.cliOptions.refresh });
153
- const rows = rankScreenerQuotes(category, screener.data);
166
+ // Ranked by the live print, as the list is; then Last and Chg% are the regular
167
+ // session, as `ticker` reads it, and a pre-market or after-hours print is its own column.
168
+ const rows = rankScreenerQuotes(category, screener.data).map((quote) => {
169
+ const { extended, ...row } = moverRegularSession(quote);
170
+ return {
171
+ ...row,
172
+ extendedSession: extended?.session ?? null,
173
+ extendedPrice: extended?.price ?? null,
174
+ extendedChange: extended?.change ?? null,
175
+ extendedChangePercent: extended?.changePercent ?? null,
176
+ };
177
+ });
154
178
  // A screener snapshot, as the MOST report reads it: delayed, each row dated by its own last price.
155
179
  ctx.printResult({ data: rows, freshness: rowsFreshness(rows, { status: "delayed" }, { stale: screener.stale === true }) }, {
156
180
  columns: [
157
181
  ...MOVER_COLUMNS.slice(0, 4),
182
+ ...EXTENDED_COLUMNS,
158
183
  { key: "volume", header: "Volume", align: "right", value: (row) => formatCompact(Number(row.volume)) },
159
- { key: "marketCap", header: "Mkt Cap", align: "right", value: (row) => row.marketCap == null ? "" : formatCompact(Number(row.marketCap)) },
184
+ { ...MARKET_CAP_CELL, value: (row) => row.marketCap == null ? "" : formatCompact(Number(row.marketCap)) },
160
185
  ],
161
186
  });
162
187
  }
@@ -180,12 +205,12 @@ async function runQuoteBasket(symbols: string[], ctx: Parameters<CliCommandDef["
180
205
  });
181
206
  }
182
207
 
183
- /** The UTC date, or the UTC time with its zone named, of an event's timestamp. */
208
+ /** The UTC date, or the UTC time with its zone named (and the reader's own beside it), of an event's timestamp. */
184
209
  function utcDateTimePart(value: unknown, part: "date" | "time"): string {
185
210
  const date = new Date(String(value));
186
211
  if (Number.isNaN(date.getTime())) return "";
187
212
  const iso = date.toISOString();
188
- return part === "date" ? iso.slice(0, 10) : `${iso.slice(11, 16)} UTC`;
213
+ return part === "date" ? iso.slice(0, 10) : `${iso.slice(11, 16)} UTC${localTimeSuffix(date.getTime())}`;
189
214
  }
190
215
 
191
216
  const ECON_USAGE = "econ [--country <region>] [--impact <level>] [--from <yyyy-mm-dd>]";
@@ -204,13 +229,22 @@ function econEmptyMessage(from: string | null, listedThrough: string | null, sto
204
229
  : `No matching events${range} through ${listedThrough}.`;
205
230
  }
206
231
 
207
- /** A `yyyy-mm-dd` option, checked here; null when it was not given. */
208
- function parseDateOption(value: string | undefined, flag: string, ctx: Parameters<CliCommandDef["execute"]>[1]): string | null {
232
+ /**
233
+ * A `yyyy-mm-dd` option, checked here; null when it was not given. `expand`
234
+ * reads a shorthand as a full date first, and `form` is how the message names
235
+ * what the option takes.
236
+ */
237
+ function parseDateOption(
238
+ value: string | undefined,
239
+ flag: string,
240
+ ctx: Parameters<CliCommandDef["execute"]>[1],
241
+ { expand = (text: string) => text, form = "yyyy-mm-dd" }: { expand?: (text: string) => string; form?: string } = {},
242
+ ): string | null {
209
243
  if (value == null) return null;
210
- const date = value.trim();
244
+ const date = expand(value.trim());
211
245
  const time = /^\d{4}-\d{2}-\d{2}$/.test(date) ? Date.parse(`${date}T00:00:00Z`) : Number.NaN;
212
246
  if (!Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== date) {
213
- ctx.fail(`${flag} takes a date as yyyy-mm-dd, got "${value}".`);
247
+ ctx.fail(`${flag} takes a date as ${form}, got "${value}".`);
214
248
  }
215
249
  return date;
216
250
  }
@@ -281,9 +315,15 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
281
315
  const FRED_USAGE = "fred <series-id> [--start <yyyy-mm-dd>] [--sort desc|asc]";
282
316
  const FRED_LIST_USAGE = "fred --list [filter]";
283
317
 
284
- /** `--start`, checked here: Gloom Cloud cannot read anything but yyyy-mm-dd. */
318
+ /** A year or a month stands for the first day of it. */
319
+ function startOfPeriod(text: string): string {
320
+ if (/^\d{4}$/.test(text)) return `${text}-01-01`;
321
+ return /^\d{4}-\d{2}$/.test(text) ? `${text}-01` : text;
322
+ }
323
+
324
+ /** `--start`, checked here: Gloom Cloud cannot read anything but yyyy-mm-dd, so yyyy and yyyy-mm are spelled out. */
285
325
  function parseStartDate(value: string | undefined, ctx: Parameters<CliCommandDef["execute"]>[1]): string {
286
- return parseDateOption(value, "--start", ctx) ?? "2021-01-01";
326
+ return parseDateOption(value, "--start", ctx, { expand: startOfPeriod, form: "yyyy-mm-dd, yyyy-mm or yyyy" }) ?? DEFAULT_FRED_START;
287
327
  }
288
328
 
289
329
  /** Gloom Cloud serves a curated set of FRED series and answers 400 for any other id. */
@@ -336,15 +376,37 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
336
376
  throw error;
337
377
  });
338
378
  const rows = data.observations;
379
+ // The default start hides older history and a start before the series begins is moved up silently: say where it really starts.
380
+ const notes = [
381
+ ...fredHistoryNotes({ startDate, startGiven: start != null, seriesStart: data.info?.observationStart }),
382
+ laggingSeriesNote(seriesId),
383
+ ].filter((note): note is string => note != null);
384
+ // Nothing on or after the start: name where the series ends, so an empty answer is not a dead end.
385
+ const latest = rows.length > 0 ? null : await apiClient.getCloudFredSeries(seriesId, { limit: 1, sortOrder: "desc" })
386
+ .then((payload) => payload.observations[0] ?? null).catch(() => null);
387
+ const periodicity = periodicityOf(data.info?.frequency);
339
388
  ctx.printResult({
340
389
  data: rows,
341
- metadata: { info: data.info, seriesId, startDate, sortOrder },
342
- freshness: rowsFreshness(rows, { source: "FRED", status: "not-a-feed", basis: "published statistics", observedKey: "date", oldest: null }),
390
+ metadata: {
391
+ info: data.info, seriesId, startDate, sortOrder,
392
+ ...(notes.length ? { notes } : {}),
393
+ ...(latest ? { latestObservation: latest } : {}),
394
+ },
395
+ freshness: rowsFreshness(rows, {
396
+ source: "FRED", status: "not-a-feed", basis: "published statistics", observedKey: "date", oldest: null,
397
+ // A monthly or quarterly observation is dated the first of its period, not a day it was seen.
398
+ ...(periodicity ? { periodicity } : {}),
399
+ }),
343
400
  }, {
344
401
  dateKey: "date",
402
+ empty: fredEmptyMessage(startDate, latest),
403
+ ...(notes.length ? { footnote: notes.join("\n") } : {}),
345
404
  textColumns: [
346
405
  { key: "date", header: "Date" },
347
- { key: "value", header: data.info?.units ? `Value (${data.info.units})` : "Value", align: "right" },
406
+ isSpreadSeries(seriesId)
407
+ // A spread is read in basis points; JSON and CSV keep FRED's percentage points.
408
+ ? { key: "value", header: "Value (bp)", align: "right", format: spreadBasisPointsCell }
409
+ : { key: "value", header: data.info?.units ? `Value (${data.info.units})` : "Value", align: "right" },
348
410
  ],
349
411
  });
350
412
  }
@@ -358,12 +420,16 @@ async function runYieldCurve(rawArgs: string[], ctx: Parameters<CliCommandDef["e
358
420
  const series = Object.keys(YIELD_TENORS);
359
421
  const results = await Promise.all(series.map(async (seriesId) => {
360
422
  const data = await apiClient.getCloudFredSeries(seriesId, { startDate, sortOrder: "desc" });
361
- const latest = data.observations[0];
423
+ const [latest, previous] = data.observations;
362
424
  return {
363
425
  seriesId,
364
426
  date: latest?.date ?? "",
365
427
  value: latest?.value ?? null,
366
428
  title: data.info?.title ?? "",
429
+ // The move since the previous observation FRED has, not calendar yesterday: a Monday reads against Friday.
430
+ previousDate: previous?.date ?? null,
431
+ previousValue: previous?.value ?? null,
432
+ changeBasisPoints: latest?.value != null && previous?.value != null ? toBasisPoints(latest.value - previous.value, 1) : null,
367
433
  };
368
434
  }));
369
435
  ctx.printResult({
@@ -373,7 +439,13 @@ async function runYieldCurve(rawArgs: string[], ctx: Parameters<CliCommandDef["e
373
439
  }, {
374
440
  textColumns: [
375
441
  { key: "tenor", header: "Tenor", value: (row) => YIELD_TENORS[String(row.seriesId)] ?? row.seriesId },
376
- { key: "value", header: "Yield %", align: "right" },
442
+ { key: "value", header: "Yield %", align: "right", format: (value) => typeof value === "number" ? value.toFixed(2) : "-" },
443
+ {
444
+ key: "changeBasisPoints",
445
+ header: "1D bp",
446
+ align: "right",
447
+ format: (value) => typeof value === "number" ? formatBasisPoints(value / 100) : "-",
448
+ },
377
449
  { key: "date", header: "Date" },
378
450
  { key: "seriesId", header: "Series" },
379
451
  ],
@@ -419,9 +491,10 @@ async function runCorrelation(rawArgs: string[], ctx: Parameters<CliCommandDef["
419
491
  asOf: newestReportTime(lastBars),
420
492
  oldest: oldestReportTime(lastBars),
421
493
  });
494
+ const warnings = [...listingReadNotes(leftListing!), ...listingReadNotes(rightListing!), ...sessionNote ? [sessionNote] : []];
422
495
  ctx.printResult({
423
496
  data: [{ left, right, samples: sampleSize, correlation }],
424
- ...(sessionNote ? { warnings: [sessionNote] } : {}),
497
+ ...(warnings.length > 0 ? { warnings } : {}),
425
498
  metadata: { range: "1Y", basis: CORRELATION_RETURN_BASIS },
426
499
  freshness,
427
500
  }, {
@@ -41,9 +41,10 @@ export const remoteCliCommand: CliCommandDef = {
41
41
  return;
42
42
  }
43
43
 
44
+ // No data folder means this profile never opened the app, so none is running to drive.
44
45
  const dataDir = await getDataDir();
45
46
  if (!dataDir) {
46
- ctx.fail("No data directory configured.", "Run gloomberb once before using remote control.");
47
+ ctx.fail("No running Gloomberb app found.", "Open the desktop app or run gloomberb, then try again.");
47
48
  return;
48
49
  }
49
50
 
@@ -6,6 +6,7 @@ import {
6
6
  import type { DataProvider } from "../../types/data-provider";
7
7
  import type { TickerRecord } from "../../types/ticker";
8
8
  import {
9
+ LISTING_SEARCH_LIMIT,
9
10
  searchTickerCandidates,
10
11
  type TickerSearchCandidate,
11
12
  } from "../../tickers/search";
@@ -14,6 +15,10 @@ import { fail } from "../errors";
14
15
  import type { MarketContext } from "../types";
15
16
  import type { CliCommandContext } from "../../types/plugin";
16
17
  import { indexRootFor, indexRootTryLine } from "../index-roots";
18
+ import { knownGapMessage } from "../known-gaps";
19
+ import { instrumentTypeLabel } from "../../tickers/instrument-kind";
20
+ import { canonicalExchange, isUsListingExchange } from "../../utils/exchanges";
21
+ import { groupByCompany } from "../../tickers/company-name";
17
22
 
18
23
  interface SearchCommandDependencies {
19
24
  initMarketData?: () => Promise<MarketContext>;
@@ -41,18 +46,64 @@ export async function searchCandidatesForCli({
41
46
  tickers.map((ticker) => [ticker.metadata.ticker.toUpperCase(), ticker] as const),
42
47
  );
43
48
  try {
44
- return await searchTickerCandidates({
49
+ const candidates = await searchTickerCandidates({
45
50
  query: trimmedQuery,
46
51
  tickers: localTickerMap,
47
52
  dataProvider,
48
53
  localLimit,
49
- totalLimit,
54
+ // Every company's listing of a symbol several use (CAR's ASX line is past the first ten), cut after ordering.
55
+ totalLimit: Math.max(totalLimit, LISTING_SEARCH_LIMIT),
56
+ searchContext: { limit: LISTING_SEARCH_LIMIT },
50
57
  });
58
+ return (await withPrimaryListingFirst(trimmedQuery, candidates, dataProvider)).slice(0, totalLimit);
51
59
  } catch {
52
60
  return [];
53
61
  }
54
62
  }
55
63
 
64
+ function candidateExchange(candidate: TickerSearchCandidate): string {
65
+ return canonicalExchange(candidate.result?.exchange === "SMART"
66
+ ? candidate.result.primaryExchange
67
+ : candidate.result?.exchange || candidate.result?.primaryExchange || candidate.ticker?.metadata.exchange || candidate.right);
68
+ }
69
+
70
+ /**
71
+ * For a bare symbol several companies list (CAR: Avis, Carrefour, CAR Group),
72
+ * the listing `quote CAR` reads goes first as the primary listing, then each
73
+ * other company's first listing, then the rest of their venues, so a short
74
+ * list still shows every company. A saved listing is the user's choice and
75
+ * stays where search put it. Any other query keeps search's order.
76
+ */
77
+ async function withPrimaryListingFirst(
78
+ query: string,
79
+ candidates: TickerSearchCandidate[],
80
+ dataProvider: DataProvider,
81
+ ): Promise<TickerSearchCandidate[]> {
82
+ const symbol = query.toUpperCase();
83
+ if (!/^[A-Z0-9][A-Z0-9.\-]{0,11}$/.test(symbol)) return candidates;
84
+ const exact = candidates.filter((candidate) => candidate.symbol.toUpperCase() === symbol);
85
+ if (exact.some((candidate) => candidate.kind === "ticker" || candidate.saved)) return candidates;
86
+ const listed = exact.filter((candidate) => candidate.kind === "search" && (candidate.instrumentClass === "equity" || candidate.instrumentClass === "other"));
87
+ const companies = groupByCompany(listed, resolveSearchName);
88
+ if (companies.length < 2) return candidates;
89
+ const quote = await dataProvider.getQuote(symbol, "").catch(() => null);
90
+ const quoted = canonicalExchange(quote?.listingExchangeName || quote?.exchangeName);
91
+ if (!quoted) return candidates;
92
+ // The US exchanges share one symbol: a quote on any of them is the symbol's one US line.
93
+ const primary = listed.find((candidate) => candidateExchange(candidate) === quoted)
94
+ ?? (isUsListingExchange(quoted) ? listed.find((candidate) => isUsListingExchange(candidateExchange(candidate))) : undefined);
95
+ if (!primary) return candidates;
96
+ const firsts = companies.filter((group) => !group.includes(primary)).map((group) => group[0]!);
97
+ const ordered = [primary, ...firsts, ...listed.filter((candidate) => candidate !== primary && !firsts.includes(candidate))];
98
+ const at = candidates.indexOf(listed[0]!);
99
+ const rest = candidates.filter((candidate) => !listed.includes(candidate));
100
+ return [...rest.slice(0, at), ...ordered, ...rest.slice(at)].map((candidate) => (
101
+ candidate === primary ? { ...candidate, category: "Primary Listing" as const }
102
+ : listed.includes(candidate) || candidate.category === "Primary Listing" ? { ...candidate, category: "Other Listings" as const }
103
+ : candidate
104
+ ));
105
+ }
106
+
56
107
  /** "Saved", or the broker a row came from; never the data service's internal id. */
57
108
  function searchSourceLabel(candidate: TickerSearchCandidate): string {
58
109
  return candidate.kind === "ticker" ? "Saved" : candidate.result?.brokerLabel || "";
@@ -62,6 +113,13 @@ function resolveSearchName(candidate: TickerSearchCandidate): string {
62
113
  return candidate.detail.split(" | ")[0] || candidate.detail || "—";
63
114
  }
64
115
 
116
+ /** The listing's type as the table shows it; a perpetual price a provider types as a coin says what it is. */
117
+ function searchType(candidate: TickerSearchCandidate): string | undefined {
118
+ return instrumentTypeLabel(candidate.result?.type
119
+ || candidate.result?.brokerContract?.secType
120
+ || candidate.ticker?.metadata.assetCategory, resolveSearchName(candidate));
121
+ }
122
+
65
123
  function searchCandidateRows(candidates: TickerSearchCandidate[]) {
66
124
  return candidates.map((candidate) => ({
67
125
  category: candidate.category,
@@ -92,6 +150,22 @@ function indexRootNote(query: string, candidates: TickerSearchCandidate[]): stri
92
150
  return indexRootTryLine(root);
93
151
  }
94
152
 
153
+ /** Search finds tickers and companies; a function, a dataset or a term is in the catalog, so an empty search points there. */
154
+ function catalogHint(query: string): string {
155
+ return `Search finds tickers and company names. Try: gloomberb catalog "${query.replace(/"/g, "")}"`;
156
+ }
157
+
158
+ /**
159
+ * The notes beside the results: the index a bare root names, data the query
160
+ * asks for that no source carries yet, and for an empty search the catalog,
161
+ * unless a known gap already said where to look instead.
162
+ */
163
+ function searchNotes(query: string, candidates: TickerSearchCandidate[]): string[] {
164
+ const gap = knownGapMessage(query);
165
+ return [indexRootNote(query, candidates), gap, candidates.length === 0 && !gap ? catalogHint(query) : null]
166
+ .filter((note): note is string => !!note);
167
+ }
168
+
95
169
  export function buildSearchReport({
96
170
  query,
97
171
  candidates,
@@ -102,9 +176,11 @@ export function buildSearchReport({
102
176
  const lines = [renderSection(`Search: ${query}`)];
103
177
  const indexNote = indexRootNote(query, candidates);
104
178
  if (indexNote) lines.push("", indexNote);
179
+ const gap = knownGapMessage(query);
180
+ if (gap) lines.push("", gap);
105
181
 
106
182
  if (candidates.length === 0) {
107
- lines.push(cliStyles.muted("No matches found."));
183
+ if (!gap) lines.push(cliStyles.muted("No matches found."), cliStyles.muted(catalogHint(query)));
108
184
  return lines.join("\n");
109
185
  }
110
186
 
@@ -131,10 +207,7 @@ export function buildSearchReport({
131
207
  || candidate.ticker?.metadata.exchange
132
208
  || candidate.right
133
209
  || "—",
134
- candidate.result?.type
135
- || candidate.result?.brokerContract?.secType
136
- || candidate.ticker?.metadata.assetCategory
137
- || "—",
210
+ searchType(candidate) ?? "—",
138
211
  ...(showSource ? [searchSourceLabel(candidate) || "—"] : []),
139
212
  ]),
140
213
  ));
@@ -160,11 +233,11 @@ export async function search(query: string, dependencies: SearchCommandDependenc
160
233
  });
161
234
 
162
235
  if (dependencies.printResult) {
163
- const indexNote = indexRootNote(trimmedQuery, candidates);
236
+ const warnings = searchNotes(trimmedQuery, candidates);
164
237
  dependencies.printResult({
165
238
  data: searchCandidateRows(candidates),
166
239
  metadata: { query: trimmedQuery },
167
- warnings: indexNote ? [indexNote] : undefined,
240
+ warnings: warnings.length ? warnings : undefined,
168
241
  }, {
169
242
  columns: [
170
243
  { key: "category", header: "Category" },