gloomberb 0.16.0 → 0.16.2

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 (691) hide show
  1. package/README.md +2 -1
  2. package/package.json +2 -1
  3. package/src/api-client/beneficial-owners.ts +92 -0
  4. package/src/api-client/data.ts +44 -11
  5. package/src/api-client/errors.ts +12 -0
  6. package/src/api-client/geo.ts +363 -0
  7. package/src/api-client/index.ts +5 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/members.ts +26 -0
  10. package/src/api-client/paths.ts +30 -1
  11. package/src/api-client/perps.ts +41 -0
  12. package/src/api-client/request.ts +5 -2
  13. package/src/api-client/types.ts +9 -1
  14. package/src/app/global-shortcuts.ts +14 -6
  15. package/src/app/keybindings/actions.ts +1 -1
  16. package/src/app/runtime/update.ts +38 -2
  17. package/src/app.tsx +10 -6
  18. package/src/brokers/account-cache.ts +24 -0
  19. package/src/cli/commands/command-utils.ts +8 -0
  20. package/src/cli/commands/crypto-hints.ts +75 -0
  21. package/src/cli/commands/market.ts +414 -106
  22. package/src/cli/commands/overview.ts +77 -32
  23. package/src/cli/commands/ticker.ts +101 -43
  24. package/src/cli/desktop-pane-shot.ts +34 -4
  25. package/src/cli/freshness.ts +61 -0
  26. package/src/cli/help.ts +1 -0
  27. package/src/cli/helpers.ts +4 -8
  28. package/src/cli/index.ts +12 -2
  29. package/src/cli/listing-arg.ts +321 -0
  30. package/src/cli/pane-functions/access-gate.ts +42 -0
  31. package/src/cli/pane-functions/catalog.ts +28 -1
  32. package/src/cli/pane-functions/data.ts +3 -1
  33. package/src/cli/pane-functions/dom.ts +20 -2
  34. package/src/cli/pane-functions/freshness.ts +400 -0
  35. package/src/cli/pane-functions/function-help.ts +36 -22
  36. package/src/cli/pane-functions/headless.ts +39 -11
  37. package/src/cli/pane-functions/index.ts +15 -4
  38. package/src/cli/pane-functions/options.ts +3 -0
  39. package/src/cli/pane-functions/report.ts +4 -0
  40. package/src/cli/pane-functions/resolver.ts +35 -7
  41. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  42. package/src/cli/pane-functions/screenshot.ts +7 -3
  43. package/src/cli/result.ts +41 -27
  44. package/src/cli/ticker-resolution.ts +7 -5
  45. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  46. package/src/components/chart/composite/drawing-store.ts +59 -0
  47. package/src/components/chart/composite/legend.tsx +319 -0
  48. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  49. package/src/components/chart/composite/panel-gestures.ts +59 -0
  50. package/src/components/chart/composite/panel-labels.ts +107 -0
  51. package/src/components/chart/composite/panel-surface.tsx +876 -0
  52. package/src/components/chart/composite/tool-catalog.ts +98 -0
  53. package/src/components/chart/composite/toolbar.tsx +186 -0
  54. package/src/components/chart/composite/tools.ts +8 -0
  55. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  56. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  57. package/src/components/command-bar/commands/collection.ts +5 -1
  58. package/src/components/command-bar/list/model.ts +5 -0
  59. package/src/components/command-bar/list/view.tsx +16 -3
  60. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  61. package/src/components/command-bar/routes/root/command-items.ts +9 -2
  62. package/src/components/command-bar/routes/root/plugin-install.ts +2 -2
  63. package/src/components/command-bar/routes/root/provider-search.ts +19 -5
  64. package/src/components/command-bar/routes/root/runtime.ts +14 -5
  65. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  66. package/src/components/command-bar/routes/ticker-search/results.ts +31 -5
  67. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  68. package/src/components/command-bar/routing/actions.ts +2 -0
  69. package/src/components/command-bar/selection-runtime.ts +38 -0
  70. package/src/components/command-bar/surface/index.tsx +7 -2
  71. package/src/components/command-bar/workflow/ops.ts +1 -1
  72. package/src/components/command-bar/workflow/tickers.ts +6 -0
  73. package/src/components/data-table/view.tsx +29 -10
  74. package/src/components/feedback-dialog.tsx +2 -1
  75. package/src/components/form-modal/field-row.tsx +2 -2
  76. package/src/components/index.ts +2 -2
  77. package/src/components/layout/detached-pane-shell.tsx +6 -3
  78. package/src/components/layout/floating-pane.tsx +6 -0
  79. package/src/components/layout/header.tsx +24 -7
  80. package/src/components/layout/missing-pane.tsx +2 -2
  81. package/src/components/layout/pane/content.tsx +23 -10
  82. package/src/components/layout/pane/error-boundary.tsx +103 -0
  83. package/src/components/layout/pane/footer/registration.tsx +68 -23
  84. package/src/components/layout/pane/header.tsx +61 -28
  85. package/src/components/layout/pane/index.tsx +6 -0
  86. package/src/components/layout/pane/title.ts +31 -7
  87. package/src/components/layout/shell/drag/index.ts +6 -5
  88. package/src/components/layout/shell/drag/runtime.ts +3 -0
  89. package/src/components/layout/shell/index.tsx +6 -0
  90. package/src/components/layout/shell/menu.ts +2 -1
  91. package/src/components/layout/shell/pane/actions.ts +2 -1
  92. package/src/components/layout/shell/pane/layers.tsx +29 -10
  93. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -3
  94. package/src/components/layout/shell/ticker-link-menu.ts +23 -9
  95. package/src/components/metric-treemap/heat-surface.tsx +745 -0
  96. package/src/components/metric-treemap/heat-tiles.ts +228 -0
  97. package/src/components/metric-treemap/index.tsx +2 -0
  98. package/src/components/metric-treemap/layout.ts +8 -0
  99. package/src/components/metric-treemap/scene.ts +432 -0
  100. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  101. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  102. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  103. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  104. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  105. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -0
  106. package/src/components/ticker/list-table-view.tsx +13 -1
  107. package/src/components/toggle-list.tsx +38 -12
  108. package/src/components/ui/country-flag-art.tsx +449 -0
  109. package/src/components/ui/country-flag.tsx +41 -0
  110. package/src/components/ui/data-table/types.ts +13 -0
  111. package/src/components/ui/fields.tsx +8 -0
  112. package/src/components/ui/icon.tsx +2 -0
  113. package/src/components/ui/index.ts +4 -0
  114. package/src/components/ui/multi-select/dialog.tsx +108 -22
  115. package/src/components/ui/range-track.tsx +53 -0
  116. package/src/components/ui/status.tsx +8 -2
  117. package/src/components/ui/tabs.tsx +11 -4
  118. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  119. package/src/core/state/app/types.ts +10 -1
  120. package/src/data/config/store/normalize.ts +17 -1
  121. package/src/i18n/es.ts +24 -4
  122. package/src/i18n/ja.ts +24 -4
  123. package/src/i18n/ko.ts +24 -4
  124. package/src/i18n/zh-cn.ts +24 -4
  125. package/src/i18n/zh-tw.ts +24 -4
  126. package/src/layout/ticker-navigation.ts +28 -2
  127. package/src/market-data/coordinator/chart.ts +1 -1
  128. package/src/market-data/coordinator/quotes.ts +23 -4
  129. package/src/market-data/fx-closes.ts +72 -0
  130. package/src/market-data/market/freshness.ts +78 -6
  131. package/src/market-data/market/session-close-note.ts +57 -0
  132. package/src/market-data/market/status.ts +151 -0
  133. package/src/market-data/published-apac-sessions.ts +66 -0
  134. package/src/market-data/quotes/contributions.ts +8 -0
  135. package/src/market-data/quotes/freshness.ts +36 -0
  136. package/src/market-data/quotes/resolution.ts +24 -8
  137. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  138. package/src/market-data/quotes/time.ts +16 -0
  139. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  140. package/src/plugins/builtin/alerts/cli.ts +10 -5
  141. package/src/plugins/builtin/alerts/index.tsx +1 -0
  142. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  143. package/src/plugins/builtin/analytics/risk-client.ts +330 -79
  144. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  145. package/src/plugins/builtin/analytics/risk-headless.ts +61 -49
  146. package/src/plugins/builtin/analytics/risk-math.ts +33 -13
  147. package/src/plugins/builtin/analytics/risk-model.ts +282 -64
  148. package/src/plugins/builtin/analytics/risk-pane.tsx +62 -30
  149. package/src/plugins/builtin/analytics/risk-test-data.ts +206 -0
  150. package/src/plugins/builtin/attention/headless.ts +6 -4
  151. package/src/plugins/builtin/attention/index.tsx +1 -1
  152. package/src/plugins/builtin/attention/pane.tsx +1 -1
  153. package/src/plugins/builtin/awards/headless.ts +1 -0
  154. package/src/plugins/builtin/awards/pane.tsx +1 -1
  155. package/src/plugins/builtin/backtest/headless.ts +1 -0
  156. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  157. package/src/plugins/builtin/buildout/cells.tsx +15 -12
  158. package/src/plugins/builtin/buildout/detail/company.tsx +3 -3
  159. package/src/plugins/builtin/buildout/detail/index.tsx +0 -1
  160. package/src/plugins/builtin/buildout/detail/intel.tsx +6 -6
  161. package/src/plugins/builtin/buildout/detail/site.tsx +4 -7
  162. package/src/plugins/builtin/buildout/detail/ui.tsx +0 -19
  163. package/src/plugins/builtin/buildout/index.tsx +1 -0
  164. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  165. package/src/plugins/builtin/catalysts/index.tsx +2 -2
  166. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  167. package/src/plugins/builtin/cds/headless.ts +2 -0
  168. package/src/plugins/builtin/cds/index.tsx +1 -0
  169. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  170. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  171. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  172. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  173. package/src/plugins/builtin/chat/channels.ts +19 -3
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  175. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  176. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  177. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  178. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  179. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  180. package/src/plugins/builtin/chat/index.tsx +1 -0
  181. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  182. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  183. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  184. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  185. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  186. package/src/plugins/builtin/chat/message/types.ts +14 -2
  187. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  188. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  189. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  190. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  191. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  192. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  193. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  194. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  195. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  196. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  197. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  198. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  199. package/src/plugins/builtin/correlation/compute.ts +59 -16
  200. package/src/plugins/builtin/correlation/geo.ts +34 -0
  201. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  202. package/src/plugins/builtin/correlation/headless.ts +42 -8
  203. package/src/plugins/builtin/correlation/history.ts +2 -0
  204. package/src/plugins/builtin/correlation/index.tsx +68 -14
  205. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  206. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  207. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  208. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  209. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  210. package/src/plugins/builtin/correlation/settings.ts +1 -1
  211. package/src/plugins/builtin/cot/headless.ts +6 -3
  212. package/src/plugins/builtin/cot/index.tsx +3 -3
  213. package/src/plugins/builtin/cot/model.ts +36 -3
  214. package/src/plugins/builtin/cpi/headless.ts +4 -0
  215. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  216. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  217. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  218. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  219. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  220. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  221. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  222. package/src/plugins/builtin/debt-maturities/wall-bar.tsx +14 -41
  223. package/src/plugins/builtin/dividend-yield/client.ts +4 -2
  224. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  225. package/src/plugins/builtin/doe/headless.ts +7 -0
  226. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  227. package/src/plugins/builtin/earnings/client.ts +2 -2
  228. package/src/plugins/builtin/earnings/headless.ts +5 -0
  229. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  230. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  231. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  232. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  234. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  235. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  236. package/src/plugins/builtin/econ/index.tsx +1 -0
  237. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  238. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  239. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  240. package/src/plugins/builtin/executives/index.tsx +1 -0
  241. package/src/plugins/builtin/exposure/headless.ts +3 -1
  242. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  243. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  244. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  245. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  246. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  247. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  248. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  249. package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
  250. package/src/plugins/builtin/fx-matrix/index.tsx +71 -33
  251. package/src/plugins/builtin/fx-matrix/live-legs.ts +17 -0
  252. package/src/plugins/builtin/fx-matrix/pairs.ts +68 -14
  253. package/src/plugins/builtin/fx-matrix/settings.ts +62 -0
  254. package/src/plugins/builtin/gpu/headless.ts +7 -3
  255. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  256. package/src/plugins/builtin/gpu/model.ts +27 -1
  257. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  258. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  259. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  260. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  261. package/src/plugins/builtin/holders/headless.ts +139 -5
  262. package/src/plugins/builtin/holders/index.tsx +10 -3
  263. package/src/plugins/builtin/holders/pane.tsx +20 -3
  264. package/src/plugins/builtin/holders/report-header.ts +95 -0
  265. package/src/plugins/builtin/holders/table-model.ts +20 -5
  266. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  267. package/src/plugins/builtin/holders/types.ts +2 -1
  268. package/src/plugins/builtin/insider/headless.ts +2 -0
  269. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  270. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  271. package/src/plugins/builtin/jobs/headless.ts +1 -0
  272. package/src/plugins/builtin/jobs/index.tsx +1 -0
  273. package/src/plugins/builtin/jobs/share-bars.tsx +5 -28
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  275. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  276. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +30 -2
  278. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  279. package/src/plugins/builtin/market-heatmap/index.tsx +245 -81
  280. package/src/plugins/builtin/market-heatmap/live.ts +88 -0
  281. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  282. package/src/plugins/builtin/market-heatmap/portfolio.ts +50 -11
  283. package/src/plugins/builtin/market-movers/client.ts +3 -2
  284. package/src/plugins/builtin/market-movers/headless.ts +10 -1
  285. package/src/plugins/builtin/market-movers/screener.ts +18 -5
  286. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  287. package/src/plugins/builtin/members/client.ts +53 -0
  288. package/src/plugins/builtin/members/headless.ts +35 -0
  289. package/src/plugins/builtin/members/index.tsx +20 -0
  290. package/src/plugins/builtin/members/model.ts +68 -0
  291. package/src/plugins/builtin/members/pane.tsx +175 -0
  292. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  293. package/src/plugins/builtin/mna/headless.ts +1 -0
  294. package/src/plugins/builtin/mna/index.tsx +2 -1
  295. package/src/plugins/builtin/mna/pane.tsx +5 -2
  296. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  297. package/src/plugins/builtin/news/headless.ts +2 -0
  298. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  299. package/src/plugins/builtin/notes/cli.ts +14 -6
  300. package/src/plugins/builtin/notes/index.tsx +1 -0
  301. package/src/plugins/builtin/openfda/index.tsx +1 -0
  302. package/src/plugins/builtin/options/index.tsx +2 -1
  303. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  304. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  305. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  306. package/src/plugins/builtin/pe-band/client.ts +74 -2
  307. package/src/plugins/builtin/pe-band/headless.ts +26 -8
  308. package/src/plugins/builtin/pe-band/model.ts +102 -22
  309. package/src/plugins/builtin/pe-band/pane.tsx +34 -16
  310. package/src/plugins/builtin/pe-band/report-dates.ts +78 -0
  311. package/src/plugins/builtin/perps/client.ts +66 -5
  312. package/src/plugins/builtin/perps/equity-identity.ts +16 -0
  313. package/src/plugins/builtin/perps/equity-row.tsx +6 -4
  314. package/src/plugins/builtin/perps/headless.ts +144 -16
  315. package/src/plugins/builtin/perps/index.tsx +6 -6
  316. package/src/plugins/builtin/perps/model.ts +189 -12
  317. package/src/plugins/builtin/perps/pane.tsx +248 -47
  318. package/src/plugins/builtin/plugin-marketplace/activation.ts +19 -0
  319. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -2
  320. package/src/plugins/builtin/plugin-marketplace/store.ts +2 -0
  321. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  322. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  323. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  324. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  325. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  326. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  327. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  328. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  329. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  330. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  331. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  332. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  333. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  334. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  335. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +43 -2
  336. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +48 -5
  337. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  338. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  339. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  340. package/src/plugins/builtin/power/headless.ts +3 -1
  341. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  342. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  343. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  344. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  345. package/src/plugins/builtin/research/events-headless.ts +2 -0
  346. package/src/plugins/builtin/research/headless.ts +2 -0
  347. package/src/plugins/builtin/research/index.tsx +1 -0
  348. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  349. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  350. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  351. package/src/plugins/builtin/research-search/index.tsx +1 -0
  352. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  353. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  354. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  355. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  356. package/src/plugins/builtin/scanner/headless.ts +14 -0
  357. package/src/plugins/builtin/scanner/hilo-bars.tsx +12 -70
  358. package/src/plugins/builtin/scanner/hilo-model.ts +0 -18
  359. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  360. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  361. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  362. package/src/plugins/builtin/sec/forms.ts +12 -0
  363. package/src/plugins/builtin/sec/headless.ts +2 -0
  364. package/src/plugins/builtin/sectors/client.ts +1 -0
  365. package/src/plugins/builtin/sectors/headless.ts +9 -1
  366. package/src/plugins/builtin/sectors/move-bar.tsx +10 -32
  367. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  368. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  369. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  370. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  371. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  372. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  373. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  374. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  375. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  376. package/src/plugins/builtin/social-mentions/index.tsx +1 -1
  377. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  378. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  379. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  380. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  381. package/src/plugins/builtin/themes/headless.ts +3 -0
  382. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  383. package/src/plugins/builtin/thirteenf/index.tsx +2 -1
  384. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  385. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  386. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  387. package/src/plugins/builtin/ticker-detail/overview/components.tsx +5 -60
  388. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  389. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +1 -1
  390. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  391. package/src/plugins/builtin/ticker-detail/pane.tsx +1 -1
  392. package/src/plugins/builtin/ticker-detail/quote-footer.ts +7 -1
  393. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -4
  394. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -2
  395. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  396. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  397. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  398. package/src/plugins/builtin/volatility/headless.ts +6 -1
  399. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  400. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  401. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  402. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  403. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  404. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  405. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  406. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  407. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  408. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  409. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  410. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  411. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  412. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  413. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  414. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  415. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  416. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  417. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  418. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  564. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  565. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  566. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  567. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  568. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  569. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  570. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  571. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  572. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  573. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  574. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  575. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  576. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  577. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  578. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  579. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  580. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  581. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  582. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  583. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  584. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  585. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  586. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  587. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  588. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  589. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  590. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  591. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  592. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  593. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  594. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  595. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  596. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  597. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  598. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  599. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  600. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  601. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  602. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  603. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  604. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  605. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  606. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  607. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  608. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  609. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  610. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  611. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  612. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  613. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  614. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  615. package/src/plugins/catalog-browser.ts +2 -0
  616. package/src/plugins/catalog-manifest.ts +1 -0
  617. package/src/plugins/catalog-ui.ts +3 -1
  618. package/src/plugins/catalog.ts +1 -1
  619. package/src/plugins/host-link.ts +33 -1
  620. package/src/plugins/installer.ts +48 -12
  621. package/src/plugins/loader.ts +30 -3
  622. package/src/plugins/manager-node.ts +15 -5
  623. package/src/plugins/registry/index.ts +1 -1
  624. package/src/plugins/registry/pane-settings.ts +26 -7
  625. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  626. package/src/renderers/dom/build-assets.ts +81 -3
  627. package/src/renderers/dom/data-table/row.tsx +52 -16
  628. package/src/renderers/dom/desktop/controls.tsx +7 -3
  629. package/src/renderers/dom/desktop/icons.tsx +1 -0
  630. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  631. package/src/renderers/dom/desktop/popover.tsx +73 -14
  632. package/src/renderers/dom/host/box.tsx +69 -26
  633. package/src/renderers/dom/host/input.tsx +14 -3
  634. package/src/renderers/dom/host/tabs.tsx +37 -1
  635. package/src/renderers/dom/styles.css +41 -7
  636. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +11 -6
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  638. package/src/renderers/electrobun/bun/desktop/update.ts +181 -87
  639. package/src/renderers/electrobun/bun/index.ts +73 -6
  640. package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
  641. package/src/renderers/electrobun/shared/protocol.ts +3 -0
  642. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  643. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -1
  644. package/src/renderers/electrobun/view/main.tsx +7 -1
  645. package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
  646. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  647. package/src/renderers/electrobun/view/update-host.ts +45 -21
  648. package/src/renderers/opentui/host.tsx +5 -0
  649. package/src/sources/gloomberb-cloud/index.ts +25 -4
  650. package/src/sources/gloomberb-cloud/normalizers.ts +36 -5
  651. package/src/sources/provider-router/cached-routes.ts +4 -1
  652. package/src/sources/provider-router/financials.ts +11 -8
  653. package/src/sources/provider-router/history.ts +15 -10
  654. package/src/sources/sec-registrant.ts +75 -0
  655. package/src/state/app/context/index.tsx +24 -8
  656. package/src/state/hooks/quote-streaming.ts +41 -26
  657. package/src/sync/core-contributors.ts +4 -0
  658. package/src/telemetry/benign-window-error.ts +7 -0
  659. package/src/telemetry/crash-reports-dom.ts +2 -0
  660. package/src/telemetry/crash-reports.ts +4 -1
  661. package/src/test-support/cli-context.ts +10 -2
  662. package/src/test-support/daily-nav.ts +25 -0
  663. package/src/theme/heat-colors.ts +357 -2
  664. package/src/tickers/instrument-kind.ts +34 -7
  665. package/src/tickers/search/asset-classes.ts +24 -9
  666. package/src/tickers/search/index.ts +44 -11
  667. package/src/tickers/search/ranking.ts +23 -3
  668. package/src/time-series/resolve.ts +1 -1
  669. package/src/time-series/use-chart-resolution.ts +1 -1
  670. package/src/types/context-menu.ts +6 -0
  671. package/src/types/data-provider.ts +2 -0
  672. package/src/types/financials.ts +5 -0
  673. package/src/types/headless.ts +79 -2
  674. package/src/types/plugin.ts +38 -6
  675. package/src/types/ticker.ts +15 -0
  676. package/src/ui/context-menu.tsx +1 -0
  677. package/src/ui/host.tsx +16 -0
  678. package/src/updater/index.ts +38 -13
  679. package/src/utils/async-deadline.ts +21 -0
  680. package/src/utils/cli-output.ts +5 -1
  681. package/src/utils/crypto-pair.ts +41 -0
  682. package/src/utils/currency-units.ts +14 -0
  683. package/src/utils/depositary-receipt.ts +26 -0
  684. package/src/utils/exchanges.ts +72 -0
  685. package/src/utils/fire-and-forget.ts +24 -0
  686. package/src/utils/format.ts +13 -0
  687. package/src/utils/price-earnings.ts +70 -3
  688. package/src/utils/price-history.ts +58 -13
  689. package/src/utils/rpc-timeout-error.ts +27 -0
  690. package/src/utils/sec.ts +13 -1
  691. package/src/utils/utc-time.ts +79 -0
