gloomberb 0.16.1 → 0.16.3

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 (643) 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 +26 -8
  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 +2 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/paths.ts +30 -1
  10. package/src/api-client/perps.ts +37 -0
  11. package/src/api-client/request.ts +5 -2
  12. package/src/api-client/types.ts +2 -0
  13. package/src/brokers/account-cache.ts +69 -0
  14. package/src/cli/command-options.ts +74 -0
  15. package/src/cli/commands/command-utils.ts +37 -0
  16. package/src/cli/commands/crypto-hints.ts +75 -0
  17. package/src/cli/commands/market.ts +560 -136
  18. package/src/cli/commands/overview.ts +136 -48
  19. package/src/cli/commands/ticker.ts +220 -67
  20. package/src/cli/desktop-pane-shot.ts +37 -4
  21. package/src/cli/freshness.ts +61 -0
  22. package/src/cli/help.ts +27 -6
  23. package/src/cli/helpers.ts +4 -8
  24. package/src/cli/history-rows.ts +187 -15
  25. package/src/cli/index.ts +23 -8
  26. package/src/cli/listing-arg.ts +321 -0
  27. package/src/cli/options.ts +5 -1
  28. package/src/cli/pane-functions/access-gate.ts +42 -0
  29. package/src/cli/pane-functions/capabilities.ts +5 -0
  30. package/src/cli/pane-functions/catalog.ts +28 -1
  31. package/src/cli/pane-functions/data.ts +3 -1
  32. package/src/cli/pane-functions/dom.ts +87 -16
  33. package/src/cli/pane-functions/freshness.ts +400 -0
  34. package/src/cli/pane-functions/function-help.ts +31 -22
  35. package/src/cli/pane-functions/headless.ts +201 -41
  36. package/src/cli/pane-functions/index.ts +24 -6
  37. package/src/cli/pane-functions/options.ts +3 -0
  38. package/src/cli/pane-functions/report.ts +12 -1
  39. package/src/cli/pane-functions/resolver.ts +63 -9
  40. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  41. package/src/cli/pane-functions/screenshot.ts +11 -3
  42. package/src/cli/registry.ts +6 -3
  43. package/src/cli/report-tables.ts +460 -0
  44. package/src/cli/result.ts +53 -29
  45. package/src/cli/ticker-resolution.ts +7 -5
  46. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  47. package/src/components/chart/composite/drawing-store.ts +59 -0
  48. package/src/components/chart/composite/legend.tsx +319 -0
  49. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  50. package/src/components/chart/composite/panel-gestures.ts +59 -0
  51. package/src/components/chart/composite/panel-labels.ts +107 -0
  52. package/src/components/chart/composite/panel-surface.tsx +876 -0
  53. package/src/components/chart/composite/tool-catalog.ts +98 -0
  54. package/src/components/chart/composite/toolbar.tsx +186 -0
  55. package/src/components/chart/composite/tools.ts +8 -0
  56. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  57. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  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/ticker-search/actions.ts +5 -0
  62. package/src/components/command-bar/workflow/ops.ts +1 -1
  63. package/src/components/command-bar/workflow/tickers.ts +6 -0
  64. package/src/components/data-table/export.ts +6 -51
  65. package/src/components/data-table/view.tsx +32 -11
  66. package/src/components/feedback-dialog.tsx +2 -1
  67. package/src/components/form-modal/field-row.tsx +2 -2
  68. package/src/components/layout/detached-pane-shell.tsx +6 -3
  69. package/src/components/layout/pane/content.tsx +23 -10
  70. package/src/components/layout/pane/error-boundary.tsx +103 -0
  71. package/src/components/layout/pane/footer/registration.tsx +68 -23
  72. package/src/components/layout/pane/header.tsx +11 -11
  73. package/src/components/layout/shell/active-drag.ts +67 -35
  74. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  75. package/src/components/layout/shell/drag/index.ts +12 -7
  76. package/src/components/layout/shell/drag/live.ts +122 -0
  77. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  78. package/src/components/layout/shell/drag/runtime.ts +62 -50
  79. package/src/components/layout/shell/drag/slide.ts +10 -0
  80. package/src/components/layout/shell/index.tsx +37 -51
  81. package/src/components/layout/shell/menu.ts +2 -1
  82. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  83. package/src/components/layout/shell/native/surfaces.ts +30 -14
  84. package/src/components/layout/shell/pane/actions.ts +2 -1
  85. package/src/components/layout/shell/pane/layers.tsx +419 -168
  86. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  87. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  88. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  89. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  90. package/src/components/metric-treemap/layout.ts +2 -0
  91. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  93. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  94. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  95. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  96. package/src/components/ticker/list-table-view.tsx +13 -1
  97. package/src/components/ui/fields.tsx +8 -0
  98. package/src/components/ui/range-track.tsx +10 -3
  99. package/src/components/ui/status.tsx +8 -2
  100. package/src/components/ui/tabs.tsx +7 -4
  101. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  102. package/src/data/config/store/normalize.ts +17 -1
  103. package/src/data/json-persistence.ts +17 -3
  104. package/src/data/json-storage.ts +2 -0
  105. package/src/data/memory-resource-store.ts +46 -0
  106. package/src/i18n/es.ts +19 -1
  107. package/src/i18n/ja.ts +19 -1
  108. package/src/i18n/ko.ts +19 -1
  109. package/src/i18n/zh-cn.ts +19 -1
  110. package/src/i18n/zh-tw.ts +19 -1
  111. package/src/market-data/coordinator/chart.ts +1 -1
  112. package/src/market-data/coordinator/quotes.ts +23 -4
  113. package/src/market-data/fx-closes.ts +72 -0
  114. package/src/market-data/market/freshness.ts +123 -6
  115. package/src/market-data/market/session-close-note.ts +57 -0
  116. package/src/market-data/market/status.ts +151 -0
  117. package/src/market-data/published-apac-sessions.ts +66 -0
  118. package/src/market-data/quotes/contributions.ts +2 -0
  119. package/src/market-data/quotes/freshness.ts +5 -0
  120. package/src/market-data/quotes/resolution.ts +2 -0
  121. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  122. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  123. package/src/plugins/builtin/alerts/cli.ts +10 -5
  124. package/src/plugins/builtin/alerts/index.tsx +1 -0
  125. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  126. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  127. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  128. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  129. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  130. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  131. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  132. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  133. package/src/plugins/builtin/attention/headless.ts +6 -4
  134. package/src/plugins/builtin/attention/index.tsx +1 -1
  135. package/src/plugins/builtin/attention/pane.tsx +2 -2
  136. package/src/plugins/builtin/awards/headless.ts +1 -0
  137. package/src/plugins/builtin/awards/pane.tsx +1 -1
  138. package/src/plugins/builtin/backtest/headless.ts +1 -0
  139. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  140. package/src/plugins/builtin/buildout/index.tsx +1 -0
  141. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  142. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  143. package/src/plugins/builtin/cds/headless.ts +2 -0
  144. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  145. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  146. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  147. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  148. package/src/plugins/builtin/chat/channels.ts +19 -3
  149. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  150. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  151. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  152. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  153. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  154. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  155. package/src/plugins/builtin/chat/index.tsx +1 -0
  156. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  157. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  158. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  159. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  160. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  161. package/src/plugins/builtin/chat/message/types.ts +14 -2
  162. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  163. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  164. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  165. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  166. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  167. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  168. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  169. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  170. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  171. package/src/plugins/builtin/company-attention/pane.tsx +2 -2
  172. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  173. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  174. package/src/plugins/builtin/correlation/compute.ts +59 -16
  175. package/src/plugins/builtin/correlation/geo.ts +34 -0
  176. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  177. package/src/plugins/builtin/correlation/headless.ts +42 -8
  178. package/src/plugins/builtin/correlation/history.ts +2 -0
  179. package/src/plugins/builtin/correlation/index.tsx +68 -14
  180. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  181. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  182. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  183. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  184. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  185. package/src/plugins/builtin/correlation/settings.ts +1 -1
  186. package/src/plugins/builtin/cot/headless.ts +6 -3
  187. package/src/plugins/builtin/cot/index.tsx +3 -3
  188. package/src/plugins/builtin/cot/model.ts +36 -3
  189. package/src/plugins/builtin/cpi/headless.ts +4 -0
  190. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  191. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  192. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  193. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  194. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  195. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  196. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  197. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  198. package/src/plugins/builtin/doe/headless.ts +7 -0
  199. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  200. package/src/plugins/builtin/earnings/headless.ts +5 -0
  201. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  202. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  203. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  204. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  205. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  206. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  207. package/src/plugins/builtin/econ/index.tsx +1 -0
  208. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  209. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  210. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  211. package/src/plugins/builtin/executives/index.tsx +1 -0
  212. package/src/plugins/builtin/exposure/headless.ts +3 -1
  213. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  214. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  215. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  216. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  217. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  218. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  219. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  220. package/src/plugins/builtin/gpu/headless.ts +7 -3
  221. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  222. package/src/plugins/builtin/gpu/model.ts +27 -1
  223. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  224. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  225. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  226. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  227. package/src/plugins/builtin/holders/client.ts +3 -6
  228. package/src/plugins/builtin/holders/format.ts +31 -1
  229. package/src/plugins/builtin/holders/headless.ts +161 -6
  230. package/src/plugins/builtin/holders/index.tsx +10 -3
  231. package/src/plugins/builtin/holders/pane.tsx +42 -16
  232. package/src/plugins/builtin/holders/report-header.ts +136 -0
  233. package/src/plugins/builtin/holders/table-model.ts +20 -5
  234. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  235. package/src/plugins/builtin/holders/types.ts +2 -1
  236. package/src/plugins/builtin/insider/headless.ts +2 -0
  237. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  238. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  239. package/src/plugins/builtin/jobs/headless.ts +1 -0
  240. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  241. package/src/plugins/builtin/kelly-sizer/pane.tsx +12 -6
  242. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  243. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  244. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  245. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  246. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  247. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  248. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  249. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  250. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  251. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  252. package/src/plugins/builtin/members/client.ts +20 -3
  253. package/src/plugins/builtin/members/headless.ts +5 -3
  254. package/src/plugins/builtin/members/model.ts +10 -2
  255. package/src/plugins/builtin/members/pane.tsx +23 -6
  256. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  257. package/src/plugins/builtin/mna/headless.ts +1 -0
  258. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  259. package/src/plugins/builtin/news/headless.ts +2 -0
  260. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  261. package/src/plugins/builtin/notes/cli.ts +14 -6
  262. package/src/plugins/builtin/notes/index.tsx +1 -0
  263. package/src/plugins/builtin/openfda/index.tsx +1 -0
  264. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  265. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  266. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  267. package/src/plugins/builtin/pe-band/client.ts +48 -3
  268. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  269. package/src/plugins/builtin/pe-band/model.ts +92 -16
  270. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  271. package/src/plugins/builtin/perps/client.ts +50 -3
  272. package/src/plugins/builtin/perps/headless.ts +144 -16
  273. package/src/plugins/builtin/perps/index.tsx +6 -6
  274. package/src/plugins/builtin/perps/model.ts +189 -12
  275. package/src/plugins/builtin/perps/pane.tsx +249 -48
  276. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  277. package/src/plugins/builtin/portfolio-list/allocation.ts +248 -0
  278. package/src/plugins/builtin/portfolio-list/cached-account.ts +42 -0
  279. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  280. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  281. package/src/plugins/builtin/portfolio-list/cli/render.ts +232 -39
  282. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  283. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  284. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  285. package/src/plugins/builtin/portfolio-list/headless.ts +100 -16
  286. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  287. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  288. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  289. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  290. package/src/plugins/builtin/portfolio-list/pane/index.tsx +53 -16
  291. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  292. package/src/plugins/builtin/portfolio-list/summary/index.tsx +46 -5
  293. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  294. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  295. package/src/plugins/builtin/power/headless.ts +3 -1
  296. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  297. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  298. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  299. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  300. package/src/plugins/builtin/research/events-headless.ts +2 -0
  301. package/src/plugins/builtin/research/headless.ts +2 -0
  302. package/src/plugins/builtin/research/index.tsx +1 -0
  303. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  304. package/src/plugins/builtin/research/relative-valuation-model.ts +18 -6
  305. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  306. package/src/plugins/builtin/research-search/index.tsx +1 -0
  307. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  308. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  309. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  310. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  311. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  312. package/src/plugins/builtin/scanner/headless.ts +14 -0
  313. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  314. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  315. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  316. package/src/plugins/builtin/sec/forms.ts +12 -0
  317. package/src/plugins/builtin/sec/headless.ts +2 -0
  318. package/src/plugins/builtin/sectors/client.ts +1 -0
  319. package/src/plugins/builtin/sectors/headless.ts +9 -1
  320. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  321. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  322. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  323. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  324. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  325. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  326. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  327. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  328. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  329. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  330. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  331. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  332. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  333. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  334. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  335. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  336. package/src/plugins/builtin/themes/headless.ts +3 -0
  337. package/src/plugins/builtin/themes/pane.tsx +3 -0
  338. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  339. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  340. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  341. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  342. package/src/plugins/builtin/ticker-detail/headless.ts +27 -5
  343. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  344. package/src/plugins/builtin/ticker-detail/overview/model.ts +17 -5
  345. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +27 -26
  346. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  347. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  348. package/src/plugins/builtin/volatility/headless.ts +6 -1
  349. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  350. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +159 -0
  351. package/src/plugins/builtin/world-venue-map/basemap.ts +232 -0
  352. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  353. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  354. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  355. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  356. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  357. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  358. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  359. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  360. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +448 -0
  361. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  362. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  363. package/src/plugins/builtin/world-venue-map/index.ts +69 -6
  364. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +565 -0
  365. package/src/plugins/builtin/world-venue-map/layers.ts +406 -0
  366. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  367. package/src/plugins/builtin/world-venue-map/map.tsx +557 -85
  368. package/src/plugins/builtin/world-venue-map/model.ts +23 -4
  369. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +202 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/pane.tsx +35 -44
  564. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  565. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  566. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  567. package/src/plugins/loader.ts +13 -1
  568. package/src/plugins/registry/pane-settings.ts +1 -0
  569. package/src/renderers/browser/app-services.ts +3 -1
  570. package/src/renderers/dom/build-assets.ts +81 -3
  571. package/src/renderers/dom/data-table/dom.ts +31 -0
  572. package/src/renderers/dom/data-table/index.tsx +5 -0
  573. package/src/renderers/dom/data-table/row.tsx +2 -0
  574. package/src/renderers/dom/desktop/controls.tsx +7 -3
  575. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  576. package/src/renderers/dom/desktop/popover.tsx +73 -14
  577. package/src/renderers/dom/host/box.tsx +113 -17
  578. package/src/renderers/dom/host/drag-shield.ts +24 -0
  579. package/src/renderers/dom/host/input.tsx +14 -3
  580. package/src/renderers/dom/host/live-frame.ts +54 -0
  581. package/src/renderers/dom/host/mouse.ts +8 -1
  582. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  583. package/src/renderers/dom/host/tabs.tsx +3 -2
  584. package/src/renderers/dom/styles.css +16 -9
  585. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  586. package/src/renderers/electrobun/bun/desktop/backend.ts +2 -0
  587. package/src/renderers/electrobun/bun/desktop/initialization.ts +13 -0
  588. package/src/renderers/electrobun/shared/protocol.ts +10 -1
  589. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  590. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -0
  591. package/src/renderers/electrobun/view/host/native.ts +0 -10
  592. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  593. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  594. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  595. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  596. package/src/renderers/opentui/host.tsx +5 -0
  597. package/src/renderers/opentui/ui-host.tsx +7 -1
  598. package/src/sources/gloomberb-cloud/index.ts +25 -4
  599. package/src/sources/gloomberb-cloud/normalizers.ts +43 -23
  600. package/src/sources/provider-router/batches.ts +3 -3
  601. package/src/sources/provider-router/cached-routes.ts +4 -1
  602. package/src/sources/provider-router/financial-routes.ts +2 -2
  603. package/src/sources/provider-router/financials.ts +76 -12
  604. package/src/sources/provider-router/history.ts +15 -10
  605. package/src/sources/provider-router/primary.ts +2 -2
  606. package/src/sources/sec-registrant.ts +75 -0
  607. package/src/state/app/context/index.tsx +24 -8
  608. package/src/state/hooks/quote-streaming.ts +41 -26
  609. package/src/state/pane-scroll-registry.tsx +67 -35
  610. package/src/sync/core-contributors.ts +4 -0
  611. package/src/telemetry/crash-reports.ts +4 -1
  612. package/src/test-support/cli-context.ts +10 -2
  613. package/src/test-support/margin-account.ts +108 -0
  614. package/src/theme/heat-colors.ts +266 -84
  615. package/src/time-series/resolution.ts +15 -0
  616. package/src/time-series/resolve.ts +1 -1
  617. package/src/time-series/use-chart-resolution.ts +1 -1
  618. package/src/types/context-menu.ts +6 -0
  619. package/src/types/data-provider.ts +2 -0
  620. package/src/types/financials.ts +35 -3
  621. package/src/types/headless.ts +79 -2
  622. package/src/types/plugin.ts +20 -1
  623. package/src/types/ticker.ts +15 -0
  624. package/src/ui/host.tsx +26 -0
  625. package/src/ui/index.tsx +8 -3
  626. package/src/ui/scroll-box-axis.ts +26 -0
  627. package/src/updater/index.ts +9 -11
  628. package/src/utils/async-deadline.ts +21 -0
  629. package/src/utils/cli-output.ts +5 -1
  630. package/src/utils/crypto-pair.ts +41 -0
  631. package/src/utils/currency-units.ts +19 -0
  632. package/src/utils/depositary-receipt.ts +70 -0
  633. package/src/utils/display-number.ts +63 -0
  634. package/src/utils/exchanges.ts +72 -0
  635. package/src/utils/fire-and-forget.ts +24 -0
  636. package/src/utils/format.ts +13 -0
  637. package/src/utils/fundamentals.ts +17 -1
  638. package/src/utils/price-earnings.ts +70 -3
  639. package/src/utils/price-history-integrity.ts +19 -6
  640. package/src/utils/price-history.ts +58 -13
  641. package/src/utils/rpc-timeout-error.ts +27 -0
  642. package/src/utils/sec.ts +13 -1
  643. package/src/utils/utc-time.ts +79 -0
