gloomberb 0.16.4 → 0.16.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (852) hide show
  1. package/README.md +14 -91
  2. package/package.json +5 -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/central-bank-rates.ts +6 -1
  15. package/src/api-client/chat.ts +26 -0
  16. package/src/api-client/credit-boards.ts +2 -0
  17. package/src/api-client/data.ts +73 -3
  18. package/src/api-client/earnings.ts +6 -0
  19. package/src/api-client/europe-energy.ts +106 -0
  20. package/src/api-client/futures-curve.ts +56 -0
  21. package/src/api-client/index.ts +13 -1
  22. package/src/api-client/market-discovery.ts +13 -1
  23. package/src/api-client/normalizers.ts +32 -0
  24. package/src/api-client/paths.ts +7 -0
  25. package/src/api-client/perps.ts +8 -0
  26. package/src/api-client/request.ts +9 -2
  27. package/src/api-client/research-activity.ts +15 -10
  28. package/src/api-client/socket.ts +29 -0
  29. package/src/api-client/themes.ts +22 -1
  30. package/src/api-client/types.ts +78 -2
  31. package/src/api-client/yield-curves.ts +8 -0
  32. package/src/app/global-shortcuts.ts +50 -2
  33. package/src/app/keybindings/actions.ts +12 -1
  34. package/src/app/pane-runtime/plugin-bindings.ts +2 -2
  35. package/src/app/pane-runtime/ticker-open-runtime.ts +16 -6
  36. package/src/app/runtime/desktop-deeplink.ts +10 -5
  37. package/src/app/runtime/startup.ts +1 -1
  38. package/src/app.tsx +6 -0
  39. package/src/architecture/core-builtin-allowlist.json +8 -26
  40. package/src/ask-gloom/pane-context.ts +114 -0
  41. package/src/ask-gloom/pane-request.ts +70 -0
  42. package/src/ask-gloom/pane-text.ts +201 -0
  43. package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
  44. package/src/capabilities/chart-series.ts +19 -2
  45. package/src/capabilities/factories.ts +1 -0
  46. package/src/cli/central-bank-events.ts +23 -0
  47. package/src/cli/command-options.ts +17 -5
  48. package/src/cli/commands/crypto-hints.ts +35 -2
  49. package/src/cli/commands/fred-series.ts +146 -0
  50. package/src/cli/commands/market.ts +501 -115
  51. package/src/cli/commands/news-hints.ts +62 -0
  52. package/src/cli/commands/news-search.ts +53 -0
  53. package/src/cli/commands/options-leaps.ts +5 -2
  54. package/src/cli/commands/overview.ts +132 -26
  55. package/src/cli/commands/remote.ts +2 -1
  56. package/src/cli/commands/search.ts +317 -13
  57. package/src/cli/commands/system.ts +55 -8
  58. package/src/cli/commands/ticker.ts +155 -18
  59. package/src/cli/context.ts +45 -14
  60. package/src/cli/desktop-pane-shot.ts +76 -7
  61. package/src/cli/earnings-calendar.ts +71 -0
  62. package/src/cli/errors.ts +7 -5
  63. package/src/cli/exit-codes.ts +8 -0
  64. package/src/cli/export-source.ts +58 -0
  65. package/src/cli/freshness.ts +18 -7
  66. package/src/cli/glossary.ts +483 -0
  67. package/src/cli/help.ts +10 -0
  68. package/src/cli/helpers.ts +2 -5
  69. package/src/cli/history-rows.ts +118 -7
  70. package/src/cli/index-roots.ts +7 -0
  71. package/src/cli/index.ts +29 -9
  72. package/src/cli/known-gaps.ts +84 -0
  73. package/src/cli/listing-arg.ts +551 -97
  74. package/src/cli/not-a-ticker.ts +24 -9
  75. package/src/cli/pane-functions/access-gate.ts +13 -2
  76. package/src/cli/pane-functions/capabilities.ts +4 -3
  77. package/src/cli/pane-functions/catalog.ts +214 -40
  78. package/src/cli/pane-functions/data.ts +42 -8
  79. package/src/cli/pane-functions/discovery.ts +1 -0
  80. package/src/cli/pane-functions/dom.ts +37 -8
  81. package/src/cli/pane-functions/freshness.ts +393 -49
  82. package/src/cli/pane-functions/function-help.ts +31 -15
  83. package/src/cli/pane-functions/headless.ts +98 -30
  84. package/src/cli/pane-functions/index.ts +101 -19
  85. package/src/cli/pane-functions/market-session.ts +22 -8
  86. package/src/cli/pane-functions/options.ts +71 -22
  87. package/src/cli/pane-functions/report-notices.ts +25 -7
  88. package/src/cli/pane-functions/report.ts +15 -8
  89. package/src/cli/pane-functions/resolver.ts +74 -19
  90. package/src/cli/pane-functions/screenshot.ts +250 -27
  91. package/src/cli/perpetual-contract.ts +60 -0
  92. package/src/cli/registry.ts +3 -1
  93. package/src/cli/report-tables.ts +26 -7
  94. package/src/cli/result.ts +45 -11
  95. package/src/cli/series-search.ts +111 -0
  96. package/src/cli/staged-tickers.ts +52 -0
  97. package/src/cli/symbol-args.ts +27 -0
  98. package/src/cli/symbol-list.ts +154 -0
  99. package/src/cli/symbol-rows.ts +154 -0
  100. package/src/cli/ticker-resolution.ts +311 -27
  101. package/src/cli/types.ts +5 -0
  102. package/src/components/chart/composite/axis-overlays.tsx +14 -3
  103. package/src/components/chart/composite/composite-chart.tsx +71 -11
  104. package/src/components/chart/composite/crosshair-readout.tsx +150 -0
  105. package/src/components/chart/composite/downsample.ts +1 -1
  106. package/src/components/chart/composite/format.ts +29 -0
  107. package/src/components/chart/composite/interactions.ts +9 -2
  108. package/src/components/chart/composite/legend.tsx +100 -33
  109. package/src/components/chart/composite/panel-bitmap.ts +70 -35
  110. package/src/components/chart/composite/panel-labels.ts +18 -17
  111. package/src/components/chart/composite/panel-series.ts +1 -0
  112. package/src/components/chart/composite/panel-surface.tsx +46 -21
  113. package/src/components/chart/composite/price-axis-labels.tsx +3 -1
  114. package/src/components/chart/composite/rasterizer.ts +50 -8
  115. package/src/components/chart/composite/scene.ts +2 -1
  116. package/src/components/chart/composite/time-scale.ts +26 -9
  117. package/src/components/chart/composite/types.ts +18 -0
  118. package/src/components/chart/composite/use-composite-viewport.ts +30 -2
  119. package/src/components/chart/native/raster/crosshair.ts +8 -3
  120. package/src/components/chart/native/raster/primitives.ts +85 -21
  121. package/src/components/chart/native/surface/manager.ts +33 -8
  122. package/src/components/chart/surface3d/chart.tsx +1 -1
  123. package/src/components/command-bar/action-runtime.ts +1 -1
  124. package/src/components/command-bar/commands/collection-list.ts +210 -0
  125. package/src/components/command-bar/commands/collection.ts +40 -1
  126. package/src/components/command-bar/commands/direct/index.ts +4 -0
  127. package/src/components/command-bar/commands/registry.ts +8 -0
  128. package/src/components/command-bar/panel/index.tsx +11 -3
  129. package/src/components/command-bar/panel/runtime.ts +3 -0
  130. package/src/components/command-bar/panel/types.ts +2 -0
  131. package/src/components/command-bar/routes/root/plugin-install.ts +4 -4
  132. package/src/components/command-bar/routes/root/provider-search.ts +4 -3
  133. package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
  134. package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
  135. package/src/components/command-bar/routing/actions.ts +1 -1
  136. package/src/components/command-bar/workflow/tickers.ts +42 -17
  137. package/src/components/layout/detached-pane-shell.tsx +68 -39
  138. package/src/components/layout/floating-pane.tsx +10 -1
  139. package/src/components/layout/header.tsx +25 -5
  140. package/src/components/layout/missing-pane.tsx +3 -3
  141. package/src/components/layout/pane/capture-stamp.ts +41 -0
  142. package/src/components/layout/pane/capture-status.ts +12 -0
  143. package/src/components/layout/pane/content.tsx +7 -13
  144. package/src/components/layout/pane/footer/index.tsx +6 -2
  145. package/src/components/layout/pane/footer/keyboard.ts +3 -0
  146. package/src/components/layout/pane/footer/model.ts +41 -1
  147. package/src/components/layout/pane/header.tsx +50 -1
  148. package/src/components/layout/pane/index.tsx +21 -3
  149. package/src/components/layout/pane/sidebar.tsx +88 -21
  150. package/src/components/layout/shell/active-drag.ts +21 -5
  151. package/src/components/layout/shell/cursor-occlusion.ts +2 -1
  152. package/src/components/layout/shell/drag/index.ts +39 -9
  153. package/src/components/layout/shell/drag/live.ts +61 -51
  154. package/src/components/layout/shell/drag/runtime.ts +54 -12
  155. package/src/components/layout/shell/index.tsx +84 -65
  156. package/src/components/layout/shell/menu.ts +14 -1
  157. package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
  158. package/src/components/layout/shell/pane/actions.ts +32 -10
  159. package/src/components/layout/shell/pane/ask.ts +131 -0
  160. package/src/components/layout/shell/pane/capture.ts +162 -0
  161. package/src/components/layout/shell/pane/layers.tsx +218 -50
  162. package/src/components/layout/shell/pane/management-shortcuts.ts +46 -1
  163. package/src/components/layout/shell/shortcuts.ts +21 -4
  164. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
  165. package/src/components/markdown-text.tsx +62 -20
  166. package/src/components/metric-treemap/heat-surface.tsx +8 -1
  167. package/src/components/onboarding/account-step/form-panel.tsx +1 -1
  168. package/src/components/onboarding/account-step/index.tsx +1 -1
  169. package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
  170. package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
  171. package/src/components/onboarding/onboarding-keyboard.ts +1 -1
  172. package/src/components/onboarding/onboarding-wizard.tsx +2 -2
  173. package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
  174. package/src/components/onboarding/wizard-account.ts +2 -2
  175. package/src/components/pane-template-wizard.tsx +9 -1
  176. package/src/components/price-selector-dialog.tsx +11 -2
  177. package/src/components/reader/article-reader.tsx +642 -0
  178. package/src/components/reader/article-text.ts +527 -0
  179. package/src/components/sign-in-gate.tsx +3 -3
  180. package/src/components/ticker/list-table-view.tsx +43 -2
  181. package/src/components/ui/action-row.tsx +2 -1
  182. package/src/components/ui/button.tsx +7 -1
  183. package/src/components/ui/choice-dialog.tsx +6 -4
  184. package/src/components/ui/confirm-dialog.tsx +28 -18
  185. package/src/components/ui/data-table/index.tsx +18 -6
  186. package/src/components/ui/data-table/opentui/index.tsx +37 -6
  187. package/src/components/ui/data-table/types.ts +12 -0
  188. package/src/components/ui/dialog-actions.tsx +64 -0
  189. package/src/components/ui/display.tsx +17 -3
  190. package/src/components/ui/frame.tsx +35 -2
  191. package/src/components/ui/icon.tsx +6 -1
  192. package/src/components/ui/query-bar.tsx +10 -1
  193. package/src/components/ui/text-prompt-dialog.tsx +11 -9
  194. package/src/data/cached-query.ts +2 -0
  195. package/src/data/config/store/migrations.ts +7 -0
  196. package/src/data/config/store/normalize.ts +7 -2
  197. package/src/data/config/store/portfolio-as-of-column.ts +57 -0
  198. package/src/data/ticker-repository.ts +5 -0
  199. package/src/data/ticker-store.ts +15 -0
  200. package/src/docs/content.ts +215 -0
  201. package/src/docs/load.ts +139 -0
  202. package/src/docs/requests.ts +39 -0
  203. package/src/docs/snapshot.chunk.ts +8 -0
  204. package/src/docs/snapshot.json +7555 -0
  205. package/src/docs/topics.ts +216 -0
  206. package/src/i18n/es.ts +65 -5
  207. package/src/i18n/ja.ts +65 -5
  208. package/src/i18n/ko.ts +65 -5
  209. package/src/i18n/zh-cn.ts +65 -5
  210. package/src/i18n/zh-tw.ts +65 -5
  211. package/src/layout/desks.ts +1 -1
  212. package/src/layout/pane-manager/dock-tree.ts +18 -0
  213. package/src/layout/pane-manager/index.ts +1 -0
  214. package/src/layout-marketplace/gallery.tsx +1 -1
  215. package/src/market-data/coordinator/chart.ts +10 -4
  216. package/src/market-data/coordinator/index.ts +6 -3
  217. package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
  218. package/src/market-data/futures-contract.ts +151 -0
  219. package/src/market-data/hooks.tsx +10 -2
  220. package/src/market-data/market/format.ts +39 -21
  221. package/src/market-data/market/freshness.ts +84 -20
  222. package/src/market-data/market/session-close-note.ts +3 -2
  223. package/src/market-data/market/status.ts +19 -23
  224. package/src/market-data/published-apac-sessions.ts +23 -1
  225. package/src/market-data/quotes/contributions.ts +5 -0
  226. package/src/market-data/quotes/metadata.ts +19 -24
  227. package/src/market-data/quotes/resolution.ts +5 -0
  228. package/src/market-data/quotes/screener-live-quotes.ts +5 -4
  229. package/src/market-data/result-types.ts +2 -0
  230. package/src/notifications/app-notifier.ts +17 -1
  231. package/src/notifications/notification-log.ts +180 -0
  232. package/src/plugins/auto-update-node.ts +2 -2
  233. package/src/plugins/auto-update.ts +2 -2
  234. package/src/plugins/builtin/account-management/index.tsx +1 -0
  235. package/src/plugins/builtin/account-management/model.ts +1 -56
  236. package/src/plugins/builtin/account-management/pane.tsx +6 -4
  237. package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
  238. package/src/plugins/builtin/alerts/catalysts.ts +1 -1
  239. package/src/plugins/builtin/alerts/cli.ts +313 -47
  240. package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
  241. package/src/plugins/builtin/alerts/events.ts +23 -2
  242. package/src/plugins/builtin/alerts/focus.ts +27 -0
  243. package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
  244. package/src/plugins/builtin/alerts/history.ts +2 -0
  245. package/src/plugins/builtin/alerts/index.tsx +47 -11
  246. package/src/plugins/builtin/alerts/pane.tsx +27 -1
  247. package/src/plugins/builtin/alerts/research-builder.ts +8 -3
  248. package/src/plugins/builtin/alerts/research-rules.ts +18 -2
  249. package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
  250. package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
  251. package/src/plugins/builtin/alerts/scheduled.ts +254 -0
  252. package/src/plugins/builtin/alerts/targets.ts +92 -0
  253. package/src/plugins/builtin/analytics/index.tsx +8 -3
  254. package/src/plugins/builtin/analytics/risk-headless.ts +60 -27
  255. package/src/plugins/builtin/analytics/risk-math.ts +91 -27
  256. package/src/plugins/builtin/analytics/risk-model.ts +35 -5
  257. package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
  258. package/src/plugins/builtin/attention/headless.ts +1 -1
  259. package/src/plugins/builtin/attention/index.tsx +2 -0
  260. package/src/plugins/builtin/attention/pane.tsx +3 -1
  261. package/src/plugins/builtin/attention/series.ts +26 -0
  262. package/src/plugins/builtin/awards/headless.ts +88 -18
  263. package/src/plugins/builtin/awards/index.tsx +2 -0
  264. package/src/plugins/builtin/awards/model.ts +41 -1
  265. package/src/plugins/builtin/awards/pane.tsx +22 -13
  266. package/src/plugins/builtin/awards/series.ts +29 -0
  267. package/src/plugins/builtin/backtest/headless.ts +2 -2
  268. package/src/plugins/builtin/bond-calculator/model.ts +2 -1
  269. package/src/plugins/builtin/broker-manager/index.tsx +1 -0
  270. package/src/plugins/builtin/catalysts/headless.ts +1 -1
  271. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  272. package/src/plugins/builtin/cds/client.ts +49 -12
  273. package/src/plugins/builtin/cds/headless.ts +119 -19
  274. package/src/plugins/builtin/cds/index.tsx +3 -1
  275. package/src/plugins/builtin/cds/model.ts +214 -32
  276. package/src/plugins/builtin/cds/pane.tsx +58 -14
  277. package/src/plugins/builtin/cds/series.ts +19 -0
  278. package/src/plugins/builtin/central-bank-rates/client.ts +4 -0
  279. package/src/plugins/builtin/central-bank-rates/headless.ts +46 -16
  280. package/src/plugins/builtin/central-bank-rates/index.tsx +3 -1
  281. package/src/plugins/builtin/central-bank-rates/model.ts +99 -0
  282. package/src/plugins/builtin/central-bank-rates/pane.tsx +1 -1
  283. package/src/plugins/builtin/central-bank-rates/series.ts +36 -0
  284. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +193 -255
  285. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
  286. package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
  287. package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
  288. package/src/plugins/builtin/chart-composer/headless-schema.ts +1 -1
  289. package/src/plugins/builtin/chart-composer/headless.ts +70 -5
  290. package/src/plugins/builtin/chart-composer/index.tsx +15 -3
  291. package/src/plugins/builtin/chart-composer/pane.tsx +81 -38
  292. package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
  293. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
  294. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
  295. package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
  296. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  297. package/src/plugins/builtin/chat/content/composer.tsx +5 -1
  298. package/src/plugins/builtin/chat/content/footer.tsx +12 -0
  299. package/src/plugins/builtin/chat/content/index.tsx +89 -5
  300. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
  301. package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
  302. package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
  303. package/src/plugins/builtin/chat/content/scroll.ts +127 -27
  304. package/src/plugins/builtin/chat/content/shortcuts.ts +2 -2
  305. package/src/plugins/builtin/chat/content/transcript.tsx +145 -78
  306. package/src/plugins/builtin/chat/content/types.ts +1 -0
  307. package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
  308. package/src/plugins/builtin/chat/controller/index.ts +123 -1
  309. package/src/plugins/builtin/chat/controller/message-loading.ts +3 -0
  310. package/src/plugins/builtin/chat/controller/messages.ts +7 -2
  311. package/src/plugins/builtin/chat/controller/realtime.ts +10 -1
  312. package/src/plugins/builtin/chat/controller/send.ts +61 -1
  313. package/src/plugins/builtin/chat/controller/state.ts +8 -0
  314. package/src/plugins/builtin/chat/ghost-user.ts +20 -0
  315. package/src/plugins/builtin/chat/index.tsx +23 -5
  316. package/src/plugins/builtin/chat/layout.ts +78 -5
  317. package/src/plugins/builtin/chat/message/ai-badge.tsx +18 -0
  318. package/src/plugins/builtin/chat/message/desktop.tsx +55 -6
  319. package/src/plugins/builtin/chat/message/header.tsx +16 -4
  320. package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
  321. package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
  322. package/src/plugins/builtin/chat/message/render-state.ts +18 -2
  323. package/src/plugins/builtin/chat/message/terminal.tsx +43 -4
  324. package/src/plugins/builtin/chat/message/types.ts +12 -0
  325. package/src/plugins/builtin/chat/reactions.ts +197 -0
  326. package/src/plugins/builtin/chat/share/flow.tsx +87 -0
  327. package/src/plugins/builtin/chat/share/targets.ts +27 -0
  328. package/src/plugins/builtin/chat/status-widget.tsx +6 -3
  329. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +148 -27
  330. package/src/plugins/builtin/chat/unread-inbox.ts +22 -0
  331. package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
  332. package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
  333. package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
  334. package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
  335. package/src/plugins/builtin/cloud/askg/model.ts +27 -8
  336. package/src/plugins/builtin/cloud/askg/pane-attachment.ts +94 -0
  337. package/src/plugins/builtin/cloud/askg/pane.tsx +141 -12
  338. package/src/plugins/builtin/cloud/askg/pending-question.ts +11 -0
  339. package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
  340. package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
  341. package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
  342. package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
  343. package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
  344. package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
  345. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  346. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
  347. package/src/plugins/builtin/cloud/plugin.tsx +19 -2
  348. package/src/plugins/builtin/cloud/team/model.ts +5 -0
  349. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  350. package/src/plugins/builtin/cloud/team/store.ts +8 -3
  351. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
  352. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
  353. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
  354. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
  355. package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
  356. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  357. package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
  358. package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
  359. package/src/plugins/builtin/company-attention/headless.ts +21 -7
  360. package/src/plugins/builtin/company-attention/index.tsx +2 -0
  361. package/src/plugins/builtin/company-attention/model.ts +26 -7
  362. package/src/plugins/builtin/company-attention/pane.tsx +12 -9
  363. package/src/plugins/builtin/company-attention/series.ts +52 -0
  364. package/src/plugins/builtin/company-kpis/headless.ts +97 -10
  365. package/src/plugins/builtin/company-kpis/model.ts +24 -2
  366. package/src/plugins/builtin/company-kpis/pane.tsx +17 -16
  367. package/src/plugins/builtin/composite-plugins.ts +19 -13
  368. package/src/plugins/builtin/connections/index.ts +1 -0
  369. package/src/plugins/builtin/cot/headless.ts +1 -1
  370. package/src/plugins/builtin/cot/index.tsx +3 -1
  371. package/src/plugins/builtin/cot/model.ts +40 -11
  372. package/src/plugins/builtin/cot/series.ts +27 -0
  373. package/src/plugins/builtin/cpi/headless.ts +3 -3
  374. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +10 -1
  375. package/src/plugins/builtin/credit-boards/client.ts +6 -2
  376. package/src/plugins/builtin/credit-boards/headless.ts +108 -31
  377. package/src/plugins/builtin/credit-boards/index.tsx +2 -0
  378. package/src/plugins/builtin/credit-boards/model.ts +11 -1
  379. package/src/plugins/builtin/credit-boards/series.ts +66 -0
  380. package/src/plugins/builtin/credit-conditions/client.ts +12 -9
  381. package/src/plugins/builtin/credit-conditions/headless.ts +13 -2
  382. package/src/plugins/builtin/credit-conditions/index.tsx +18 -9
  383. package/src/plugins/builtin/credit-conditions/model.ts +19 -11
  384. package/src/plugins/builtin/credit-conditions/series.ts +16 -0
  385. package/src/plugins/builtin/credit-documents/headless.ts +33 -3
  386. package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
  387. package/src/plugins/builtin/crypto-board/headless.ts +2 -3
  388. package/src/plugins/builtin/crypto-board/index.tsx +2 -0
  389. package/src/plugins/builtin/crypto-board/model.ts +12 -0
  390. package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
  391. package/src/plugins/builtin/crypto-board/series.ts +48 -0
  392. package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
  393. package/src/plugins/builtin/debt-maturities/headless.ts +17 -11
  394. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  395. package/src/plugins/builtin/debt-maturities/series.ts +45 -0
  396. package/src/plugins/builtin/dividend-yield/client.ts +3 -0
  397. package/src/plugins/builtin/dividend-yield/headless.ts +7 -4
  398. package/src/plugins/builtin/dividend-yield/index.tsx +3 -1
  399. package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
  400. package/src/plugins/builtin/dividend-yield/series.ts +20 -0
  401. package/src/plugins/builtin/dividend-yield/types.ts +2 -0
  402. package/src/plugins/builtin/docs/blocks.tsx +181 -0
  403. package/src/plugins/builtin/docs/index.tsx +68 -0
  404. package/src/plugins/builtin/docs/pane.tsx +426 -0
  405. package/src/plugins/builtin/docs/targets.ts +72 -0
  406. package/src/plugins/builtin/docs/use-docs.ts +60 -0
  407. package/src/plugins/builtin/doe/headless.ts +12 -8
  408. package/src/plugins/builtin/doe/index.tsx +2 -0
  409. package/src/plugins/builtin/doe/series.ts +62 -0
  410. package/src/plugins/builtin/earnings/client.ts +3 -2
  411. package/src/plugins/builtin/earnings/format.ts +1 -5
  412. package/src/plugins/builtin/earnings/headless.ts +58 -6
  413. package/src/plugins/builtin/earnings/history-model.ts +15 -1
  414. package/src/plugins/builtin/earnings/history-table.ts +13 -2
  415. package/src/plugins/builtin/earnings/history.tsx +7 -3
  416. package/src/plugins/builtin/earnings/index.tsx +9 -0
  417. package/src/plugins/builtin/earnings-calls/headless.ts +28 -2
  418. package/src/plugins/builtin/earnings-calls/index.tsx +1 -1
  419. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -0
  420. package/src/plugins/builtin/econ/fred-catalog.ts +198 -0
  421. package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
  422. package/src/plugins/builtin/econ/index.tsx +2 -0
  423. package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
  424. package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
  425. package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
  426. package/src/plugins/builtin/equity-screener/headless.ts +3 -3
  427. package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
  428. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -3
  429. package/src/plugins/builtin/exposure/drivers.ts +13 -0
  430. package/src/plugins/builtin/exposure/headless.ts +205 -13
  431. package/src/plugins/builtin/exposure/holdings.ts +1 -1
  432. package/src/plugins/builtin/exposure/index.tsx +1 -0
  433. package/src/plugins/builtin/exposure/model.ts +23 -3
  434. package/src/plugins/builtin/exposure/pane.tsx +14 -12
  435. package/src/plugins/builtin/fear-greed/index.tsx +18 -10
  436. package/src/plugins/builtin/fear-greed/series.ts +52 -0
  437. package/src/plugins/builtin/futures/index.tsx +43 -3
  438. package/src/plugins/builtin/futures/model.ts +1 -1
  439. package/src/plugins/builtin/futures/returns.ts +1 -1
  440. package/src/plugins/builtin/futures/series.ts +13 -0
  441. package/src/plugins/builtin/futures/table.tsx +1 -1
  442. package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
  443. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  444. package/src/plugins/builtin/futures-curve/headless.ts +71 -10
  445. package/src/plugins/builtin/futures-curve/index.tsx +1 -1
  446. package/src/plugins/builtin/futures-curve/model.ts +66 -11
  447. package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
  448. package/src/plugins/builtin/fx-matrix/client.ts +63 -0
  449. package/src/plugins/builtin/fx-matrix/export.ts +2 -1
  450. package/src/plugins/builtin/fx-matrix/headless.ts +154 -0
  451. package/src/plugins/builtin/fx-matrix/index.tsx +4 -4
  452. package/src/plugins/builtin/gpu/board.tsx +13 -10
  453. package/src/plugins/builtin/gpu/changes.tsx +4 -4
  454. package/src/plugins/builtin/gpu/client.ts +19 -1
  455. package/src/plugins/builtin/gpu/equities.tsx +14 -10
  456. package/src/plugins/builtin/gpu/headless.ts +58 -27
  457. package/src/plugins/builtin/gpu/history.tsx +3 -3
  458. package/src/plugins/builtin/gpu/index.tsx +2 -0
  459. package/src/plugins/builtin/gpu/model.ts +101 -16
  460. package/src/plugins/builtin/gpu/pane.tsx +28 -12
  461. package/src/plugins/builtin/gpu/series.ts +38 -0
  462. package/src/plugins/builtin/help/function-card.tsx +25 -6
  463. package/src/plugins/builtin/help/function-index.ts +2 -0
  464. package/src/plugins/builtin/help/index.tsx +31 -9
  465. package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
  466. package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
  467. package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
  468. package/src/plugins/builtin/holders/client.ts +22 -3
  469. package/src/plugins/builtin/holders/format.ts +18 -3
  470. package/src/plugins/builtin/holders/headless.ts +36 -133
  471. package/src/plugins/builtin/holders/pane.tsx +11 -4
  472. package/src/plugins/builtin/holders/report-header.ts +40 -5
  473. package/src/plugins/builtin/holders/report-rows.ts +240 -0
  474. package/src/plugins/builtin/insider/client.ts +35 -20
  475. package/src/plugins/builtin/insider/headless.ts +41 -11
  476. package/src/plugins/builtin/insider/index.tsx +26 -9
  477. package/src/plugins/builtin/insider/insider-data.ts +10 -0
  478. package/src/plugins/builtin/insider/issuer.ts +49 -0
  479. package/src/plugins/builtin/insider/model.ts +3 -0
  480. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -1
  481. package/src/plugins/builtin/iv-history/client.ts +20 -6
  482. package/src/plugins/builtin/iv-history/format.ts +9 -0
  483. package/src/plugins/builtin/iv-history/headless.ts +56 -21
  484. package/src/plugins/builtin/iv-history/index.tsx +2 -0
  485. package/src/plugins/builtin/iv-history/model.ts +143 -8
  486. package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
  487. package/src/plugins/builtin/iv-history/series.ts +29 -0
  488. package/src/plugins/builtin/jobs/headless.ts +26 -3
  489. package/src/plugins/builtin/jobs/index.tsx +3 -1
  490. package/src/plugins/builtin/jobs/series.ts +19 -0
  491. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  492. package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
  493. package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
  494. package/src/plugins/builtin/macro-day/client.ts +44 -3
  495. package/src/plugins/builtin/macro-day/headless.ts +13 -10
  496. package/src/plugins/builtin/macro-day/model.ts +56 -10
  497. package/src/plugins/builtin/macro-day/pane.tsx +30 -20
  498. package/src/plugins/builtin/market-heatmap/headless.ts +20 -5
  499. package/src/plugins/builtin/market-heatmap/index.tsx +6 -0
  500. package/src/plugins/builtin/market-heatmap/model.ts +19 -11
  501. package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
  502. package/src/plugins/builtin/market-movers/client.ts +7 -5
  503. package/src/plugins/builtin/market-movers/index.tsx +3 -2
  504. package/src/plugins/builtin/market-movers/model.ts +30 -0
  505. package/src/plugins/builtin/market-movers/screener.ts +31 -0
  506. package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
  507. package/src/plugins/builtin/market-valuation/index.tsx +3 -3
  508. package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
  509. package/src/plugins/builtin/members/headless.ts +8 -3
  510. package/src/plugins/builtin/members/model.ts +31 -3
  511. package/src/plugins/builtin/members/pane.tsx +6 -8
  512. package/src/plugins/builtin/mna/distress/headless.ts +4 -4
  513. package/src/plugins/builtin/mna/headless.ts +18 -6
  514. package/src/plugins/builtin/money-markets/headless.ts +5 -4
  515. package/src/plugins/builtin/money-markets/index.tsx +2 -0
  516. package/src/plugins/builtin/money-markets/series.ts +41 -0
  517. package/src/plugins/builtin/news/index.tsx +0 -2
  518. package/src/plugins/builtin/news/wire/news/detail-view.tsx +53 -104
  519. package/src/plugins/builtin/news/wire/news/footer.ts +2 -25
  520. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +0 -2
  521. package/src/plugins/builtin/notes/cli.ts +12 -7
  522. package/src/plugins/builtin/notes/index.tsx +1 -0
  523. package/src/plugins/builtin/options/index.tsx +3 -0
  524. package/src/plugins/builtin/options/view.tsx +1 -1
  525. package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
  526. package/src/plugins/builtin/options-positioning/client.ts +7 -3
  527. package/src/plugins/builtin/options-positioning/headless.ts +4 -3
  528. package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
  529. package/src/plugins/builtin/options-scenario/client.ts +2 -1
  530. package/src/plugins/builtin/options-scenario/headless.ts +1 -0
  531. package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
  532. package/src/plugins/builtin/perps/client.ts +90 -16
  533. package/src/plugins/builtin/perps/equity-row.tsx +52 -17
  534. package/src/plugins/builtin/perps/freshness.ts +66 -0
  535. package/src/plugins/builtin/perps/headless.ts +47 -32
  536. package/src/plugins/builtin/perps/index.tsx +23 -4
  537. package/src/plugins/builtin/perps/list-perpetuals.ts +118 -0
  538. package/src/plugins/builtin/perps/model.ts +20 -5
  539. package/src/plugins/builtin/perps/open-market.ts +15 -0
  540. package/src/plugins/builtin/perps/pane.tsx +76 -20
  541. package/src/plugins/builtin/perps/series.ts +76 -0
  542. package/src/plugins/builtin/plugin-marketplace/index.tsx +1 -1
  543. package/src/plugins/builtin/plugin-marketplace/pane.tsx +73 -67
  544. package/src/plugins/builtin/plugin-module.ts +3 -0
  545. package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
  546. package/src/plugins/builtin/portfolio-list/allocation.ts +5 -4
  547. package/src/plugins/builtin/portfolio-list/cell-version.ts +8 -1
  548. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +161 -0
  549. package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
  550. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +384 -74
  551. package/src/plugins/builtin/portfolio-list/cli/render.ts +437 -71
  552. package/src/plugins/builtin/portfolio-list/cli/shared.ts +21 -4
  553. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +121 -47
  554. package/src/plugins/builtin/portfolio-list/column-values.ts +104 -26
  555. package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
  556. package/src/plugins/builtin/portfolio-list/headless.ts +84 -18
  557. package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
  558. package/src/plugins/builtin/portfolio-list/index.tsx +5 -1
  559. package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
  560. package/src/plugins/builtin/portfolio-list/mutations.ts +2 -9
  561. package/src/plugins/builtin/portfolio-list/pane/data.ts +5 -1
  562. package/src/plugins/builtin/portfolio-list/pane/index.tsx +52 -9
  563. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +33 -11
  564. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +21 -1
  565. package/src/plugins/builtin/portfolio-list/settings.ts +17 -0
  566. package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
  567. package/src/plugins/builtin/portfolio-list/summary/totals.ts +9 -4
  568. package/src/plugins/builtin/portfolio-list/table.tsx +37 -7
  569. package/src/plugins/builtin/power/client.ts +16 -0
  570. package/src/plugins/builtin/power/evidence.ts +63 -10
  571. package/src/plugins/builtin/power/headless.ts +77 -9
  572. package/src/plugins/builtin/power/index.tsx +28 -3
  573. package/src/plugins/builtin/power/model.ts +73 -2
  574. package/src/plugins/builtin/power/pane.tsx +37 -9
  575. package/src/plugins/builtin/power/prices-client.ts +139 -0
  576. package/src/plugins/builtin/power/prices-headless.ts +137 -0
  577. package/src/plugins/builtin/power/prices-model.ts +103 -0
  578. package/src/plugins/builtin/power/prices.tsx +179 -0
  579. package/src/plugins/builtin/rate-path/headless.ts +46 -18
  580. package/src/plugins/builtin/realized-vol/headless.ts +5 -0
  581. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  582. package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
  583. package/src/plugins/builtin/research/event-model.ts +68 -24
  584. package/src/plugins/builtin/research/events-headless.ts +2 -1
  585. package/src/plugins/builtin/research/headless.ts +2 -1
  586. package/src/plugins/builtin/research/index.tsx +2 -0
  587. package/src/plugins/builtin/research-search/article.ts +65 -0
  588. package/src/plugins/builtin/research-search/document-view.tsx +33 -215
  589. package/src/plugins/builtin/research-search/model.ts +0 -9
  590. package/src/plugins/builtin/research-search/pane.tsx +41 -30
  591. package/src/plugins/builtin/research-search/snippet.ts +0 -29
  592. package/src/plugins/builtin/revenue-breakdown/headless.ts +18 -6
  593. package/src/plugins/builtin/seasonality/client.ts +15 -7
  594. package/src/plugins/builtin/seasonality/headless.ts +36 -12
  595. package/src/plugins/builtin/seasonality/index.tsx +2 -2
  596. package/src/plugins/builtin/seasonality/model.ts +119 -0
  597. package/src/plugins/builtin/seasonality/pane.tsx +108 -37
  598. package/src/plugins/builtin/sec/headless.ts +25 -3
  599. package/src/plugins/builtin/sec/index.tsx +2 -1
  600. package/src/plugins/builtin/series-inventory.ts +93 -0
  601. package/src/plugins/builtin/shared/cloud-denied.ts +15 -0
  602. package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
  603. package/src/plugins/builtin/shared/command-tab.ts +30 -12
  604. package/src/plugins/builtin/shared/earnings-report.ts +55 -0
  605. package/src/plugins/builtin/shared/fred-points.ts +20 -0
  606. package/src/plugins/builtin/shared/free-preview.ts +47 -0
  607. package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
  608. package/src/plugins/builtin/shared/open-description.ts +29 -0
  609. package/src/plugins/builtin/shared/report-freshness.ts +12 -1
  610. package/src/plugins/builtin/shared/series-warning.ts +5 -0
  611. package/src/plugins/builtin/shared/volatility/options.ts +4 -1
  612. package/src/plugins/builtin/short-interest/headless.ts +1 -0
  613. package/src/plugins/builtin/short-interest/index.tsx +2 -0
  614. package/src/plugins/builtin/short-interest/series.ts +36 -0
  615. package/src/plugins/builtin/short-interest/watch-headless.ts +3 -2
  616. package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
  617. package/src/plugins/builtin/short-volume/headless.ts +2 -1
  618. package/src/plugins/builtin/short-volume/index.tsx +2 -0
  619. package/src/plugins/builtin/short-volume/series.ts +23 -0
  620. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  621. package/src/plugins/builtin/social-mentions/index.tsx +2 -0
  622. package/src/plugins/builtin/social-mentions/series.ts +23 -0
  623. package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
  624. package/src/plugins/builtin/supply-chain/headless.ts +43 -11
  625. package/src/plugins/builtin/supply-chain/model.ts +13 -0
  626. package/src/plugins/builtin/supply-chain/pane.tsx +8 -6
  627. package/src/plugins/builtin/themes/headless.ts +74 -10
  628. package/src/plugins/builtin/themes/model.ts +71 -4
  629. package/src/plugins/builtin/themes/pane.tsx +22 -5
  630. package/src/plugins/builtin/thirteenf/api.ts +1 -0
  631. package/src/plugins/builtin/thirteenf/data.ts +6 -4
  632. package/src/plugins/builtin/thirteenf/headless.ts +217 -36
  633. package/src/plugins/builtin/thirteenf/model.ts +39 -1
  634. package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
  635. package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
  636. package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
  637. package/src/plugins/builtin/thirteenf/types.ts +2 -0
  638. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +114 -0
  639. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +57 -2
  640. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +19 -55
  641. package/src/plugins/builtin/ticker-detail/headless-schema.ts +30 -0
  642. package/src/plugins/builtin/ticker-detail/headless.ts +86 -0
  643. package/src/plugins/builtin/ticker-detail/index.tsx +26 -11
  644. package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
  645. package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
  646. package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
  647. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
  648. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +49 -5
  649. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
  650. package/src/plugins/builtin/time-sales/headless.ts +2 -1
  651. package/src/plugins/builtin/time-sales/index.tsx +2 -1
  652. package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
  653. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  654. package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
  655. package/src/plugins/builtin/vol-surface/evidence.ts +7 -1
  656. package/src/plugins/builtin/vol-surface/headless.ts +51 -17
  657. package/src/plugins/builtin/vol-surface/heat-grid.tsx +113 -0
  658. package/src/plugins/builtin/vol-surface/model.ts +39 -6
  659. package/src/plugins/builtin/vol-surface/pane.tsx +14 -13
  660. package/src/plugins/builtin/vol-surface/stored.ts +63 -3
  661. package/src/plugins/builtin/vol-surface/surface.tsx +30 -2
  662. package/src/plugins/builtin/volatility/index.tsx +2 -0
  663. package/src/plugins/builtin/volatility/series.ts +84 -0
  664. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +6 -5
  665. package/src/plugins/builtin/world-venue-map/basemap.ts +6 -3
  666. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +2 -1
  667. package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
  668. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +6 -6
  669. package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
  670. package/src/plugins/builtin/world-venue-map/index.ts +2 -1
  671. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +24 -13
  672. package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
  673. package/src/plugins/builtin/world-venue-map/map.tsx +144 -13
  674. package/src/plugins/builtin/world-venue-map/model.ts +56 -19
  675. package/src/plugins/builtin/world-venue-map/pane.tsx +5 -2
  676. package/src/plugins/builtin/yield-curve/chart.ts +2 -2
  677. package/src/plugins/builtin/yield-curve/curves.ts +21 -8
  678. package/src/plugins/builtin/yield-curve/headless.ts +71 -18
  679. package/src/plugins/builtin/yield-curve/index.tsx +4 -2
  680. package/src/plugins/builtin/yield-curve/series.ts +86 -0
  681. package/src/plugins/catalog-browser.ts +2 -1
  682. package/src/plugins/installer.ts +2 -2
  683. package/src/plugins/loader.ts +15 -0
  684. package/src/plugins/manager-node.ts +1 -1
  685. package/src/plugins/{builtin/plugin-marketplace → marketplace}/feed.ts +2 -2
  686. package/src/plugins/{builtin/plugin-marketplace → marketplace}/model.ts +6 -6
  687. package/src/plugins/{builtin/plugin-marketplace → marketplace}/store.ts +1 -1
  688. package/src/plugins/registry/host-actions.ts +11 -0
  689. package/src/plugins/registry/index.ts +89 -4
  690. package/src/portfolio/collection-name.ts +31 -0
  691. package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
  692. package/src/portfolio/position-value.ts +40 -0
  693. package/src/portfolio/valuation-basis.ts +79 -0
  694. package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
  695. package/src/public/time-series.ts +17 -0
  696. package/src/react/auto-refresh.ts +13 -3
  697. package/src/react/input.ts +33 -1
  698. package/src/react/key-repeat.ts +86 -0
  699. package/src/remote/app-host.tsx +20 -4
  700. package/src/remote/confirm-change.tsx +85 -0
  701. package/src/remote/controller-utils.ts +8 -1
  702. package/src/remote/controller.ts +144 -7
  703. package/src/remote/pane-screenshot.ts +47 -0
  704. package/src/remote/schema.ts +65 -2
  705. package/src/remote/types.ts +30 -0
  706. package/src/remote/watchlist.ts +306 -0
  707. package/src/renderers/browser/main.tsx +4 -3
  708. package/src/renderers/browser/research-entry.ts +4 -2
  709. package/src/renderers/cloudflare/worker.ts +1 -1
  710. package/src/renderers/dom/data-table/frozen-column.ts +3 -1
  711. package/src/renderers/dom/data-table/index.tsx +33 -6
  712. package/src/renderers/dom/data-table/row.tsx +30 -1
  713. package/src/renderers/dom/desktop/control-styles.ts +63 -1
  714. package/src/renderers/dom/desktop/controls.tsx +30 -23
  715. package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
  716. package/src/renderers/dom/desktop/icons.tsx +16 -0
  717. package/src/renderers/dom/dom-ui-host.tsx +3 -0
  718. package/src/renderers/dom/host/block-glyph-canvas.tsx +6 -3
  719. package/src/renderers/dom/host/box.tsx +8 -0
  720. package/src/renderers/dom/host/chart-surface.tsx +33 -2
  721. package/src/renderers/dom/host/live-frame.ts +6 -2
  722. package/src/renderers/dom/host/overflow-fade.ts +14 -1
  723. package/src/renderers/dom/host/scroll-box.tsx +32 -0
  724. package/src/renderers/dom/host/size-observer.ts +41 -0
  725. package/src/renderers/dom/host/sticky-scroll.ts +128 -0
  726. package/src/renderers/dom/host/style.ts +1 -1
  727. package/src/renderers/dom/host/surface-3d.tsx +19 -7
  728. package/src/renderers/dom/host/surface-fit.ts +406 -0
  729. package/src/renderers/dom/host/text.tsx +1 -1
  730. package/src/renderers/dom/input-host.tsx +1 -0
  731. package/src/renderers/dom/styles.css +102 -5
  732. package/src/renderers/electrobun/bun/application-menu/click.ts +18 -0
  733. package/src/renderers/electrobun/bun/index.ts +3 -2
  734. package/src/renderers/electrobun/shared/protocol.ts +1 -1
  735. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +29 -2
  736. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +63 -20
  737. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
  738. package/src/renderers/electrobun/view/main.tsx +5 -3
  739. package/src/renderers/electrobun/view/remote/asset-data-client.ts +2 -0
  740. package/src/renderers/opentui/chart-surface.tsx +1 -0
  741. package/src/renderers/opentui/frame-capture.ts +33 -0
  742. package/src/renderers/opentui/host.tsx +10 -13
  743. package/src/renderers/opentui/pane-image/glyphs-license.txt +107 -0
  744. package/src/renderers/opentui/pane-image/glyphs.json +1 -0
  745. package/src/renderers/opentui/pane-image/index.ts +107 -0
  746. package/src/renderers/opentui/pane-image/raster.ts +428 -0
  747. package/src/renderers/opentui/start.tsx +3 -3
  748. package/src/renderers/opentui/test-utils.tsx +3 -0
  749. package/src/renderers/opentui/ui-host.tsx +29 -1
  750. package/src/series/capability.ts +75 -0
  751. package/src/series/csv.ts +160 -0
  752. package/src/series/files.ts +107 -0
  753. package/src/series/registry.ts +198 -0
  754. package/src/series/resolve.ts +108 -0
  755. package/src/series/standalone.ts +48 -0
  756. package/src/series/types.ts +153 -0
  757. package/src/series/validate.ts +171 -0
  758. package/src/shares/chat.ts +34 -0
  759. package/src/shares/dialog.tsx +295 -0
  760. package/src/shares/flow.tsx +56 -0
  761. package/src/shares/live.ts +14 -22
  762. package/src/shares/pane-image.ts +152 -0
  763. package/src/shares/personal-pane.ts +64 -0
  764. package/src/shares/portable-layout.ts +4 -2
  765. package/src/shares/prepare.ts +20 -0
  766. package/src/sources/gloomberb-cloud/index.ts +109 -10
  767. package/src/sources/gloomberb-cloud/normalizers.ts +3 -0
  768. package/src/sources/gloomberb-cloud/perpetuals.ts +249 -0
  769. package/src/sources/history-result.ts +1 -0
  770. package/src/sources/history-retention.ts +11 -0
  771. package/src/sources/listing-symbols.ts +4 -1
  772. package/src/sources/listing-target.ts +4 -0
  773. package/src/sources/provider-router/batches.ts +4 -1
  774. package/src/sources/provider-router/cache.ts +4 -2
  775. package/src/sources/provider-router/cached-routes.ts +1 -1
  776. package/src/sources/provider-router/financial-routes.ts +1 -0
  777. package/src/sources/provider-router/financials.ts +13 -0
  778. package/src/sources/provider-router/history.ts +43 -25
  779. package/src/sources/provider-router/index.ts +14 -0
  780. package/src/sources/provider-router/search.ts +1 -0
  781. package/src/state/hooks/inline-tickers.ts +49 -11
  782. package/src/sync/core-contributors.ts +1 -0
  783. package/src/test-support/shell-pane-keys.tsx +1 -0
  784. package/src/theme/theme-context.tsx +15 -1
  785. package/src/tickers/company-name.ts +75 -0
  786. package/src/tickers/instrument-kind.ts +34 -1
  787. package/src/tickers/listing-name.ts +34 -0
  788. package/src/tickers/listing-venues.ts +132 -0
  789. package/src/tickers/open-target.ts +13 -4
  790. package/src/tickers/perpetual-listing.ts +151 -0
  791. package/src/tickers/prediction-listing.ts +26 -0
  792. package/src/tickers/search/index.ts +72 -11
  793. package/src/tickers/search/ranking.ts +98 -20
  794. package/src/tickers/search/types.ts +7 -1
  795. package/src/tickers/ticker-changes.ts +213 -0
  796. package/src/tickers/us-listing.ts +34 -0
  797. package/src/time-series/economic.ts +2 -6
  798. package/src/time-series/hooks.ts +9 -0
  799. package/src/time-series/ice-credit.ts +39 -0
  800. package/src/time-series/resolve.ts +211 -29
  801. package/src/time-series/studies.ts +1 -0
  802. package/src/time-series/types.ts +12 -2
  803. package/src/time-series/use-chart-resolution.ts +32 -0
  804. package/src/types/config.ts +10 -2
  805. package/src/types/data-provider.ts +11 -0
  806. package/src/types/financials.ts +62 -1
  807. package/src/types/headless.ts +113 -0
  808. package/src/types/instrument.ts +4 -0
  809. package/src/types/plugin.ts +52 -2
  810. package/src/types/price-history.ts +7 -0
  811. package/src/types/ticker-changes.ts +26 -0
  812. package/src/types/ticker.ts +14 -0
  813. package/src/ui/context-menu.tsx +13 -1
  814. package/src/ui/host.tsx +57 -3
  815. package/src/ui/live-resize.ts +91 -0
  816. package/src/ui/live-stretch.ts +153 -0
  817. package/src/ui/measured-cells.ts +58 -0
  818. package/src/ui/sticky-scroll.ts +7 -0
  819. package/src/utils/basis-points.ts +17 -0
  820. package/src/utils/central-banks.ts +76 -0
  821. package/src/utils/cli-output.ts +131 -15
  822. package/src/utils/concurrency.ts +25 -0
  823. package/src/utils/crypto-pair.ts +20 -0
  824. package/src/utils/currency-pair.ts +19 -0
  825. package/src/utils/currency-units.ts +11 -0
  826. package/src/utils/datetime-format.ts +27 -6
  827. package/src/utils/decimal-comma.ts +19 -0
  828. package/src/utils/dom-screenshot-css.ts +265 -0
  829. package/src/utils/dom-screenshot-rasters.ts +276 -0
  830. package/src/utils/dom-screenshot.ts +439 -52
  831. package/src/utils/double-escape-close.ts +15 -1
  832. package/src/utils/exchange-rate-snapshot.ts +6 -2
  833. package/src/utils/exchanges.ts +143 -1
  834. package/src/utils/format.ts +35 -5
  835. package/src/utils/forward-dividend.ts +14 -0
  836. package/src/utils/fundamentals.ts +66 -0
  837. package/src/utils/futures-generic.ts +6 -12
  838. package/src/utils/fx-market-hours.ts +15 -5
  839. package/src/utils/fx-status.ts +6 -1
  840. package/src/utils/inline-content-tokenizer.ts +22 -3
  841. package/src/utils/market-capitalization.ts +8 -0
  842. package/src/utils/one-shot.ts +73 -0
  843. package/src/utils/options.ts +37 -8
  844. package/src/utils/price-history.ts +99 -42
  845. package/src/utils/sec-eps-basis.ts +24 -11
  846. package/src/utils/sec.ts +61 -7
  847. package/src/utils/utc-time.ts +63 -4
  848. package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
  849. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/activation.ts +0 -0
  850. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/ids.ts +0 -0
  851. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/packs.ts +0 -0
  852. /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