@@ -0,0 +1,175 @@
1
+ import { useCallback, useMemo, useRef, useState } from "react";
2
+ import type { FundChange, FundMember } from "../../../api-client/members";
3
+ import { DataTableStackView, DataTableView, DetailScrollBody, EmptyState, KeyValueRow, PaneFooterScope, PaneStatusBody, Prose, QueryBar, StatGrid,
4
+ isSectionedItemRow, renderSectionedRowHeader, useExternalLinkFooter, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, useQueryBarSearch,
5
+ type DataTableCell, type DataTableColumn, type PaneFooterSegment, type SectionedRow } from "../../../components";
6
+ import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
7
+ import { useAsyncResource, useAutoRefresh, usePaneInstance, usePaneTickerIdentity, usePaneTitle, usePluginPaneState, usePluginTickerActions } from "../../../public/react";
8
+ import { priceColor } from "../../../theme/colors";
9
+ import { useThemeColors } from "../../../theme/theme-context";
10
+ import type { PaneProps } from "../../../types/plugin";
11
+ import { Box, type ScrollBoxRenderable } from "../../../ui";
12
+ import { nextHeaderSort, type SortPreference } from "../../../utils/sort-values";
13
+ import { isPlainArrowUp, stopSearchFocusNavigation } from "../../../utils/search-focus-navigation";
14
+ import { cachedChanges, cachedFunds, cachedMembers, loadChanges, loadFunds, loadMembers } from "./client";
15
+ import { canonicalFund, changeColumns, changeLabel, changeReason, coveredFund, decimal, DEFAULT_SORT, isTab, memberColumns, memberRows, membersTitle, notCoveredMessage, percent, TABS, type MembersTab } from "./model";
16
+
17
+ type ViewProps = Pick<PaneProps, "width" | "height" | "focused"> & { fund: string; active: boolean };
18
+ const rowKey = (row: SectionedRow<FundMember>) => row.key;
19
+
20
+ function Holdings({ fund, tab, width, height, focused, active }: ViewProps & { tab: MembersTab }) {
21
+ const colors = useThemeColors();
22
+ const { pinTicker } = usePluginTickerActions();
23
+ const resource = useAsyncResource(useCallback((force: boolean) => loadMembers(fund, force), [fund]), { initialData: useCallback(() => cachedMembers(fund), [fund]) });
24
+ const data = resource.data?.payload;
25
+ const [sort, setSort] = usePluginPaneState<SortPreference<string>>(`sort:${fund}`, DEFAULT_SORT);
26
+ const [selected, setSelected] = usePluginPaneState<string | null>(`member:${fund}`, null);
27
+ const [query, setQuery] = useState("");
28
+ const search = useQueryBarSearch();
29
+ const movers = tab === "movers";
30
+ const rows = useMemo(() => memberRows(data?.members ?? [], tab, query, sort), [data, tab, query, sort]);
31
+ const columns = useMemo(() => memberColumns(width, movers), [width, movers]);
32
+ useAutoRefresh(resource.updatedAt, () => { if (active) void resource.load(); });
33
+ usePaneRefreshKey(() => void resource.reload(), { focused, enabled: active && !search.active });
34
+ const info = useMemo<PaneFooterSegment[]>(() => {
35
+ if (!data) return [];
36
+ const dates = [...new Set(data.members.flatMap((row) => row.changePercent !== null && row.dailyAsOf ? [row.dailyAsOf.slice(0, 10)] : []))].sort();
37
+ const date = dates.length === 1 ? dates[0] : dates.length ? `${dates[0]} to ${dates.at(-1)}` : data.fundDailyAsOf?.slice(0, 10);
38
+ const partial = data.aggregate.fresh1D < data.aggregate.total;
39
+ return [
40
+ { id: "coverage", parts: [{ text: `${partial && width >= 60 ? "Partial " : ""}1D ${data.aggregate.fresh1D}/${data.aggregate.total} · ${decimal(data.aggregate.coveredWeight * 100, 1)}% wt`, tone: partial ? "warning" : "muted" }] },
41
+ { id: "quote-date", title: `${date ?? "Quote date unavailable"} · 15m delayed · holdings ${data.asOf}`,
42
+ parts: [{ text: data.quotesUnavailable ? "returns unavailable" : width < 60 && dates.length === 1 ? `${date!.slice(5)} delayed` : `${date ? `${date} · ` : ""}15m delayed`, tone: "muted" }] },
43
+ ...(width >= 60 ? [{ id: "holdings-date", parts: [{ text: `holdings ${data.asOf}`, tone: "muted" as const }] }] : []),
44
+ ];
45
+ }, [data, width]);
46
+ usePaneStatusFooter({ registrationId: "members:status", loading: !!data && resource.loading,
47
+ stale: !!data && (data.stale || resource.data!.stale || !!resource.error), info });
48
+ usePaneNoticeFooter({ registrationId: "members:notices", focused, notices: [
49
+ ...(resource.error || resource.data?.refreshError ? [resource.error ?? resource.data!.refreshError!] : []),
50
+ ...(data?.quotesUnavailable ? ["Member returns and current prices are unavailable. Dated holdings remain available."] : []),
51
+ ...(data && data.aggregate.fresh1D < data.aggregate.total ? [`Fresh 1D: ${data.aggregate.fresh1D}/${data.aggregate.total} holdings, ${decimal(data.aggregate.coveredWeight * 100, 1)}% of whole-fund weight. Missing returns are excluded.`] : []),
52
+ ...(movers && data && data.aggregate.fundReturn === null ? ["Fund 1D is unavailable; the difference cannot be calculated."] : []),
53
+ ...(movers && data?.aggregate.withinTolerance === false ? [`The covered contribution sum differs from the fund by ${percent(data.aggregate.residual, " pp")}.`] : []),
54
+ ...(data?.excludedDerivatives ? [`${data.excludedDerivatives} derivative positions are excluded from these share holdings.`] : []),
55
+ ] });
56
+ const renderCell = useCallback((item: SectionedRow<FundMember>, column: DataTableColumn): DataTableCell => {
57
+ if (!isSectionedItemRow(item)) return { text: "" };
58
+ const row = item.item;
59
+ if (column.id === "symbol") return { text: row.symbol ?? "--", color: colors.textBright };
60
+ if (column.id === "name" || column.id === "sector") return { text: row[column.id] ?? "--", color: colors.text };
61
+ if (column.id === "weight") return { text: row.weight == null ? "--" : decimal(row.weight * 100), value: row.weight == null ? null : row.weight * 100 };
62
+ if (column.id === "shares") return { text: decimal(row.shares, 0), value: row.shares };
63
+ if (column.id === "price") return { text: decimal(row.price), value: row.price };
64
+ const value = row[column.id as "changePercent"];
65
+ return { text: percent(value, column.id === "contribution" ? "" : "%", column.id === "contribution" ? 3 : 2), value, color: value === null ? colors.textMuted : priceColor(value, colors) };
66
+ }, [colors]);
67
+ return <PaneStatusBody subject="fund holdings" loading={!data && resource.loading} error={!data ? resource.error : null}>
68
+ {data && <DataTableView<SectionedRow<FundMember>> items={rows} columns={columns} rootWidth={width} rootHeight={height} focused={focused && !search.active}
69
+ rootBefore={<Box flexDirection="column"><QueryBar width={width} search={{ value: query, onChange: setQuery, placeholder: "ticker, name or sector", focused, ...search.searchProps }} />
70
+ {movers && <StatGrid width={width} items={[
71
+ { label: "Covered contrib.", value: percent(data.aggregate.sum, " pp"), color: priceColor(data.aggregate.sum ?? 0, colors) },
72
+ { label: `${fund} 1D`, value: percent(data.aggregate.fundReturn), color: priceColor(data.aggregate.fundReturn ?? 0, colors) },
73
+ { label: "Difference", value: percent(data.aggregate.residual, " pp"), tone: data.aggregate.withinTolerance === false ? "warning" : "muted" },
74
+ ]} />}</Box>}
75
+ selection={{ kind: "id", selectedId: selected, getId: rowKey, onChange: (id) => setSelected(String(id)) }} getItemKey={rowKey}
76
+ isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
77
+ sortColumnId={movers ? null : sort.columnId} sortDirection={sort.direction}
78
+ onHeaderClick={movers ? undefined : (id) => setSort((previous) => nextHeaderSort(previous, id, { firstDirection: (key) => ["symbol", "name", "sector"].includes(key) ? "asc" : "desc", resetTo: DEFAULT_SORT }))}
79
+ onSortChange={movers ? undefined : (columnId, direction) => setSort({ columnId, direction })}
80
+ onRootKeyDown={(event, context) => { if (context.selectedIndex <= 0 && isPlainArrowUp(event)) { stopSearchFocusNavigation(event); search.focus(); return true; } return false; }}
81
+ onActivate={(row) => { if (isSectionedItemRow(row) && row.item.symbol) pinTicker(row.item.symbol, { floating: true, instrument: null }); }}
82
+ renderCell={renderCell} selectedTextOverridesCellColor resetScrollKey={`${tab}:${query}`} emptyStateTitle={query ? "No matching holdings." : movers ? "No covered movers." : "No holdings."} />}
83
+ </PaneStatusBody>;
84
+ }
85
+
86
+ function Changes({ fund, width, height, focused, active }: ViewProps) {
87
+ const colors = useThemeColors();
88
+ const resource = useAsyncResource(useCallback((force: boolean) => loadChanges(fund, force), [fund]), { initialData: useCallback(() => cachedChanges(fund), [fund]) });
89
+ const data = resource.data?.payload;
90
+ const [selected, setSelected] = usePluginPaneState<string | null>(`change:${fund}`, null);
91
+ const [opened, setOpened] = usePluginPaneState<string | null>(`opened:${fund}`, null);
92
+ const detail = data?.changes.find((row) => row.id === opened);
93
+ const selectedRow = detail ?? data?.changes.find((row) => row.id === selected) ?? data?.changes[0];
94
+ const scrollRef = useRef<ScrollBoxRenderable | null>(null);
95
+ useAutoRefresh(resource.updatedAt, () => { if (active) void resource.load(); });
96
+ usePaneRefreshKey(() => void resource.reload(), { focused, enabled: active });
97
+ useExternalLinkFooter({ registrationId: "members:source", focused, url: selectedRow?.url });
98
+ usePaneStatusFooter({ registrationId: "members:changes", loading: !!data && resource.loading, stale: !!data && (data.stale || resource.data!.stale || !!resource.error),
99
+ info: data?.asOf ? [{ id: "updated", parts: [{ text: `as of ${data.asOf.slice(0, 10)}`, tone: "muted" }] }] : [] });
100
+ usePaneNoticeFooter({ registrationId: "members:changes-notices", focused, notices: resource.error || resource.data?.refreshError ? [resource.error ?? resource.data!.refreshError!] : [] });
101
+ const renderCell = useCallback((row: FundChange, column: DataTableColumn): DataTableCell => {
102
+ if (column.id === "effectiveDate") return { text: row.effectiveDate ?? "--" };
103
+ if (column.id === "daysToGo") return { text: row.daysToGo === null ? "--" : `${row.daysToGo}${column.label === "In" ? "d" : ""}`, value: row.daysToGo, color: row.daysToGo === null ? colors.textMuted : colors.warning, keepColorWhenSelected: row.daysToGo !== null };
104
+ if (column.id === "added") return { text: row.added ?? "--", color: colors.positive };
105
+ if (column.id === "removed") return { text: row.removed ?? "--", color: colors.negative };
106
+ if (column.id === "estimate") { const value = row.estimates.length === 1 ? row.estimates[0]!.days : null; return { text: percent(value, ""), value }; }
107
+ return { text: changeReason(row), color: colors.text };
108
+ }, [colors]);
109
+ return <PaneStatusBody subject="index changes" loading={!data && resource.loading} error={!data ? resource.error : null}>
110
+ {data && !data.available ? <EmptyState title="Index changes are not covered for this fund." /> : data && <DataTableStackView<FundChange>
111
+ rootWidth={width} rootHeight={height} focused={focused} columns={changeColumns(width)} items={data.changes}
112
+ getItemKey={(row) => row.id} selection={{ kind: "id", selectedId: selected, getId: (row) => row.id, onChange: (id) => setSelected(String(id)) }}
113
+ onActivate={(row) => setOpened(row.id)} sortColumnId={null} sortDirection="desc" renderCell={renderCell} selectedTextOverridesCellColor
114
+ detailOpen={!!detail} onBack={() => setOpened(null)} detailTitle={detail ? changeLabel(detail) : undefined} detailScrollRef={scrollRef}
115
+ detailContent={detail && <DetailScrollBody ref={scrollRef} resetScrollKey={detail.id}>
116
+ <KeyValueRow label="Effective" value={detail.effectiveDate ?? "Not confirmed"} />
117
+ {detail.announcedAt && <KeyValueRow label="Announced" value={detail.announcedAt.slice(0, 10)} />}
118
+ {detail.headline && <Prose text={detail.headline} width={Math.max(8, width - 4)} />}
119
+ {detail.reason !== detail.headline && <Prose text={detail.reason} width={Math.max(8, width - 4)} />}
120
+ {detail.estimates.map((estimate) => <KeyValueRow key={estimate.symbol} label={`${estimate.symbol} est. ADV days`} labelWidth={24} value={percent(estimate.days, "")} />)}
121
+ {!!detail.estimates.length && <KeyValueRow label="Holdings as of" labelWidth={24} value={detail.estimates[0]!.asOf} />}
122
+ {!!detail.estimates.length && <Prose text={data.estimateLabel} width={Math.max(8, width - 4)} />}
123
+ {detail.kind === "history" && data.attribution && <Prose text={data.attribution.text} width={Math.max(8, width - 4)} />}
124
+ </DetailScrollBody>} emptyStateTitle="No stored index changes." />}
125
+ </PaneStatusBody>;
126
+ }
127
+
128
+ function FundView({ fund, ...props }: Pick<PaneProps, "width" | "height" | "focused"> & { fund: string }) {
129
+ const instance = usePaneInstance();
130
+ const initial = isTab(instance?.settings?.tab) ? instance.settings.tab : "members";
131
+ const [storedTab, setTab] = usePluginPaneState<MembersTab>("tab", initial);
132
+ const tab = isTab(storedTab) ? storedTab : "members";
133
+ const [visited, setVisited] = useState({ holdings: tab !== "changes", changes: tab === "changes" });
134
+ const selectTab = (value: string) => { if (isTab(value)) { setTab(value); setVisited((previous) => ({ holdings: previous.holdings || value !== "changes", changes: previous.changes || value === "changes" })); } };
135
+ const { strip, rows } = usePaneTabs({ tabs: TABS, activeValue: tab, onSelect: selectTab, focused: props.focused, compact: true, variant: "bare" });
136
+ const height = Math.max(1, props.height - rows);
137
+ return <Box width={props.width} height={props.height} flexDirection="column">
138
+ {strip}
139
+ {visited.holdings && <Box visible={tab !== "changes"} flexGrow={1} flexBasis={0} overflow="hidden"><PaneFooterScope active={tab !== "changes"}>
140
+ <Holdings fund={fund} {...props} height={height} tab={tab} active={tab !== "changes"} focused={props.focused && tab !== "changes"} />
141
+ </PaneFooterScope></Box>}
142
+ {visited.changes && <Box visible={tab === "changes"} flexGrow={1} flexBasis={0} overflow="hidden"><PaneFooterScope active={tab === "changes"}>
143
+ <Changes fund={fund} {...props} height={height} active={tab === "changes"} focused={props.focused && tab === "changes"} />
144
+ </PaneFooterScope></Box>}
145
+ </Box>;
146
+ }
147
+ function FundPicker({ width, height, focused, choose }: Pick<PaneProps, "width" | "height" | "focused"> & { choose: (fund: string) => void }) {
148
+ const resource = useAsyncResource(loadFunds, { initialData: cachedFunds });
149
+ const [selected, setSelected] = useState<string | null>(null);
150
+ usePaneRefreshKey(() => void resource.reload(), { focused });
151
+ usePaneStatusFooter({ registrationId: "members:picker", loading: resource.loading && !!resource.data, stale: resource.data?.stale });
152
+ return <PaneStatusBody subject="covered funds" loading={!resource.data && resource.loading} error={!resource.data ? resource.error : null}>
153
+ {resource.data && <DataTableView items={resource.data.payload.funds} emptyStateTitle="No covered funds." rootWidth={width} rootHeight={height} focused={focused}
154
+ columns={[{ id: "ticker", label: "Fund", align: "left", width: 8 }, { id: "name", label: "Index", align: "left", width: 24, flexGrow: 1 }, { id: "asOf", label: "Holdings as of", align: "left", width: 16 }]}
155
+ getItemKey={(row) => row.ticker} selection={{ kind: "id", selectedId: selected, getId: (row) => row.ticker, onChange: (id) => setSelected(String(id)) }}
156
+ onActivate={(row) => choose(row.ticker)} sortColumnId={null} sortDirection="asc" renderCell={(row, col) => ({ text: String(row[col.id as "ticker"] ?? "--") })} />}
157
+ </PaneStatusBody>;
158
+ }
159
+ type FundPaneProps = Pick<PaneProps, "width" | "height" | "focused"> & { fund: string; requested: string };
160
+ /** The covered list is the server's, so a fund it adds opens without an app update. A list that cannot be read leaves the fund's own load to report the failure. */
161
+ function CoveredFund({ fund, requested, ...props }: FundPaneProps) {
162
+ const resource = useAsyncResource(loadFunds, { initialData: cachedFunds });
163
+ const funds = resource.data?.payload.funds;
164
+ if (funds?.length && !coveredFund(funds, fund)) return <PaneStatusBody empty emptyTitle={notCoveredMessage(requested, funds)} />;
165
+ return <FundView key={fund} fund={fund} {...props} />;
166
+ }
167
+ export function MembersPane(props: PaneProps) {
168
+ const { symbol } = usePaneTickerIdentity();
169
+ const [chosen, choose] = usePluginPaneState("fund", "");
170
+ const requested = symbol ?? chosen;
171
+ const fund = canonicalFund(requested);
172
+ usePaneTitle(membersTitle(requested));
173
+ if (!fund) return <FundPicker {...props} choose={choose} />;
174
+ return <CoveredFund fund={fund} requested={requested} {...props} />;
175
+ }
@@ -140,6 +140,7 @@ const text = (value: unknown) => (typeof value === "string" ? value : "");
140
140
 