@@ -1,6 +1,6 @@
1
1
  import type { CliCommandDef } from "../../types/plugin";
2
2
  import { TIME_RANGES, type TimeRange } from "../../time-series/range";
3
- import type { EarningsEvent, QuoteBatchResult, SecFilingItem } from "../../types/data-provider";
3
+ import type { EarningsEvent, QuoteBatchResult, QuoteSubscriptionTarget, SecFilingItem } from "../../types/data-provider";
4
4
  import type { NewsArticle, NewsFeed, NewsQuery } from "../../news/types";
5
5
  import type {
6
6
  AnalystResearchData,
@@ -13,8 +13,9 @@ import { currencyMinorDigits, formatMarketPrice, formatMarketPriceWithCurrency,
13
13
  import { getActiveQuoteDisplay, marketStateLabel } from "../../market-data/market/status";
14
14
  import { formatCompact, formatDistributionAmount, formatPercent } from "../../utils/format";
15
15
  import { withCliServices, withMarketData } from "../context";
16
- import { isoDate, parsePositiveInt, requireArg, takeOption } from "./command-utils";
17
- import { CLI_COMMAND_GROUPS } from "../help";
16
+ import { isoDate, parsePositiveInt, rejectExtraArgs, requireOneArg, takeFlag, takeOption } from "./command-utils";
17
+ import type { BuiltinCliCommandDef } from "../command-options";
18
+ import { CLI_COMMAND_GROUPS, TABLE_SECTION_OPTION } from "../help";
18
19
  import {
19
20
  formatChangePercentCell,
20
21
  formatCountCell,
@@ -33,14 +34,79 @@ import {
33
34
  } from "../../plugins/builtin/research/analyst-model";
34
35
  import { optionQuoteSide } from "../../plugins/builtin/options/market-reference";
35
36
  import { getPublishedUsEquityCalendarYears, getPublishedUsEquitySession } from "../../market-data/published-us-sessions";
36
- import { renderFundamentalsReport } from "./ticker";
37
- import { historyPriceDecimals, historyRows } from "../history-rows";
37
+ import { fundamentalsReportTables, renderFundamentalsReport } from "./ticker";
38
+ import type { CliResultColumn } from "../result";
39
+ import {
40
+ exportRowsTable,
41
+ reportFooterLines,
42
+ selectReportTables,
43
+ type CliReportTables,
44
+ } from "../report-tables";
45
+ import {
46
+ historyFacts,
47
+ historyFlagNote,
48
+ historyAllRangeYears,
49
+ historyIntervalsByRange,
50
+ historyNotes,
51
+ historyPriceDecimals,
52
+ historyRows,
53
+ historyUnit,
54
+ } from "../history-rows";
55
+ import { CRYPTO_BOARD_HINT, quoteNotes } from "./crypto-hints";
56
+ import { formatUtcTime } from "../../utils/utc-time";
57
+ import { isFiniteNumber } from "../../utils/guards";
58
+ import { exportedFundamentals } from "../../utils/price-earnings";
59
+ import { fundamentalsFreshness, quotesFreshness, rowsFreshness } from "../freshness";
60
+ import {
61
+ barHistoryFreshness,
62
+ barResolutionFromDates,
63
+ REPORTED_DATA,
64
+ SEC_FILINGS,
65
+ } from "../../plugins/builtin/shared/report-freshness";
66
+ import {
67
+ EXCHANGE_OPTION,
68
+ listingHeading,
69
+ listingIdentity,
70
+ listingTitle,
71
+ listingVenues,
72
+ loadForListing,
73
+ loadListingQuote,
74
+ notTradedMessage,
75
+ requireCliListing,
76
+ type CliListing,
77
+ type ListingIdentity,
78
+ } from "../listing-arg";
79
+ import { secRegistrantMismatchMessage, SecRegistrantMismatchError, areDifferentCompanies } from "../../sources/sec-registrant";
80
+ import { isCryptoPairSymbol } from "../../utils/crypto-pair";
81
+ import { isUsListingExchange } from "../../utils/exchanges";
82
+ import { nonUsSecListingVenue } from "../../utils/sec";
83
+ import type { MarketContext } from "../types";
84
+ import {
85
+ holderListFacts,
86
+ holderShareBasisMarker,
87
+ holderShareBasisNote,
88
+ holderValueBasis,
89
+ moneyColumnHeader,
90
+ nonUsHolderCaveat,
91
+ sharedReportDate,
92
+ } from "../../plugins/builtin/holders/report-header";
93
+ import { formatHolderOwnershipPercent, holderValueCurrency } from "../../plugins/builtin/holders/format";
94
+ import { fetchBeneficialOwners } from "../../plugins/builtin/holders/beneficial-client";
95
+ import { filingFormMatches, SEC_FILING_FETCH_LIMIT } from "../../plugins/builtin/sec/forms";
96
+ import {
97
+ BENEFICIAL_REPORT_COLUMNS,
98
+ beneficialCoverageNotices,
99
+ beneficialListComplete,
100
+ beneficialListFacts,
101
+ beneficialListUnreadable,
102
+ beneficialRouteForm,
103
+ buildBeneficialReportRows,
104
+ HOLDER_FORMS,
105
+ parseHolderForm,
106
+ type HolderForm,
107
+ } from "../../plugins/builtin/holders/beneficial-report";
38
108
 
39
109
  const VALID_RANGES = new Set<TimeRange>(TIME_RANGES);
40
- const EXCHANGE_OPTION = {
41
- flags: "--exchange <code>",
42
- description: "Listing exchange, for a symbol that trades in several places",
43
- };
44
110
  const VALID_NEWS_FEEDS = new Set<NewsFeed>(["latest", "top", "breaking", "ticker", "sector", "topic"]);
45
111
 
46
112
  type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & { error: string | null };
@@ -67,6 +133,11 @@ function normalizeSymbols(args: string[]): string[] {
67
133
  .filter(Boolean);
68
134
  }
69
135
 
136
+ /** What JSON metadata says about the listing a command resolved. */
137
+ function listingMetadata(identity: ListingIdentity) {
138
+ return { symbol: identity.symbol, exchange: identity.exchange || null, name: identity.name };
139
+ }
140
+
70
141
  const QUOTE_LEAD_COLUMNS = [
71
142
  { key: "symbol", header: "Symbol" },
72
143
  { key: "name", header: "Name" },
@@ -86,7 +157,8 @@ function quoteColumns() {
86
157
  return [
87
158
  ...QUOTE_LEAD_COLUMNS,
88
159
  { key: "currency", header: "Cur" },
89
- { key: "source", header: "Source" },
160
+ // Whether the price is real-time or delayed: a feed state, not where it came from.
161
+ { key: "source", header: "Feed", format: (value: unknown) => value === "live" || value === "delayed" ? value : "" },
90
162
  { key: "updatedAt", header: "Updated" },
91
163
  ];
92
164
  }
@@ -199,6 +271,7 @@ function holderRows(data: HolderData, ownerTypes?: Set<string>) {
199
271
  value: holder.value ?? null,
200
272
  percentHeld: holder.percentHeld ?? null,
201
273
  changeShares: holder.changeShares ?? null,
274
+ shareBasis: holder.shareBasis ?? null,
202
275
  }));
203
276
  }
204
277
 
@@ -332,46 +405,123 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
332
405
  if (symbols.length === 0) ctx.fail(`Usage: gloomberb ${commandName} <symbol...>`);
333
406
 
334
407
  await withMarketData(ctx, async (market) => {
335
- const results = await market.dataProvider.getQuotesBatch(
336
- symbols.map((symbol) => ({ symbol, exchange })),
337
- { forceRefresh: ctx.cliOptions.refresh },
338
- );
339
- const data = results.map((result) => ({
408
+ // With several symbols, --exchange is for the ones that name no exchange of their own.
409
+ const listings = await Promise.all(symbols.map((symbol) => requireCliListing(
410
+ symbol, exchange, market, ctx, { ownExchangeWins: symbols.length > 1 },
411
+ )));
412
+ const targets: QuoteSubscriptionTarget[] = listings.map((listing) => ({ symbol: listing.request.symbol, exchange: listing.request.exchange }));
413
+ const results = await market.dataProvider.getQuotesBatch(targets, { forceRefresh: ctx.cliOptions.refresh });
414
+ const listingOf = (result: QuoteBatchResult, index: number) => listings[targets.indexOf(result.target)] ?? listings[index]!;
415
+ // A listing with no quote on an exchange its symbol is not listed on says so; venues are only looked up for those.
416
+ const notTraded = await Promise.all(results.map((result, index) => (
417
+ result.quote ? null : notTradedMessage(listingOf(result, index), market)
418
+ )));
419
+ if (listings.length === 1 && notTraded[0]) ctx.fail(notTraded[0]);
420
+ // Each row names its listing by key (SAN:EPA) and company, so the table needs no line above it.
421
+ const data = results.map((result, index) => ({
340
422
  target: result.target,
423
+ listing: listingMetadata(listingIdentity(listingOf(result, index), result.quote)),
341
424
  quote: result.quote,
342
- error: errorMessage(result.error),
425
+ error: notTraded[index] ?? errorMessage(result.error),
343
426
  }));
344
- ctx.printResult({ data }, { rows: quoteRows, columns: commandName === "compare" ? compareColumns() : quoteColumns() });
427
+ // Text mode shows only "unavailable" in the cell; the JSON rows already carry each reason.
428
+ const notes = ctx.cliOptions.format === "text" ? quoteNotes(data, { exchange }) : [];
429
+ ctx.printResult({
430
+ data,
431
+ warnings: notes.length > 0 ? notes : undefined,
432
+ freshness: quotesFreshness(data.map((row) => row.quote)),
433
+ }, {
434
+ rows: quoteRows,
435
+ columns: commandName === "compare" ? compareColumns() : quoteColumns(),
436
+ });
345
437
  });
346
438
  }
347
439
 
440
+ /** A crypto pair that will not load points at the crypto board. Structured formats keep the error as thrown. */
441
+ function failHistory(error: unknown, symbol: string, ctx: Parameters<CliCommandDef["execute"]>[1]): never {
442
+ if (ctx.cliOptions.format !== "text" || !isCryptoPairSymbol(symbol)) throw error;
443
+ return ctx.fail(errorMessage(error) ?? `No history available for ${symbol}`, CRYPTO_BOARD_HINT);
444
+ }
445
+
446
+ const HISTORY_USAGE = "history <symbol> [--range <range>]";
447
+ const HISTORY_ALL_RANGE = `ALL gives the full history the source has, up to ${historyAllRangeYears()} years`;
448
+ /** Options people reach for to ask for dates, which history answers with a range. */
449
+ const DATE_WINDOW_OPTIONS = new Set(["--from", "--to", "--start", "--end", "--since", "--until", "--date", "--start-date", "--end-date"]);
450
+
348
451
  async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
349
452
  const args = [...rawArgs];
350
453
  const range = parseRange(takeOption(args, "--range"), ctx);
351
- const requestedExchange = takeOption(args, "--exchange") ?? "";
352
- const symbol = requireArg(args[0]?.toUpperCase(), "Usage: gloomberb history <symbol> [--range <range>]", ctx);
454
+ const requestedExchange = takeOption(args, "--exchange");
455
+ const raw = requireOneArg(args, HISTORY_USAGE, "symbol", ctx);
353
456
  await withMarketData(ctx, async (market) => {
354
- const localTicker = requestedExchange ? null : await market.store.loadTicker(symbol);
355
- const exchange = requestedExchange || localTicker?.metadata.exchange || "";
457
+ const listing = await requireCliListing(raw, requestedExchange, market, ctx);
458
+ const { symbol, exchange } = listing.request;
356
459
  const context = { cacheMode: ctx.cliOptions.refresh ? "refresh" as const : "default" as const };
357
- const { points, resolution } = market.dataProvider.getPriceHistoryWithMetadata
358
- ? await market.dataProvider.getPriceHistoryWithMetadata(symbol, exchange, range, context)
359
- : { points: await market.dataProvider.getPriceHistory(symbol, exchange, range, context), resolution: null };
360
- const data = historyRows(points, resolution);
361
- const decimals = historyPriceDecimals(data, localTicker?.metadata.assetCategory);
460
+ const load = () => market.dataProvider.getPriceHistoryWithMetadata
461
+ ? market.dataProvider.getPriceHistoryWithMetadata(symbol, exchange, range, context)
462
+ : market.dataProvider.getPriceHistory(symbol, exchange, range, context).then((points) => ({ points, resolution: null }));
463
+ const [{ points, resolution }, quote] = await Promise.all([
464
+ loadForListing(listing, market, ctx, load, (loaded) => loaded.points.length === 0)
465
+ .catch((error) => failHistory(error, listing.key, ctx)),
466
+ loadListingQuote(market.dataProvider, listing),
467
+ ]);
468
+ const identity = listingIdentity(listing, quote);
469
+ // Without a quote currency, the listing metadata research reads (as `ticker` does) may still state it.
470
+ const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(() => null);
471
+ const unit = historyUnit(listing.symbol, listed ?? quote);
472
+ const data = historyRows(points, resolution, unit.currency);
473
+ const interval = data[0]?.interval ?? null;
474
+ const decimals = historyPriceDecimals(data, listing.saved?.metadata.assetCategory);
362
475
  const price = (value: unknown) => typeof value === "number" ? value.toFixed(decimals) : "";
363
476
  // Intraday bars print in UTC, as the charts and time and sales label them, not the host zone.
364
477
  const intraday = data.some((row) => row.date.length > 10);
365
- ctx.printResult({ data, metadata: { symbol, range, exchange, resolution } }, {
478
+ // A bar history: dated by its last bar, stale once bars of its size stop arriving.
479
+ const freshness = rowsFreshness(data, {
480
+ ...barHistoryFreshness(intraday ? null : barResolutionFromDates(data.map((row) => row.date))),
481
+ observedKey: "date",
482
+ oldest: null,
483
+ });
484
+ const notes = [...historyNotes(range, interval, data[0]?.date), historyFlagNote(data) ?? []].flat();
485
+ const priceColumns: CliResultColumn[] = [
486
+ intraday
487
+ ? { key: "date", header: "Time", format: (value) => typeof value === "string" ? formatUtcTime(value) : "" }
488
+ : { key: "date", header: "Date" },
489
+ { key: "open", header: "Open", align: "right", format: price },
490
+ { key: "high", header: "High", align: "right", format: price },
491
+ { key: "low", header: "Low", align: "right", format: price },
492
+ { key: "close", header: "Close", align: "right", format: price },
493
+ { key: "volume", header: "Volume", align: "right", format: formatCountCell },
494
+ ];
495
+ // Text shows a flag only on a bar that has one; the column is dropped when none does.
496
+ const flagColumn: CliResultColumn = {
497
+ key: "flag", header: "Flag", format: (value) => typeof value === "string" ? cliStyles.warning(value) : "",
498
+ };
499
+ ctx.printResult({
500
+ data,
501
+ metadata: {
502
+ ...listingMetadata(identity),
503
+ range,
504
+ resolution,
505
+ requestedRange: range,
506
+ currency: unit.currency,
507
+ unit: unit.unit,
508
+ interval,
509
+ firstDate: data[0]?.date ?? null,
510
+ lastDate: data.at(-1)?.date ?? null,
511
+ bars: data.length,
512
+ asOf: freshness?.asOf ?? null,
513
+ },
514
+ ...(notes.length > 0 ? { warnings: notes } : {}),
515
+ freshness,
516
+ }, {
517
+ heading: `${listingHeading(identity)}\n${cliStyles.muted(historyFacts(unit, data).join(" · "))}`,
518
+ textColumns: [...priceColumns, flagColumn],
519
+ // Every exported row says its currency and bar size, so it survives head and concatenation.
366
520
  columns: [
367
- intraday
368
- ? { key: "date", header: "Time (UTC)", format: (value) => typeof value === "string" ? value.slice(0, 16).replace("T", " ") : "" }
369
- : { key: "date", header: "Date" },
370
- { key: "open", header: "Open", align: "right", format: price },
371
- { key: "high", header: "High", align: "right", format: price },
372
- { key: "low", header: "Low", align: "right", format: price },
373
- { key: "close", header: "Close", align: "right", format: price },
374
- { key: "volume", header: "Volume", align: "right", format: formatCountCell },
521
+ ...priceColumns,
522
+ { key: "currency", header: "Currency" },
523
+ { key: "interval", header: "Interval" },
524
+ flagColumn,
375
525
  ],
376
526
  });
377
527
  });
@@ -379,46 +529,84 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
379
529
 
380
530
  type FinancialsView = "statements" | "fundamentals" | "valuation";
381
531
 
532
+ const STATEMENT_COLUMNS: CliResultColumn<ReturnType<typeof financialStatementRows>[number]>[] = [
533
+ { key: "date", header: "Date" },
534
+ { key: "revenue", header: "Revenue", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
535
+ { key: "grossProfit", header: "Gross", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
536
+ { key: "operatingIncome", header: "Op Inc", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
537
+ { key: "netIncome", header: "Net Inc", align: "right", format: (value) => value == null ? "" : formatCompact(Number(value)) },
538
+ { key: "eps", header: "EPS", align: "right", format: (value) => value == null ? "" : formatPerShareNumber(Number(value)) },
539
+ { key: "currency", header: "Cur" },
540
+ ];
541
+
382
542
  async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1], view: FinancialsView) {
383
543
  const args = [...rawArgs];
384
- const exchange = takeOption(args, "--exchange") ?? "";
544
+ const exchangeOption = takeOption(args, "--exchange");
545
+ const sectionFlag = args.some((arg) => arg === "--section" || arg.startsWith("--section="));
546
+ const section = takeOption(args, "--section");
385
547
  const commandName = view === "statements" ? "financials" : view;
386
- const symbol = requireArg(args[0]?.toUpperCase(), `Usage: gloomberb ${commandName} <symbol>`, ctx);
548
+ const tabular = ctx.cliOptions.format === "csv" || ctx.cliOptions.format === "ndjson";
549
+ if (sectionFlag && !tabular) ctx.fail("--section picks one table of --csv or --ndjson output.");
550
+ if (sectionFlag && !section?.trim()) ctx.fail("--section needs a section title or number.");
551
+ const raw = requireOneArg(args, `${commandName} <symbol>`, "symbol", ctx);
552
+ const selectTables = (tables: CliReportTables) => {
553
+ try {
554
+ return selectReportTables(tables, section);
555
+ } catch (error) {
556
+ return ctx.fail(error instanceof Error ? error.message : String(error));
557
+ }
558
+ };
387
559
  await withMarketData(ctx, async (market) => {
388
- const financials = await market.dataProvider.getTickerFinancials(symbol, exchange, {
389
- cacheMode: ctx.cliOptions.refresh ? "refresh" : "default",
390
- });
560
+ const listing = await requireCliListing(raw, exchangeOption, market, ctx);
561
+ const financials = await loadForListing(listing, market, ctx, () => market.dataProvider.getTickerFinancials(
562
+ listing.request.symbol, listing.request.exchange, { cacheMode: ctx.cliOptions.refresh ? "refresh" : "default" },
563
+ ));
564
+ const identity = listingIdentity(listing, financials.quote);
391
565
  const data: FinancialsCliData = {
392
- symbol,
393
- exchange,
566
+ symbol: listing.key,
567
+ exchange: identity.exchange,
394
568
  providerId: financials.quote?.providerId ?? null,
395
569
  ...financials,
396
570
  };
397
571
  if (view !== "statements") {
398
- ctx.printResult({ data }, { text: (financialsData) => renderFundamentalsReport(financialsData, view) });
572
+ const freshness = fundamentalsFreshness(financials);
573
+ ctx.printResult({
574
+ // JSON reads a multiple over a loss as null with its reason, never as a number.
575
+ data: { ...data, fundamentals: exportedFundamentals(data.fundamentals) },
576
+ freshness,
577
+ }, {
578
+ text: () => renderFundamentalsReport(data, view),
579
+ ...(tabular ? { tables: selectTables(fundamentalsReportTables(data, view, freshness)) } : {}),
580
+ });
399
581
  return;
400
582
  }
583
+ const rows = financialStatementRows(data);
584
+ const freshness = rowsFreshness(rows, {
585
+ ...REPORTED_DATA, basis: "financial statements", observedKey: "date", oldest: null,
586
+ });
587
+ // NDJSON keeps the raw statement rows it always wrote; CSV gets the table and its closing lines.
588
+ const tables = tabular
589
+ ? selectTables({
590
+ tables: [exportRowsTable("Annual statements", STATEMENT_COLUMNS, rows)],
591
+ footer: reportFooterLines({ freshness }),
592
+ })
593
+ : undefined;
401
594
  ctx.printResult({
402
595
  data,
403
596
  metadata: {
404
- symbol,
597
+ ...listingMetadata(identity),
405
598
  providerId: financials.quote?.providerId,
406
599
  annualStatements: financials.annualStatements.length,
407
600
  quarterlyStatements: financials.quarterlyStatements.length,
408
- fundamentals: financials.fundamentals,
601
+ fundamentals: exportedFundamentals(financials.fundamentals),
409
602
  profile: financials.profile,
410
603
  },
604
+ freshness,
411
605
  }, {
412
- rows: financialStatementRows,
413
- columns: [
414
- { key: "date", header: "Date" },
415
- { key: "revenue", header: "Revenue", align: "right", value: (row) => row.revenue == null ? "" : formatCompact(Number(row.revenue)) },
416
- { key: "grossProfit", header: "Gross", align: "right", value: (row) => row.grossProfit == null ? "" : formatCompact(Number(row.grossProfit)) },
417
- { key: "operatingIncome", header: "Op Inc", align: "right", value: (row) => row.operatingIncome == null ? "" : formatCompact(Number(row.operatingIncome)) },
418
- { key: "netIncome", header: "Net Inc", align: "right", value: (row) => row.netIncome == null ? "" : formatCompact(Number(row.netIncome)) },
419
- { key: "eps", header: "EPS", align: "right", format: (value) => value == null ? "" : formatPerShareNumber(Number(value)) },
420
- { key: "currency", header: "Cur" },
421
- ],
606
+ heading: listingHeading(identity),
607
+ rows: () => rows,
608
+ columns: STATEMENT_COLUMNS,
609
+ ...(tables && ctx.cliOptions.format === "csv" ? { tables } : {}),
422
610
  });
423
611
  });
424
612
  }
@@ -426,24 +614,40 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
426
614
  async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
427
615
  const args = [...rawArgs];
428
616
  const feed = parseNewsFeed(takeOption(args, "--feed"));
429
- const ticker = args[0]?.toUpperCase();
617
+ const exchangeOption = takeOption(args, "--exchange");
618
+ if (exchangeOption && !args[0]) ctx.fail("--exchange needs a symbol: gloomberb news <symbol> --exchange <code>");
619
+ rejectExtraArgs(args, 1, { usage: "news [symbol] [--feed <feed>]", takes: "one symbol at most", advice: "Run it once per symbol." }, ctx);
430
620
  await withMarketData(ctx, async (market) => {
621
+ const listing = args[0] ? await requireCliListing(args[0], exchangeOption, market, ctx) : null;
431
622
  const limit = ctx.cliOptions.limit ?? 20;
432
- const articles = await market.dataProvider.getNews({
433
- feed: feed ?? (ticker ? "ticker" : "latest"),
623
+ const loadNews = () => market.dataProvider.getNews({
624
+ feed: feed ?? (listing ? "ticker" : "latest"),
434
625
  // Still set for news plugins that read the deprecated scope.
435
- scope: ticker ? "ticker" : "global",
436
- ticker,
626
+ scope: listing ? "ticker" : "global",
627
+ ticker: listing?.request.symbol,
628
+ exchange: listing?.request.exchange || undefined,
437
629
  limit,
438
630
  });
439
- ctx.printResult({ data: articles, metadata: { ticker: ticker ?? null, feed: feed ?? null } }, {
631
+ const [articles, quote] = await Promise.all([
632
+ listing ? loadForListing(listing, market, ctx, loadNews, (found) => found.length === 0) : loadNews(),
633
+ listing ? loadListingQuote(market.dataProvider, listing) : null,
634
+ ]);
635
+ const identity = listing ? listingIdentity(listing, quote) : null;
636
+ ctx.printResult({
637
+ data: articles,
638
+ metadata: { ticker: listing?.key ?? null, ...(identity ? listingMetadata(identity) : {}), feed: feed ?? null },
639
+ freshness: rowsFreshness(newsRows(articles), {
640
+ status: "not-a-feed", basis: "published stories", observedKey: "publishedAt", oldest: null,
641
+ }),
642
+ }, {
643
+ ...(identity ? { heading: listingHeading(identity) } : {}),
440
644
  rows: newsRows,
441
645
  columns: [
442
- // UTC, as the news panes print it, rather than the host zone unlabeled.
646
+ // UTC with the zone named, as every CLI time prints, rather than the host zone unlabeled.
443
647
  {
444
648
  key: "publishedAt",
445
- header: "Published (UTC)",
446
- format: (value) => typeof value === "string" ? value.slice(0, 16).replace("T", " ") : "",
649
+ header: "Published",
650
+ format: (value) => typeof value === "string" ? formatUtcTime(value) : "",
447
651
  },
448
652
  { key: "source", header: "Source", maxWidth: 20 },
449
653
  { key: "title", header: "Title" },
@@ -454,22 +658,67 @@ async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
454
658
  });
455
659
  }
456
660
 
661
+ const FILING_COLUMNS = [
662
+ { key: "filingDate", header: "Date" },
663
+ { key: "form", header: "Form" },
664
+ { key: "companyName", header: "Company", maxWidth: 24 },
665
+ { key: "url", header: "URL", optional: true },
666
+ ];
667
+
668
+ /** The US listing the SEC files a symbol under, from the symbol's venues: NYSE for SAN's Banco Santander. */
669
+ async function registrantUsExchange(market: MarketContext, symbol: string, registrantName: string): Promise<string | null> {
670
+ const venues = (await listingVenues(symbol, market).catch(() => []))
671
+ .filter((venue) => !nonUsSecListingVenue(symbol, venue.exchange) && !areDifferentCompanies(venue.name, registrantName));
672
+ return (venues.find((venue) => isUsListingExchange(venue.exchange)) ?? venues[0])?.exchange ?? null;
673
+ }
674
+
457
675
  async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
458
676
  const args = [...rawArgs];
459
677
  const count = parsePositiveInt(takeOption(args, "--count"), ctx.cliOptions.limit ?? 15, "Count", ctx);
460
- const exchange = takeOption(args, "--exchange") ?? "";
461
- const symbol = requireArg(args[0]?.toUpperCase(), "Usage: gloomberb filings <symbol>", ctx);
678
+ const exchangeOption = takeOption(args, "--exchange");
679
+ const form = takeOption(args, "--form")?.trim() || null;
680
+ const raw = requireOneArg(args, "filings <symbol> [--count <n>] [--form <form>]", "symbol", ctx);
462
681
  await withMarketData(ctx, async (market) => {
463
- const filings = await market.dataProvider.getSecFilings(symbol, count, exchange);
464
- ctx.printResult({ data: filings, metadata: { symbol } }, {
465
- rows: filingRows,
466
- columns: [
467
- { key: "filingDate", header: "Date" },
468
- { key: "form", header: "Form" },
469
- { key: "companyName", header: "Company", maxWidth: 24 },
470
- { key: "url", header: "URL", optional: true },
471
- ],
472
- });
682
+ const listing = await requireCliListing(raw, exchangeOption, market, ctx);
683
+ const { symbol, exchange } = listing.request;
684
+ // A listing outside the US sends its company to the lookup, which needs its quote first.
685
+ const quotePromise = loadListingQuote(market.dataProvider, listing);
686
+ const identity = nonUsSecListingVenue(symbol, exchange) ? listingIdentity(listing, await quotePromise) : null;
687
+ try {
688
+ const context = identity?.name ? { listingName: identity.name } : undefined;
689
+ const [filings, quote] = await Promise.all([
690
+ loadForListing(listing, market, ctx, () => form
691
+ // A form filter searches every filing the service lists for the issuer, as the SEC pane does.
692
+ ? market.dataProvider.getSecFilings(symbol, SEC_FILING_FETCH_LIMIT, exchange, context)
693
+ .then((all) => all.filter((filing) => filingFormMatches(filing.form, form)).slice(0, count))
694
+ : market.dataProvider.getSecFilings(symbol, count, exchange, context),
695
+ (found) => found.length === 0),
696
+ quotePromise,
697
+ ]);
698
+ const resolved = identity ?? listingIdentity(listing, quote);
699
+ ctx.printResult({
700
+ data: filings,
701
+ metadata: { ...listingMetadata(resolved), ...(form ? { form } : {}) },
702
+ freshness: rowsFreshness(filingRows(filings), { ...SEC_FILINGS, observedKey: "filingDate" }),
703
+ }, {
704
+ heading: listingHeading(resolved),
705
+ rows: filingRows,
706
+ columns: FILING_COLUMNS,
707
+ empty: form ? `No ${form} filings found for ${listingTitle(resolved)}.` : `No SEC filings found for ${listingTitle(resolved)}.`,
708
+ });
709
+ } catch (error) {
710
+ if (!(error instanceof SecRegistrantMismatchError)) throw error;
711
+ // Another company's filings never show under this listing; say whose they were.
712
+ const message = secRegistrantMismatchMessage(
713
+ error.listing, error.registrantName, await registrantUsExchange(market, error.listing.symbol, error.registrantName),
714
+ );
715
+ const resolved = identity ?? listingIdentity(listing, await quotePromise);
716
+ ctx.printResult({
717
+ data: [] as SecFilingItem[],
718
+ metadata: { ...listingMetadata(resolved), secRegistrant: error.registrantName },
719
+ warnings: ctx.cliOptions.format === "text" ? undefined : [message],
720
+ }, { rows: filingRows, columns: FILING_COLUMNS, empty: message });
721
+ }
473
722
  });
474
723
  }
475
724
 
@@ -487,6 +736,52 @@ function insiderSummary(data: HolderData, ownerTypes: Set<string>): string {
487
736
  return [share, transactions].filter(Boolean).join("\n");
488
737
  }
489
738
 
739
+ /** `holders --form 13d|13g|all`: the 13D/13G beneficial owners, joined by name to the 13F holders. */
740
+ async function printBeneficialOwners(
741
+ listing: CliListing,
742
+ market: MarketContext,
743
+ form: Exclude<HolderForm, "13f">,
744
+ history: boolean,
745
+ ctx: Parameters<CliCommandDef["execute"]>[1],
746
+ ) {
747
+ const { symbol, exchange } = listing.request;
748
+ const [payload, holders, quote] = await Promise.all([
749
+ loadForListing(listing, market, ctx, () => fetchBeneficialOwners(symbol, { form: beneficialRouteForm(form), history })),
750
+ market.dataProvider.getHolders?.(symbol, exchange).catch(() => null) ?? null,
751
+ loadListingQuote(market.dataProvider, listing),
752
+ ]);
753
+ const identity = listingIdentity(listing, quote);
754
+ const rows = buildBeneficialReportRows(payload, { history, holders });
755
+ const notices = beneficialCoverageNotices(payload.coverage);
756
+ ctx.printResult({
757
+ data: rows,
758
+ ...(notices.length ? { warnings: notices } : {}),
759
+ metadata: {
760
+ ...listingMetadata(identity),
761
+ name: identity.name ?? (payload.companyName || null),
762
+ cik: payload.cik || null,
763
+ form,
764
+ history,
765
+ asOf: payload.asOf,
766
+ coverage: payload.coverage,
767
+ complete: beneficialListComplete(payload),
768
+ },
769
+ }, {
770
+ heading: `${listingHeading(identity)}${cliStyles.muted(beneficialListFacts(payload, form, rows.length, history).map((fact) => ` · ${fact}`).join(""))}`,
771
+ textColumns: BENEFICIAL_REPORT_COLUMNS,
772
+ columns: [
773
+ ...BENEFICIAL_REPORT_COLUMNS,
774
+ { key: "status", header: "Status" },
775
+ { key: "filerCik", header: "Filer CIK" },
776
+ { key: "accessionNumber", header: "Accession" },
777
+ { key: "filingUrl", header: "URL" },
778
+ ],
779
+ empty: beneficialListUnreadable(payload)
780
+ ? `13D/13G filings for ${listingTitle(identity)} are listed but could not be read.`
781
+ : `No 13D/13G filings in the last 4 years for ${listingTitle(identity)}.`,
782
+ });
783
+ }
784
+
490
785
  async function runHolders(
491
786
  rawArgs: string[],
492
787
  ctx: Parameters<CliCommandDef["execute"]>[1],
@@ -494,43 +789,112 @@ async function runHolders(
494
789
  ownerTypes?: Set<string>,
495
790
  ) {
496
791
  const args = [...rawArgs];
497
- const exchange = takeOption(args, "--exchange") ?? "";
498
- const symbol = requireArg(args[0]?.toUpperCase(), `Usage: gloomberb ${commandName} <symbol>`, ctx);
792
+ const exchangeOption = takeOption(args, "--exchange");
793
+ const formOption = commandName === "holders" ? takeOption(args, "--form") : undefined;
794
+ const history = commandName === "holders" && takeFlag(args, "--history");
795
+ const raw = requireOneArg(args, `${commandName} <symbol>`, "symbol", ctx);
796
+ const form = formOption == null ? "13f" : parseHolderForm(formOption);
797
+ if (!form) ctx.fail(`Unknown form "${formOption}".`, `Use one of ${HOLDER_FORMS.join(", ")}.`);
798
+ if (form === "13f" && history) ctx.fail("--history lists 13D/13G reports.", "Add --form 13d, 13g or all.");
499
799
  await withMarketData(ctx, async (market) => {
500
- const data = await market.dataProvider.getHolders(symbol, exchange);
501
- ctx.printResult({ data, metadata: { symbol, summary: data.summary } }, {
502
- rows: (holderData) => holderRows(holderData, ownerTypes),
800
+ const listing = await requireCliListing(raw, exchangeOption, market, ctx);
801
+ if (form !== "13f") return printBeneficialOwners(listing, market, form, history, ctx);
802
+ const [data, quote] = await Promise.all([
803
+ loadForListing(
804
+ listing, market, ctx,
805
+ () => market.dataProvider.getHolders(listing.request.symbol, listing.request.exchange),
806
+ (found) => found.holders.length === 0,
807
+ ),
808
+ loadListingQuote(market.dataProvider, listing),
809
+ ]);
810
+ const identity = listingIdentity(listing, quote);
811
+ const rows = holderRows(data, ownerTypes);
812
+ const limit = ctx.cliOptions.limit;
813
+ const shown = limit == null ? rows.length : Math.min(limit, rows.length);
814
+ const institutional = !ownerTypes?.has("insider");
815
+ const total = institutional ? data.summary?.institutionsCount ?? null : null;
816
+ const reportDate = sharedReportDate(rows);
817
+ // A London line's values can be dollars from the 13F filings; the listing's currency is not their unit.
818
+ const valueCurrency = holderValueCurrency(data);
819
+ const valueBasis = rows.length > 0 ? holderValueBasis(reportDate, data.valueBasis, valueCurrency) : null;
820
+ const positionsBasis = rows.length > 0 ? nonUsHolderCaveat(identity.exchange || data.exchange, data.currency) : null;
821
+ const shareBasisNote = holderShareBasisNote(data, rows);
822
+ const markedRows = rows.some((row) => holderShareBasisMarker(row));
823
+ // The heading names the listing; its unit, date and how much of the list follows on the same line.
824
+ const facts = holderListFacts({
825
+ currency: valueCurrency, listingCurrency: data.currency, asOf: data.asOf, shown, reported: rows.length, total,
826
+ });
827
+ ctx.printResult({
828
+ data,
829
+ metadata: {
830
+ ...listingMetadata(identity),
831
+ summary: data.summary,
832
+ currency: data.currency ?? null,
833
+ valueCurrency: valueCurrency ?? null,
834
+ asOf: data.asOf ?? null,
835
+ shown,
836
+ reported: rows.length,
837
+ total,
838
+ truncated: shown < (total ?? rows.length),
839
+ valueBasis,
840
+ positionsBasis,
841
+ },
842
+ }, {
843
+ heading: `${listingHeading(identity)}${cliStyles.muted(facts.map((fact) => ` · ${fact}`).join(""))}`,
844
+ rows: () => rows,
503
845
  columns: [
504
846
  { key: "type", header: "Type" },
505
847
  { key: "name", header: "Holder" },
506
848
  { key: "reportDate", header: "Date" },
507
849
  { key: "shares", header: "Shares", align: "right", format: formatCountCell },
508
- { key: "value", header: "Value", align: "right", value: (row) => row.value == null ? "" : formatCompact(Number(row.value)) },
509
- { key: "percentHeld", header: "% Held", align: "right", format: formatFractionPercentCell },
850
+ // Marks the rows of a home line held as receipts; absent when no row is.
851
+ ...(markedRows ? [{ key: "shareBasis", header: "Basis", value: (row: Record<string, unknown>) => holderShareBasisMarker(row) }] : []),
852
+ {
853
+ key: "value",
854
+ header: moneyColumnHeader("Value", valueCurrency),
855
+ align: "right",
856
+ value: (row) => row.value == null ? "" : formatCompact(Number(row.value)),
857
+ },
858
+ {
859
+ key: "percentHeld",
860
+ header: "% Held",
861
+ align: "right",
862
+ format: (value) => isFiniteNumber(value) ? formatHolderOwnershipPercent(value) : "",
863
+ },
510
864
  ],
511
- ...(commandName === "insider" && ownerTypes
512
- ? { summary: (holderData: HolderData) => insiderSummary(holderData, ownerTypes) }
513
- : {}),
514
- empty: `No holders reported for ${symbol}.`,
865
+ summary: (holderData: HolderData) => [
866
+ [valueBasis ? `Value = ${valueBasis}.` : "", positionsBasis ?? "", shareBasisNote ?? ""].filter(Boolean).join(" "),
867
+ commandName === "insider" && ownerTypes ? insiderSummary(holderData, ownerTypes) : "",
868
+ ].filter(Boolean).join("\n"),
869
+ empty: `No holders reported for ${listingTitle(identity)}.`,
515
870
  });
516
871
  });
517
872
  }
518
873
 
519
874
  async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
520
875
  const args = [...rawArgs];
521
- const exchange = takeOption(args, "--exchange") ?? "";
522
- const symbol = requireArg(args[0]?.toUpperCase(), "Usage: gloomberb analyst <symbol>", ctx);
876
+ const exchangeOption = takeOption(args, "--exchange");
877
+ const raw = requireOneArg(args, "analyst <symbol>", "symbol", ctx);
523
878
  await withMarketData(ctx, async (market) => {
524
- const data = await market.dataProvider.getAnalystResearch(symbol, exchange);
879
+ const listing = await requireCliListing(raw, exchangeOption, market, ctx);
880
+ const [data, quote] = await Promise.all([
881
+ loadForListing(listing, market, ctx, () => market.dataProvider.getAnalystResearch(listing.request.symbol, listing.request.exchange)),
882
+ loadListingQuote(market.dataProvider, listing),
883
+ ]);
884
+ const identity = listingIdentity(listing, quote);
525
885
  ctx.printResult({
526
886
  data,
527
887
  metadata: {
528
- symbol,
888
+ ...listingMetadata(identity),
529
889
  recommendationRating: data.recommendationRating,
530
890
  priceTarget: data.priceTarget,
531
891
  recommendations: data.recommendations,
532
892
  },
893
+ freshness: rowsFreshness(analystRows(data), {
894
+ ...REPORTED_DATA, basis: "analyst ratings", observedKey: "date", oldest: null,
895
+ }, { stale: data.stale === true }),
533
896
  }, {
897
+ heading: listingHeading(identity),
534
898
  rows: analystRows,
535
899
  summary: analystSummary,
536
900
  columns: [
@@ -546,11 +910,23 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
546
910
 
547
911
  async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
548
912
  const args = [...rawArgs];
549
- const exchange = takeOption(args, "--exchange") ?? "";
550
- const symbol = requireArg(args[0]?.toUpperCase(), "Usage: gloomberb events <symbol>", ctx);
913
+ const exchangeOption = takeOption(args, "--exchange");
914
+ const raw = requireOneArg(args, "events <symbol>", "symbol", ctx);
551
915
  await withMarketData(ctx, async (market) => {
552
- const data = await market.dataProvider.getCorporateActions(symbol, exchange);
553
- ctx.printResult({ data, metadata: { symbol } }, {
916
+ const listing = await requireCliListing(raw, exchangeOption, market, ctx);
917
+ const [data, quote] = await Promise.all([
918
+ loadForListing(listing, market, ctx, () => market.dataProvider.getCorporateActions(listing.request.symbol, listing.request.exchange)),
919
+ loadListingQuote(market.dataProvider, listing),
920
+ ]);
921
+ const identity = listingIdentity(listing, quote);
922
+ ctx.printResult({
923
+ data,
924
+ metadata: listingMetadata(identity),
925
+ freshness: rowsFreshness(corporateActionRows(data), {
926
+ ...REPORTED_DATA, basis: "corporate actions", observedKey: "date", oldest: null,
927
+ }),
928
+ }, {
929
+ heading: listingHeading(identity),
554
930
  rows: corporateActionRows,
555
931
  columns: [
556
932
  { key: "date", header: "Date" },
@@ -564,22 +940,38 @@ async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execu
564
940
  async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
565
941
  const args = [...rawArgs];
566
942
  const expiration = takeOption(args, "--expiration");
567
- const exchange = takeOption(args, "--exchange") ?? "";
568
- const symbol = requireArg(args[0]?.toUpperCase(), "Usage: gloomberb options <symbol> [--expiration <unix>]", ctx);
943
+ const exchangeOption = takeOption(args, "--exchange");
944
+ const raw = requireOneArg(args, "options <symbol> [--expiration <unix>]", "symbol", ctx);
569
945
  await withMarketData(ctx, async (market) => {
946
+ const listing = await requireCliListing(raw, exchangeOption, market, ctx);
947
+ const { symbol, exchange } = listing.request;
570
948
  const expirationDate = expiration == null ? undefined : Number(expiration);
571
- const result = await market.dataProvider.getCachedQuery?.("getOptionsChain", [symbol, exchange, expirationDate, undefined])
572
- .load({ force: ctx.cliOptions.refresh });
573
- const chain = result?.value ?? await market.dataProvider.getOptionsChain(symbol, exchange, expirationDate, {
574
- cacheMode: ctx.cliOptions.refresh ? "refresh" : "default",
949
+ const quotePromise = loadListingQuote(market.dataProvider, listing);
950
+ const { result, chain } = await loadForListing(listing, market, ctx, async () => {
951
+ const cached = await market.dataProvider.getCachedQuery?.("getOptionsChain", [symbol, exchange, expirationDate, undefined])
952
+ .load({ force: ctx.cliOptions.refresh });
953
+ return {
954
+ result: cached,
955
+ chain: cached?.value ?? await market.dataProvider.getOptionsChain(symbol, exchange, expirationDate, {
956
+ cacheMode: ctx.cliOptions.refresh ? "refresh" : "default",
957
+ }),
958
+ };
575
959
  });
960
+ const identity = listingIdentity(listing, await quotePromise);
576
961
  // A failed refresh falls back to the stored chain, which can be days old.
577
962
  const refreshWarning = result?.refreshError == null ? null
578
963
  : `Options refresh failed; showing the chain stored ${new Date(result.fetchedAt).toISOString()}`
579
964
  + (chain.asOf ? ` (last trade ${chain.asOf})` : "");
580
- const sessionWarning = refreshWarning ? null : priorSessionChainWarning(chain, exchange, Date.now());
965
+ const sessionWarning = refreshWarning ? null : priorSessionChainWarning(chain, identity.exchange, Date.now());
581
966
  const warnings = refreshWarning ? [refreshWarning] : sessionWarning ? [sessionWarning] : undefined;
582
- ctx.printResult({ data: chain, metadata: { symbol, expirations: chain.expirationDates }, warnings }, {
967
+ // Dated by the chain's last trade; a chain that failed to refresh is the stored one, stale.
968
+ const freshness = rowsFreshness([], { asOf: chain.asOf ?? null }, {
969
+ ...(chain.dataSource ? { dataSource: chain.dataSource } : {}),
970
+ ...(chain.delayMinutes != null ? { delayMinutes: chain.delayMinutes } : {}),
971
+ stale: refreshWarning != null,
972
+ });
973
+ ctx.printResult({ data: chain, metadata: { ...listingMetadata(identity), expirations: chain.expirationDates }, warnings, freshness }, {
974
+ heading: listingHeading(identity),
583
975
  rows: optionRows,
584
976
  columns: [
585
977
  { key: "side", header: "Side" },
@@ -596,8 +988,10 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
596
988
  });
597
989
  }
598
990
 
991
+ const FX_USAGE = "fx <currency>";
992
+
599
993
  async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
600
- const currency = requireArg(rawArgs[0]?.trim().toUpperCase(), "Usage: gloomberb fx <currency>", ctx);
994
+ const currency = requireOneArg(rawArgs.map((arg) => arg.trim().toUpperCase()), FX_USAGE, "currency", ctx);
601
995
  await withMarketData(ctx, async (market) => {
602
996
  const baseCurrency = market.config.baseCurrency.trim().toUpperCase();
603
997
  const load = (code: string) => market.dataProvider.getCachedQuery("getExchangeRate", [code])
@@ -619,9 +1013,10 @@ async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]
619
1013
  ...(asOf || stale ? [{
620
1014
  key: "asOf",
621
1015
  header: "As Of",
622
- format: (value: unknown, row: { stale: boolean }) => (
623
- row.stale ? cliStyles.warning(`${value ?? ""} stale`.trim()) : String(value ?? "")
624
- ),
1016
+ format: (value: unknown, row: { stale: boolean }) => {
1017
+ const time = typeof value === "string" ? formatUtcTime(value) : "";
1018
+ return row.stale ? cliStyles.warning(`${time} stale`.trim()) : time;
1019
+ },
625
1020
  }] : []),
626
1021
  ],
627
1022
  });
@@ -629,11 +1024,23 @@ async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]
629
1024
  }
630
1025
 
631
1026
  async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
632
- const symbols = normalizeSymbols([...rawArgs]);
1027
+ const args = [...rawArgs];
1028
+ const exchangeOption = takeOption(args, "--exchange");
1029
+ const symbols = normalizeSymbols(args);
633
1030
  if (symbols.length === 0) ctx.fail("Usage: gloomberb earnings <symbol...>");
634
1031
  await withCliServices(ctx, async (services) => {
635
- const events = await services.dataProvider.getEarningsCalendar(symbols);
636
- ctx.printResult({ data: events }, {
1032
+ const listings = await Promise.all(symbols.map((symbol) => requireCliListing(
1033
+ symbol, exchangeOption, services, ctx, { ownExchangeWins: symbols.length > 1 },
1034
+ )));
1035
+ const events = await loadForListing(
1036
+ listings, services, ctx,
1037
+ () => services.dataProvider.getEarningsCalendar(listings.map((listing) => listing.key)),
1038
+ (found) => found.length === 0,
1039
+ );
1040
+ ctx.printResult({
1041
+ data: events,
1042
+ freshness: rowsFreshness(earningsRows(events), { status: "not-a-feed", basis: "calendar", observedKey: "date", oldest: null }),
1043
+ }, {
637
1044
  rows: earningsRows,
638
1045
  columns: [
639
1046
  { key: "date", header: "Date" },
@@ -647,7 +1054,7 @@ async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["exe
647
1054
  });
648
1055
  }
649
1056
 
650
- export const marketDataCliCommands: CliCommandDef[] = [
1057
+ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
651
1058
  {
652
1059
  name: "quote",
653
1060
  description: "Show the latest price for one or more symbols",
@@ -655,7 +1062,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
655
1062
  group: CLI_COMMAND_GROUPS.research,
656
1063
  usage: ["quote <symbol...>"],
657
1064
  options: [EXCHANGE_OPTION],
658
- examples: ["quote AAPL MSFT NVDA", "quote BTC-USD EURUSD=X", "quote AAPL --json"],
1065
+ examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote AAPL --json"],
659
1066
  },
660
1067
  execute: (args, ctx) => runQuote(args, ctx, "quote"),
661
1068
  },
@@ -666,7 +1073,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
666
1073
  group: CLI_COMMAND_GROUPS.research,
667
1074
  usage: ["compare <symbol...>"],
668
1075
  options: [EXCHANGE_OPTION],
669
- examples: ["compare KO PEP", "compare SPY QQQ IWM --csv"],
1076
+ examples: ["compare KO PEP", "compare SAN:EPA SAN:NYSE", "compare SPY QQQ IWM --csv"],
670
1077
  },
671
1078
  execute: (args, ctx) => runQuote(args, ctx, "compare"),
672
1079
  },
@@ -675,13 +1082,23 @@ export const marketDataCliCommands: CliCommandDef[] = [
675
1082
  description: "Fetch open, high, low, close, and volume over a range",
676
1083
  help: {
677
1084
  group: CLI_COMMAND_GROUPS.research,
678
- usage: ["history <symbol> [--range <range>]"],
1085
+ usage: [HISTORY_USAGE],
679
1086
  options: [
680
- { flags: "--range <range>", description: `${TIME_RANGES.join(", ")} (default 1Y)` },
1087
+ { flags: "--range <range>", description: `${TIME_RANGES.join(", ")} (default 1Y); ${HISTORY_ALL_RANGE}` },
681
1088
  EXCHANGE_OPTION,
682
1089
  ],
683
- examples: ["history AAPL", "history AAPL --range 5Y --csv > aapl.csv"],
1090
+ sections: [{
1091
+ title: "Bars",
1092
+ lines: [
1093
+ `Each range comes in one bar size: ${historyIntervalsByRange()}. The line under the heading, and the --json metadata, give the currency, the bar size actually served, the first and last bar and the bar count. A warning says when the bars are coarser, or start later, than the range asked for.`,
1094
+ "A bar whose prices contradict each other (high below the open or close, or low above them) is left blank and flagged, such as high<open, with the count in a warning. Every exported row (--csv, --ndjson, --json) carries currency, interval and flag.",
1095
+ ],
1096
+ }],
1097
+ examples: ["history AAPL", "history BHP:ASX --range 5Y", "history ZAR=X --range ALL", "history AAPL --range 5Y --csv > aapl.csv"],
684
1098
  },
1099
+ unknownOptionHint: (flag) => DATE_WINDOW_OPTIONS.has(flag)
1100
+ ? `history takes a --range instead of dates: ${TIME_RANGES.join(", ")}. ${HISTORY_ALL_RANGE}.`
1101
+ : null,
685
1102
  execute: runHistory,
686
1103
  },
687
1104
  {
@@ -694,7 +1111,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
694
1111
  { flags: "--expiration <unix>", description: "Expiration as Unix seconds; defaults to the nearest one" },
695
1112
  EXCHANGE_OPTION,
696
1113
  ],
697
- examples: ["options AAPL", "options AAPL --json"],
1114
+ examples: ["options AAPL", "options AAPL:NASDAQ --json"],
698
1115
  },
699
1116
  execute: runOptions,
700
1117
  },
@@ -731,8 +1148,8 @@ export const marketDataCliCommands: CliCommandDef[] = [
731
1148
  help: {
732
1149
  group: CLI_COMMAND_GROUPS.companyData,
733
1150
  usage: ["financials <symbol>"],
734
- options: [EXCHANGE_OPTION],
735
- examples: ["financials MSFT", "financials MSFT --json"],
1151
+ options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
1152
+ examples: ["financials MSFT", "financials SAN:EPA", "financials MSFT --json", "financials MSFT --csv > msft.csv"],
736
1153
  },
737
1154
  execute: (args, ctx) => runFinancials(args, ctx, "statements"),
738
1155
  },
@@ -742,8 +1159,8 @@ export const marketDataCliCommands: CliCommandDef[] = [
742
1159
  help: {
743
1160
  group: CLI_COMMAND_GROUPS.companyData,
744
1161
  usage: ["fundamentals <symbol>"],
745
- options: [EXCHANGE_OPTION],
746
- examples: ["fundamentals NVDA"],
1162
+ options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
1163
+ examples: ["fundamentals NVDA", "fundamentals ASML:AMS", "fundamentals NVDA --csv --section fundamentals"],
747
1164
  },
748
1165
  execute: (args, ctx) => runFinancials(args, ctx, "fundamentals"),
749
1166
  },
@@ -753,8 +1170,8 @@ export const marketDataCliCommands: CliCommandDef[] = [
753
1170
  help: {
754
1171
  group: CLI_COMMAND_GROUPS.companyData,
755
1172
  usage: ["valuation <symbol>"],
756
- options: [EXCHANGE_OPTION],
757
- examples: ["valuation NVDA"],
1173
+ options: [EXCHANGE_OPTION, TABLE_SECTION_OPTION],
1174
+ examples: ["valuation NVDA", "valuation BP:LSE", "valuation NVDA --csv"],
758
1175
  },
759
1176
  execute: (args, ctx) => runFinancials(args, ctx, "valuation"),
760
1177
  },
@@ -764,7 +1181,8 @@ export const marketDataCliCommands: CliCommandDef[] = [
764
1181
  help: {
765
1182
  group: CLI_COMMAND_GROUPS.companyData,
766
1183
  usage: ["earnings <symbol...>"],
767
- examples: ["earnings AAPL MSFT GOOGL"],
1184
+ options: [EXCHANGE_OPTION],
1185
+ examples: ["earnings AAPL MSFT GOOGL", "earnings SAN:EPA"],
768
1186
  },
769
1187
  execute: runEarnings,
770
1188
  },
@@ -775,7 +1193,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
775
1193
  group: CLI_COMMAND_GROUPS.companyData,
776
1194
  usage: ["events <symbol>"],
777
1195
  options: [EXCHANGE_OPTION],
778
- examples: ["events KO"],
1196
+ examples: ["events KO", "events BHP:ASX"],
779
1197
  },
780
1198
  execute: runEvents,
781
1199
  },
@@ -786,18 +1204,22 @@ export const marketDataCliCommands: CliCommandDef[] = [
786
1204
  group: CLI_COMMAND_GROUPS.companyData,
787
1205
  usage: ["analyst <symbol>"],
788
1206
  options: [EXCHANGE_OPTION],
789
- examples: ["analyst TSLA"],
1207
+ examples: ["analyst TSLA", "analyst SAN:EPA"],
790
1208
  },
791
1209
  execute: runAnalyst,
792
1210
  },
793
1211
  {
794
1212
  name: "holders",
795
- description: "Fetch institutional, fund, and insider holders",
1213
+ description: "Fetch institutional holders, or the 13D/13G beneficial owners",
796
1214
  help: {
797
1215
  group: CLI_COMMAND_GROUPS.companyData,
798
- usage: ["holders <symbol>"],
799
- options: [EXCHANGE_OPTION],
800
- examples: ["holders AAPL"],
1216
+ usage: ["holders <symbol> [--form 13f|13d|13g|all] [--history]"],
1217
+ options: [
1218
+ { flags: "--form <form>", description: "13f for the holder table (default); 13d, 13g or all for beneficial owners over 5%" },
1219
+ { flags: "--history", description: "With --form 13d, 13g or all, every report newest first instead of the latest per filer" },
1220
+ EXCHANGE_OPTION,
1221
+ ],
1222
+ examples: ["holders AAPL", "holders SAN:EPA", "holders CAR --form all", "holders CAR --form 13g --history --json"],
801
1223
  },
802
1224
  execute: (args, ctx) => runHolders(args, ctx, "holders"),
803
1225
  },
@@ -808,7 +1230,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
808
1230
  group: CLI_COMMAND_GROUPS.companyData,
809
1231
  usage: ["insider <symbol>"],
810
1232
  options: [EXCHANGE_OPTION],
811
- examples: ["insider NVDA"],
1233
+ examples: ["insider NVDA", "insider BP:LSE"],
812
1234
  },
813
1235
  execute: (args, ctx) => runHolders(args, ctx, "insider", new Set(["insider", "direct"])),
814
1236
  },
@@ -823,7 +1245,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
823
1245
  title: "13F filings",
824
1246
  lines: ["For each fund's reported position and its change over the quarter, run gloomberb fn 13F <symbol>."],
825
1247
  }],
826
- examples: ["13f AAPL"],
1248
+ examples: ["13f AAPL", "13f BP:NYSE"],
827
1249
  },
828
1250
  execute: (args, ctx) => runHolders(args, ctx, "13f", new Set(["institution", "fund"])),
829
1251
  },
@@ -832,12 +1254,13 @@ export const marketDataCliCommands: CliCommandDef[] = [
832
1254
  description: "Fetch recent SEC filings",
833
1255
  help: {
834
1256
  group: CLI_COMMAND_GROUPS.companyData,
835
- usage: ["filings <symbol> [--count <n>]"],
1257
+ usage: ["filings <symbol> [--count <n>] [--form <form>]"],
836
1258
  options: [
837
1259
  { flags: "--count <n>", description: "Number of filings (default 15)" },
1260
+ { flags: "--form <form>", description: "Only this form and its amendments, such as 10-K or 13D" },
838
1261
  EXCHANGE_OPTION,
839
1262
  ],
840
- examples: ["filings AAPL", "filings AAPL --count 40 --json"],
1263
+ examples: ["filings AAPL", "filings BHP:ASX", "filings AAPL --count 40 --json", "filings CAR --form 13G"],
841
1264
  },
842
1265
  execute: runFilings,
843
1266
  },
@@ -849,8 +1272,9 @@ export const marketDataCliCommands: CliCommandDef[] = [
849
1272
  usage: ["news [symbol] [--feed <feed>]"],
850
1273
  options: [
851
1274
  { flags: "--feed <feed>", description: "latest, top, or breaking for market news (default latest)" },
1275
+ EXCHANGE_OPTION,
852
1276
  ],
853
- examples: ["news", "news TSLA", "news --feed top --limit 10"],
1277
+ examples: ["news", "news TSLA", "news SAN:EPA", "news --feed top --limit 10"],
854
1278
  },
855
1279
  execute: runNews,
856
1280
  },
@@ -859,7 +1283,7 @@ export const marketDataCliCommands: CliCommandDef[] = [
859
1283
  description: "Convert a currency into your base currency",
860
1284
  help: {
861
1285
  group: CLI_COMMAND_GROUPS.markets,
862
- usage: ["fx <currency>"],
1286
+ usage: [FX_USAGE],
863
1287
  examples: ["fx EUR", "fx JPY --json"],
864
1288
  },
865
1289
  execute: runFx,