@@ -1,4 +1,6 @@
1
1
  import { createBaseConverter } from "../../../../cli/base-converter";
2
+ import { savedListingName } from "../../../../cli/listing-arg";
3
+ import { currentListingName } from "../../../../tickers/listing-name";
2
4
  import { withMarketData } from "../../../../cli/scoped-context";
3
5
  import { countCollectionTickers } from "../../../../cli/helpers";
4
6
  import {
@@ -8,9 +10,18 @@ import {
8
10
  renderTable,
9
11
  } from "../../../../utils/cli-output";
10
12
  import { formatCompact, formatPercentRaw } from "../../../../utils/format";
11
- import { currencyMinorDigits, formatMarketCostWithCurrency, formatMarketPriceWithCurrency, formatMarketQuantity, quoteFormatOptions } from "../../../../market-data/market/format";
13
+ import { currencyPriceMinDigits, formatMarketCostWithCurrency, formatMarketPrice, formatMarketPriceWithCurrency, formatMarketQuantity, quoteFormatOptions } from "../../../../market-data/market/format";
12
14
  import { resolvePriceBasis } from "../../../../market-data/market/price-basis";
13
- import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue, resolvePortfolioPositionPnl } from "../position-metrics";
15
+ import { getPortfolioPositionMetrics, resolvePortfolioMarketValue, resolvePortfolioPositionPnl } from "../../../../portfolio/position-metrics";
16
+ import {
17
+ getPortfolioValuationDisplay,
18
+ getPortfolioValuationTime,
19
+ VALUATION_BASIS_LABELS,
20
+ valuationTimeIso,
21
+ type PortfolioValuationBasis,
22
+ } from "../../../../portfolio/valuation-basis";
23
+ import { formatUtcDayTime, parseReportTime } from "../../../../utils/utc-time";
24
+ import { quoteFreshnessFields } from "../../shared/report-freshness";
14
25
  import { resolvePortfolioTotalsCurrency } from "../summary/totals";
