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
@@ -27,27 +27,46 @@ import { SupplyFlow } from "./flow";
27
27
  import { SupplyGraphPane } from "./graph-pane";
28
28
  import {
29
29
  cellText, counterpartyKind, counterpartyKindLabel, counterpartyLabel, counterpartyName, disclosedValue, nativeValue, percentage, ROLE_COLORS, roleLabel,
30
- shareParts, sortRows, type SupplySort,
30
+ shareParts, sortRows, supplyRoleCounts, type SupplySort,
31
31
  } from "./model";
32
32
  import { RowEvidence } from "./row-evidence";
33
33
  import { evidenceDate, evidenceLabel, isUnconfirmed, matchesSupplyOptions, supplyOptions, TIER_OPTIONS } from "./trust";
34
34
 
35
35
  type Item = { kind: "edge"; row: SupplyRow } | { kind: "locked"; id: string };
36
36
  const itemId = (item: Item) => item.kind === "edge" ? item.row.id : item.id;
37
- const ROLE_ORDER: SupplyRole[] = ["customer", "supplier", "partner", "competitor", "investee"];
38
37
  const ROLE_PLURALS: Record<SupplyRole, string> = { customer: "Customers", supplier: "Suppliers", partner: "Partners", competitor: "Competitors", investee: "Investees" };
39
- const BAR_CELLS = 8;
38
+ /** A share figure is at most five cells (`99.9%`, `100%`), right-aligned so the bars line up. */
39
+ const SHARE_FIGURE_CELLS = 5;
40
+ /** Bar, gap and figure: below this a share cell shows the figure alone. */
41
+ const SHARE_BAR_MIN_CELLS = 3 + 1 + SHARE_FIGURE_CELLS;
40
42
 
41
43
  /**
42
- * A narrow pane folds the ticker into the name and leaves the filing to the
43
- * evidence view before anything scrolls sideways. Value shows only when some
44
- * row discloses a value, retaining its original currency and units.
44
+ * Cells of a share column's bar: as long as the column allows after the figure
45
+ * and the longest basis that fits, between 3 and 8 cells, and the same on every
46
+ * row so the bars and figures line up. Zero leaves the figure alone.
45
47
  */