141
141
  export const distressHeadless: HeadlessPaneDefinition<"rows"> = {
142
142
  shape: "rows",
143
+ freshness: { source: "Public filings and insolvency notices", status: "not-a-feed", basis: "filed records" },
143
144
  description: "Dated public records about companies in difficulty: US 8-K bankruptcy, obligation and listing filings, SEC going-concern disclosures, Taiwan exchange listing designations, and French and UK company insolvency notices. No scores or predictions.",
144
145
  argument: { kind: "none" },
145
146
  discovery: {
@@ -19,6 +19,7 @@ const COLUMNS: HeadlessPaneColumn[] = [
19
19
 
20
20
  export const mnaHeadless: HeadlessPaneDefinition<"rows"> = {
21
21
  shape: "rows",
22
+ freshness: { status: "not-a-feed", basis: "reported deals" },
22
23
  argument: {
23
24
  kind: "ticker",
24
25
  optional: true,
@@ -33,6 +33,7 @@ export const mnaModule: PluginModule = {
33
33
  icon: "M",
34
34
  component: MnaPane,
35
35
  defaultPosition: "right",
36
+ tickerFollower: true,
36
37
  defaultMode: "floating",
37
38
  defaultFloatingSize: { width: 120, height: 30 },
38
39
  tableExport: true,
@@ -51,7 +52,7 @@ export const mnaModule: PluginModule = {
51
52
  createInstance: (_context, options) => {
52
53
  const symbol = (options?.symbol ?? options?.arg)?.trim().toUpperCase();
53
54
  return symbol
54
- ? { instanceId: `${MNA_PANE_ID}:${symbol}`, title: `M&A ${symbol}`, placement: "floating", settings: { ticker: symbol } }
55
+ ? { instanceId: `${MNA_PANE_ID}:${symbol}`, title: `M&A ${symbol}`, binding: { kind: "fixed" as const, symbol }, placement: "floating", settings: { ticker: symbol } }
55
56
  : { placement: "floating" };
56
57
  },
57
58
  },
@@ -26,6 +26,7 @@ import { usePlanAccess } from "../../../api-client/plan-access";
26
26
  import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginPaneState, useShortcut } from "../../../public/react";
27
27
  import { usePaneInstance } from "../../../state/app/context";
28
28
  import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
29
+ import { listingIdentity } from "../shared/ticker-request";
29
30
  import { blendHex } from "../../../theme/colors";
30
31
  import { useThemeColors } from "../../../theme/theme-context";
31
32
  import type { PaneProps, TickerResearchTabProps } from "../../../types/plugin";
@@ -130,10 +131,12 @@ const MNA_TABS: { label: string; value: MnaTab }[] = [
130
131
  { label: "Distress", value: "distress" },
131
132
  ];
132
133
 
133
- /** `MA`, or `MA ACVA` with the ticker kept in pane settings. */
134
+ /** `MA`, or `MA ACVA`. A link supplies the ticker; an older pane still has it in settings. */
134
135
  export function MnaPane(props: PaneProps) {
136
+ const { symbol: bound } = usePaneTickerIdentity();
135
137
  const [ticker] = usePaneSettingValue("ticker", "");
136
- const symbol = typeof ticker === "string" ? ticker.trim().toUpperCase() : "";
138
+ const fromSettings = typeof ticker === "string" ? ticker.trim().toUpperCase() : "";
139
+ const symbol = listingIdentity(bound)?.symbol || fromSettings;
137
140
  // One company's deals have no market-wide distress tab beside them.
138
141
  if (symbol) return <MnaDealsView key={symbol} focused={props.focused} width={props.width} height={props.height} symbol={symbol} />;
139
142
  return <MnaBoard focused={props.focused} width={props.width} height={props.height} />;
@@ -6,6 +6,7 @@ export const moneyMarketsHeadless: HeadlessPaneDefinition<"bundle"> = {
6
6
  discovery: { aliases: ["BTMM"], dataRequirements: ["Gloom Cloud money-markets endpoint"],
7
7
  limitations: ["Published daily and weekly observations", "Treasury bill discount yields", "Net liquidity is a same-date proxy"] },
8
8
  shape: "bundle", argument: { kind: "none" },
9
+ freshness: { source: "FRED", status: "not-a-feed", basis: "published daily and weekly data" },
9
10
  options: [{ key: "tab", type: "enum", description: "Money-market view.", values: [{ value: "rates" }, { value: "bills" }, { value: "liquidity" }], defaultValue: "rates" }],
10
11
  describe: "US money markets",
11
12
  async load(args, ctx) {
@@ -81,6 +81,8 @@ function projectNewsHeadless(
81
81
  return {
82
82
  asOf: now.toISOString(),
83
83
  items,
84
+ // Published stories, dated by the newest; the list is a history, so its last story is not stale data.
85
+ freshness: { status: "not-a-feed", basis: "published stories", observedKey: "publishedAt", oldest: null },
84
86
  metadata: {
85
87
  nextCursor: response.nextCursor,
86
88
  sentiment,
@@ -25,6 +25,7 @@ import { NEWS_INDUSTRY_CODES, NEWS_QUERY_PRESETS, parseNewsIndustryCode } from "
25
25
  import { createRssNewsCapability } from "./rss/source";
26
26
  import { rssCliCommand } from "./rss/cli";
27
27
  import { newsFeedHeadless } from "../headless";
28
+ import type { PaneDef } from "../../../../types/plugin";
28
29
 
29
30
  const TopPane = createNewsPresetPane({
30
31
  paneKey: "top:curated",
@@ -61,8 +62,11 @@ export const BreakingPane = createNewsPresetPane({
61
62
 
62
63
  let disposeBreakingNewsNotifications: (() => void) | null = null;
63
64
 
65
+ // A rendered news pane is a list of published stories, dated by its newest.
66
+ const NEWS_REPORT_FRESHNESS: PaneDef["reportFreshness"] = { status: "not-a-feed", basis: "published stories" };
67
+
64
68
  const newsWirePanes: PluginModule["panes"] = [
65
- { id: "news-top", name: "Top News", icon: "T", component: TopPane, defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 90, height: 30 } },
69
+ { id: "news-top", reportFreshness: NEWS_REPORT_FRESHNESS, name: "Top News", icon: "T", component: TopPane, defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 90, height: 30 } },
66
70
  {
67
71
  id: "news-feed",
68
72
  name: "News Feed",
@@ -75,6 +79,7 @@ const newsWirePanes: PluginModule["panes"] = [
75
79
  },
76
80
  {
77
81
  id: "news-industry",
82
+ reportFreshness: NEWS_REPORT_FRESHNESS,
78
83
  name: TOPIC_NEWS_TITLE,
79
84
  icon: "S",
80
85
  component: IndustryPane,
@@ -85,6 +90,7 @@ const newsWirePanes: PluginModule["panes"] = [
85
90
  },
86
91
  {
87
92
  id: NEWS_STORY_PANE_ID,
93
+ reportFreshness: NEWS_REPORT_FRESHNESS,
88
94
  name: "Story",
89
95
  icon: "A",
90
96
  component: NewsStoryPane,
@@ -94,6 +100,7 @@ const newsWirePanes: PluginModule["panes"] = [
94
100
  },
95
101
  {
96
102
  id: "news-breaking",
103
+ reportFreshness: NEWS_REPORT_FRESHNESS,
97
104
  name: "Breaking News",
98
105
  icon: "!",
99
106
  component: BreakingPane,
@@ -2,7 +2,8 @@ import { apiClient } from "../../../api-client";
2
2
  import { withCliServices, withConfigData } from "../../../cli/scoped-context";
3
3
  import { CLI_COMMAND_GROUPS } from "../../../cli/help";
4
4
  import { dryRunNote } from "../../../cli/helpers";
5
- import { requireArg } from "../../../cli/commands/command-utils";
5
+ import { requireArg, takeOption } from "../../../cli/commands/command-utils";
6
+ import { EXCHANGE_OPTION, requireListingArg } from "../../../cli/listing-arg";
6
7
  import { createPluginPersistence } from "../../plugin-persistence";
7
8
  import type { CliCommandDef } from "../../../types/plugin";
8
9
  import { cliStyles } from "../../../utils/cli-output";
@@ -17,9 +18,16 @@ export const notesCliCommand: CliCommandDef = {
17
18
  help: {
18
19
  group: CLI_COMMAND_GROUPS.portfolios,
19
20
  usage: ["notes show <symbol>", "notes set <symbol> <text...>", "notes delete <symbol>", "notes quick list", "notes export [dir]"],
20
- examples: ["notes show AAPL", "notes set AAPL \"Services margin above 70%\"", "notes export ~/notes"],
21
+ options: [EXCHANGE_OPTION],
22
+ examples: ["notes show AAPL", "notes set AAPL \"Services margin above 70%\"", "notes show SAN:EPA", "notes export ~/notes"],
21
23
  },
22
- execute: async (args, ctx) => {
24
+ execute: async (rawArgs, ctx) => {
25
+ const args = [...rawArgs];
26
+ const exchange = takeOption(args, "--exchange");
27
+ // A note belongs to one listing: SAN:EPA, or SAN with --exchange EPA.
28
+ const noteSymbol = async (usage: string) => (
29
+ await requireListingArg(requireArg(args[1], usage, ctx), exchange, ctx)
30
+ ).key;
23
31
  await withConfigData(ctx, async (config) => {
24
32
  const notes = new NotesFiles(config.dataDir);
25
33
  const action = args[0] ?? "list";
@@ -62,14 +70,14 @@ export const notesCliCommand: CliCommandDef = {
62
70
  return;
63
71
  }
64
72
  if (action === "show") {
65
- const symbol = requireArg(args[1]?.toUpperCase(), "Usage: gloomberb notes show <symbol>", ctx);
73
+ const symbol = await noteSymbol("Usage: gloomberb notes show <symbol>");
66
74
  ctx.printResult({ data: { symbol, text: await notes.load(symbol) } }, {
67
75
  text: (data) => data.text?.trim() ? data.text.trimEnd() : cliStyles.muted(`No note for ${symbol}.`),
68
76
  });
69
77
  return;
70
78
  }
71
79
  if (action === "set") {
72
- const symbol = requireArg(args[1]?.toUpperCase(), "Usage: gloomberb notes set <symbol> <text...>", ctx);
80
+ const symbol = await noteSymbol("Usage: gloomberb notes set <symbol> <text...>");
73
81
  const text = args.slice(2).join(" ");
74
82
  if (!ctx.cliOptions.dryRun) await notes.save(symbol, text);
75
83
  ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, symbol, bytes: text.length } }, {
@@ -78,7 +86,7 @@ export const notesCliCommand: CliCommandDef = {
78
86
  return;
79
87
  }
80
88
  if (action === "delete" || action === "rm") {
81
- const symbol = requireArg(args[1]?.toUpperCase(), "Usage: gloomberb notes delete <symbol>", ctx);
89
+ const symbol = await noteSymbol("Usage: gloomberb notes delete <symbol>");
82
90
  // Only picks the message; an unreadable note is still deleted.
83
91
  const existed = await notes.load(symbol).then((text) => !!text?.trim(), () => true);
84
92
  if (!ctx.cliOptions.dryRun) await notes.delete(symbol);
@@ -79,6 +79,7 @@ export const notesPlugin: GloomPlugin = {
79
79
 
80
80
  ctx.registerPane({
81
81
  id: "quick-notes",
82
+ reportFreshness: { source: "Your notes", status: "not-a-feed", basis: "your notes" },
82
83
  name: "Notes",
83
84
  icon: "N",
84
85
  component: QuickNotesPane,
@@ -31,6 +31,7 @@ export const openFdaPlugin: GloomPlugin = {
31
31
  panes: [
32
32
  {
33
33
  id: OPENFDA_PANE_ID,
34
+ reportFreshness: { source: "US FDA", status: "not-a-feed", basis: "published reports" },
34
35
  name: "FDA Reports",
35
36
  icon: "F",
36
37
  component: OpenFdaPane,
@@ -3,6 +3,7 @@ import type { PluginModule } from "../plugin-module";
3
3
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
4
4
  import type { OptionsViewProps } from "./types";
5
5
  import { OptionsView } from "./view";
6
+ import { isKnownMutualFund } from "../../../tickers/instrument-kind";
6
7
 
7
8
  /** The registered chain shows IV rank; isolated view renders stay offline. */
8
9
  function OptionsPane(props: OptionsViewProps) {
@@ -95,7 +96,7 @@ export const optionsModule: PluginModule = {
95
96
  order: 35,
96
97
  component: OptionsResearchTab,
97
98
  instruments: ["equity", "fund", "index", "future", "option"],
98
- isVisible: ({ hasOptionsChain }) => hasOptionsChain,
99
+ isVisible: ({ ticker, financials, hasOptionsChain }) => hasOptionsChain && !isKnownMutualFund(ticker, financials),
99
100
  });
100
101
  },
101
102
  };
@@ -39,6 +39,7 @@ function report(draft: OptionCalcDraft, valuation: OptionValuation | null,
39
39
  ],
40
40
  complete: valuation !== null && errors.length === 0,
41
41
  errors, unavailableSymbols: surface?.error && draft.symbol ? [draft.symbol] : [],
42
+ ...(surface ? { freshness: { source: "Your inputs and Gloom Cloud volatility" } } : {}),
42
43
  metadata: { model, inputs: draft, valuation, impliedVolatility: implied, surface, effectiveTreeSteps,
43
44
  warnings: surface?.warnings ?? [], method: model === "american" ? "CRR American tree with discrete cash dividends" : "European Black-Scholes",
44
45
  greekUnits: { delta: "underlying units per option unit", gamma: "delta per underlying price unit", theta: "currency per calendar day",
@@ -49,6 +50,7 @@ function report(draft: OptionCalcDraft, valuation: OptionValuation | null,
49
50
 
50
51
  export const optionsCalculatorHeadless: HeadlessPaneDefinition<"bundle"> = {
51
52
  shape: "bundle", argument: { kind: "none" }, describe: "OVME option valuation",
53
+ freshness: { source: "Your inputs", status: "not-a-feed", basis: "calculator" },
52
54
  discovery: { limitations: ["Discrete dividends use days from valuation and cash per underlying unit; model values exclude fees and assignment costs."] },
53
55
  options: [
54
56
  { key: "model", type: "enum", values: [{ value: "european" }, { value: "american" }], defaultValue: "european", description: "European closed form or American CRR tree" },
@@ -23,6 +23,7 @@ const gamma = (value: unknown) => formatGamma(typeof value === "number" ? value
23
23
  /** OPX opens on Strikes, GEX on its own tab; the report carries every section either way. */
24
24
  export const optionsPositioningHeadless = (initialTab: PositioningTab): HeadlessPaneDefinition<"bundle"> => ({
25
25
  shape: "bundle",
26
+ freshness: { status: "not-a-feed", basis: "daily open interest", cadence: "daily" },
26
27
  argument: { kind: "ticker", optional: true, description: "US option underlying, or SPX; SPY when omitted" },
27
28
  describe: (args) => `${initialTab === "gex" ? "GEX" : "OPX"} ${args.symbols[0] ?? "SPY"}`,
28
29
  discovery: {
@@ -59,6 +60,8 @@ export const optionsPositioningHeadless = (initialTab: PositioningTab): Headless
59
60
  ];
60
61
  const changes = openInterest.previousOiDate != null;
61
62
  return {
63
+ // Open interest is published once a day for the prior session; the spot beside it is context.
64
+ freshness: { asOf: openInterest.oiDate },
62
65
  sections: [
63
66
  { title: `Expiries · OI as of ${openInterest.oiDate ?? "--"}`, columns: [
64
67
  { key: "date", header: "Expiry" }, { key: "days", header: "Days" },
@@ -57,7 +57,12 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
57
57
  // A seeded strategy starts at the spot its quote mids imply; the last print is reported beside it.
58
58
  const lastPrint = midVolatility && !supplied(settings.spot) && market.spot != null && money(market.spot) !== money(position!.spot)
59
59
  ? market.spot : null;
60
+ const chain = market.chain;
60
61
  return {
62
+ freshness: suppliedInputs
63
+ ? { source: "Your inputs", status: "not-a-feed", basis: "scenario" }
64
+ : { asOf: market.asOf, ...(chain?.dataSource === "live" || chain?.dataSource === "delayed" ? { status: chain.dataSource } : {}),
65
+ ...(chain?.delayMinutes ? { delayMinutes: chain.delayMinutes } : {}) },
61
66
  sections: scenario ? [
62
67
  { title: "Position", columns: [
63
68
  { key: "side", header: "Side" }, { key: "strike", header: "Strike" }, { key: "expiry", header: "Expiry" },
@@ -1,10 +1,17 @@
1
+ import { apiClient } from "../../../api-client";
1
2
  import { getSharedMarketDataCoordinator, MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
3
+ import { fxLegForCurrency } from "../../../market-data/coordinator/fx-legs";
4
+ import { dailyFxCloses, FxHistoryError, type FxCloses } from "../../../market-data/fx-closes";
2
5
  import type { InstrumentRef } from "../../../market-data/request-types";
3
6
  import type { ManualChartResolution } from "../../../time-series/resolution";
4
7
  import type { DataProvider } from "../../../types/data-provider";
5
8
  import type { PricePoint, TickerFinancials } from "../../../types/financials";
6
- import { abortable, abortError } from "../../../utils/async-deadline";
9
+ import { abortable, abortError, settleWithin } from "../../../utils/async-deadline";
7
10
  import { errorMessage } from "../../../utils/errors";
11
+ import { isUsListingExchange } from "../../../utils/exchanges";
12
+ import { fetchEarningsHistory } from "../earnings/client";
13
+ import { epsFxCurrencies } from "./model";
14
+ import { reportDatesFrom, type ReportDate } from "./report-dates";
8
15
 
9
16
  export interface PeBandInputs {
10
17
  financials: TickerFinancials | null;
@@ -14,25 +21,81 @@ export interface PeBandInputs {
14
21
  error: string | null;
15
22
  /** Price history failed: the pane can still list EPS, but draws nothing. */
16
23
  historyError: string | null;
24
+ /** Report dates for US listings; empty for the rest and when the earnings history is unavailable. */
25
+ reports: ReportDate[];
26
+ /** Daily FX closes for each currency a dollar-priced listing reports its EPS in. */
27
+ fx: Map<string, FxCloses>;
28
+ /** Why a currency's FX closes are missing; its EPS then stays unconverted. */
29
+ fxError: string | null;
17
30
  fetchedAt: number;
18
31
  }
19
32
 
20
33
  const CANCELLED = "P/E band load was cancelled";
21
34
  /** Weekly bars: daily history is only served to 5Y, and the band covers every year of statements on record. */
22
35
  const PE_BAND_HISTORY_RESOLUTION: ManualChartResolution = "1wk";
36
+ /** The most reports the server returns for one company: about ten years of quarters. */
37
+ const REPORT_LIMIT = 40;
38
+ /** Report dates only refine the figures' dates, so the pane does not wait longer than this for them. */
39
+ const REPORT_DEADLINE_MS = 8_000;
40
+
41
+ /** Daily FX closes back this far before the oldest statement, so its weeks find a close. */
42
+ const FX_LEAD_DAYS = 14;
43
+ /** About thirty years of daily closes; a longer reply is not a daily FX history. */
44
+ const FX_MAX_ROWS = 8_000;
45
+
46
+ type ReportClient = Pick<typeof apiClient, "getCloudEarningsHistory" | "getCloudHistory">;
47
+
48
+ /**
49
+ * Completed daily closes of each currency's dollar pair, from just before the
50
+ * oldest statement in that currency. Never rejects but on cancel: a missing
51
+ * or stale history is said in `error`, and that currency's EPS stays unconverted.
52
+ */
53
+ async function loadFxCloses(financials: TickerFinancials, client: ReportClient, signal: AbortSignal | undefined, now: Date):
54
+ Promise<{ fx: Map<string, FxCloses>; error: string | null }> {
55
+ const fx = new Map<string, FxCloses>();
56
+ const errors: string[] = [];
57
+ const rows = [...financials.annualStatements, ...financials.quarterlyStatements];
58
+ await Promise.all(epsFxCurrencies(financials).map(async (currency) => {
59
+ const leg = fxLegForCurrency(currency)!;
60
+ const oldest = rows.filter((row) => row.currency?.trim().toUpperCase() === currency && Number.isFinite(Date.parse(row.date)))
61
+ .reduce((min, row) => Math.min(min, Date.parse(row.date)), now.getTime());
62
+ try {
63
+ const response = await client.getCloudHistory(leg.instrument.symbol, leg.instrument.exchange ?? "", {
64
+ interval: "1day", startDate: new Date(oldest - FX_LEAD_DAYS * 86_400_000).toISOString().slice(0, 10),
65
+ endDate: now.toISOString().slice(0, 10), outputsize: FX_MAX_ROWS,
66
+ }, signal ? { signal } : undefined);
67
+ fx.set(currency, dailyFxCloses(response, leg, now, FX_MAX_ROWS));
68
+ } catch (error) {
69
+ if (signal?.aborted) throw abortError(CANCELLED);
70
+ errors.push(`daily ${currency} FX closes are ${error instanceof FxHistoryError && error.problem === "stale" ? "stale" : "unavailable"}`);
71
+ }
72
+ }));
73
+ return { fx, error: errors.length ? errors.join("; ") : null };
74
+ }
75
+
76
+ /** Never rejects: a listing without reports, an outage or a refused session leaves the figures as they are. */
77
+ function loadReportDates(symbol: string, client: ReportClient): Promise<ReportDate[]> {
78
+ return settleWithin(fetchEarningsHistory(symbol, client, REPORT_LIMIT), REPORT_DEADLINE_MS)
79
+ .then((payload) => (payload ? reportDatesFrom(payload) : []));
80
+ }
23
81
 
24
82
  /**
25
83
  * Statements with the extended SEC history where the listing has one (the
26
- * snapshot alone carries about six quarters), and every week of closes.
84
+ * snapshot alone carries about six quarters), and every week of closes. US
85
+ * listings also get the company's report dates, which date the figures that
86
+ * have no publication date of their own.
27
87
  */
28
88
  export async function loadPeBandInputs(
29
89
  request: { instrument: InstrumentRef; signal?: AbortSignal; forceRefresh?: boolean },
30
90
  marketData: DataProvider,
31
91
  coordinator: MarketDataCoordinator = getSharedMarketDataCoordinator() ?? new MarketDataCoordinator(marketData),
92
+ cloud: ReportClient = apiClient,
32
93
  ): Promise<PeBandInputs> {
33
94
  if (request.signal?.aborted) throw abortError(CANCELLED);
34
95
  const now = Date.now();
35
96
  const { instrument } = request;
97
+ // Asked alongside the statements when the instrument names a US exchange; otherwise once the quote has said where it lists.
98
+ const early = isUsListingExchange(instrument.exchange) ? loadReportDates(instrument.symbol, cloud) : null;
36
99
  const [financials, chart] = await abortable(Promise.allSettled([
37
100
  marketData.getTickerFinancials(instrument.symbol, instrument.exchange ?? "", {
38
101
  brokerId: instrument.instrument?.brokerId ?? instrument.brokerId,
@@ -49,9 +112,18 @@ export async function loadPeBandInputs(
49
112
  const value = financials.status === "fulfilled" ? financials.value : null;
50
113
  const entry = chart.status === "fulfilled" ? chart.value : null;
51
114
  const history = entry ? resolveEntryValue(entry) ?? [] : [];
115
+ const quote = value?.quote;
116
+ const [reports, fx] = await abortable(Promise.all([
117
+ early ?? (isUsListingExchange(quote?.listingExchangeName ?? quote?.exchangeName ?? quote?.fullExchangeName)
118
+ ? loadReportDates(instrument.symbol, cloud) : Promise.resolve([])),
119
+ value ? loadFxCloses(value, cloud, request.signal, new Date(now)) : Promise.resolve({ fx: new Map<string, FxCloses>(), error: null }),
120
+ ]), request.signal, CANCELLED);
52
121
  return {
53
122
  financials: value,
54
123
  history,
124
+ reports,
125
+ fx: fx.fx,
126
+ fxError: fx.error,
55
127
  stale: !!value?.fundamentals?.stale || !!entry?.error || (entry?.staleAt != null && entry.staleAt <= now),
56
128
  error: financials.status === "rejected" ? errorMessage(financials.reason) : null,
57
129
  historyError: chart.status === "rejected" ? errorMessage(chart.reason)