15
26
  import {
16
27
  EXTENDED_SESSION_LABELS,
@@ -21,16 +32,25 @@ import {
21
32
  type ExtendedSession,
22
33
  type ExtendedSessionDisplay,
23
34
  } from "../../../../market-data/market/status";
24
- import { headlineQuoteDisplay } from "../column-values";
35
+ import { formatPerpetualRate, headlineQuoteDisplay } from "../column-values";
25
36
  import type { AppConfig } from "../../../../types/config";
26
37
  import type { DataProvider } from "../../../../types/data-provider";
27
38
  import type { CliCommandContext } from "../../../../types/plugin";
28
39
  import type { MarketContext } from "../../../../cli/types";
29
40
  import type { Portfolio, TickerPosition, TickerRecord } from "../../../../types/ticker";
41
+ import type { TickerFinancials } from "../../../../types/financials";
42
+ import type { TickerInstrumentKind } from "../../../../types/instrument";
43
+ import { resolveTickerInstrumentKind } from "../../../../tickers/instrument-kind";
44
+ import { perpetualListing } from "../../../../tickers/perpetual-listing";
45
+ import { loadPerpetualMarket } from "../../../../sources/gloomberb-cloud/perpetuals";
46
+ import { listedMarketCapitalization, selectMarketCapitalization } from "../../../../utils/market-capitalization";
47
+ import { chunked, mapWithConcurrency } from "../../../../utils/concurrency";
48
+ import { liveMarketCapitalization } from "../live-valuation";
30
49
  import { instrumentFromTicker } from "../../../../market-data/request-types";
31
50
  import { toMarketDataContext } from "../../../../market-data/selectors";
32
51
  import { cliFreshnessFooter } from "../../../../cli/result";
33
- import { quotesFreshness } from "../../../../cli/freshness";
52
+ import { renderReportGlossary, reportGlossary, type GlossaryEntry } from "../../../../cli/glossary";
53
+ import { quotesFreshness, rowsFreshness } from "../../../../cli/freshness";
34
54
  import type { BrokerAccount } from "../../../../types/trading";
35
55
  import {
36
56
  BROKER_ACCOUNT_MISSING_NOTE,
@@ -47,20 +67,31 @@ import {
47
67
  type PortfolioAllocation,
48
68
  } from "../allocation";
49
69
  import { findCachedPortfolioAccount } from "../cached-account";
70
+ import { importedFromText } from "../imported-from";
71
+ import { findNamedCollection } from "../../../../portfolio/collection-name";
50
72
 
51
73
  /** Money prices pad to the currency's minor unit ($337.90, not $337.9), as the ticker view does. */
52
74
  function priceFormatOptions(quote: Parameters<typeof quoteFormatOptions>[0] & { currency?: string }, assetCategory?: string) {
53
75
  const options = quoteFormatOptions(quote, assetCategory);
54
76
  return resolvePriceBasis(options.priceBasis, options.assetCategory) === "per-unit"
55
- ? { ...options, minimumFractionDigits: Math.min(2, currencyMinorDigits(quote?.currency)) }
77
+ ? { ...options, minimumFractionDigits: currencyPriceMinDigits(quote?.currency) }
56
78
  : options;
57
79
  }
58
80
 
81
+ /** A Last as `quote` prints it: money in the quote's currency, and an index level in points, padded to two decimals. */
82
+ function formatListPrice(value: number, quote: Parameters<typeof quoteFormatOptions>[0] & { currency: string }, assetCategory?: string): string {
83
+ const options = priceFormatOptions(quote, assetCategory);
84
+ return options.assetCategory?.trim().toUpperCase() === "INDEX"
85
+ ? formatMarketPrice(value, { ...options, minimumFractionDigits: 2 })
86
+ : formatMarketPriceWithCurrency(value, quote.currency, options);
87
+ }
88
+
59
89
  /**
60
90
  * What a collection's Last and Chg show, as the PF and watchlist panes do: the
61
91
  * traded price until the regular close, then that close and its move, with a
62
92
  * pre-market or after-hours print beside them, measured from that close.
63
- * Market value and P&L keep the live price.
93
+ * Market value and P&L keep the latest print unless the book is valued at the
94
+ * regular close (`portfolio show --close`), which is this Last.
64
95
  */
65
96
  export function collectionQuoteHeadline(quote: CollectionQuote | undefined): {
66
97
  headline: ActiveQuoteDisplay | null;
@@ -82,6 +113,20 @@ export function extendedQuoteFields(extended: ExtendedSessionDisplay | null) {
82
113
  };
83
114
  }
84
115
 
116
+ /**
117
+ * The listing a row is (SAN:EPA, as the Ticker column names it) and its company, as export fields:
118
+ * the name its quote gives now, as `quote` names it, else the one it was saved with.
119
+ */
120
+ function listingColumns(ticker: TickerRecord, quote: CollectionQuote | undefined) {
121
+ return { listing: listingLabel(ticker), name: currentListingName(ticker, quote) };
122
+ }
123
+
124
+ /** The Chg column's move as export fields: from the close the headline price is measured against. */
125
+ function quoteMoveColumns(headline: ActiveQuoteDisplay | null) {
126
+ const known = (value: number | undefined) => value != null && Number.isFinite(value) ? value : null;
127
+ return { quoteChange: known(headline?.change), quoteChangePercent: known(headline?.changePercent) };
128
+ }
129
+
85
130
  /** The extended sessions among the rows' prints, in the order their columns go. */
86
131
  export function extendedSessionsOf(sessions: Iterable<ExtendedSession | null | undefined>): ExtendedSession[] {
87
132
  const present = new Set(sessions);
@@ -129,8 +174,23 @@ export function renderCollectionOverview(config: AppConfig, tickers: TickerRecor
129
174
  return blocks.join("\n");
130
175
  }
131
176
 
132
- export async function showCollection(name: string, ctx: CliCommandContext) {
133
- await withMarketData(ctx, (context) => showCollectionWithMarketData(name, ctx, context));
177
+ export interface ShowCollectionOptions {
178
+ /** `--explain`: follow the report with what the terms it shows mean, as `fn <function> --explain` does. */
179
+ explain?: boolean;
180
+ /** What a portfolio's value is priced at; the latest print when absent. */
181
+ basis?: PortfolioValuationBasis;
182
+ }
183
+
184
+ export async function showCollection(name: string, ctx: CliCommandContext, options: ShowCollectionOptions = {}) {
185
+ await withMarketData(ctx, (context) => showCollectionWithMarketData(name, ctx, context, options));
186
+ }
187
+
188
+ /** What `--explain` names a report by when it shows no terms. */
189
+ const EXPLAINED_REPORT = "This report";
190
+
191
+ /** The terms as `--json` lists them, in the shape `fn --explain` gives `data.glossary`. */
192
+ function glossaryExport(entries: readonly GlossaryEntry[]) {
193
+ return entries.map(({ term, short, definition }) => ({ term, ...(short ? { short } : {}), definition }));
134
194
  }
135
195
 
136
196
  /** A portfolio or watchlist named by its id or its name, either in any case. */
@@ -139,14 +199,9 @@ export type CollectionMatch =
139
199
  | { kind: "watchlist"; id: string; name: string };
140
200
 
141
201
  export function findCollection(config: AppConfig, name: string): CollectionMatch | null {
142
- const normalized = name.trim().toLowerCase();
143
- const portfolio = config.portfolios.find((entry) =>
144
- entry.id.toLowerCase() === normalized || entry.name.toLowerCase() === normalized
145
- );
202
+ const portfolio = findNamedCollection(config.portfolios, name);
146
203
  if (portfolio) return { kind: "portfolio", id: portfolio.id, name: portfolio.name, portfolio };
147
- const watchlist = config.watchlists.find((entry) =>
148
- entry.id.toLowerCase() === normalized || entry.name.toLowerCase() === normalized
149
- );
204
+ const watchlist = findNamedCollection(config.watchlists, name);
150
205
  return watchlist ? { kind: "watchlist", id: watchlist.id, name: watchlist.name } : null;
151
206
  }
152
207
 
@@ -184,7 +239,7 @@ export async function loadCollectionQuotes(
184
239
  interface PortfolioPositionValue {
185
240
  ticker: TickerRecord;
186
241
  quote: CollectionQuote | undefined;
187
- /** The live print, which values the position. */
242
+ /** The live print, extended hours included. */
188
243
  activeQuote: ReturnType<typeof getActiveQuoteDisplay> | null;
189
244
  /** Last and Chg as shown: the regular session's (collectionQuoteHeadline). */
190
245
  headline: ActiveQuoteDisplay | null;
@@ -195,11 +250,28 @@ interface PortfolioPositionValue {
195
250
  metrics: ReturnType<typeof getPortfolioPositionMetrics> | null;
196
251
  /** Unrealized P&L in the portfolio's currency. */
197
252
  pnl: number | null;
253
+ /** The price the position is valued at, on the valuation's basis, and when it is from. */
254
+ valuation: PortfolioValuationFields;
255
+ /**
256
+ * The position's move over the day of that price, in the portfolio's
257
+ * currency, and its value at the close before; null when either is unknown.
258
+ */
259
+ dayMove: { change: number; previousValue: number } | null;
198
260
  /** The `portfolio show --json` row. */
199
261
  row: Record<string, unknown>;
200
262
  }
201
263
 
264
+ /** What a row's value is priced at: exported after every other field, so existing columns keep their places. */
265
+ interface PortfolioValuationFields {
266
+ valuationBasis: PortfolioValuationBasis;
267
+ /** In the quote's currency. */
268
+ valuationPrice: number | null;
269
+ /** An ISO instant in UTC, or an ISO date for a close whose hour the venue's calendar does not give. */
270
+ valuationAsOf: string | null;
271
+ }
272
+
202
273
  export interface PortfolioValuation {
274
+ basis: PortfolioValuationBasis;
203
275
  positions: PortfolioPositionValue[];
204
276
  /** One per ticker, for weights and targets. */
205
277
  holdings: AllocationHolding[];
@@ -213,7 +285,8 @@ export interface PortfolioValuation {
213
285
  /**
214
286
  * Values every position of a portfolio in the portfolio's currency, as
215
287
  * `portfolio show` prints it: cost basis, market value and unrealized P&L
216
- * from the current quote, or the broker's snapshot where that is the basis.
288
+ * from the current quote at `basis` (the latest print unless given), or the
289
+ * broker's snapshot where that is the basis.
217
290
  */
218
291
  export async function valuePortfolioPositions({
219
292
  tickers,
@@ -222,6 +295,7 @@ export async function valuePortfolioPositions({
222
295
  currency,
223
296
  baseCurrency,
224
297
  toBase,
298
+ basis = "latest-print",
225
299
  }: {
226
300
  tickers: readonly TickerRecord[];
227
301
  quotes: ReadonlyMap<string, CollectionQuote>;
@@ -229,6 +303,7 @@ export async function valuePortfolioPositions({
229
303
  currency: string;
230
304
  baseCurrency: string;
231
305
  toBase: (value: number, fromCurrency: string) => Promise<number>;
306
+ basis?: PortfolioValuationBasis;
232
307
  }): Promise<PortfolioValuation> {
233
308
  const id = portfolioId;
234
309
  let totalPnl = 0;
@@ -249,12 +324,15 @@ export async function valuePortfolioPositions({
249
324
 
250
325
  if (positions.length === 0) {
251
326
  const quoteCurrency = quote?.currency || ticker.metadata.currency || baseCurrency;
252
- const unitPrice = activeQuote ? known(await toBase(activeQuote.price, quoteCurrency)) : null;
327
+ // What a holding would be priced at, for the trade that reaches its target.
328
+ const priced = basis === "regular-close" ? headline : activeQuote;
329
+ const unitPrice = priced ? known(await toBase(priced.price, quoteCurrency)) : null;
253
330
  holdings.push({ symbol: ticker.metadata.ticker, held: false, marketValue: 0, units: 0, unitPrice });
254
- values.push({ ticker, quote, activeQuote, headline, extended, position: null, metrics: null, pnl: null,
255
- row: { symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange, shares: null, avgCost: null,
256
- positionCurrency: null, quotePrice: known(headline?.price), ...extendedQuoteFields(extended), quoteCurrency: quote?.currency ?? null,
257
- costBasis: null, marketValue: null, unrealizedPnl: null, baseCurrency: currency } });
331
+ values.push({ ticker, quote, activeQuote, headline, extended, position: null, metrics: null, pnl: null, dayMove: null,
332
+ valuation: { valuationBasis: basis, valuationPrice: known(priced?.price), valuationAsOf: valuationTimeIso(getPortfolioValuationTime(quote, basis)) },
333
+ row: { symbol: ticker.metadata.ticker, ...listingColumns(ticker, quote), exchange: ticker.metadata.exchange, shares: null, avgCost: null,
334
+ positionCurrency: null, quotePrice: known(headline?.price), ...quoteMoveColumns(headline), ...extendedQuoteFields(extended), quoteCurrency: quote?.currency ?? null,
335
+ volume: known(quote?.volume), costBasis: null, marketValue: null, unrealizedPnl: null, baseCurrency: currency } });
258
336
  continue;
259
337
  }
260
338
 
@@ -263,7 +341,7 @@ export async function valuePortfolioPositions({
263
341
  for (const position of positions) {
264
342
  const quoteCurrency = quote?.currency || ticker.metadata.currency || baseCurrency;
265
343
  const metrics = getPortfolioPositionMetrics({ ...ticker, metadata: { ...ticker.metadata, positions: [position] } }, id, quoteCurrency, undefined, quote);
266
- const valuationQuote = getPortfolioQuoteDisplay(metrics, quote);
344
+ const valuationQuote = getPortfolioValuationDisplay(metrics, quote, basis);
267
345
  const positionCurrency = metrics.positionCurrency;
268
346
  const costBasisBase = positionCurrency ? await toBase(metrics.totalCost, positionCurrency) : Number.NaN;
269
347
 
@@ -285,19 +363,56 @@ export async function valuePortfolioPositions({
285
363
  holding.marketValue = holding.marketValue == null || marketValue == null ? null : holding.marketValue + marketValue;
286
364
  holding.units += metrics.totalShares;
287
365
  holding.unitPrice ??= valuationQuote ? known(await toBase(valuationQuote.price, quoteCurrency)) : null;
366
+ const change = valuationQuote?.change;
367
+ const dayMove = valuationQuote && change != null && Number.isFinite(change)
368
+ ? { change: await toBase(metrics.totalPriceUnits * change, quoteCurrency),
369
+ previousValue: await toBase(metrics.grossPriceUnits * (valuationQuote.price - change), quoteCurrency) }
370
+ : null;
288
371
  values.push({ ticker, quote, activeQuote, headline, extended, position, metrics, pnl,
289
- row: { symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange,
372
+ dayMove: dayMove && Number.isFinite(dayMove.change) && Number.isFinite(dayMove.previousValue) ? dayMove : null,
373
+ valuation: { valuationBasis: basis, valuationPrice: known(valuationQuote?.price),
374
+ valuationAsOf: valuationTimeIso(getPortfolioValuationTime(quote, basis, metrics)) },
375
+ row: { symbol: ticker.metadata.ticker, ...listingColumns(ticker, quote), exchange: ticker.metadata.exchange,
290
376
  shares: metrics.totalShares, avgCost: known(position.avgCost), positionCurrency,
291
377
  priceBasis: position.priceBasis ?? null, quantityUnit: metrics.priceBasis === "percent-of-par" ? "face" : null, quotePriceBasis: quote?.priceBasis ?? null,
292
- // Last as shown, the regular close after it; market value and P&L are at the live price.
293
- quotePrice: known(headline?.price), ...extendedQuoteFields(extended), quoteCurrency, quoteAsOf: quote?.lastUpdated ?? null,
378
+ // Last as shown, the regular close after it; market value and P&L are at `valuationPrice`.
379
+ quotePrice: known(headline?.price), ...quoteMoveColumns(headline), ...extendedQuoteFields(extended), quoteCurrency,
380
+ volume: known(quote?.volume), quoteAsOf: quote?.lastUpdated ?? null,
294
381
  costBasis: known(direction * costBasisBase), marketValue,
295
382
  unrealizedPnl: known(pnl), baseCurrency: currency, dateAcquired: position.dateAcquired ?? null,
296
383
  pnlBasis: selectedPnl.basis, brokerUnrealizedPnl: known(position.unrealizedPnl), brokerPnlCurrency: positionCurrency, brokerPnlAsOf: null } });
297
384
  }
298
385
  }
299
386
 
300
- return { positions: values, holdings, totalPnl, unavailablePnl, unavailableCost, unavailableMarketValue, brokerPnlSymbols };
387
+ return { basis, positions: values, holdings, totalPnl, unavailablePnl, unavailableCost, unavailableMarketValue, brokerPnlSymbols };
388
+ }
389
+
390
+ /**
391
+ * The holdings' move over the day of the prices they are valued at, in the
392
+ * portfolio's currency, and that move against their value at the close
393
+ * before. Null when any priced holding's move is unknown, as the pane's Day is.
394
+ */
395
+ export function portfolioDayMove(valuation: Pick<PortfolioValuation, "positions">): { change: number; changePercent: number | null } | null {
396
+ let change = 0;
397
+ let previousValue = 0;
398
+ for (const entry of valuation.positions) {
399
+ if (!entry.position) continue;
400
+ if (!entry.dayMove) return null;
401
+ change += entry.dayMove.change;
402
+ previousValue += entry.dayMove.previousValue;
403
+ }
404
+ return { change, changePercent: previousValue !== 0 ? (change / previousValue) * 100 : null };
405
+ }
406
+
407
+ /** `Day's move: +$1,165.49 (+0.51%)`, the line a book valued at the close carries; `color` tints the figures by sign. */
408
+ export function dayMoveLine(
409
+ move: ReturnType<typeof portfolioDayMove>,
410
+ currency: string,
411
+ color: (text: string, value: number) => string = (text) => text,
412
+ ): string {
413
+ if (!move) return "Day's move: unavailable";
414
+ const percent = move.changePercent != null ? ` (${formatPercentRaw(move.changePercent)})` : "";
415
+ return `Day's move: ${color(`${formatAllocationMoney(move.change, currency, { signed: true })}${percent}`, move.change)}`;
301
416
  }
302
417
 
303
418
  /** A portfolio's cash line, as `portfolio show` and `fn PF` report it. */
@@ -344,6 +459,16 @@ function signed(text: string, value: number | null | undefined): string {
344
459
 
345
460
  const NOT_AVAILABLE = "n/a";
346
461
 
462
+ /**
463
+ * What a table row calls a listing: SAN:EPA or SAN:NYSE, as the add, remove and
464
+ * target messages name it, not the key it is stored under (SAN, SAN:XNYS),
465
+ * which leaves two listings of one symbol looking alike. JSON and CSV keep the
466
+ * stored key in `symbol`.
467
+ */
468
+ export function listingLabel(ticker: TickerRecord): string {
469
+ return savedListingName(ticker).key;
470
+ }
471
+
347
472
  /** One cell per extended column: the print and its move from the close, as `quote` prints it, or blank. */
348
473
  function extendedCells(
349
474
  sessions: readonly ExtendedSession[],
@@ -352,14 +477,64 @@ function extendedCells(
352
477
  assetCategory: string | undefined,
353
478
  ): string[] {
354
479
  return sessions.map((session) => extended?.session === session && quote
355
- ? `${formatMarketPriceWithCurrency(extended.price, quote.currency, priceFormatOptions(quote, assetCategory))} ${colorBySign(formatPercentRaw(extended.changePercent), extended.change)}`
480
+ ? `${formatListPrice(extended.price, quote, assetCategory)} ${colorBySign(formatPercentRaw(extended.changePercent), extended.change)}`
356
481
  : "");
357
482
  }
358
483
 
484
+ /**
485
+ * The observations a portfolio's value is dated by, one per quote: when the
486
+ * price it is valued at is from, with the quote's feed, delay and venue.
487
+ */
488
+ function valuationObservations(positions: readonly PortfolioPositionValue[]): Record<string, unknown>[] {
489
+ const seen = new Set<string>();
490
+ return positions.flatMap(({ ticker, quote, valuation }) => {
491
+ if (!quote || seen.has(ticker.metadata.ticker)) return [];
492
+ seen.add(ticker.metadata.ticker);
493
+ return [{ ...quoteFreshnessFields(quote), valuationAsOf: valuation.valuationAsOf }];
494
+ });
495
+ }
496
+
497
+ /**
498
+ * How current a valued portfolio is: dated by the prices its value uses, the
499
+ * regular close when it is valued there, and in exports by the oldest of them
500
+ * too (`citeOldest`), so an audit file does not date an equity's Friday price
501
+ * by the newest row. `note` names the basis where no header line does.
502
+ */
503
+ function portfolioValuationFreshness(
504
+ valuation: Pick<PortfolioValuation, "positions" | "basis">,
505
+ source: string,
506
+ { note = false, now = Date.now() }: { note?: boolean; now?: number } = {},
507
+ ) {
508
+ const observations = valuationObservations(valuation.positions);
509
+ if (observations.length === 0) return undefined;
510
+ return rowsFreshness(observations, {
511
+ source,
512
+ observedKey: "valuationAsOf",
513
+ citeOldest: true,
514
+ ...(note ? { note: `priced at ${VALUATION_BASIS_LABELS[valuation.basis]}` } : {}),
515
+ }, undefined, now);
516
+ }
517
+
518
+ /** `Priced at the latest print, extended hours included`: the line under a portfolio's title. */
519
+ function valuationBasisLine(basis: PortfolioValuationBasis): string {
520
+ return `Priced at ${VALUATION_BASIS_LABELS[basis]}`;
521
+ }
522
+
523
+ /**
524
+ * An As of cell: when the price a row's value uses is from (`valuationAsOf`),
525
+ * in UTC as its header says; a close dated only by its day says so.
526
+ */
527
+ export function valuationTimeText(value: unknown, now = Date.now()): string {
528
+ const time = parseReportTime(value);
529
+ if (!time) return "—";
530
+ return time.dateOnly ? `${formatUtcDayTime(time, now)} close` : formatUtcDayTime(time, now);
531
+ }
532
+
359
533
  async function showCollectionWithMarketData(
360
534
  name: string,
361
535
  ctx: CliCommandContext,
362
536
  { config, store, dataProvider, persistence }: MarketContext,
537
+ { explain = false, basis = "latest-print" }: ShowCollectionOptions,
363
538
  ) {
364
539
  const tickers = (await store.loadAllTickers()).sort((left, right) =>
365
540
  left.metadata.ticker.localeCompare(right.metadata.ticker)
@@ -374,10 +549,13 @@ async function showCollectionWithMarketData(
374
549
  }
375
550
 
376
551
  const matchedPortfolio = collection.kind === "portfolio" ? collection.portfolio : undefined;
552
+ if (!matchedPortfolio && basis === "regular-close") {
553
+ ctx.fail(`--close values a portfolio; "${collection.name}" is a watchlist.`, "Usage: gloomberb portfolio show <portfolio> --close");
554
+ }
377
555
  const isPortfolio = !!matchedPortfolio;
378
556
  const id = collection.id;
379
557
  const displayName = collection.name;
380
- const structured = isPortfolio && ctx.cliOptions?.format != null && ctx.cliOptions.format !== "text";
558
+ const structured = ctx.cliOptions?.format != null && ctx.cliOptions.format !== "text";
381
559
  const filtered = tickers.filter((ticker) =>
382
560
  isPortfolio ? ticker.metadata.portfolios.includes(id) : ticker.metadata.watchlists.includes(id)
383
561
  );
@@ -390,25 +568,32 @@ async function showCollectionWithMarketData(
390
568
  if (filtered.length === 0 && !structured && !hasCash) {
391
569
  console.log(cliStyles.bold(displayName));
392
570
  console.log(cliStyles.muted("No tickers in this collection."));
571
+ if (explain) console.log(`\n${renderReportGlossary(EXPLAINED_REPORT, [])}`);
393
572
  return;
394
573
  }
574
+ // The terms the report shows, which --explain follows it with.
575
+ let explained: GlossaryEntry[] = [];
395
576
 
396
577
  const quotes = await loadCollectionQuotes(filtered, collection, dataProvider);
397
578
  // Worst of across the holdings' quotes, as the PORT and watchlist reports state it.
398
579
  const broker = !!(matchedPortfolio?.brokerId || matchedPortfolio?.brokerInstanceId);
399
- const freshness = quotesFreshness(quotes.values(), {
400
- source: !isPortfolio ? "Local watchlist and Gloom Cloud"
401
- : broker ? "Your broker account and Gloom Cloud" : "Local portfolio and Gloom Cloud",
402
- });
580
+ const source = !isPortfolio ? "Local watchlist and Gloom Cloud"
581
+ : broker ? "Your broker account and Gloom Cloud" : "Local portfolio and Gloom Cloud";
582
+ let freshness = isPortfolio ? undefined : quotesFreshness(quotes.values(), { source });
403
583
 
404
584
  if (!structured) {
405
585
  console.log(cliStyles.bold(displayName + (isPortfolio ? ` (${currency})` : "")));
406
586
  console.log(cliStyles.muted(`${filtered.length} ticker${filtered.length === 1 ? "" : "s"}`));
587
+ const imported = matchedPortfolio ? importedFromText(matchedPortfolio) : null;
588
+ if (imported) console.log(cliStyles.muted(imported));
589
+ if (isPortfolio) console.log(cliStyles.muted(valuationBasisLine(basis)));
407
590
  console.log("");
408
591
  }
409
592
 
410
593
  if (matchedPortfolio) {
411
- const valuation = await valuePortfolioPositions({ tickers: filtered, quotes, portfolioId: id, currency, baseCurrency: config.baseCurrency, toBase });
594
+ const valuation = await valuePortfolioPositions({ tickers: filtered, quotes, portfolioId: id, currency, baseCurrency: config.baseCurrency, toBase, basis });
595
+ // Exports carry no header line, so their source line names the basis.
596
+ freshness = portfolioValuationFreshness(valuation, source, { note: structured });
412
597
  const {
413
598
  positions: valued,
414
599
  totalPnl,
@@ -420,6 +605,9 @@ async function showCollectionWithMarketData(
420
605
  const { allocation, cash } = await valuePortfolioAllocation({ valuation, portfolio: matchedPortfolio, account, toBase });
421
606
  const allocationBySymbol = new Map(allocation.rows.map((row) => [row.symbol, row]));
422
607
  const showTargets = allocation.targetSum != null;
608
+ // Never dropped on a narrow terminal, so --explain reads the terms off them.
609
+ const allocationHeaders = showTargets ? ["Weight", "Target", "Drift", "Trade", "Trade Value"] : ["Weight"];
610
+ explained = reportGlossary("PF", allocationHeaders.join(" "));
423
611
  // A ticker held in several lots carries its allocation on its first row.
424
612
  const firstRows = new Set<string>();
425
613
  const positionsExport = valued.map((entry): Record<string, unknown> & {
@@ -436,9 +624,12 @@ async function showCollectionWithMarketData(
436
624
  drift: figures?.drift ?? null,
437
625
  tradeShares: figures?.tradeUnits ?? null,
438
626
  tradeValue: figures?.tradeValue ?? null,
627
+ ...entry.valuation,
439
628
  };
440
629
  });
441
630
  const totalPnlValue = unavailablePnl.size > 0 ? null : totalPnl;
631
+ // At the close, the value is Last times the shares, so the day's move from the close before is said beside it.
632
+ const dayMove = basis === "regular-close" ? portfolioDayMove(valuation) : null;
442
633
  const cashExport = cash && allocation.cash ? {
443
634
  ...cash,
444
635
  weight: allocation.cash.weight,
@@ -461,17 +652,21 @@ async function showCollectionWithMarketData(
461
652
  ...(cashExport ? [{ symbol: CASH_SYMBOL, marketValue: cashExport.value, baseCurrency: currency, weight: cashExport.weight,
462
653
  targetWeight: cashExport.targetWeight, drift: cashExport.drift, tradeShares: null, tradeValue: cashExport.tradeValue }] : []),
463
654
  { symbol: "TOTAL", marketValue: allocation.total, unrealizedPnl: totalPnlValue, baseCurrency: currency,
464
- weight: allocation.total != null && allocation.total > 0 ? 100 : null, targetWeight: allocation.targetSum },
655
+ weight: allocation.total != null && allocation.total > 0 ? 100 : null, targetWeight: allocation.targetSum, valuationBasis: basis },
465
656
  ];
466
657
  ctx.printResult({ data: positionsExport, metadata: {
467
658
  portfolioId: id, portfolioName: displayName, baseCurrency: currency, currency,
659
+ valuationBasis: basis,
468
660
  total: allocation.total, cash: cashExport, targetSum: allocation.targetSum,
661
+ ...(basis === "regular-close" ? { dayChange: dayMove?.change ?? null, dayChangePercent: dayMove?.changePercent ?? null } : {}),
469
662
  unpricedCount: allocation.unpriced.length, unpricedSymbols: allocation.unpriced,
470
663
  totalUnrealizedPnl: totalPnlValue,
471
664
  complete: unavailablePnl.size === 0 && unavailableCost.size === 0 && unavailableMarketValue.size === 0,
472
665
  unavailableSymbols: [...new Set([...unavailablePnl, ...unavailableCost, ...unavailableMarketValue])],
473
666
  unavailableCostSymbols: [...unavailableCost], brokerPnlSymbols: [...brokerPnlSymbols],
474
667
  accountingBasis, ...(manualAccounting ? { manualAccounting } : {}),
668
+ ...(matchedPortfolio.importedFrom ? { importedFrom: matchedPortfolio.importedFrom } : {}),
669
+ ...(explain ? { glossary: glossaryExport(explained) } : {}),
475
670
  }, freshness }, { rows: (data) => [...(data as Record<string, unknown>[]), ...extraRows] });
476
671
  return;
477
672
  }
@@ -497,9 +692,11 @@ async function showCollectionWithMarketData(
497
692
  };
498
693
 
499
694
  const extendedSessions = extendedSessionsOf(valued.map(({ extended }) => extended?.session));
500
- const rows: string[][] = valued.map(({ ticker, quote, headline, extended, position, metrics, pnl, row }, index) => {
695
+ const now = Date.now();
696
+ const rows: string[][] = valued.map(({ ticker, quote, headline, extended, position, metrics, pnl, row, valuation: priced }, index) => {
697
+ const asOf = valuationTimeText(priced.valuationAsOf, now);
501
698
  const priceText = quote && headline
502
- ? colorBySign(formatMarketPriceWithCurrency(headline.price, quote.currency, priceFormatOptions(quote, ticker.metadata.assetCategory)), headline.change)
699
+ ? colorBySign(formatListPrice(headline.price, quote, ticker.metadata.assetCategory), headline.change)
503
700
  : "—";
504
701
  const changeText = headline ? colorBySign(formatPercentRaw(headline.changePercent), headline.change) : "—";
505
702
  const quoteCells = [priceText, changeText, ...extendedCells(extendedSessions, extended, quote, ticker.metadata.assetCategory)];
@@ -509,26 +706,27 @@ async function showCollectionWithMarketData(
509
706
  : undefined;
510
707
  const symbolUnits = allocationBySymbol.get(ticker.metadata.ticker)?.units ?? 0;
511
708
  if (!position || !metrics) {
512
- return [ticker.metadata.ticker, ...quoteCells, "—", "—", "—",
513
- ...allocationCells(figures, { unpriced: false, units: symbolUnits, assetCategory: ticker.metadata.assetCategory }), "—"];
709
+ return [listingLabel(ticker), ...quoteCells, "—", "—", "—",
710
+ ...allocationCells(figures, { unpriced: false, units: symbolUnits, assetCategory: ticker.metadata.assetCategory }), "—", asOf];
514
711
  }
515
712
  const positionCurrency = metrics.positionCurrency;
516
713
  const marketValue = row.marketValue as number | null;
517
714
  const unpriced = marketValue == null;
518
715
  return [
519
- ticker.metadata.ticker,
716
+ listingLabel(ticker),
520
717
  ...quoteCells,
521
718
  formatMarketQuantity(metrics.totalShares, { assetCategory: ticker.metadata.assetCategory, multiplier: position.multiplier, priceBasis: metrics.priceBasis, quantityCurrency: positionCurrency }),
522
719
  formatMarketCostWithCurrency(position.avgCost, positionCurrency, { assetCategory: ticker.metadata.assetCategory, multiplier: position.multiplier, priceBasis: metrics.priceBasis }),
523
720
  unpriced ? NOT_AVAILABLE : wholeMoney(marketValue),
524
721
  ...allocationCells(figures, { unpriced, units: symbolUnits, assetCategory: ticker.metadata.assetCategory }),
525
722
  pnl == null || !Number.isFinite(pnl) ? "—" : colorBySign(money(pnl, { signed: true }), pnl),
723
+ asOf,
526
724
  ];
527
725
  });
528
726
  if (cashExport) {
529
727
  const label = cashExport.currency && cashExport.currency !== currency ? `${CASH_SYMBOL} ${cashExport.currency}` : CASH_SYMBOL;
530
728
  rows.push([label, "", "", ...extendedSessions.map(() => ""), "", "", cashExport.value == null ? NOT_AVAILABLE : wholeMoney(cashExport.value),
531
- ...allocationCells(cashExport, { unpriced: cashExport.value == null }), ""]);
729
+ ...allocationCells(cashExport, { unpriced: cashExport.value == null }), "", ""]);
532
730
  }
533
731
  const total = allocation.total;
534
732
  rows.push([
@@ -537,6 +735,7 @@ async function showCollectionWithMarketData(
537
735
  total != null && total > 0 ? formatAllocationWeight(100) : "—",
538
736
  ...(showTargets ? [formatAllocationWeight(allocation.targetSum), "", "", ""] : []),
539
737
  totalPnlValue == null ? "—" : colorBySign(money(totalPnlValue, { signed: true }), totalPnlValue),
738
+ "",
540
739
  ]);
541
740
 
542
741
  const quantityHeader = filtered.some(ticker => ticker.metadata.assetCategory?.toUpperCase() === "BOND") || positionsExport.some(row => row.quantityUnit === "face") ? "Qty" : "Shares";
@@ -551,24 +750,22 @@ async function showCollectionWithMarketData(
551
750
  { header: quantityHeader, align: "right" },
552
751
  { header: "Avg Cost", align: "right", optional: true, dropPriority: 3 },
553
752
  { header: "Value", align: "right" },
554
- { header: "Weight", align: "right" },
555
- ...(showTargets
556
- ? [
557
- { header: "Target", align: "right" as const },
558
- { header: "Drift", align: "right" as const },
559
- { header: "Trade", align: "right" as const },
560
- { header: "Trade Value", align: "right" as const },
561
- ]
562
- : []),
753
+ ...allocationHeaders.map((header) => ({ header, align: "right" as const })),
563
754
  { header: "P&L", align: "right", optional: true },
755
+ // When each row's price is from, so a Friday close never hides behind a newer row.
756
+ { header: "As of (UTC)" },
564
757
  ],
565
758
  rows,
566
759
  ));
567
760
  console.log("");
761
+ if (basis === "regular-close") console.log(dayMoveLine(dayMove, currency, colorBySign));
568
762
  const notes: string[] = [];
763
+ // The notes name the rows as the table does.
764
+ const labels = new Map(filtered.map((ticker) => [ticker.metadata.ticker, listingLabel(ticker)]));
765
+ const named = (symbols: Iterable<string>) => [...symbols].map((symbol) => labels.get(symbol) ?? symbol).join(", ");
569
766
  if (allocation.unpriced.length > 0) {
570
767
  const count = allocation.unpriced.length;
571
- notes.push(cliStyles.warning(`Total and weights exclude ${count} unpriced holding${count === 1 ? "" : "s"}: ${allocation.unpriced.join(", ")}.`));
768
+ notes.push(cliStyles.warning(`Total and weights exclude ${count} unpriced holding${count === 1 ? "" : "s"}: ${named(allocation.unpriced)}.`));
572
769
  }
573
770
  if (cashExport?.source === "broker") notes.push(cliStyles.muted("Cash is the broker account's balance as of its last sync."));
574
771
  if (matchedPortfolio.brokerInstanceId && !account) notes.push(cliStyles.warning(BROKER_ACCOUNT_MISSING_NOTE));
@@ -579,34 +776,38 @@ async function showCollectionWithMarketData(
579
776
  }
580
777
  const targetNote = describeTargetSum(allocation.targetSum);
581
778
  if (targetNote) notes.push(cliStyles.muted(targetNote));
582
- if (brokerPnlSymbols.size > 0) notes.push(cliStyles.muted(`P&L for ${[...brokerPnlSymbols].join(", ")} is the broker's last snapshot.`));
583
- if (unavailableCost.size > 0) notes.push(cliStyles.muted(`Cost unavailable for ${[...unavailableCost].join(", ")}.`));
584
- if (unavailablePnl.size > 0) notes.push(cliStyles.muted(`P&L unavailable for ${[...unavailablePnl].join(", ")}.`));
779
+ if (brokerPnlSymbols.size > 0) notes.push(cliStyles.muted(`P&L for ${named(brokerPnlSymbols)} is the broker's last snapshot.`));
780
+ if (unavailableCost.size > 0) notes.push(cliStyles.muted(`Cost unavailable for ${named(unavailableCost)}.`));
781
+ if (unavailablePnl.size > 0) notes.push(cliStyles.muted(`P&L unavailable for ${named(unavailablePnl)}.`));
585
782
  for (const note of notes) console.log(note);
586
783
  } else {
587
- const rows: string[][] = [];
588
- const shown = new Map(filtered.map((ticker) => [ticker, collectionQuoteHeadline(quotes.get(ticker.metadata.ticker))]));
589
- const extendedSessions = extendedSessionsOf([...shown.values()].map(({ extended }) => extended?.session));
590
- for (const ticker of filtered) {
591
- const quote = quotes.get(ticker.metadata.ticker);
592
- const { headline, extended } = shown.get(ticker)!;
784
+ const watched = await valueWatchlistRows({ tickers: filtered, quotes, dataProvider, currency, toBase });
785
+ if (structured) {
786
+ ctx.printResult({
787
+ data: watched.map((entry) => entry.row),
788
+ metadata: { watchlistId: id, watchlistName: displayName, baseCurrency: currency, ...(explain ? { glossary: [] } : {}) },
789
+ freshness,
790
+ });
791
+ return;
792
+ }
793
+ const extendedSessions = extendedSessionsOf(watched.map(({ extended }) => extended?.session));
794
+ // Funding is the one column a list earns by holding perpetual markets; any other list's table is unchanged.
795
+ const showFunding = watched.some(({ funding }) => funding !== undefined);
796
+ const rows = watched.map(({ ticker, quote, headline, extended, venue, marketCap, funding }) => {
593
797
  const priceText = quote && headline
594
- ? colorBySign(formatMarketPriceWithCurrency(headline.price, quote.currency, priceFormatOptions(quote, ticker.metadata.assetCategory)), headline.change)
798
+ ? colorBySign(formatListPrice(headline.price, quote, ticker.metadata.assetCategory), headline.change)
595
799
  : "—";
596
800
  const changeText = headline ? colorBySign(formatPercentRaw(headline.changePercent), headline.change) : "—";
597
- const marketCapText = quote?.marketCap != null
598
- ? `${formatCompact(await toBase(quote.marketCap, quote.currency || ticker.metadata.currency || currency))} ${currency}`
599
- : "—";
600
-
601
- rows.push([
602
- ticker.metadata.ticker,
603
- exchangeShortName(quote?.exchangeName, quote?.fullExchangeName) || ticker.metadata.exchange || "—",
801
+ return [
802
+ listingLabel(ticker),
803
+ venue ?? "—",
604
804
  priceText,
605
805
  changeText,
606
806
  ...extendedCells(extendedSessions, extended, quote, ticker.metadata.assetCategory),
607
- marketCapText,
608
- ]);
609
- }
807
+ ...showFunding ? [funding == null ? "—" : colorBySign(formatPerpetualRate(funding, 4), funding)] : [],
808
+ marketCap == null ? "—" : `${formatCompact(marketCap)} ${currency}`,
809
+ ];
810
+ });
610
811
 
611
812
  console.log(renderTable(
612
813
  [
@@ -615,11 +816,176 @@ async function showCollectionWithMarketData(
615
816
  { header: "Last", align: "right" },
616
817
  { header: "Chg", align: "right" },
617
818
  ...extendedSessions.map((session) => ({ header: EXTENDED_SESSION_LABELS[session], align: "right" as const, optional: true, dropPriority: 1 })),
819
+ // As the TUI's FUND 8H: per eight hours whatever interval the venue pays on.
820
+ ...showFunding ? [{ header: "Fund 8H", align: "right" as const, optional: true, dropPriority: 1 }] : [],
618
821
  // A narrow terminal drops these first.
619
822
  { header: "Mkt Cap", align: "right", optional: true, dropPriority: 2 },
620
823
  ],
621
824
  rows,
622
825
  ));
826
+ const notes = marketCapNotes(watched);
827
+ if (notes.length > 0) console.log(["", ...notes.map((note) => cliStyles.muted(note))].join("\n"));
623
828
  }
624
829
  if (freshness) console.log(`\n${cliFreshnessFooter(freshness)}`);
830
+ if (explain) console.log(`\n${renderReportGlossary(EXPLAINED_REPORT, explained)}`);
831
+ }
832
+
833
+ /** One watchlist row: the quote as the table shows it, the market cap in the base currency, and the `--json` and `--csv` row. */
834
+ interface WatchlistRowValue {
835
+ ticker: TickerRecord;
836
+ quote: CollectionQuote | undefined;
837
+ headline: ActiveQuoteDisplay | null;
838
+ extended: ExtendedSessionDisplay | null;
839
+ kind: TickerInstrumentKind;
840
+ /** The Exchange column: the quote's venue, else the saved one. */
841
+ venue: string | null;
842
+ marketCap: number | null;
843
+ /** A perpetual market's funding per eight hours, as a fraction; null when its market cannot be read, undefined for any other row. */
844
+ funding: number | null | undefined;
845
+ row: Record<string, unknown>;
846
+ }
847
+
848
+ /** Financials requested at once, the most the app asks for at a time. */
849
+ const FINANCIALS_BATCH_SIZE = 64;
850
+ /** Financials asked for one at a time, at most this many at once, from a source without batches. */
851
+ const FINANCIALS_CONCURRENCY = 8;
852
+
853
+ /**
854
+ * The financials of the listings whose quote carries no market cap, through
855
+ * the same batched request the watchlist pane makes. A lightweight quote, as
856
+ * US listings are served, leaves the capitalization to the fundamentals.
857
+ */
858
+ async function loadMarketCapFinancials(
859
+ tickers: readonly TickerRecord[],
860
+ quotes: ReadonlyMap<string, CollectionQuote>,
861
+ dataProvider: Pick<DataProvider, "getTickerFinancials" | "getTickerFinancialsBatch">,
862
+ ): Promise<Map<string, TickerFinancials>> {
863
+ const needed = tickers.filter((ticker) => !selectMarketCapitalization(quotes.get(ticker.metadata.ticker), undefined));
864
+ const financials = new Map<string, TickerFinancials>();
865
+ if (needed.length === 0) return financials;
866
+ const targets = needed.map((ticker) => ({ symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange }));
867
+ if (dataProvider.getTickerFinancialsBatch) {
868
+ await Promise.all(chunked(targets, FINANCIALS_BATCH_SIZE).map(async (batch) => {
869
+ const results = await dataProvider.getTickerFinancialsBatch!(batch).catch(() => []);
870
+ batch.forEach((target, index) => {
871
+ const value = (results.find((result) => result.target === target) ?? results[index])?.financials;
872
+ if (value) financials.set(target.symbol, value);
873
+ });
874
+ }));
875
+ return financials;
876
+ }
877
+ await mapWithConcurrency(targets, FINANCIALS_CONCURRENCY, async (target) => {
878
+ const value = await dataProvider.getTickerFinancials(target.symbol, target.exchange).catch(() => null);
879
+ if (value) financials.set(target.symbol, value);
880
+ });
881
+ return financials;
882
+ }
883
+
884
+ /** Perpetual markets read at once, the most the app asks for at a time. */
885
+ const PERPETUAL_CONCURRENCY = 8;
886
+
887
+ /**
888
+ * The funding per eight hours of each perpetual market in a list, by ticker, as `quote` reads it: null for a market
889
+ * that cannot be read. A list without perpetual markets reads nothing.
890
+ */
891
+ async function loadPerpetualFunding(tickers: readonly TickerRecord[]): Promise<Map<string, number | null>> {
892
+ const markets = tickers.flatMap((ticker) => {
893
+ const listing = perpetualListing(ticker.metadata.ticker, ticker.metadata.exchange);
894
+ return listing ? [{ symbol: ticker.metadata.ticker, listing }] : [];
895
+ });
896
+ const funding = new Map<string, number | null>();
897
+ await mapWithConcurrency(markets, PERPETUAL_CONCURRENCY, async ({ symbol, listing }) => {
898
+ funding.set(symbol, await loadPerpetualMarket(listing).then((market) => market?.row.fundingRate8h ?? null, () => null));
899
+ });
900
+ return funding;
901
+ }
902
+
903
+ /** Watchlist rows as `watchlist show` prints them, with the market cap the watchlist pane shows. */
904
+ async function valueWatchlistRows({
905
+ tickers,
906
+ quotes,
907
+ dataProvider,
908
+ currency,
909
+ toBase,
910
+ }: {
911
+ tickers: readonly TickerRecord[];
912
+ quotes: ReadonlyMap<string, CollectionQuote>;
913
+ dataProvider: Pick<DataProvider, "getTickerFinancials" | "getTickerFinancialsBatch">;
914
+ currency: string;
915
+ toBase: (value: number, fromCurrency: string) => Promise<number>;
916
+ }): Promise<WatchlistRowValue[]> {
917
+ const [financials, perpetualFunding] = await Promise.all([
918
+ loadMarketCapFinancials(tickers, quotes, dataProvider),
919
+ loadPerpetualFunding(tickers),
920
+ ]);
921
+ const known = (value: number | null | undefined): number | null => value != null && Number.isFinite(value) ? value : null;
922
+ // The export carries funding on the table's terms: only for a list that holds perpetual markets.
923
+ const hasPerpetuals = perpetualFunding.size > 0;
924
+ const values: WatchlistRowValue[] = [];
925
+ for (const ticker of tickers) {
926
+ const symbol = ticker.metadata.ticker;
927
+ const quote = quotes.get(symbol);
928
+ const snapshot = financials.get(symbol);
929
+ const { headline, extended } = collectionQuoteHeadline(quote);
930
+ const cap = liveMarketCapitalization(quote, snapshot?.fundamentals) ?? liveMarketCapitalization(snapshot?.quote, snapshot?.fundamentals);
931
+ const marketCap = cap ? listedMarketCapitalization(known(await toBase(cap.value, cap.currency))) : null;
932
+ const kind = resolveTickerInstrumentKind(ticker, { quote: quote ?? snapshot?.quote });
933
+ const venue = exchangeShortName(quote?.tradingExchangeName || quote?.exchangeName, quote?.fullExchangeName) || ticker.metadata.exchange || null;
934
+ const funding = perpetualFunding.get(symbol);
935
+ values.push({
936
+ ticker, quote, headline, extended, kind, venue, marketCap, funding,
937
+ row: {
938
+ symbol,
939
+ ...listingColumns(ticker, quote),
940
+ exchange: venue,
941
+ kind,
942
+ currency: quote?.currency || ticker.metadata.currency || null,
943
+ price: known(headline?.price),
944
+ change: known(headline?.change),
945
+ changePercent: known(headline?.changePercent),
946
+ ...extendedQuoteFields(extended),
947
+ volume: known(quote?.volume),
948
+ ...hasPerpetuals ? { fundingRate8h: known(funding) } : {},
949
+ marketCap,
950
+ marketCapCurrency: currency,
951
+ quoteAsOf: known(quote?.lastUpdated),
952
+ },
953
+ });
954
+ }
955
+ return values;
956
+ }
957
+
958
+ /** Kinds of listing that have no market cap at all, as a note names them. */
959
+ const NO_MARKET_CAP: Partial<Record<TickerInstrumentKind, string>> = {
960
+ fund: "funds",
961
+ index: "indices",
962
+ currency: "currencies",
963
+ future: "futures",
964
+ perpetual: "perpetuals",
965
+ prediction: "prediction markets",
966
+ option: "options",
967
+ bond: "bonds",
968
+ };
969
+ const NOTE_NAMES = 10;
970
+
971
+ function namedList(labels: readonly string[]): string {
972
+ const more = labels.length - NOTE_NAMES;
973
+ return `${labels.slice(0, NOTE_NAMES).join(", ")}${more > 0 ? ` and ${more} more` : ""}`;
974
+ }
975
+
976
+ /** Why a Mkt Cap cell is blank: funds and indices have none; a company or coin whose figure did not come is unavailable. */
977
+ function marketCapNotes(rows: readonly WatchlistRowValue[]): string[] {
978
+ const blank = rows.filter((row) => row.marketCap == null);
979
+ const notes: string[] = [];
980
+ const byKind = new Map<string, string[]>();
981
+ for (const row of blank) {
982
+ const group = NO_MARKET_CAP[row.kind] ?? "";
983
+ byKind.set(group, [...byKind.get(group) ?? [], listingLabel(row.ticker)]);
984
+ }
985
+ for (const [group, labels] of byKind) {
986
+ if (group) notes.push(`No market cap for ${group}: ${namedList(labels)}.`);
987
+ }
988
+ const unavailable = byKind.get("");
989
+ if (unavailable?.length) notes.push(`Market cap unavailable for ${namedList(unavailable)}.`);
990
+ return notes;
625
991
  }