46
- function tableColumns(width: number, desktop: boolean, locked: boolean, valueWidth: number | null): DataTableColumn[] {
48
+ function shareBarCells(width: number, bases: readonly number[]): number {
49
+ if (width < SHARE_BAR_MIN_CELLS) return 0;
50
+ // Room for the longest basis that can show at all; a longer one gives way, not every bar.
51
+ const basis = longestBasis(bases, width);
52
+ return Math.min(width >= 36 ? 8 : 5, Math.max(3, width - SHARE_FIGURE_CELLS - 2 - basis));
53
+ }
54
+ const longestBasis = (bases: readonly number[], width: number) => Math.max(0, ...bases.filter((cells) => SHARE_BAR_MIN_CELLS + 1 + cells <= width));
55
+
56
+ /**
57
+ * Columns that fit the pane, giving up the least first: the share bar shortens
58
+ * to keep the whole basis, the name shortens to 18 cells, the ticker folds into
59
+ * the name and the period is left to the evidence view; then the share drops
60
+ * its basis and its bar, and the name shortens further. Only a pane narrower
61
+ * than that scrolls sideways, so the pane edge never cuts a share in half.
62
+ * Value shows only when some row discloses a value, retaining its original
63
+ * currency and units.
64
+ */
65
+ function tableColumns(width: number, desktop: boolean, locked: boolean, valueWidth: number | null, bases: readonly number[]): DataTableColumn[] {
47
66
  const wide = width >= 150, medium = width >= 110;
48
67
  const name: DataTableColumn = { id: "name", label: "Counterparty", width: Math.max(locked && !desktop ? 34 : 0, wide ? 30 : medium ? 26 : 20), flexGrow: 1, align: "left" };
49
68
  const share: DataTableColumn = { id: "pct", label: "Share", width: wide ? 34 : medium ? 30 : 26, align: "left" };
50
- const columns: DataTableColumn[] = [
69
+ let columns: DataTableColumn[] = [
51
70
  name,
52
71
  { id: "evidence", label: "Evidence", width: 12, align: "left" },
53
72
  ...(medium ? [{ id: "ticker", label: "Ticker", width: 8, align: "left" as const }] : []),
@@ -61,40 +80,57 @@ function tableColumns(width: number, desktop: boolean, locked: boolean, valueWid
61
80
  { id: "corroboration", label: "Origins", width: 8, align: "right" as const },
62
81
  ] : []),
63
82
  ];
64
- // Preserve trust labels, native units and final columns before expanding the share and name.
83
+ const preferred = { share: share.width, name: name.width };
65
84
  // The table ignores its last trailing gutter when deciding whether it fits.
66
- if (valueWidth !== null || width >= 80) {
67
- share.width = Math.max(valueWidth !== null ? 10 : 14, share.width - Math.max(0, getTableWidth(columns) - width - 1));
68
- name.width = Math.max(18, name.width - Math.max(0, getTableWidth(columns) - width - 1));
85
+ const overflow = () => getTableWidth(columns) - width - 1;
86
+ // Native units keep their column before the share keeps its bar or basis.
87
+ const shareFloor = valueWidth !== null ? SHARE_BAR_MIN_CELLS + 1 : SHARE_BAR_MIN_CELLS + 1 + longestBasis(bases, preferred.share);
88
+ const fit = (shareMin: number, nameMin = 18) => {
89
+ share.width = preferred.share; name.width = preferred.name;
90
+ share.width = Math.max(shareMin, share.width - Math.max(0, overflow()));
91
+ name.width = Math.max(nameMin, name.width - Math.max(0, overflow()));
92
+ };
93
+ fit(shareFloor);
94
+ for (const id of ["ticker", "fy"]) {
95
+ if (overflow() <= 0) break;
96
+ if (!columns.some((column) => column.id === id)) continue;
97
+ columns = columns.filter((column) => column.id !== id);
98
+ fit(shareFloor);
69
99
  }
100
+ // The narrowest panes keep the figure alone, then a shorter name, before anything scrolls.
101
+ if (overflow() > 0) fit(SHARE_BAR_MIN_CELLS);
102
+ if (overflow() > 0) fit(SHARE_FIGURE_CELLS + 2, 12);
70
103
  return columns;
71
104
  }
72
105
 
73
- /** Company, then disclosed counterparties per role: the strip that heads both tabs. */
74
- function supplyFigures(data: SupplyChainPayload, views: readonly ("says" | "names")[], options: Required<SupplyOptions>, flow: boolean): StatItem[] {
75
- const shown = views.flatMap((view) => data[view]).filter((row) => matchesSupplyOptions(row, options) && !isUnconfirmed(row));
106
+ /**
107
+ * Company, then the counterparties per role on this tab, with how many
108
+ * disclosures a free preview locks: the strip that heads the table and the flow.
109
+ */
110
+ function supplyFigures(data: SupplyChainPayload, views: readonly ("says" | "names")[], options: Required<SupplyOptions>): StatItem[] {
76
111
  return [
77
112
  ...(data.entity ? [{ id: "company", label: "Company", value: data.entity.name }] : []),
78
- ...ROLE_ORDER.flatMap((role) => {
79
- const visible = shown.filter((row) => row.role === role).length;
80
- const count = data.truncated && !flow && !options.includeLeads ? views.reduce((total, view) => total + data.counts[view][role], 0) : visible;
81
- return count ? [{ id: role, label: ROLE_PLURALS[role], value: String(count), color: ROLE_COLORS[role],
82
- detail: visible < count ? `${visible} shown` : undefined }] : [];
83
- }),
113
+ ...supplyRoleCounts(data, views, options).map(({ role, counterparties, locked }) => ({ id: role, label: ROLE_PLURALS[role], value: String(counterparties),
114
+ color: ROLE_COLORS[role], detail: locked ? `+${locked} locked` : undefined })),
84
115
  ];
85
116
  }
86
117
 
87
- /** The share as a bar on a 0 to 100% scale, the figure, and what it is a share of. */
88
- function ShareCell({ row, focusId, width, selected }: { row: SupplyRow; focusId?: string; width: number; selected: boolean }) {
118
+ /**
119
+ * The share as a bar on a 0 to 100% scale, the figure, and what it is a share
120
+ * of. The basis shows whole or not at all: a column too narrow for it keeps
121
+ * the bar and figure, and the evidence view says the rest.
122
+ */
123
+ function ShareCell({ row, focusId, width, bases, selected }: { row: SupplyRow; focusId?: string; width: number; bases: readonly number[]; selected: boolean }) {
89
124
  const colors = useThemeColors();
90
125
  const share = shareParts(row, focusId);
91
126
  if (!share) return <Text fg={colors.textMuted}>{""}</Text>;
92
- if (width < 14) return <Text fg={selected ? colors.selectedText : colors.textBright} attributes={TextAttributes.BOLD}>{share.value}</Text>;
93
- const barWidth = width >= 36 ? BAR_CELLS : Math.min(5, Math.max(3, width - 11));
127
+ const bar = shareBarCells(width, bases);
128
+ if (!bar) return <Text fg={selected ? colors.selectedText : colors.textBright} attributes={TextAttributes.BOLD}>{share.value}</Text>;
129
+ const basis = bar + SHARE_FIGURE_CELLS + 2 + displayWidth(share.basis) <= width;
94
130
  return <Box flexDirection="row" width={width} height={1} gap={1} overflow="hidden">
95
- <RatioBar ratio={row.pctOfRevenue! / 100} width={barWidth} color={ROLE_COLORS[row.role]} track />
96
- <Text fg={selected ? colors.selectedText : colors.textBright} attributes={TextAttributes.BOLD}>{share.value.padStart(5)}</Text>
97
- <Text fg={selected ? colors.selectedText : colors.textDim}>{truncateToDisplayWidth(share.basis, Math.max(4, width - barWidth - 7))}</Text>
131
+ <RatioBar ratio={row.pctOfRevenue! / 100} width={bar} color={ROLE_COLORS[row.role]} track />
132
+ <Text fg={selected ? colors.selectedText : colors.textBright} attributes={TextAttributes.BOLD}>{share.value.padStart(SHARE_FIGURE_CELLS)}</Text>
133
+ {basis ? <Text fg={selected ? colors.selectedText : colors.textDim}>{share.basis}</Text> : null}
98
134
  </Box>;
99
135
  }
100
136
 
@@ -180,7 +216,8 @@ function SupplyView({ symbol, width, height, focused }: Pick<PaneProps, "width"
180
216
  const focusId = data?.entity?.id;
181
217
  const values = rows.filter((row) => nativeValue(row) !== null || row.usd !== null);
182
218
  const valueWidth = values.length ? Math.min(28, Math.max(...values.map((row) => displayWidth(disclosedValue(row).replace(/ (disclosed|derived)$/, ""))))) : null;
183
- const columns = tableColumns(width, desktop, locked > 0, valueWidth);
219
+ const bases = rows.flatMap((row) => { const share = shareParts(row, focusId); return share ? [displayWidth(share.basis)] : []; });
220
+ const columns = tableColumns(width, desktop, locked > 0, valueWidth, bases);
184
221
  const renderCell = (entry: SectionedRow<Item>, column: DataTableColumn, _index: number, state: { selected: boolean }): DataTableCell => {
185
222
  if (!isSectionedItemRow(entry)) return { text: "" };
186
223
  const item = entry.item;
@@ -222,7 +259,7 @@ function SupplyView({ symbol, width, height, focused }: Pick<PaneProps, "width"
222
259
  <Text fg={ink ?? ROLE_COLORS[row.role]}>{` ${roleLabel(row.role)}`}</Text>
223
260
  </Box> };
224
261
  case "pct": return { text: percentage(row) === "--" ? "" : `${shareParts(row, focusId)?.value ?? ""} ${shareParts(row, focusId)?.basis ?? ""}`.trim(), value: row.pctOfRevenue,
225
- content: <ShareCell row={row} focusId={focusId} width={column.width} selected={state.selected} /> };
262
+ content: <ShareCell row={row} focusId={focusId} width={column.width} bases={bases} selected={state.selected} /> };
226
263
  case "usd": return nativeValue(row) === null && row.usd === null ? { text: "", value: null }
227
264
  : { text: disclosedValue(row).replace(/ (disclosed|derived)$/, ""), value: row.nativeAmount != null ? row.nativeAmount * (row.nativeScale ?? 1) : row.usd, color: colors.text };
228
265
  case "fy": return { text: row.period, color: colors.textDim };
@@ -233,7 +270,7 @@ function SupplyView({ symbol, width, height, focused }: Pick<PaneProps, "width"
233
270
  if (graphTab) return <Box width={width} height={height} flexDirection="column">{strip}<SupplyGraphPane symbol={symbol} tab={savedTab as "graph" | "path"} width={width} height={Math.max(3, height - tabRows)} focused={focused} /></Box>;
234
271
  if (!data && isCloudSessionRequired(resource.error)) return <SignInWall placement="supply-chain-signin" action="view supply chain disclosures" needsVerification={session.needsVerification} />;
235
272
  const bodyHeight = Math.max(3, height - tabRows);
236
- const figures = data ? supplyFigures(data, tab === "flow" ? ["says", "names"] : [view], options, tab === "flow") : [];
273
+ const figures = data ? supplyFigures(data, tab === "flow" ? ["says", "names"] : [view], options) : [];
237
274
  const tierFilter = { id: "tiers", label: "Evidence", kind: "multi" as const, emptyLabel: "Primary", values: options.tiers, options: TIER_OPTIONS, onChange: (value: string[]) => { setTiers(supplyOptions(value).tiers); setSelected(null); setOpen(null); } };
238
275
  const header = <Box flexDirection="column" flexShrink={0}>
239
276
  <QueryBar width={width} filters={[{ id: "direction", label: "Direction", inline: true, value: view,
@@ -263,7 +300,9 @@ function SupplyView({ symbol, width, height, focused }: Pick<PaneProps, "width"
263
300
  </Box> : <DataTableView<SectionedRow<Item>> columns={columns}
264
301
  items={items} focused={focused && !openRow} rootWidth={width} rootHeight={bodyHeight} rootBefore={header}
265
302
  selection={{ kind: "id", selectedId: selected?.id ?? null, getId: (entry) => entry.key, onChange: (id) => setSelected(id) }} getItemKey={(entry) => entry.key}
266
- isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
303
+ isNavigable={isSectionedItemRow}
304
+ // The placeholders stand for every locked disclosure; the header counts those, as the figures do.
305
+ renderSectionHeader={(entry) => entry.kind === "section" && entry.label === "Pro" ? { text: `Pro (${locked})` } : renderSectionedRowHeader(entry)}
267
306
  onActivate={(entry) => { if (isSectionedItemRow(entry)) entry.item.kind === "edge" ? navigate(entry.item.row) : openUpgrade(); }}
268
307
  renderCell={renderCell} sortColumnId={sort.column} sortDirection={sort.direction}
269
308
  onHeaderClick={(column) => setSort((old) => ({ column, direction: old.column === column && old.direction === "desc" ? "asc" : "desc" }))}
@@ -6,6 +6,9 @@ import { aggregateText, DEFAULT_SORT, matchTheme, memberPrice, percent, PERIOD_L
6
6
  const periods: HeadlessPaneColumn[] = THEME_PERIODS.map((key) => ({ key, header: PERIOD_LABELS[key], align: "right", format: (value) => percent(value as number | null) }));
7
7
  export const themesHeadless: HeadlessPaneDefinition<"rows"> = {
8
8
  shape: "rows",
9
+ // The pane's stored snapshot: 15-minute delayed prices, refreshed every 15 minutes. As in the pane,
10
+ // the payload's stale flags are historical; a snapshot over 30 minutes old is what reads stale.
11
+ freshness: { status: "delayed", delayMinutes: 15, maxAgeMinutes: 30, ignoreStaleFlags: true },
9
12
  argument: { kind: "free-text", optional: true, placeholder: "theme", description: "Theme name or keyword, such as nuclear; omit for all themes." },
10
13
  options: [],
11
14
  discovery: { aliases: ["THEM"], dataRequirements: ["Gloom Cloud stored equity snapshot"],
@@ -8,6 +8,7 @@ import { priceColor } from "../../../theme/colors";
8
8
  import { useThemeColors } from "../../../theme/theme-context";
9
9
  import { Box, Text } from "../../../ui";
10
10
  import { nextHeaderSort } from "../../../utils/sort-values";
11
+ import { useMembersMenu } from "../shared/members-menu";
11
12
  import { cachedMembers, cachedThemes, loadMembers, loadThemes } from "./client";
12
13
  import { aggregateText, coverageWidth, DEFAULT_SORT, matchTheme, memberColumns, memberPrice, percent, sortMembers, sortThemes, themeColumns, type ThemeSort } from "./model";
13
14
 
@@ -37,6 +38,8 @@ function Members({ id, width, height, focused }: { id: string; width: number; he
37
38
  useAutoRefresh(resource.updatedAt, resource.load);
38
39
  usePaneRefreshKey(() => void resource.reload(), { focused });
39
40
  useSnapshotFooter("themes:members", resource, focused);
41
+ const memberList = useMemo(() => data ? { title: data.theme.name, watchlistName: data.theme.name, members: rows } : null, [data, rows]);
42
+ useMembersMenu("themes:members-menu", memberList);
40
43
  const renderCell = useCallback((row: ThemeMember, column: DataTableColumn): DataTableCell => {
41
44
  if (column.id === "symbol") return { text: row.symbol, color: colors.textBright };
42
45
  if (column.id === "name") return { text: row.name ?? "--", color: colors.text };
@@ -7,7 +7,16 @@ import type {
7
7
  import { formatShortDate } from "../../../utils/datetime-format";
8
8
  import { normalizeCik } from "./api";
9
9
  import { buildFundOverlap, overlapPeriod } from "./overlap";
10
- import { appendTickerHoldings, loadCrowding, loadTickerHoldings, type TickerHoldings } from "./signals";
10
+ import { appendTickerHoldings, loadCrowding, loadTickerHoldings, type CrowdingRow, type TickerHoldings } from "./signals";
11
+ import {
12
+ AS_FILED_VALUE,
13
+ compareCrowding,
14
+ crowdingForTicker,
15
+ cutText,
16
+ periodText,
17
+ summarizeTickerHoldings,
18
+ THIRTEENF_MAX_LIMIT,
19
+ } from "./report-notes";
11
20
  import {
12
21
  loadBrowserRows,
13
22
  loadFundDetail,
@@ -44,6 +53,7 @@ import type {
44
53
  ThirteenFBrowserTab,
45
54
  } from "./types";
46
55
  import { finiteOrNull } from "../../../utils/guards";
56
+ import { SEC_FILINGS } from "../shared/report-freshness";
47
57
 
48
58
  const money = (value: unknown) => formatMoneyCompact(finiteOrNull(value));
49
59
  const weight = (value: unknown) => formatWeightMaybe(finiteOrNull(value));
@@ -65,7 +75,7 @@ const BROWSER_COLUMNS: HeadlessPaneColumn[] = [
65
75
  },
66
76
  {
67
77
  key: "tableValueTotal",
68
- header: "Value",
78
+ header: "Value (USD)",
69
79
  align: "right",
70
80
  format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
71
81
  },
@@ -83,13 +93,13 @@ const HOLDING_COLUMNS: HeadlessPaneColumn[] = [
83
93
  { key: "issuer", header: "Issuer" },
84
94
  {
85
95
  key: "value",
86
- header: "Value",
96
+ header: "Value (USD)",
87
97
  align: "right",
88
98
  format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
89
99
  },
90
100
  {
91
101
  key: "estimatedPnl",
92
- header: "Est P&L",
102
+ header: "Est P&L (USD)",
93
103
  align: "right",
94
104
  format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
95
105
  },
@@ -124,7 +134,7 @@ const FILING_COLUMNS: HeadlessPaneColumn[] = [
124
134
  },
125
135
  {
126
136
  key: "tableValueTotal",
127
- header: "Value",
137
+ header: "Value (USD)",
128
138
  align: "right",
129
139
  format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
130
140
  },
@@ -177,6 +187,30 @@ const defaultDependencies: ThirteenFHeadlessDependencies = {
177
187
  loadDetail: (cik, name, _args, ctx) => loadFundDetail(cik, name, ctx.signal),
178
188
  };
179
189
 
190
+ const VIEW_TITLES: Record<string, string> = {
191
+ overlap: "13F Overlap",
192
+ crowding: "13F Crowding",
193
+ "ticker-holdings": "13F Holders",
194
+ byTicker: "13F Holders",
195
+ "by-ticker": "13F Holders",
196
+ holdings: "13F Holdings",
197
+ filings: "13F Filings",
198
+ performance: "13F Performance",
199
+ funds: "13F Funds",
200
+ latest: "13F Latest Filings",
201
+ };
202
+
203
+ /** The view a query resolved to and what it is about, never the bare `auto` it was asked as. */
204
+ function thirteenFTitle(args: HeadlessPaneLoadArgs, metadata: Record<string, unknown> | undefined): string {
205
+ const query = typeof args.argument === "string" ? args.argument.trim() : "";
206
+ const view = String(metadata?.view ?? args.options.view);
207
+ const text = (value: unknown) => (typeof value === "string" && value ? value : "");
208
+ const subject = view === "overlap" && metadata?.firstFund && metadata.secondFund
209
+ ? `${text(metadata.firstFund)} vs ${text(metadata.secondFund)}`
210
+ : text(metadata?.ticker) || text(metadata?.fund) || (view === "latest" || view === "performance" ? "" : query);
211
+ return [VIEW_TITLES[view] ?? "13F Funds", subject].filter(Boolean).join(" | ");
212
+ }
213
+
180
214
  function browserTab(view: HeadlessThirteenFView, query: string): ThirteenFBrowserTab {
181
215
  if (view === "performance" || view === "funds" || view === "latest") return view;
182
216
  if (view === "by-ticker") return "byTicker";
@@ -210,6 +244,7 @@ export function createThirteenFHeadless(
210
244
  ): HeadlessPaneDefinition<"rows"> {
211
245
  return {
212
246
  shape: "rows",
247
+ freshness: { ...SEC_FILINGS, basis: "13F filings", observedKey: "filedAsOfDate" },
213
248
  argument: {
214
249
  kind: "free-text",
215
250
  placeholder: "fund, ticker, or CIK",
@@ -245,10 +280,10 @@ export function createThirteenFHeadless(
245
280
  type: "integer",
246
281
  defaultValue: 50,
247
282
  minimum: 1,
248
- maximum: 200,
283
+ maximum: THIRTEENF_MAX_LIMIT,
249
284
  },
250
285
  ],
251
- describe: (args) => `13F Funds | ${String(args.options.view)}`,
286
+ describe: (args, result) => thirteenFTitle(args, result?.metadata),
252
287
  async load(args, ctx) {
253
288
  const query = typeof args.argument === "string" ? args.argument.trim() : "";
254
289
  const requestedView = String(args.options.view) as HeadlessThirteenFView;
@@ -264,21 +299,23 @@ export function createThirteenFHeadless(
264
299
  const [first, second] = await Promise.all([dependencies.loadDetail(fund.cik, fund.name, args, ctx), dependencies.loadDetail(other.cik, other.name, args, ctx)]);
265
300
  const comparedPeriod = overlapPeriod(first, second);
266
301
  const rows = buildFundOverlap(first, second);
267
- return { columns: [{ key: "ticker", header: "Ticker" }, { key: "type", header: "Type" }, { key: "issuer", header: "Issuer" }, { key: "weight", header: "First weight", align: "right", format: weight }, { key: "comparedWeight", header: "Second weight", align: "right", format: weight }], rows: rows.slice(0, limit).map(row => ({ ...row })), errors: [...(first.warnings ?? []), ...(second.warnings ?? []), ...(!comparedPeriod ? ["No reporting quarter is loaded for both funds."] : [])], metadata: { firstFund: first.name, secondFund: second.name, firstPeriod: first.latestForm?.periodOfReport ?? null, secondPeriod: second.latestForm?.periodOfReport ?? null, comparedPeriod, truncated: rows.length > limit, notices: comparedPeriod ? [`Quarter compared: ${comparedPeriod}`] : [] } };
302
+ const cut = cutText(rows.length, limit, "positions");
303
+ return { columns: [{ key: "ticker", header: "Ticker" }, { key: "type", header: "Type" }, { key: "issuer", header: "Issuer" }, { key: "weight", header: "First weight", align: "right", format: weight }, { key: "comparedWeight", header: "Second weight", align: "right", format: weight }], rows: rows.slice(0, limit).map(row => ({ ...row })), errors: [...(first.warnings ?? []), ...(second.warnings ?? []), ...(!comparedPeriod ? ["No reporting quarter is loaded for both funds."] : [])], metadata: { view, firstFund: first.name, secondFund: second.name, firstPeriod: first.latestForm?.periodOfReport ?? null, secondPeriod: second.latestForm?.periodOfReport ?? null, comparedPeriod, shown: Math.min(rows.length, limit), total: rows.length, truncated: rows.length > limit, notices: [[comparedPeriod ? `Quarter compared: ${comparedPeriod}` : "", cut].filter(Boolean).join(" | ")].filter(Boolean) } };
268
304
  }
269
305
  if (view === "crowding") {
270
306
  const { rows: sourceRows, ...metadata } = await loadCrowding(ctx.signal);
271
307
  const rank = String(args.options.rank ?? "new");
272
- const rows = [...sourceRows].sort((left, right) => {
273
- if (rank === "new") return right.newCount - left.newCount;
274
- if (rank === "exits") return right.exitCount - left.exitCount;
275
- if (left.weightChange == null) return right.weightChange == null ? 0 : 1;
276
- if (right.weightChange == null) return -1;
277
- return (left.weightChange - right.weightChange) * (rank === "decreases" ? 1 : -1);
278
- });
279
- return { columns: [{ key: "ticker", header: "Ticker" }, { key: "issuer", header: "Issuer" }, { key: "type", header: "Type" }, { key: "holderCount", header: "Funds", align: "right" }, { key: "newCount", header: "New", align: "right" }, { key: "exitCount", header: "Exits", align: "right" }, { key: "weightChange", header: "Weight change", align: "right", format: weightPoints }, { key: "comparedFunds", header: "Compared", align: "right" }, { key: "totalValue", header: "Value", align: "right", format: money }], rows: rows.slice(0, limit).map(row => ({ ...row })),
280
- // The pane states the fund sample beside the table; the text report needs it too.
281
- metadata: { ...metadata, view, rank, truncated: rows.length > limit, notices: [`${metadata.period}: ${metadata.loadedFunds}/${metadata.sourceFunds} ranked funds`] }, errors: metadata.warnings };
308
+ const ranked = [...sourceRows].sort((left, right) => compareCrowding(rank, left, right));
309
+ // The pane states the fund sample beside the table; the text report needs it too.
310
+ const sample = `${metadata.period}: ${metadata.loadedFunds}/${metadata.sourceFunds} ranked funds`;
311
+ const ticker = query ? query.replace(/^\$/, "").toUpperCase() : null;
312
+ const focus = ticker ? crowdingForTicker(ranked, ticker, rank) : null;
313
+ const rows: Array<CrowdingRow & { rank?: number }> = focus ? focus.rows : ranked;
314
+ const columns: HeadlessPaneColumn[] = [{ key: "ticker", header: "Ticker" }, { key: "issuer", header: "Issuer" }, { key: "type", header: "Type" }, { key: "holderCount", header: "Funds", align: "right" }, { key: "newCount", header: "New", align: "right" }, { key: "exitCount", header: "Exits", align: "right" }, { key: "weightChange", header: "Weight change", align: "right", format: weightPoints }, { key: "comparedFunds", header: "Compared", align: "right" }, { key: "totalValue", header: "Value (USD)", align: "right", format: money }];
315
+ const cut = cutText(rows.length, limit, "securities");
316
+ return { columns: focus ? [{ key: "rank", header: "Rank", align: "right" }, ...columns] : columns, rows: rows.slice(0, limit).map(row => ({ ...row })),
317
+ metadata: { ...metadata, view, rank, ...(ticker ? { ticker, found: rows.length > 0, rankedSecurities: ranked.length } : {}), shown: Math.min(rows.length, limit), total: rows.length, truncated: rows.length > limit, notices: [[sample, cut].filter(Boolean).join(" | "), ...(focus ? [focus.notice] : [])] },
318
+ freshness: { asOf: metadata.period }, errors: metadata.warnings };
282
319
  }
283
320
  let holdings: TickerHoldings | null = null;
284
321
  if (view === "ticker-holdings") {
@@ -297,16 +334,42 @@ export function createThirteenFHeadless(
297
334
  }
298
335
  if (holdings) {
299
336
  const { rows, ...metadata } = holdings;
300
- return { columns: [{ key: "fund", header: "Fund" }, { key: "cik", header: "CIK" }, { key: "type", header: "Type" }, { key: "value", header: "Value", align: "right", format: money }, { key: "shares", header: "Shares", align: "right", format: shares }, { key: "weight", header: "13F weight", align: "right", format: weight }, { key: "action", header: "Action" }], rows: rows.slice(0, limit).map(row => ({ ...row })), metadata: { ...metadata, view, truncated: rows.length > limit || metadata.hasMore }, errors: metadata.warnings };
337
+ const offset = Number(args.options.offset ?? 0);
338
+ const summary = summarizeTickerHoldings({ ...metadata, offset, limit, loaded: rows });
339
+ return {
340
+ columns: [{ key: "fund", header: "Fund" }, { key: "cik", header: "CIK" }, { key: "type", header: "Type" }, { key: "value", header: "Value (USD)", align: "right", format: money }, { key: "shares", header: "Shares", align: "right", format: shares }, { key: "weight", header: "13F weight", align: "right", format: weight }, { key: "action", header: "Action" }],
341
+ rows: rows.slice(0, limit).map(row => ({ ...row })),
342
+ metadata: {
343
+ ...metadata,
344
+ view,
345
+ offset,
346
+ shown: Math.min(rows.length, limit),
347
+ shownFunds: summary.shownFunds,
348
+ total: summary.total,
349
+ truncated: summary.truncated,
350
+ hasMore: summary.truncated,
351
+ nextOffset: summary.nextOffset,
352
+ // The value of the rows shown, not of every fund a page loaded.
353
+ totalValue: summary.shownValue,
354
+ valueScope: "shown funds",
355
+ valueBasis: "as reported at period end",
356
+ notices: summary.notices,
357
+ },
358
+ freshness: { asOf: metadata.period },
359
+ errors: metadata.warnings,
360
+ };
301
361
  }
302
362
 
303
363
  if (view === "holdings" || view === "filings") {
304
364
  const fund = await resolveFund(query, args, ctx, dependencies);
305
365
  const detail = await dependencies.loadDetail(fund.cik, fund.name, args, ctx);
366
+ const latestPeriod = detail.latestForm?.periodOfReport ?? null;
306
367
  if (view === "filings") {
307
- const rows = sortTimelineRows(buildTimelineRows(detail.forms), DEFAULT_TIMELINE_SORT)
368
+ const all = sortTimelineRows(buildTimelineRows(detail.forms), DEFAULT_TIMELINE_SORT);
369
+ const rows = all
308
370
  .slice(0, limit)
309
371
  .map((row) => ({ ...row }));
372
+ const cut = cutText(all.length, limit, "filings");
310
373
  return {
311
374
  columns: FILING_COLUMNS,
312
375
  rows,
@@ -315,14 +378,21 @@ export function createThirteenFHeadless(
315
378
  view,
316
379
  cik: detail.cik,
317
380
  fund: detail.name,
318
- latestPeriod: detail.latestForm?.periodOfReport ?? null,
381
+ latestPeriod,
382
+ shown: rows.length,
383
+ total: all.length,
384
+ truncated: all.length > limit,
385
+ notices: [[`CIK ${detail.cik}`, cut].filter(Boolean).join(" | ")],
319
386
  },
320
387
  };
321
388
  }
322
- const rows = sortHoldingRows(
389
+ const all = sortHoldingRows(
323
390
  buildFundHoldingRows(detail),
324
391
  DEFAULT_HOLDING_SORT,
325
- )
392
+ );
393
+ const previousPeriod = detail.previousForm?.periodOfReport ?? null;
394
+ const cut = cutText(all.length, limit, "positions, exits included");
395
+ const rows = all
326
396
  .slice(0, limit)
327
397
  .map((row) => ({
328
398
  ...row,
@@ -333,12 +403,21 @@ export function createThirteenFHeadless(
333
403
  columns: HOLDING_COLUMNS,
334
404
  rows,
335
405
  ...(detail.warnings?.length ? { errors: detail.warnings } : {}),
406
+ // Positions as of the quarter they report; the rows carry no filing date.
407
+ ...(latestPeriod ? { freshness: { asOf: latestPeriod } } : {}),
336
408
  metadata: {
337
409
  view,
338
410
  cik: detail.cik,
339
411
  fund: detail.name,
340
- latestPeriod: detail.latestForm?.periodOfReport ?? null,
341
- previousPeriod: detail.previousForm?.periodOfReport ?? null,
412
+ latestPeriod,
413
+ previousPeriod,
414
+ shown: rows.length,
415
+ total: all.length,
416
+ truncated: all.length > limit,
417
+ notices: [
418
+ [`CIK ${detail.cik}`, periodText(latestPeriod, hasComparable13FQuarter(detail) ? previousPeriod : null), AS_FILED_VALUE].filter(Boolean).join(" | "),
419
+ ...(cut ? [`${cut[0]!.toUpperCase()}${cut.slice(1)}`] : []),
420
+ ],
342
421
  comparisonAvailable: hasComparable13FQuarter(detail),
343
422
  currentFilings: detail.latestReport?.filings.map((form) => form.accessionNumber) ?? [],
344
423
  previousFilings: detail.previousReport?.filings.map((form) => form.accessionNumber) ?? [],
@@ -352,6 +431,7 @@ export function createThirteenFHeadless(
352
431
 
353
432
  const tab = browserTab(view === "ticker-holdings" ? requestedView : view, query);
354
433
  const result = await dependencies.loadBrowser(tab, query, limit, args, ctx);
434
+ const more = result.hasMore || result.rows.length > limit;
355
435
  return {
356
436
  columns: result.rows[0]?.priorReturns?.length ? [...BROWSER_COLUMNS.filter(column => !["filedAsOfDate", "tableEntryTotal"].includes(column.key)), ...result.rows[0].priorReturns.map((point, index) => ({ key: `return${index + 1}`, header: point.quarter, align: "right" as const, format: (value: unknown) => formatRawPercentMaybe(typeof value === "number" ? value : null) }))]
357
437
  : tab === "performance" ? BROWSER_COLUMNS : BROWSER_COLUMNS.filter(column => column.key !== "estQuarterReturn"),
@@ -365,6 +445,11 @@ export function createThirteenFHeadless(
365
445
  period: result.period ?? null,
366
446
  quarter: result.quarter ?? null,
367
447
  hasMore: result.hasMore ?? false,
448
+ shown: Math.min(result.rows.length, limit),
449
+ truncated: more,
450
+ notices: more
451
+ ? [[`Showing the first ${Math.min(result.rows.length, limit)}`, limit < THIRTEENF_MAX_LIMIT ? `more: --limit ${THIRTEENF_MAX_LIMIT}` : ""].filter(Boolean).join(" | ")]
452
+ : [],
368
453
  },
369
454
  };
370
455
  },
@@ -0,0 +1,149 @@
1
+ import type { CrowdingRow, TickerHolderRow } from "./signals";
2
+ import { formatMoneyCompact } from "./format";
3
+
4
+ /**
5
+ * Summary lines for the 13F text reports. Each says which quarters the rows
6
+ * compare, that values are as filed, and, whenever rows are cut, how many
7
+ * are shown and the option that shows the rest.
8
+ */
9
+
10
+ /** The largest `--limit` the 13F report accepts. */
11
+ export const THIRTEENF_MAX_LIMIT = 200;
12
+
13
+ export const AS_FILED_VALUE = "Value (USD) as reported at period end, not today's price";
14
+
15
+ const count = (value: number) => value.toLocaleString("en-US");
16
+
17
+ export function periodText(period: string | null | undefined, previousPeriod?: string | null): string {
18
+ if (!period) return "";
19
+ return previousPeriod ? `Period ${period} vs ${previousPeriod}` : `Period ${period}`;
20
+ }
21
+
22
+ /** "showing 50 of 374" from the start of the list, "showing 51-100 of 374" past it. */
23
+ function shownRangeText(offset: number, end: number, total: number): string {
24
+ return offset > 0
25
+ ? `showing ${count(offset + 1)}-${count(end)} of ${count(total)}`
26
+ : `showing ${count(end)} of ${count(total)}`;
27
+ }
28
+
29
+ /** "showing 50 of 104 positions | more: --limit 104"; empty when nothing is cut. */
30
+ export function cutText(total: number, limit: number, noun: string): string {
31
+ if (total <= limit) return "";
32
+ return [
33
+ `showing ${count(limit)} of ${count(total)} ${noun}`,
34
+ limit < THIRTEENF_MAX_LIMIT ? `more: --limit ${Math.min(THIRTEENF_MAX_LIMIT, total)}` : "",
35
+ ].filter(Boolean).join(" | ");
36
+ }
37
+
38
+ export interface TickerHoldingsSummary {
39
+ notices: string[];
40
+ /** Funds in the list: holders, then the funds that exited. */
41
+ total: number;
42
+ /** The fund offset where the next page starts. */
43
+ nextOffset: number;
44
+ /** Funds with rows shown. */
45
+ shownFunds: number;
46
+ /** Rows follow the ones shown. */
47
+ truncated: boolean;
48
+ /** Reported value of the positions shown, exits excluded. */
49
+ shownValue: number | null;
50
+ }
51
+
52
+ /**
53
+ * The ticker-holdings list ranks a quarter's holders by size, then the funds
54
+ * that exited. A fund can have several rows (shares, calls, puts), so pages
55
+ * are counted in funds.
56
+ */
57
+ export function summarizeTickerHoldings(input: {
58
+ period: string;
59
+ previousPeriod?: string | null;
60
+ holderCount: number;
61
+ newCount?: number | null;
62
+ exitCount?: number | null;
63
+ offset: number;
64
+ limit: number;
65
+ /** Rows of the pages loaded from `offset`, in list order. */
66
+ loaded: readonly Pick<TickerHolderRow, "cik" | "value" | "action">[];
67
+ /** The source has pages after the loaded ones, from `nextOffset`. */
68
+ hasMore: boolean;
69
+ nextOffset: number;
70
+ }): TickerHoldingsSummary {
71
+ const shownRows = input.loaded.slice(0, input.limit);
72
+ const funds = [...new Set(shownRows.map((row) => row.cik))];
73
+ const cut = input.loaded.length > shownRows.length;
74
+ const lastCut = cut && input.loaded[shownRows.length]?.cik === shownRows.at(-1)?.cik;
75
+ // A fund whose holdings could not be read keeps its place in the source's
76
+ // list without rows, so a cut page restarts after the funds shown whole:
77
+ // that can repeat a few, never skip one.
78
+ const nextOffset = cut ? input.offset + Math.max(1, funds.length - (lastCut ? 1 : 0)) : input.nextOffset;
79
+ const truncated = cut || input.hasMore;
80
+ const partial = truncated || input.offset > 0;
81
+ const compared = !!input.previousPeriod;
82
+ const exits = compared ? input.exitCount ?? 0 : 0;
83
+ const listed = input.holderCount + exits;
84
+ // The source's next offset steps a whole page, past the end of the list on the last one.
85
+ const total = truncated ? Math.max(listed, nextOffset + 1) : Math.max(listed, input.offset + funds.length);
86
+ const end = !truncated ? total : cut ? input.offset + funds.length : nextOffset;
87
+ const shownValue = shownRows
88
+ .filter((row) => row.action !== "exit")
89
+ .reduce<number | null>((sum, row) => (sum != null && row.value != null ? sum + row.value : null), 0);
90
+ const holders = [
91
+ `${count(input.holderCount)} holders`,
92
+ compared && input.newCount != null ? ` (${count(input.newCount)} new)` : "",
93
+ compared && input.exitCount != null ? `, ${count(input.exitCount)} exited` : "",
94
+ !partial && shownValue ? `, total ${formatMoneyCompact(shownValue)}` : "",
95
+ ].join("");
96
+ const notices = [[periodText(input.period, input.previousPeriod), holders, AS_FILED_VALUE].filter(Boolean).join(" | ")];
97
+ if (partial) {
98
+ const shown = [
99
+ `${shownRangeText(input.offset, end, total)} funds`,
100
+ exits > 0 ? ", exits last" : "",
101
+ shownValue ? `; the ${count(funds.length)} shown hold ${formatMoneyCompact(shownValue)}` : "",
102
+ ].join("");
103
+ // The limit counts rows and a fund can have several, so the larger page is the safe suggestion.
104
+ const more = truncated
105
+ ? `more: --offset ${nextOffset}${input.limit < THIRTEENF_MAX_LIMIT ? ` or --limit ${THIRTEENF_MAX_LIMIT}` : ""}`
106
+ : null;
107
+ notices.push([`${shown[0]!.toUpperCase()}${shown.slice(1)}`, more].filter(Boolean).join(" | "));
108
+ }
109
+ return { notices, total, nextOffset, shownFunds: funds.length, truncated, shownValue };
110
+ }
111
+
112
+ type CrowdingRank = "new" | "exits" | "increases" | "decreases";
113
+
114
+ const RANK_LABELS: Record<CrowdingRank, string> = {
115
+ new: "new holders",
116
+ exits: "exits",
117
+ increases: "weight increase",
118
+ decreases: "weight decrease",
119
+ };
120
+
121
+ export function compareCrowding(rank: string, left: CrowdingRow, right: CrowdingRow): number {
122
+ if (rank === "new") return right.newCount - left.newCount;
123
+ if (rank === "exits") return right.exitCount - left.exitCount;
124
+ if (left.weightChange == null) return right.weightChange == null ? 0 : 1;
125
+ if (right.weightChange == null) return -1;
126
+ return (left.weightChange - right.weightChange) * (rank === "decreases" ? 1 : -1);
127
+ }
128
+
129
+ /**
130
+ * One ticker's place in the crowding ranking. Ties share a rank, so the
131
+ * number is how many securities rank strictly ahead, plus one.
132
+ */
133
+ export function crowdingForTicker(
134
+ ranked: readonly CrowdingRow[],
135
+ ticker: string,
136
+ rank: string,
137
+ ): { rows: Array<CrowdingRow & { rank: number }>; notice: string } {
138
+ const label = RANK_LABELS[rank as CrowdingRank] ?? rank;
139
+ const rows = ranked
140
+ .filter((row) => row.ticker.toUpperCase() === ticker)
141
+ .map((row) => ({ ...row, rank: 1 + ranked.filter((other) => compareCrowding(rank, other, row) < 0).length }));
142
+ const of = `of ${count(ranked.length)} securities by ${label}`;
143
+ const notice = rows.length === 0
144
+ ? `${ticker} is not in this ranking | full ranking: fn 13F --view crowding | its 13F holders: fn 13F ${ticker}`
145
+ : rows.length === 1
146
+ ? `${ticker} ranks ${count(rows[0]!.rank)} ${of} | full ranking: fn 13F --view crowding`
147
+ : `${ticker} ranks ${rows.map((row) => `${count(row.rank)} (${row.type})`).join(", ")} ${of} | full ranking: fn 13F --view crowding`;
148
+ return { rows, notice };
149
+ }
@@ -166,10 +166,14 @@ function aggregateQuarterlyStatements(
166
166
  }
167
167
  const currencies = new Set(statements.map((statement) => statement.currency));
168
168
  if (currencies.size > 1) return null;
169
+ // Its counts and EPS are on the quarters' share basis when all four state the same one.
170
+ const shareBases = new Set(statements.map((statement) => statement.shareBasis));
171
+ const shareBasis = shareBases.size === 1 ? [...shareBases][0] : undefined;
169
172
 
170
173
  const aggregate: FinancialTableStatement = {
171
174
  date,
172
175
  currency: [...currencies][0],
176
+ ...(shareBasis ? { shareBasis } : {}),
173
177
  aggregation: {
174
178
  kind: "trailing-four-quarters",
175
179
  periodEnd: statements.at(-1)!.date,