gloomberb 0.18.3 → 0.18.4

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 (886) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -6,6 +6,7 @@ import {
6
6
  ChartTableHeader,
7
7
  DetailScrollBody,
8
8
  KeyValueRow,
9
+ QueryBar,
9
10
  Prose,
10
11
  SectionHeading,
11
12
  usePaneNoticeFooter,
@@ -31,6 +32,8 @@ import { handleRefreshKey } from "../../../components/data-table/table-pane";
31
32
  import { SignInWall } from "../cloud/auth-actions";
32
33
  import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
33
34
  import { useBoundTicker as useSymbolBinding, useTickerRequest } from "../shared/ticker-request";
35
+ import { loadEarningsCalendar } from "../earnings/data/cache";
36
+ import { calendarEventOf, newYorkDay, withResolvedEarnings } from "../shared/earnings-calendar";
34
37
  import {
35
38
  documentContentKey,
36
39
  formatCompactDocumentLabel,
@@ -50,7 +53,7 @@ import {
50
53
  type EventStatus,
51
54
  } from "./event-model";
52
55
 
53
- type EventColumnId = "date" | "status" | "period" | "qEps" | "qRevenue" | "annualEps" | "annualRevenue" | "value" | "detail";
56
+ type EventColumnId = "date" | "status" | "period" | "qEps" | "qRevenue" | "annualEps" | "annualRevenue" | "value" | "detail" | "eps" | "revenue" | "surprise" | "growth";
54
57
  type EventColumn = DataTableColumn & { id: EventColumnId };
55
58
 
56
59
  const SEC_EVENT_FILING_LIMIT = 50;
@@ -138,15 +141,15 @@ export function matchEarningsSecFiling(row: { status: string; date: string; date
138
141
  return best?.filing ?? null;
139
142
  }
140
143
 
141
- /** The one currency every EPS and revenue cell is in, so the headers carry it once. */
142
- function sharedMetricCurrency(rows: readonly EventRow[]): string | undefined {
144
+ /** Share the currency label; EE marks any unstated currencies separately. */
145
+ function sharedMetricCurrency(rows: readonly EventRow[], allowUnstated = false): string | undefined {
143
146
  const currencies = new Set<string | undefined>();
144
147
  for (const row of rows) {
145
148
  for (const [value, currency] of [
146
149
  [row.qEps, row.epsCurrency], [row.annualEps, row.epsCurrency],
147
150
  [row.qRevenue, row.revenueCurrency], [row.annualRevenue, row.revenueCurrency],
148
151
  ] as const) {
149
- if (value != null && Number.isFinite(value)) currencies.add(currency || undefined);
152
+ if (value != null && Number.isFinite(value) && (currency || !allowUnstated)) currencies.add(currency || undefined);
150
153
  }
151
154
  }
152
155
  const [only] = currencies;
@@ -154,11 +157,23 @@ function sharedMetricCurrency(rows: readonly EventRow[]): string | undefined {
154
157
  }
155
158
 
156
159
  export function buildEventColumns(width: number, unit?: string, variant: "corporate-actions" | "earnings-estimates" = "corporate-actions"): EventColumn[] {
160
+ if (variant === "earnings-estimates") {
161
+ return fitChartTableColumns<EventColumn>([
162
+ { id: "date", label: "DATE", width: 10, align: "left" },
163
+ { id: "status", label: "EVENT", width: 8, align: "left" },
164
+ { id: "period", label: "PERIOD", width: 9, align: "left" },
165
+ { id: "eps", label: "EPS", width: unit ? 7 : 11, align: "right" },
166
+ { id: "revenue", label: "REVENUE", width: unit ? 9 : 13, align: "right" },
167
+ { id: "surprise", label: "EPS SURP", width: 9, align: "right" },
168
+ { id: "growth", label: "YOY GROWTH", width: 14, align: "right" },
169
+ { id: "detail", label: "DETAIL", width: 9, align: "left", flexGrow: 1 },
170
+ ], width, ["detail", "growth", "revenue"]);
171
+ }
157
172
  const withUnit = (label: string) => unit ? `${label} ${unit}` : label;
158
173
  const columns: EventColumn[] = [
159
174
  { id: "date", label: "DATE", width: 10, align: "left" },
160
175
  { id: "status", label: "EVENT", width: 8, align: "left" },
161
- { id: "period", label: "PERIOD", width: variant === "corporate-actions" ? 12 : 9, align: "left" },
176
+ { id: "period", label: "PERIOD", width: 12, align: "left" },
162
177
  { id: "qEps", label: withUnit("Q EPS"), width: 12, align: "right" },
163
178
  { id: "qRevenue", label: withUnit("Q REV"), width: 12, align: "right" },
164
179
  { id: "annualEps", label: withUnit("ANN EPS"), width: 12, align: "right" },
@@ -166,9 +181,7 @@ export function buildEventColumns(width: number, unit?: string, variant: "corpor
166
181
  { id: "value", label: "VALUE", width: 11, align: "right" },
167
182
  { id: "detail", label: "DETAIL", width: 9, align: "left", flexGrow: 1 },
168
183
  ];
169
- // EE keeps its scrollable estimate columns; EVT prioritizes the event value.
170
- return variant === "earnings-estimates" ? columns
171
- : fitChartTableColumns(columns, width, ["annualRevenue", "annualEps", "qRevenue", "qEps", "detail"]);
184
+ return fitChartTableColumns(columns, width, ["annualRevenue", "annualEps", "qRevenue", "qEps", "detail"]);
172
185
  }
173
186
 
174
187
  function toneColor(tone: EventRow["tone"]): string {
@@ -434,13 +447,16 @@ export function CorporateActionsView({
434
447
  const trailingEps = tickerFinancials?.fundamentals?.eps;
435
448
  const trailingEpsCurrency = tickerFinancials?.fundamentals?.epsCurrency;
436
449
  const fundamentalsCurrency = tickerFinancials?.fundamentals?.financialCurrency;
450
+ const marketCapCurrency = tickerFinancials?.fundamentals?.marketCapCurrency;
451
+ const quoteCurrency = tickerFinancials?.quote?.currency;
437
452
  const financialsData = useMemo<EventFinancials | null>(
438
453
  () => (quarterlyStatements || financialCurrency || trailingEps != null ? {
439
454
  quarterlyStatements: quarterlyStatements ?? [],
440
455
  financialCurrency,
441
- fundamentals: { eps: trailingEps, epsCurrency: trailingEpsCurrency, financialCurrency: fundamentalsCurrency },
456
+ fundamentals: { eps: trailingEps, epsCurrency: trailingEpsCurrency, financialCurrency: fundamentalsCurrency, marketCapCurrency },
457
+ quote: quoteCurrency ? { currency: quoteCurrency } : null,
442
458
  } : null),
443
- [financialCurrency, fundamentalsCurrency, quarterlyStatements, trailingEps, trailingEpsCurrency],
459
+ [financialCurrency, fundamentalsCurrency, marketCapCurrency, quarterlyStatements, quoteCurrency, trailingEps, trailingEpsCurrency],
444
460
  );
445
461
  const actionsLoader = useCallback((nextSymbol: string, nextExchange: string, forceRefresh: boolean) => {
446
462
  if (!dataProvider?.getCorporateActions) throw new Error("Corporate actions source unavailable");
@@ -450,12 +466,22 @@ export function CorporateActionsView({
450
466
  if (!dataProvider?.getAnalystResearch) return null;
451
467
  return dataProvider.getAnalystResearch(nextSymbol, nextExchange, forceRefresh ? { cacheMode: "refresh" } : undefined);
452
468
  }, [dataProvider, cloudSession.requestKey]);
469
+ // The earnings calendar's next report, which the rows are resolved against as `earnings` and ERN read it.
470
+ const calendarLoader = useCallback((nextSymbol: string, _exchange: string, forceRefresh: boolean) => (
471
+ loadEarningsCalendar(dataProvider, [nextSymbol], { force: forceRefresh }).then((result) => calendarEventOf(result.events, nextSymbol))
472
+ ), [dataProvider]);
453
473
  const {
454
- data: actionsData,
474
+ data: recordedActions,
455
475
  loading: actionsLoading,
456
476
  error: actionsError,
457
477
  reload: reloadActions,
458
478
  } = useTickerRequest<CorporateActionsData>(actionsLoader, symbol, exchange);
479
+ const { data: calendarEvent, reload: reloadCalendar } = useTickerRequest(calendarLoader, symbol, exchange);
480
+ const today = newYorkDay();
481
+ const actionsData = useMemo(
482
+ () => (recordedActions ? withResolvedEarnings(recordedActions, calendarEvent, today) : recordedActions),
483
+ [calendarEvent, recordedActions, today],
484
+ );
459
485
  const {
460
486
  data: analystData,
461
487
  loading: analystLoading,
@@ -471,7 +497,11 @@ export function CorporateActionsView({
471
497
  ? allRows.filter((row) => EARNINGS_STATUSES.has(row.status))
472
498
  : allRows
473
499
  ), [allRows, variant]);
474
- const unit = useMemo(() => sharedMetricCurrency(rows), [rows]);
500
+ const unit = useMemo(() => sharedMetricCurrency(rows, variant === "earnings-estimates"), [rows, variant]);
501
+ const hasUnstatedCurrency = rows.some((row) =>
502
+ ((row.qEps != null || row.annualEps != null) && !row.epsCurrency)
503
+ || ((row.qRevenue != null || row.annualRevenue != null) && !row.revenueCurrency));
504
+ const currencyMeta = [unit, hasUnstatedCurrency ? "? currency unstated" : null].filter(Boolean).join(" · ");
475
505
  const columns = useMemo(() => buildEventColumns(width, unit, variant), [width, unit, variant]);
476
506
  const sourceNotice = useMemo(() => (
477
507
  actionsLoading || analystLoading
@@ -506,7 +536,8 @@ export function CorporateActionsView({
506
536
  const reload = useCallback(() => {
507
537
  reloadActions();
508
538
  reloadAnalyst();
509
- }, [reloadActions, reloadAnalyst]);
539
+ reloadCalendar();
540
+ }, [reloadActions, reloadAnalyst, reloadCalendar]);
510
541
  const openRow = openRowId
511
542
  ? rows.find((row) => row.id === openRowId) ?? null
512
543
  : null;
@@ -593,6 +624,19 @@ export function CorporateActionsView({
593
624
  return { text: row.status, color: colors.textBright, attributes: TextAttributes.BOLD };
594
625
  case "period":
595
626
  return { text: row.period, color: colors.textDim };
627
+ case "eps":
628
+ case "revenue": {
629
+ const eps = column.id === "eps";
630
+ const value = eps ? row.qEps ?? row.annualEps : row.qRevenue ?? row.annualRevenue;
631
+ const currency = eps ? row.epsCurrency : row.revenueCurrency;
632
+ const text = formatEventMetric(value, unit ? undefined : currency, eps ? "eps" : "revenue");
633
+ return { text: value != null && !currency ? `${text} ?` : text,
634
+ value: unit && currency ? value : undefined, color: colors.textDim };
635
+ }
636
+ case "surprise":
637
+ return { text: row.status === "Earnings" ? row.value : "-", value: row.surprisePercent, color: row.status === "Earnings" ? toneColor(row.tone) : colors.textDim };
638
+ case "growth":
639
+ return { text: row.estimateGrowthMetric && row.value !== "-" ? `${row.value} ${row.estimateGrowthMetric === "eps" ? "EPS" : "rev"}` : "-", color: row.estimateGrowthMetric ? toneColor(row.tone) : colors.textDim };
596
640
  case "qEps":
597
641
  return { text: formatEventMetric(row.qEps, unit ? undefined : row.epsCurrency, "eps"), color: colors.textDim };
598
642
  case "qRevenue":
@@ -648,7 +692,8 @@ export function CorporateActionsView({
648
692
  detailScrollRef={detailScrollRef}
649
693
  rootWidth={width}
650
694
  rootHeight={height}
651
- rootBefore={<ChartTableHeader width={width} height={height} tableRows={rows.length} tableColumns={columns} />}
695
+ rootBefore={<ChartTableHeader width={width} height={height} tableRows={rows.length} tableColumns={columns}
696
+ query={variant === "earnings-estimates" && currencyMeta ? <QueryBar width={width} meta={currencyMeta} /> : undefined} />}
652
697
  onRootKeyDown={handleKeyDown}
653
698
  columns={columns}
654
699
  items={rows}
@@ -656,6 +701,10 @@ export function CorporateActionsView({
656
701
  sortDirection="desc"
657
702
  getItemKey={eventRowKey}
658
703
  renderCell={renderCell}
704
+ getExportMetadata={variant === "earnings-estimates" ? () => [
705
+ ...(unit ? [["Currency", unit]] : []),
706
+ ...(hasUnstatedCurrency ? [["?", "Currency unstated"]] : []),
707
+ ] : undefined}
659
708
  selectedTextOverridesCellColor
660
709
  getRowBackgroundColor={rowBackground}
661
710
  emptyStateTitle={loading
@@ -179,7 +179,9 @@ function estimateValue(pair: EstimatePair): string {
179
179
  * with the fundamentals, the figure `ticker` prints as EPS (TTM).
180
180
  */
181
181
  export type EventFinancials = Pick<TickerFinancials, "quarterlyStatements" | "financialCurrency"> & {
182
- fundamentals?: Pick<NonNullable<TickerFinancials["fundamentals"]>, "eps" | "epsCurrency" | "financialCurrency"> | null;
182
+ fundamentals?: Pick<NonNullable<TickerFinancials["fundamentals"]>, "eps" | "epsCurrency" | "financialCurrency" | "marketCapCurrency"> | null;
183
+ /** The listing's currency, which says whether an unlabelled EPS can be in the statements' (fundamentalsCurrency). */
184
+ quote?: Pick<NonNullable<TickerFinancials["quote"]>, "currency"> | null;
183
185
  };
184
186
 
185
187
  /**
@@ -5,6 +5,8 @@ import type {
5
5
  TickerFinancials,
6
6
  } from "../../../types/financials";
7
7
  import { formatEventMetric } from "./event-model";
8
+ import { loadEarningsCalendar } from "../earnings/data/cache";
9
+ import { calendarEventOf, newYorkDay, withResolvedEarnings } from "../shared/earnings-calendar";
8
10
 
9
11
  function metricFormat(currencyKey: "epsCurrency" | "revenueCurrency", kind: "eps" | "revenue") {
10
12
  return (value: unknown, row: Record<string, unknown>) => {
@@ -42,14 +44,20 @@ function settledError(result: PromiseSettledResult<unknown>): string | null {
42
44
  return result.reason instanceof Error ? result.reason.message : String(result.reason);
43
45
  }
44
46
 
45
- /** Corporate actions, analyst research and financials together; one failing leaves the others. */
46
- export async function loadEventSources(provider: DataProvider, symbol: string): Promise<EventSources> {
47
- const [actions, estimates, financials] = await Promise.allSettled([
47
+ /**
48
+ * Corporate actions, analyst research and financials together; one failing
49
+ * leaves the others. The earnings rows are resolved against the earnings
50
+ * calendar, so the next report reads as `earnings` and ERN give it.
51
+ */
52
+ export async function loadEventSources(provider: DataProvider, symbol: string, today = newYorkDay()): Promise<EventSources> {
53
+ const [actions, estimates, financials, calendar] = await Promise.allSettled([
48
54
  provider.getCorporateActions?.(symbol, "") ?? null,
49
55
  provider.getAnalystResearch?.(symbol, "") ?? null,
50
56
  provider.getTickerFinancials(symbol, ""),
57
+ loadEarningsCalendar(provider, [symbol]),
51
58
  ]);
52
- const actionsData = actions.status === "fulfilled" ? actions.value : null;
59
+ const event = calendar.status === "fulfilled" ? calendarEventOf(calendar.value.events, symbol) : null;
60
+ const actionsData = actions.status === "fulfilled" && actions.value ? withResolvedEarnings(actions.value, event, today) : null;
53
61
  const estimatesData = estimates.status === "fulfilled" ? estimates.value : null;
54
62
  const financialsData = financials.status === "fulfilled" ? financials.value : null;
55
63
  return {
@@ -159,7 +159,8 @@ export const researchModule: PluginModule = {
159
159
  paneId: "relative-valuation",
160
160
  label: "Relative Valuation",
161
161
  description: "Compare valuation and operating metrics across peers.",
162
- keywords: ["relative", "valuation", "comps", "peers", "rv"],
162
+ // The words an analyst asks for a comp table by.
163
+ keywords: ["relative", "valuation", "comps", "peers", "rv", "comp table", "comparable companies", "peer comparison", "competitors"],
163
164
  shortcut: { prefix: "RV", argPlaceholder: "tickers", argKind: "ticker-list" },
164
165
  canCreate: (context, options) => !!(options?.symbols?.length || options?.arg || context.activeTicker),
165
166
  createInstance: (context, options) => {
@@ -0,0 +1,58 @@
1
+ /**
2
+ * What RV's figures run through, as its footer says it under the text table,
3
+ * in the pane and in a capture: the statement period the trailing figures
4
+ * reach, and the basis of each column that is not trailing twelve months.
5
+ */
6
+
7
+ interface DatedPeer {
8
+ symbol: string;
9
+ date: string | null | undefined;
10
+ }
11
+
12
+ /**
13
+ * `2026-06-30`, or with peers dated apart, the date most of them share and
14
+ * each other peer's own: `2026-06-30 (LRN 2026-03-31)`. Null when none is dated.
15
+ */
16
+ export function peerDatesText(peers: readonly DatedPeer[]): string | null {
17
+ const dated = peers.flatMap(({ symbol, date }) => (date ? [{ symbol, date: date.slice(0, 10) }] : []));
18
+ if (dated.length === 0) return null;
19
+ const counts = new Map<string, number>();
20
+ for (const { date } of dated) counts.set(date, (counts.get(date) ?? 0) + 1);
21
+ const [common] = [...counts].sort(([leftDate, left], [rightDate, right]) => right - left || rightDate.localeCompare(leftDate))[0]!;
22
+ const others = dated.filter(({ date }) => date !== common).map(({ symbol, date }) => `${symbol} ${date}`);
23
+ return others.length > 0 ? `${common} (${others.join(", ")})` : common;
24
+ }
25
+
26
+ interface BasisRow {
27
+ symbol: string;
28
+ fundamentalsThrough?: string | null;
29
+ revenueGrowth?: number | null;
30
+ revenueGrowthQuarter?: string | null;
31
+ }
32
+
33
+ /** `fundamentals through 2026-06-30`, the newest statement period of each peer's trailing figures; null when none is dated. */
34
+ export function fundamentalsThroughText(rows: readonly BasisRow[]): string | null {
35
+ const through = peerDatesText(rows.map(({ symbol, fundamentalsThrough }) => ({ symbol, date: fundamentalsThrough })));
36
+ return through ? `fundamentals through ${through}` : null;
37
+ }
38
+
39
+ /**
40
+ * `fundamentals through 2026-06-30` and the quarter the revenue growth column
41
+ * compares with a year earlier, named by the column's header where the table
42
+ * is read: `, Q REV% for that quarter` when it is the one the fundamentals run
43
+ * through for every peer, else on its own (`Q REV%: quarter to 2026-03-31`).
44
+ * P/E, EV/S, FCF yield and the operating margin are trailing twelve months
45
+ * through that date; the forward P/E says forward in its own header.
46
+ */
47
+ export function fundamentalsBasisParts(rows: readonly BasisRow[], revenueGrowthHeader: string): string[] {
48
+ const through = fundamentalsThroughText(rows);
49
+ const growing = rows.filter((row) => row.revenueGrowth != null);
50
+ const quarter = peerDatesText(growing.map(({ symbol, revenueGrowthQuarter }) => ({ symbol, date: revenueGrowthQuarter })));
51
+ if (through && quarter && growing.every((row) => row.revenueGrowthQuarter === row.fundamentalsThrough)) {
52
+ return [`${through}, ${revenueGrowthHeader} for that quarter`];
53
+ }
54
+ return [
55
+ ...(through ? [through] : []),
56
+ ...(quarter ? [`${revenueGrowthHeader}: quarter to ${quarter}`] : []),
57
+ ];
58
+ }
@@ -4,12 +4,14 @@ import type { TickerFinancials } from "../../../types/financials";
4
4
  import { isUsListingExchange, parsePublicTickerKey } from "../../../utils/exchanges";
5
5
  import { errorMessage } from "../../../utils/errors";
6
6
  import { formatNumber } from "../../../utils/format";
7
+ import { formatPriceEarnings, NOT_MEANINGFUL } from "../../../utils/price-earnings";
7
8
  import { fetchScreen } from "../equity-screener/client";
8
9
  import { refusedLock } from "../shared/cloud-denied";
9
10
  import { resolveHeadlessInstrument } from "../shared/headless-market-data";
10
11
  import { loadShortInterest } from "../short-interest/client";
11
12
  import { buildShortWatchRow } from "../short-interest/watch-model";
12
13
  import { analystTargetCurrency, formatAnalystPrice, recommendationTotal } from "./analyst-model";
14
+ import { peerDatesText } from "./relative-valuation-basis";
13
15
 
14
16
  /** The columns `--columns` adds to RV's report, each read from the path its own function uses. */
15
17
  export const EXTRA_COLUMN_IDS = ["shortInterest", "nextEarnings", "target", "grossMargin", "evEbitda"] as const;
@@ -70,6 +72,12 @@ interface ColumnSpec {
70
72
  sourceKey: string;
71
73
  asOfKey: string;
72
74
  label: string;
75
+ /**
76
+ * What the column's figures are as of, for the report's footer, from the
77
+ * dates of its cells (`fiscal year to 2025-12-31`). A column dated only by
78
+ * when it was read (targets, the earnings calendar) has none.
79
+ */
80
+ basis?: (dates: string) => string;
73
81
  }
74
82
 
75
83
  const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
@@ -78,6 +86,7 @@ const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
78
86
  columns: [{ key: "shortInterestPercentFloat", header: "Short % Float", align: "right", format: percent }],
79
87
  sourceKey: "shortInterestSource",
80
88
  asOfKey: "shortInterestAsOf",
89
+ basis: (dates) => `settlement ${dates}`,
81
90
  },
82
91
  nextEarnings: {
83
92
  label: "Next Earnings",
@@ -93,7 +102,7 @@ const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
93
102
  target: {
94
103
  label: "Avg Target",
95
104
  columns: [
96
- { key: "targetMean", header: "Avg Target", align: "right", unitKey: "targetCurrency",
105
+ { key: "targetMean", header: "Avg Target", align: "right", unitKey: "targetCurrency", unitColumn: true,
97
106
  format: (value, row) => (typeof value === "number" ? formatAnalystPrice(value, typeof row.targetCurrency === "string" ? row.targetCurrency : undefined) : "-") },
98
107
  { key: "analystCount", header: "Analysts", align: "right", format: (value) => (typeof value === "number" ? String(value) : "-") },
99
108
  ],
@@ -105,12 +114,17 @@ const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
105
114
  columns: [{ key: "grossMarginPercent", header: "Gross Margin", align: "right", format: percent }],
106
115
  sourceKey: "grossMarginSource",
107
116
  asOfKey: "grossMarginAsOf",
117
+ // The screener stores the fiscal year's margin (its FY GROSS MGN%), dated by the year's end.
118
+ basis: (dates) => `fiscal year to ${dates}`,
108
119
  },
109
120
  evEbitda: {
110
121
  label: "EV/EBITDA",
111
- columns: [{ key: "evEbitda", header: "EV/EBITDA", align: "right", format: (value) => (typeof value === "number" ? formatNumber(value, 1) : "-") }],
122
+ // N/M over an EV or EBITDA at or below zero, as EQS shows it.
123
+ columns: [{ key: "evEbitda", header: "EV/EBITDA", align: "right", format: (value, row) => (typeof value === "number" ? formatNumber(value, 1)
124
+ : row.evEbitdaNotMeaningful ? formatPriceEarnings(NOT_MEANINGFUL) : "-") }],
112
125
  sourceKey: "evEbitdaSource",
113
126
  asOfKey: "evEbitdaAsOf",
127
+ basis: (dates) => `statements to ${dates}`,
114
128
  },
115
129
  };
116
130
 
@@ -130,6 +144,20 @@ export function extraColumns(ids: readonly ExtraColumnId[]): { text: HeadlessPan
130
144
  };
131
145
  }
132
146
 
147
+ /**
148
+ * The footer's word on each added column dated by its figures: `Gross Margin:
149
+ * fiscal year to 2025-12-31`, beside the TTM columns' `fundamentals through`.
150
+ */
151
+ export function extraColumnsBasis(ids: readonly ExtraColumnId[], rows: readonly HeadlessPaneRow[]): string[] {
152
+ return ids.flatMap((id) => {
153
+ const spec = COLUMN_SPECS[id];
154
+ const valueKey = spec.columns[0]!.key;
155
+ const dates = spec.basis && peerDatesText(rows.filter((row) => row[valueKey] != null)
156
+ .map((row) => ({ symbol: String(row.symbol), date: typeof row[spec.asOfKey] === "string" ? row[spec.asOfKey] as string : null })));
157
+ return dates ? [`${spec.label}: ${spec.basis!(dates)}`] : [];
158
+ });
159
+ }
160
+
133
161
  /** Whether a symbol is a US listing, which FINRA short interest, the earnings calendar and the screener cover. */
134
162
  function usListing(symbol: string): string | null {
135
163
  const { symbol: bare, exchange } = parsePublicTickerKey(symbol);
@@ -273,6 +301,7 @@ async function loadScreenerFields(ids: readonly ExtraColumnId[], symbols: readon
273
301
  if (wanted.includes("evEbitda")) {
274
302
  const multiple = metricFields(row.metrics.evToEbitda);
275
303
  if (multiple) Object.assign(fields, { evEbitda: multiple.value, evEbitdaSource: multiple.source, evEbitdaAsOf: multiple.asOf });
304
+ else if (row.metrics.evToEbitda?.notMeaningful) Object.assign(fields, { evEbitda: null, evEbitdaNotMeaningful: row.metrics.evToEbitda.reason ?? true });
276
305
  }
277
306
  }
278
307
  }
@@ -1,4 +1,5 @@
1
1
  import type { HeadlessPaneContext } from "../../../types/headless";
2
+ import type { QueryEntry } from "../../../market-data/result-types";
2
3
  import { MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
3
4
  import { fxLegForCurrency } from "../../../market-data/coordinator/fx-legs";
4
5
  import { resolveCurrencyUnit } from "../../../utils/currency-units";
@@ -33,6 +34,12 @@ export type ConvertedCapRow<Row extends CapRow> = Row & {
33
34
 
34
35
  const usable = (rate: number | undefined): rate is number => rate != null && Number.isFinite(rate) && rate > 0;
35
36
 
37
+ /** A loaded rate as a `UsdRate`: with its source's observation time and when it was read, from the entry it came in. */
38
+ export function usdRateFromEntry(rate: number, entry: Pick<QueryEntry<number>, "asOf" | "fetchedAt"> | null | undefined): UsdRate {
39
+ const time = (value: number | null | undefined) => (value != null && Number.isFinite(value) ? new Date(value).toISOString() : null);
40
+ return { rate, asOf: time(entry?.asOf), retrievedAt: time(entry?.fetchedAt) };
41
+ }
42
+
36
43
  function olderOf(left: string | null, right: string | null): string | null {
37
44
  if (!left || !right) return left ?? right;
38
45
  return Date.parse(left) <= Date.parse(right) ? left : right;
@@ -105,13 +112,14 @@ function rateTimeText(asOf: string): string {
105
112
 
106
113
  /**
107
114
  * The status line's note naming each rate and its date:
108
- * `market caps in USD at EUR/USD 1.1612 (2026-10-13 21:00 UTC)`.
115
+ * `market caps in USD at EUR/USD 1.1612 (2026-10-13 21:00 UTC)`; the pane
116
+ * names its column instead, `MCAP USD at …`.
109
117
  */
110
- export function conversionNote(target: string, conversions: readonly CapConversion[]): string | null {
118
+ export function conversionNote(target: string, conversions: readonly CapConversion[], subject = `market caps in ${target}`): string | null {
111
119
  if (conversions.length === 0) return null;
112
120
  const rates = conversions.map((conversion) => `${conversionPairText(conversion)} (${conversion.asOf ? rateTimeText(conversion.asOf)
113
121
  : conversion.retrievedAt ? `retrieved ${rateTimeText(conversion.retrievedAt)}` : "time not stated"})`);
114
- return `market caps in ${target} at ${rates.join(", ")}`;
122
+ return `${subject} at ${rates.join(", ")}`;
115
123
  }
116
124
 
117
125
  /**
@@ -134,8 +142,7 @@ export async function loadFxLegs(
134
142
  const entry = await coordinator.loadFxRate(currency);
135
143
  const rate = resolveEntryValue(entry);
136
144
  if (!usable(rate ?? undefined)) throw new Error(entry.error?.message ?? "no usable rate");
137
- const time = (value: number | null | undefined) => (value != null && Number.isFinite(value) ? new Date(value).toISOString() : null);
138
- legs.set(currency, { rate: rate!, asOf: time(entry.asOf), retrievedAt: time(entry.fetchedAt) });
145
+ legs.set(currency, usdRateFromEntry(rate!, entry));
139
146
  } catch (error) {
140
147
  failed.push({ currency, reason: errorMessage(error) });
141
148
  }
@@ -4,12 +4,17 @@ import { EXTENDED_SESSION_LABELS, type ExtendedSession } from "../../../market-d
4
4
  import { formatCompact, formatCurrency, formatLevelPercent, formatNumber, formatPercent, formatPercentRaw } from "../../../utils/format";
5
5
  import { loadHeadlessFinancials, loadHeadlessSymbols } from "../shared/headless-market-data";
6
6
  import { quoteFreshnessFields } from "../shared/report-freshness";
7
- import { RELATIVE_VALUATION_STALE_FUNDAMENTALS_NOTICE, RELATIVE_VALUATION_STALE_QUOTE_NOTICE, relativeValuationValues } from "./relative-valuation-model";
7
+ import {
8
+ RELATIVE_VALUATION_STALE_FUNDAMENTALS_NOTICE, RELATIVE_VALUATION_STALE_QUOTE_NOTICE, relativeValuationValues, type RelativeValuationGapKey,
9
+ } from "./relative-valuation-model";
8
10
  import { paneSchemas } from "./headless-schema";
9
11
  import { isRecord } from "../../../utils/guards";
12
+ import { errorMessage } from "../../../utils/errors";
13
+ import { notATickerSymbol } from "../../../sources/provider-errors";
10
14
  import { resolveCurrencyUnit } from "../../../utils/currency-units";
11
15
  import { pickPeers, peerMethodTitle, type PeerSelection } from "./relative-valuation-peers";
12
- import { extraColumns, loadExtraColumns, parseExtraColumns } from "./relative-valuation-columns";
16
+ import { extraColumns, extraColumnsBasis, loadExtraColumns, parseExtraColumns } from "./relative-valuation-columns";
17
+ import { fundamentalsBasisParts } from "./relative-valuation-basis";
13
18
  import { conversionNote, convertMarketCaps, loadFxLegs } from "./relative-valuation-currency";
14
19
 
15
20
  const price = (value: unknown, row: Record<string, unknown>) => (
@@ -17,6 +22,12 @@ const price = (value: unknown, row: Record<string, unknown>) => (
17
22
  );
18
23
  const percent = (value: unknown) => value == null ? "-" : formatPercentRaw(Number(value));
19
24
 
25
+ /** The reason a row lacks the figure under `key` ("no TTM FCF"), which exports move to their status column. */
26
+ function gapText(row: Record<string, unknown>, key: RelativeValuationGapKey): string {
27
+ const gaps = row.gaps as Partial<Record<RelativeValuationGapKey, string>> | undefined;
28
+ return gaps?.[key] ?? "-";
29
+ }
30
+
20
31
  /**
21
32
  * The pre-market, after-hours or overnight print and its move from the regular close in Last, for the peers that have
22
33
  * one; "mid" when it is a bid and ask midpoint.
@@ -32,32 +43,60 @@ function extendedColumn(session: ExtendedSession): HeadlessPaneColumn {
32
43
  };
33
44
  }
34
45
 
46
+ /**
47
+ * An export's extended print: the price alone, in the row's currency, then its move from the close in a
48
+ * `<session> Chg%` column, blank for a peer whose print is from another session or that has none.
49
+ */
50
+ function extendedExportColumns(session: ExtendedSession): HeadlessPaneColumn[] {
51
+ const label = EXTENDED_SESSION_LABELS[session];
52
+ return [
53
+ { key: "extendedPrice", header: label, align: "right", unitKey: "currency",
54
+ format: (value, row) => row.extendedSession === session && typeof value === "number" ? price(value, row) : "-" },
55
+ { key: "extendedChangePercent", header: `${label} Chg%`, align: "right",
56
+ format: (value, row) => row.extendedSession === session && typeof value === "number" ? percent(value) : "-" },
57
+ ];
58
+ }
59
+
60
+ const EXTENDED_SESSIONS = ["PRE", "POST", "OVERNIGHT"] as const;
61
+
62
+ /** Every session's columns, whichever peers have a print, so the header is the same before the open and after the close. */
63
+ const EXTENDED_EXPORT_COLUMNS = EXTENDED_SESSIONS.flatMap(extendedExportColumns);
64
+
65
+ // Last is in each peer's own currency: an export writes the number, and the row's `currency_or_unit` names its currency.
35
66
  const QUOTE_COLUMNS: HeadlessPaneColumn[] = [
36
67
  { key: "symbol", header: "Ticker" },
37
- { key: "price", header: "Last", align: "right", format: price },
38
- { key: "changePercent", header: "Chg%", align: "right", format: percent },
68
+ { key: "price", header: "Last", align: "right", unitKey: "currency",
69
+ format: (value, row) => value == null ? gapText(row, "price") : price(value, row) },
70
+ { key: "changePercent", header: "Chg%", align: "right", format: (value, row) => value == null ? gapText(row, "changePercent") : percent(value) },
39
71
  ];
40
72
 
73
+ /** The latest quarter's revenue against the same quarter a year earlier; the footer names the quarter. */
74
+ const REVENUE_GROWTH_HEADER = "Quarter Revenue YoY";
75
+
41
76
  const VALUATION_COLUMNS: HeadlessPaneColumn[] = [
42
77
  // In the currency each peer's cap is in, which exports write in the header or beside the figure.
43
- { key: "marketCap", header: "Mkt Cap", align: "right", unitKey: "marketCapCurrency",
44
- format: (value, row) => value == null ? "-" : `${formatCompact(Number(value), { fixedDecimals: true })}${row.marketCapCurrency ? ` ${row.marketCapCurrency}` : ""}` },
78
+ { key: "marketCap", header: "Mkt Cap", align: "right", unitKey: "marketCapCurrency", unitColumn: true,
79
+ format: (value, row) => value == null ? gapText(row, "marketCap") : `${formatCompact(Number(value), { fixedDecimals: true })}${row.marketCapCurrency ? ` ${row.marketCapCurrency}` : ""}` },
45
80
  ...([ ["trailingPE", "P/E"], ["forwardPE", "Fwd P/E"] ] as const).map(([key, header]) => ({
46
81
  key, header, align: "right" as const,
47
- format: (_value: unknown, row: Record<string, unknown>) => {
82
+ // The multiple as reported, else the comparable value itself, so an export reads the column as one of figures.
83
+ format: (value: unknown, row: Record<string, unknown>) => {
48
84
  const values = row as Partial<ReturnType<typeof relativeValuationValues>>;
49
- return formatPriceEarnings(values.notMeaningful?.[key] ? NOT_MEANINGFUL : values.reportedMultiples?.[key]);
85
+ const multiple = values.notMeaningful?.[key] ? NOT_MEANINGFUL : values.reportedMultiples?.[key] ?? (typeof value === "number" ? value : null);
86
+ return multiple == null ? gapText(row, key) : formatPriceEarnings(multiple);
50
87
  },
51
88
  })),
52
- ...[["evSales", "EV/S"]].map(([key, header]) => ({
53
- key: key!, header: header!, align: "right" as const,
54
- // A withdrawn EV says so rather than leaving a bare dash; the note under the table says why.
89
+ ...([["evSales", "EV/S"]] as const).map(([key, header]) => ({
90
+ key, header, align: "right" as const,
91
+ // A withdrawn EV says so rather than leaving a bare dash, one at or below zero reads N/M; the note under the table says why.
55
92
  format: (value: unknown, row: Record<string, unknown>) => value != null ? formatNumber(Number(value), 1)
56
- : row.evSalesUnavailable ? "EV unavailable" : "-",
93
+ : (row as Partial<ReturnType<typeof relativeValuationValues>>).notMeaningful?.evSales ? formatPriceEarnings(NOT_MEANINGFUL)
94
+ : gapText(row, key),
57
95
  })),
58
- ...[["fcfYield", "FCF Yield"], ["revenueGrowth", "Quarter Revenue YoY"], ["operatingMargin", "TTM Op Margin"]].map(([key, header]) => ({
59
- key: key!, header: header!, align: "right" as const,
60
- format: (value: unknown) => value == null ? "-" : key === "revenueGrowth" ? formatPercent(Number(value)) : formatLevelPercent(Number(value)),
96
+ ...([["fcfYield", "FCF Yield"], ["revenueGrowth", REVENUE_GROWTH_HEADER], ["operatingMargin", "TTM Op Margin"]] as const).map(([key, header]) => ({
97
+ key, header, align: "right" as const,
98
+ format: (value: unknown, row: Record<string, unknown>) => value == null ? gapText(row, key)
99
+ : key === "revenueGrowth" ? formatPercent(Number(value)) : formatLevelPercent(Number(value)),
61
100
  })),
62
101
  ];
63
102
 
@@ -76,7 +115,14 @@ async function choosePeers(symbols: readonly string[], ctx: HeadlessPaneContext)
76
115
  throw new Error(`--peers picks the peers of one symbol, such as RV ${symbols[0] ?? "DUOL"} --peers; with several symbols, leave it out.`);
77
116
  }
78
117
  const subject = symbols[0]!;
79
- return pickPeers(subject, await loadHeadlessFinancials(ctx, subject).catch(() => null), ctx);
118
+ const financials = await loadHeadlessFinancials(ctx, subject).catch((error: unknown) => {
119
+ ctx.signal.throwIfAborted();
120
+ // A word that is no ticker is said as `fn RV duolingo lrn` says it, which the CLI follows with the search to run.
121
+ if (notATickerSymbol(errorMessage(error))) throw error;
122
+ // Any other failure leaves the peers to what the symbol alone places (a curated core's member).
123
+ return null;
124
+ });
125
+ return pickPeers(subject, financials, ctx);
80
126
  }
81
127
 
82
128
  const PEER_SELECTION_SETTING = "peerSelection";
@@ -93,7 +139,7 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
93
139
  ? `Relative Valuation | ${selection.subject} and ${peerMethodTitle(selection)}`
94
140
  : `Relative Valuation | ${(result?.symbols ?? symbols).join(", ")}`;
95
141
  },
96
- columns: [...QUOTE_COLUMNS, ...VALUATION_COLUMNS],
142
+ columns: [...QUOTE_COLUMNS, ...EXTENDED_EXPORT_COLUMNS, ...VALUATION_COLUMNS],
97
143
  async expandArgument(args, ctx) {
98
144
  if (args.options.peers !== true) return null;
99
145
  const selection = await choosePeers(args.symbols, ctx);
@@ -137,23 +183,33 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
137
183
  const reason = fx?.failed.find((failure) => failure.currency === resolveCurrencyUnit(currency).currency)?.reason;
138
184
  return `No ${currency} rate${reason ? ` (${reason})` : ""}: ${left.join(", ")} ${left.length === 1 ? "keeps its" : "keep their"} market cap in ${currency}.`;
139
185
  }) ?? [];
140
- const conversionText = capCurrency && converted ? conversionNote(capCurrency, converted.conversions) : null;
186
+ // The footer: the rates the caps were converted at, the statement period the figures run through, and the
187
+ // basis of each column that is not trailing twelve months.
188
+ const footnote = [
189
+ ...capCurrency && converted ? [conversionNote(capCurrency, converted.conversions)] : [],
190
+ ...fundamentalsBasisParts(rows, REVENUE_GROWTH_HEADER),
191
+ ...extraColumnsBasis(extraIds, rows),
192
+ ].filter(Boolean).join(" · ");
141
193
  const notes = [
142
- ...selection ? [`Peers chosen automatically: ${selection.basis}. To choose your own: gloomberb fn RV ${selection.subject} <tickers>.`] : [],
194
+ ...selection ? [`Peers chosen automatically: ${selection.basis}. To choose your own: gloom fn RV ${selection.subject} <tickers>.`] : [],
143
195
  ...extra.notes,
144
196
  ];
145
197
  // Last and Chg% are the regular session; an extended print gets a column only when a peer has one.
146
- const extendedSessions = (["PRE", "POST", "OVERNIGHT"] as const).filter((session) => rows.some((row) => row.extendedSession === session));
147
- const columns = [...QUOTE_COLUMNS, ...extendedSessions.map(extendedColumn), ...VALUATION_COLUMNS];
198
+ // Exports keep every session's price and move, so their header is the same in every market state.
199
+ const extendedSessions = EXTENDED_SESSIONS.filter((session) => rows.some((row) => row.extendedSession === session));
200
+ const textColumns = [...QUOTE_COLUMNS, ...extendedSessions.map(extendedColumn), ...VALUATION_COLUMNS];
148
201
  const added = extraIds.length > 0 ? extraColumns(extraIds) : null;
149
202
  return {
150
203
  rows, unavailableSymbols, errors, complete: unavailableSymbols.length === 0 && errors.length === 0,
151
204
  ...(selection ? { symbols } : {}),
152
- ...(added ? { columns: [...columns, ...added.export], textColumns: [...columns, ...added.text] }
153
- : extendedSessions.length > 0 ? { columns } : {}),
205
+ columns: [...QUOTE_COLUMNS, ...EXTENDED_EXPORT_COLUMNS, ...VALUATION_COLUMNS, ...added?.export ?? []],
206
+ textColumns: [...textColumns, ...added?.text ?? []],
207
+ // Each peer's row names its ticker and the currency its prices are in.
208
+ rowSymbolKey: "symbol",
209
+ rowUnitKey: "currency",
154
210
  ...(notes.length > 0 ? { notes } : {}),
155
211
  ...(warnings.length > 0 ? { warnings } : {}),
156
- ...(conversionText ? { freshness: { note: conversionText } } : {}),
212
+ ...(footnote ? { freshness: { note: footnote } } : {}),
157
213
  metadata: {
158
214
  staleSymbols,
159
215
  staleFundamentalsSymbols,
@@ -165,11 +221,13 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
165
221
  priceBasis: "price, change and changePercent are the regular session: after the close, its close and that close's move. A pre-market, after-hours or overnight print is in extendedPrice, extendedChange and extendedChangePercent, measured from that close (extendedMark when it is a bid and ask midpoint). marketCap, the P/E multiples, evSales (through the equity part of the enterprise value) and fcfYield are measured at that regular session price.",
166
222
  fundamentalsBasis: "Provider multiples and operating metrics retain independent fundamentalsProvenance. Retrieval time does not establish the valuation date.",
167
223
  notices: [
168
- ...rows.some((row) => Object.keys(row.notMeaningful).length > 0
224
+ ...rows.some((row) => row.notMeaningful.trailingPE || row.notMeaningful.forwardPE
169
225
  || Object.values(row.reportedMultiples).some((value) => value != null && value <= 0)) ? [PRICE_EARNINGS_NOTICE] : [],
170
226
  ...rows.flatMap((row) => row.evSalesUnavailable ? [`${row.symbol}: ${row.evSalesUnavailable}`] : []),
227
+ ...rows.flatMap((row) => row.evSalesNote ? [`${row.symbol}: ${row.evSalesNote}`] : []),
228
+ ...rows.flatMap((row) => row.marketCapNote ? [`${row.symbol}: ${row.marketCapNote}`] : []),
171
229
  ],
172
- multipleBasis: "Comparable P/E fields require a positive finite multiple over positive EPS; notMeaningful says why one is null, and reportedMultiples preserves the finite provider values.",
230
+ multipleBasis: "Comparable P/E fields require a positive finite multiple over positive EPS, and evSales an enterprise value above zero; notMeaningful says why one is null, and reportedMultiples preserves the finite provider P/E values.",
173
231
  marketCapBasis: "Market caps retain their own currency and source. Fundamentals retrieval time is not a valuation date; quote timestamps do not date fallback caps.",
174
232
  },
175
233
  };