gloomberb 0.16.2 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -27,7 +27,7 @@ function renderCell(row: ShortVolumeObservation, column: VolumeColumn, _index: n
27
27
  function VolumeDetail({ row, width, height }: { row: ShortVolumeObservation; width: number; height: number }) {
28
28
  const { nativePaneChrome } = useUiCapabilities();
29
29
  // The stack title is the date, so the rows start with the figures.
30
- return <ScrollBox width={width} height={nativePaneChrome ? undefined : height} flexGrow={1} flexBasis={0} minHeight={0} contentOptions={{ paddingX: 1 }}>
30
+ return <ScrollBox scrollY width={width} height={nativePaneChrome ? undefined : height} flexGrow={1} flexBasis={0} minHeight={0} contentOptions={{ paddingX: 1 }}>
31
31
  <KeyValueRow labelWidth={DETAIL_LABEL_WIDTH} label="Off-exchange short ratio" value={volumePercent(row.ratioPercent)} />
32
32
  <KeyValueRow labelWidth={DETAIL_LABEL_WIDTH} label="Short shares" value={exactQuantity(row.shortVolume)} />
33
33
  <KeyValueRow labelWidth={DETAIL_LABEL_WIDTH} label="Exempt shares" value={exactQuantity(row.shortExemptVolume)} detail="included in short shares" />
@@ -59,7 +59,7 @@ function DayDetail({ symbol, row, recent, width, height }: {
59
59
  { id: "ratio", label: "Vs median", value: socialRatio(row.ratio) },
60
60
  { id: "stance", label: "Stance", value: socialStance(row.stance), detail: stanceWord(row.stance) },
61
61
  ]} />
62
- <ScrollBox width={width} height={nativePaneChrome ? undefined : Math.max(1, height - 3)} flexGrow={1} flexBasis={0} minHeight={0} contentOptions={{ paddingX: 1 }}>
62
+ <ScrollBox scrollY width={width} height={nativePaneChrome ? undefined : Math.max(1, height - 3)} flexGrow={1} flexBasis={0} minHeight={0} contentOptions={{ paddingX: 1 }}>
63
63
  <PaneStatusBody loading={posts.loading && !list.length} error={!list.length ? posts.error : null} subject="top posts"
64
64
  empty={!posts.loading && !list.length} emptyTitle="No top posts for this day.">
65
65
  <Box flexDirection="column" gap={1} width={lineWidth}>
@@ -1,5 +1,5 @@
1
1
  import type { SupplyChainPayload, SupplyOptions, SupplyRole, SupplyRow } from "../../../api-client/supply-chain";
2
- import { SERIES_COLORS } from "../../../time-series/resolve";
2
+ import { SERIES_COLORS } from "../../../theme/series-colors";
3
3
  import { revenueAmount } from "../revenue-breakdown/model";
4
4
  import { activeRelationship, corroborationLabel, evidenceDate, evidenceLabel, isUnconfirmed, matchesSupplyOptions, trustTier } from "./trust";
5
5
 
@@ -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 };
@@ -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,
@@ -107,7 +107,13 @@ const SHARE_COUNT_FIELDS = new Set<keyof FinancialStatement>([
107
107
  "basicShares", "dilutedShares", "shareIssued", "ordinarySharesNumber", "treasurySharesNumber",
108
108
  ]);
109
109
 
110
- /** Currency changes affect monetary comparisons, not counts of reported shares. */
110
+ const PER_SHARE_EARNINGS_FIELDS = new Set<keyof FinancialStatement>(["eps", "basicEps"]);
111
+
112
+ /**
113
+ * Currency changes affect monetary comparisons, not counts of reported
114
+ * shares. Counts and EPS compare only on one share basis: receipts against
115
+ * ordinary shares would read as a split.
116
+ */
111
117
  export function canCompareFinancialRow(
112
118
  row: FinancialTableRow,
113
119
  current: FinancialStatement,
@@ -116,7 +122,9 @@ export function canCompareFinancialRow(
116
122
  ): boolean {
117
123
  if (!previous) return false;
118
124
  const key = row.kind === "group" ? row.summaryKey : row.key;
119
- if (key && SHARE_COUNT_FIELDS.has(key)) return true;
125
+ const sameShareBasis = current.shareBasis === previous.shareBasis;
126
+ if (key && SHARE_COUNT_FIELDS.has(key)) return sameShareBasis;
127
+ if (key && PER_SHARE_EARNINGS_FIELDS.has(key) && !sameShareBasis) return false;
120
128
  const currentCurrency = (current.currency ?? financialCurrency)?.trim();
121
129
  const previousCurrency = (previous.currency ?? financialCurrency)?.trim();
122
130
  return !!currentCurrency && currentCurrency === previousCurrency;
@@ -269,6 +277,19 @@ export function financialStatementLimitations(financials: TickerFinancials | nul
269
277
  return limitations;
270
278
  }
271
279
 
280
+ /** "Ordinary Shares (bn ADRs)": a share-count line whose counts are depositary receipts. */
281
+ export function receiptShareCountLabel(row: Pick<FinancialTableModelRow, "key" | "label" | "unitLabel">): string {
282
+ if (!row.key || !SHARE_COUNT_FIELDS.has(row.key)) return row.unitLabel;
283
+ const unit = row.unitLabel.startsWith(`${row.label} (`) ? row.unitLabel.slice(row.label.length + 2, -1) : "";
284
+ return `${row.label} (${unit ? `${unit} ` : ""}ADRs)`;
285
+ }
286
+
287
+ /** Whether a table row shows share counts or per-share earnings. */
288
+ export function isPerShareFinancialRow(row: Pick<FinancialTableModelRow, "key" | "summaryKey">): boolean {
289
+ const key = row.key ?? row.summaryKey;
290
+ return !!key && (SHARE_COUNT_FIELDS.has(key) || PER_SHARE_EARNINGS_FIELDS.has(key));
291
+ }
292
+
272
293
  export function resolveFinancialPeriod(
273
294
  requestedPeriod: FinancialPeriod,
274
295
  hasAnnualStatements: boolean,
@@ -495,6 +516,7 @@ interface FinancialTableModelRow {
495
516
  id: string;
496
517
  key?: keyof FinancialStatement;
497
518
  summaryKey?: keyof FinancialStatement;
519
+ label: string;
498
520
  unitLabel: string;
499
521
  depth: number;
500
522
  growthDirection: FinancialGrowthDirection;
@@ -614,6 +636,7 @@ export function buildFinancialTableModel(
614
636
  id: row.id,
615
637
  key: row.kind === "metric" ? row.key : undefined,
616
638
  summaryKey: row.kind === "group" ? row.summaryKey : undefined,
639
+ label: row.label,
617
640
  unitLabel: row.unitLabel,
618
641
  depth: row.depth,
619
642
  growthDirection: row.growthDirection,
@@ -1,12 +1,16 @@
1
1
  import { FINANCIAL_VINTAGE_NOTICE } from "../../../utils/financial-statements";
2
2
  import { hasValidQuoteObservationTime } from "../../../market-data/quotes/freshness";
3
- import { getActiveQuoteDisplay } from "../../../market-data/market/status";
3
+ import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, type ExtendedSession } from "../../../market-data/market/status";
4
4
  import type { HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/headless";
5
5
  import type { TimeRange } from "../../../time-series/range";
6
6
  import { formatNumber, formatPercentRaw } from "../../../utils/format";
7
7
  import { formatMarketPrice, formatMarketPriceWithCurrency, formatPriceObservation, withCurrencyMinorDigits, type MarketFormatOptions } from "../../../market-data/market/format";
8
8
  import { pricePointValues, priceHistoryIntegrityNotice } from "../../../utils/price-history-integrity";
9
- import { buildFinancialTableModel, financialStatementCurrency, financialStatementDateNotice, financialStatementLimitations, formatFinancialHeader } from "./financials/model";
9
+ import {
10
+ buildFinancialTableModel, financialStatementCurrency, financialStatementDateNotice, financialStatementLimitations,
11
+ formatFinancialHeader, isPerShareFinancialRow, receiptShareCountLabel,
12
+ } from "./financials/model";
13
+ import { adrRatioText } from "../../../utils/depositary-receipt";
10
14
  import { paneSchemas } from "./headless-schema";
11
15
  import { loadPeriodEndHistory } from "./financials/period-end-history";
12
16
  import {
@@ -52,8 +56,18 @@ export const financialStatementsHeadless: HeadlessPaneDefinition<"rows"> = {
52
56
  const statementCurrency = financialStatementCurrency(financials, [
53
57
  ...financials.annualStatements, ...financials.quarterlyStatements,
54
58
  ]);
55
- const dates = table?.statements.map(({ date, currency, dateSource, providerDate, dateEvidence, availableAt, fieldAvailability, fieldSources, unavailableFields, epsBasis, unavailableEarnings, aggregation }) => ({
59
+ // Gloom Cloud states when a period's share counts and EPS are on the receipts.
60
+ const receiptPeriods = table?.statements.filter(({ shareBasis }) => shareBasis === "depositary_receipt").map(({ date }) => date) ?? [];
61
+ const allReceipts = receiptPeriods.length > 0 && receiptPeriods.length === table?.statements.length;
62
+ const shareBases = new Set(table?.statements.map(({ shareBasis }) => shareBasis ?? null));
63
+ const ratio = adrRatioText(financials.fundamentals?.adrRatio ?? financials.quote?.adrRatio);
64
+ const shareBasisNotice = receiptPeriods.length === 0 || !table?.rows.some(isPerShareFinancialRow) ? null
65
+ : allReceipts
66
+ ? `Share counts are in ADRs and EPS is per ADR${ratio ? `; ${ratio}` : ""}.`
67
+ : `Share counts are in ADRs and EPS is per ADR for ${receiptPeriods.join(", ")}${ratio ? ` (${ratio})` : ""}; other periods as reported.`;
68
+ const dates = table?.statements.map(({ date, currency, dateSource, providerDate, dateEvidence, availableAt, fieldAvailability, fieldSources, unavailableFields, epsBasis, unavailableEarnings, aggregation, shareBasis }) => ({
56
69
  date, currency: currency ?? statementCurrency ?? null,
70
+ shareBasis: shareBasis ?? null,
57
71
  availableAt: availableAt ?? null,
58
72
  fieldAvailability: fieldAvailability ? { ...fieldAvailability } : null,
59
73
  ...(fieldSources ? { fieldSources } : {}),
@@ -68,7 +82,7 @@ export const financialStatementsHeadless: HeadlessPaneDefinition<"rows"> = {
68
82
  label: formatFinancialHeader(date, [currency ?? statementCurrency, table.unit].filter(Boolean).join(" ") || undefined, dateSource, false, aggregation?.periodEnd).trim(),
69
83
  })) ?? [];
70
84
  const rows = table?.rows.map((row) => ({
71
- id: row.id, kind: row.kind, metric: row.unitLabel,
85
+ id: row.id, kind: row.kind, metric: allReceipts ? receiptShareCountLabel(row) : row.unitLabel,
72
86
  cells: row.cells.map((cell, index) => ({
73
87
  date: dates[index]?.date ?? "", value: cell.value ?? null,
74
88
  growth: cell.growth ?? null, formatted: cell.valueText.trim(), growthFormatted: cell.growthText.trim(),
@@ -92,7 +106,9 @@ export const financialStatementsHeadless: HeadlessPaneDefinition<"rows"> = {
92
106
  symbol, name: financials.quote?.name ?? symbol, currency: statementCurrency ?? null, unit: table?.unit ?? null, quoteCurrency: financials.quote?.currency ?? null,
93
107
  statement: table?.subTab.key ?? options.statement, statementLabel: table?.subTab.name ?? null,
94
108
  period: requestedPeriod, growthBasis: requestedPeriod === "quarterly" ? "QoQ" : "YoY", columns: dates,
95
- notices: rows.length ? [FINANCIAL_VINTAGE_NOTICE] : [],
109
+ shareBasis: shareBases.size === 1 ? [...shareBases][0] ?? null : null,
110
+ ...(receiptPeriods.length > 0 ? { adrRatio: financials.fundamentals?.adrRatio ?? financials.quote?.adrRatio ?? null } : {}),
111
+ notices: rows.length ? [FINANCIAL_VINTAGE_NOTICE, ...(shareBasisNotice ? [shareBasisNotice] : [])] : [],
96
112
  limitations: financialStatementLimitations(financials),
97
113
  dateProvenance: financialStatementDateNotice(table?.statements ?? []),
98
114
  },
@@ -179,18 +195,34 @@ function quoteAmount(value: unknown, row: Record<string, unknown>, signed = fals
179
195
  return amount === "—" ? amount : `${signed && value >= 0 ? "+" : ""}${amount}`;
180
196
  }
181
197
 
198
+ function percentCell(value: unknown): string {
199
+ return formatPercentRaw(typeof value === "number" && Number.isFinite(value) ? value : undefined);
200
+ }
201
+
202
+ const QUOTE_MONITOR_COLUMNS: HeadlessPaneColumn[] = [
203
+ { key: "symbol", header: "Ticker" },
204
+ { key: "name", header: "Name" },
205
+ { key: "price", header: "Last", align: "right", format: (value, row) => quoteAmount(value, row) },
206
+ { key: "change", header: "Change", align: "right", format: (value, row) => quoteAmount(value, row, true) },
207
+ { key: "changePercent", header: "Change %", align: "right", format: percentCell },
208
+ ];
209
+
210
+ /** The pre-market or after-hours print and its move from the regular close, for the rows that have one. */
211
+ function extendedSessionColumn(session: ExtendedSession): HeadlessPaneColumn {
212
+ return {
213
+ key: "extendedPrice", header: EXTENDED_SESSION_LABELS[session], align: "right",
214
+ format: (value, row) => row.extendedSession === session
215
+ ? `${quoteAmount(value, row)} ${percentCell(row.extendedChangePercent)}`
216
+ : "—",
217
+ };
218
+ }
219
+
182
220
  export const quoteComparisonHeadless: HeadlessPaneDefinition<"rows"> = {
183
221
  ...paneSchemas["quote-monitor-pane"],
184
222
  shape: "rows",
185
- description: "Current quotes for one or more tickers: name, last price, change and percent change.",
223
+ description: "Current quotes for one or more tickers: name, the regular session's last price, change and percent change, and any pre-market or after-hours print.",
186
224
  describe: ({ symbols }) => `Quote Monitor | ${symbols.join(", ")}`,
187
- columns: [
188
- { key: "symbol", header: "Ticker" },
189
- { key: "name", header: "Name" },
190
- { key: "price", header: "Last", align: "right", format: (value, row) => quoteAmount(value, row) },
191
- { key: "change", header: "Change", align: "right", format: (value, row) => quoteAmount(value, row, true) },
192
- { key: "changePercent", header: "Change %", align: "right", format: (value) => formatPercentRaw(typeof value === "number" && Number.isFinite(value) ? value : undefined) },
193
- ],
225
+ columns: QUOTE_MONITOR_COLUMNS,
194
226
  async load({ symbols }, ctx) {
195
227
  const loaded = await loadHeadlessSymbols(symbols, ctx, async (key) => {
196
228
  const { symbol, exchange } = await resolveHeadlessInstrument(ctx, key);
@@ -199,19 +231,25 @@ export const quoteComparisonHeadless: HeadlessPaneDefinition<"rows"> = {
199
231
  if (!hasValidQuoteObservationTime(quote)) throw new Error(`Quote observation time is unavailable for ${key}`);
200
232
  return quote;
201
233
  });
234
+ const rows = loaded.entries.map(({ symbol, data: quote }) => {
235
+ // As the pane's cards show them: the regular session, then any extended print from its close.
236
+ const { price, change, changePercent, ...extended } = getQuoteSessionFields(quote);
237
+ return {
238
+ symbol, name: quote.name ?? "", price, currency: quote.currency,
239
+ ...(quote.instrumentType ? { instrumentType: quote.instrumentType } : {}),
240
+ ...(quote.priceBasis ? { priceBasis: quote.priceBasis } : {}),
241
+ change, changePercent, ...extended,
242
+ marketCap: quote.marketCap ?? null, updatedAt: quote.lastUpdated,
243
+ ...quoteFreshnessFields(quote),
244
+ };
245
+ });
246
+ // An extended column only when a row has a print for it, so a closed board stays as narrow as before.
247
+ const extendedColumns = (["PRE", "POST"] as const)
248
+ .filter((session) => rows.some((row) => row.extendedSession === session))
249
+ .map(extendedSessionColumn);
202
250
  return {
203
- rows: loaded.entries.map(({ symbol, data: quote }) => {
204
- // The pane's cards show the live session's print, so the rows do too.
205
- const display = getActiveQuoteDisplay(quote)!;
206
- return {
207
- symbol, name: quote.name ?? "", price: display.price, currency: quote.currency,
208
- ...(quote.instrumentType ? { instrumentType: quote.instrumentType } : {}),
209
- ...(quote.priceBasis ? { priceBasis: quote.priceBasis } : {}),
210
- change: display.change, changePercent: display.changePercent,
211
- marketCap: quote.marketCap ?? null, updatedAt: quote.lastUpdated,
212
- ...quoteFreshnessFields(quote),
213
- };
214
- }),
251
+ rows,
252
+ ...(extendedColumns.length > 0 ? { columns: [...QUOTE_MONITOR_COLUMNS, ...extendedColumns] } : {}),
215
253
  unavailableSymbols: loaded.unavailableSymbols, errors: loaded.errors,
216
254
  };
217
255
  },
@@ -22,6 +22,7 @@ import type { OverviewFunctionLink, PositionTableRow, StatField } from "./types"
22
22
  import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue, resolvePortfolioPositionPnl, portfolioPnlPercent, signedPositionDirection } from "../../portfolio-list/position-metrics";
23
23
  import { liveDividendYield, liveForwardPE, liveMarketCapitalization, liveTrailingPE } from "../../portfolio-list/live-valuation";
24
24
  import { formatReportedMoney } from "../../../../utils/reported-money";
25
+ import { fundamentalsCurrency, reportedEnterpriseValue } from "../../../../utils/fundamentals";
25
26
  import { formatShortDate } from "../../../../utils/datetime-format";
26
27
  import { safeExternalUrl } from "../../../../utils/external-url";
27
28
 
@@ -68,6 +69,7 @@ function compactPositionAccount(position: TickerPosition): string {
68
69
  export function buildOverviewStats({
69
70
  quote,
70
71
  fundamentals,
72
+ financialCurrency,
71
73
  quoteCurrency,
72
74
  baseCurrency,
73
75
  marketCapExchangeRates = new Map(),
@@ -77,18 +79,20 @@ export function buildOverviewStats({
77
79
  }: {
78
80
  quote: Quote | undefined;
79
81
  fundamentals: TickerFinancials["fundamentals"] | undefined;
82
+ /** The currency the statements report in, for fundamentals that declare none. */
83
+ financialCurrency?: TickerFinancials["financialCurrency"];
80
84
  quoteCurrency: string;
81
85
  baseCurrency: string;
82
86
  toBase: CurrencyConverter;
83
87
  marketCapExchangeRates?: ReadonlyMap<string, number>;
84
88
  nextEarnings?: NextEarnings;
85
- /** The listing is a depositary receipt, so its share count is in receipts. */
89
+ /** The share count is in depositary receipts, not ordinary shares. */
86
90
  depositaryReceipt?: boolean;
87
91
  /** The listing's calendar day, YYYY-MM-DD; dates before it are left out. */
88
92
  today?: string;
89
93
  }): StatField[] {
90
94
  const stats: StatField[] = [];
91
- const money = (value: number, perShare = false) => formatReportedMoney(value, fundamentals?.financialCurrency, perShare);
95
+ const money = (value: number, perShare = false) => formatReportedMoney(value, fundamentalsCurrency({ fundamentals, financialCurrency }), perShare);
92
96
 
93
97
  if (quote?.volume != null) {
94
98
  // Live figures keep their decimals (12.30M, not 12.3M) so the digits hold still.
@@ -178,10 +182,11 @@ export function buildOverviewStats({
178
182
  valueColor: priceColor(fundamentals.revenueGrowth),
179
183
  });
180
184
  }
185
+ const enterpriseValue = reportedEnterpriseValue(fundamentals);
181
186
  if (fundamentals?.unavailableFields?.includes("enterpriseValue")) {
182
187
  stats.push({ label: "EV", value: "—" });
183
- } else if (fundamentals?.enterpriseValue != null) {
184
- stats.push({ label: "EV", value: formatCompactCurrency(fundamentals.enterpriseValue, quoteCurrency) });
188
+ } else if (enterpriseValue != null) {
189
+ stats.push({ label: "EV", value: formatCompactCurrency(enterpriseValue, quoteCurrency) });
185
190
  }
186
191
  if (finite(fundamentals?.beta)) {
187
192
  stats.push({ label: "Beta", value: formatNumber(fundamentals.beta, 2), link: RELATIONSHIP_GRAPH });
@@ -26,7 +26,7 @@ import { Box, ScrollBox, Text, useUiCapabilities } from "../../../ui";
26
26
  import { publicTickerKey, resolveExchangeTimeZone } from "../../../utils/exchanges";
27
27
  import { convertCurrency, displayWidth, formatPercentRaw, truncateToDisplayWidth } from "../../../utils/format";
28
28
  import { zonedDateKey } from "../../../utils/zoned-date-time";
29
- import { isDepositaryReceipt } from "../../../utils/depositary-receipt";
29
+ import { sharesOutstandingInReceipts } from "../../../utils/depositary-receipt";
30
30
  import {
31
31
  CompactRangeBar,
32
32
  FundamentalsGrid,
@@ -189,12 +189,13 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
189
189
  const stats = buildOverviewStats({
190
190
  quote,
191
191
  fundamentals,
192
+ financialCurrency: financials?.financialCurrency,
192
193
  quoteCurrency,
193
194
  baseCurrency,
194
195
  toBase,
195
196
  marketCapExchangeRates: exchangeRates,
196
197
  nextEarnings: financials?.nextEarnings,
197
- depositaryReceipt: isDepositaryReceipt({ ...quote, description }),
198
+ depositaryReceipt: sharesOutstandingInReceipts(quote, fundamentals, description),
198
199
  // Report and ex-dividend dates are the listing's calendar days.
199
200
  today: zonedDateKey(Date.now(), chartTimeZone ?? "America/New_York"),
200
201
  });
@@ -256,11 +256,10 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
256
256
  : null;
257
257
  usePaneMenuItems("ticker-research:company-research", () => {
258
258
  if (!companySymbol || !dialog) return null;
259
- const choices = COMPANY_RESEARCH.filter((item) => {
260
- if (!registry) return true;
261
- const owner = registry.getPaneTemplatePluginId?.(item.id);
262
- return registry.paneTemplates?.has(item.id) && (!owner || !disabledPlugins.includes(owner));
263
- });
259
+ const choices = COMPANY_RESEARCH.filter((item) => (
260
+ !registry
261
+ || (registry.paneTemplates?.has(item.id) && !registry.getDisabledPaneTemplateOwner?.(item.id, disabledPlugins))
262
+ ));
264
263
  if (!choices.length) return null;
265
264
  return [{ id: "company-research", label: t("Company Research…"), onSelect: () => {
266
265
  void dialog.prompt<string>({
@@ -8,9 +8,15 @@ import { resolveEntryData } from "../../../../market-data/selectors";
8
8
  import { useDoubleClickActivation } from "../../../../components/use-double-click-activation";
9
9
  import { FigureText } from "../../../../components/ui/figure";
10
10
  import { colors, priceColor } from "../../../../theme/colors";
11
- import { formatPercentRaw } from "../../../../utils/format";
11
+ import { displayWidth, formatPercentRaw } from "../../../../utils/format";
12
12
  import { formatMarketPriceWithCurrency, formatSignedMarketPrice, liveQuoteFormatOptions } from "../../../../market-data/market/format";
13
- import { getActiveQuoteDisplay } from "../../../../market-data/market/status";
13
+ import {
14
+ getCompletedRegularSessionDisplay,
15
+ getExtendedSessionDisplay,
16
+ getRegularSessionDisplay,
17
+ type ExtendedSessionDisplay,
18
+ } from "../../../../market-data/market/status";
19
+ import { t } from "../../../../i18n";
14
20
  import { isQuoteStaleForCurrentSession } from "../../../../market-data/quotes/freshness";
15
21
  import { formatQuoteNavAsOf } from "../../../../market-data/quotes/time";
16
22
  import { useQuoteFlashDirection } from "../../../../components/quote-flash";
@@ -28,6 +34,52 @@ function quoteTrend(value: number | null | undefined): PriceSparklineTrend {
28
34
  return value > 0 ? "positive" : "negative";
29
35
  }
30
36
 
37
+ /** Desktop body text is 12px in the card's monospace face, 0.6 of its size wide. */
38
+ const DESKTOP_DETAIL_CHAR_PX = 12 * 0.6;
39
+
40
+ /**
41
+ * The extended print's line, "After-Hours $336.08 -0.17%": the full label
42
+ * where the room allows, else "AH" or "PM" as the heat map marks them, and
43
+ * at the narrowest the mark and the move without the price.
44
+ */
45
+ function extendedLineParts(
46
+ extended: ExtendedSessionDisplay,
47
+ priceText: string,
48
+ fits: (text: string) => boolean,
49
+ ): { label: string; figures: string } | null {
50
+ const pre = extended.session === "PRE";
51
+ const percent = formatPercentRaw(extended.changePercent);
52
+ const candidates = [
53
+ { label: t(pre ? "Pre-Market" : "After-Hours"), figures: `${priceText} ${percent}` },
54
+ { label: pre ? "PM" : "AH", figures: `${priceText} ${percent}` },
55
+ { label: pre ? "PM" : "AH", figures: percent },
56
+ ];
57
+ return candidates.find(({ label, figures }) => fits(`${label} ${figures}`)) ?? null;
58
+ }
59
+
60
+ const DESKTOP_DETAIL_TEXT_STYLE = { fontSize: "12px", lineHeight: "14px", whiteSpace: "nowrap" };
61
+
62
+ function ExtendedSessionLine({
63
+ parts,
64
+ color,
65
+ dim,
66
+ desktopStyle,
67
+ }: {
68
+ parts: { label: string; figures: string };
69
+ color: string;
70
+ dim: boolean;
71
+ /** Placement on the desktop card's grid; the terminal gives the line one row. */
72
+ desktopStyle?: Record<string, string | number | undefined>;
73
+ }) {
74
+ const textStyle = desktopStyle ? DESKTOP_DETAIL_TEXT_STYLE : undefined;
75
+ return (
76
+ <Box flexDirection="row" gap={1} height={desktopStyle ? undefined : 1} overflow="hidden" style={desktopStyle}>
77
+ <Text fg={colors.textDim} style={textStyle}>{parts.label}</Text>
78
+ <Text fg={color} attributes={dim ? TextAttributes.DIM : TextAttributes.NONE} style={textStyle}>{parts.figures}</Text>
79
+ </Box>
80
+ );
81
+ }
82
+
31
83
  /** A mistyped ticker and a broken provider are not the same problem. */
32
84
  const UNKNOWN_SYMBOL_REASONS = new Set(["NOT_FOUND", "BAD_MAPPING"]);
33
85
 
@@ -83,7 +135,7 @@ export function QuoteMonitorCard({
83
135
  onSelect?: (symbol: string) => void;
84
136
  onOpen: (symbol: string) => void;
85
137
  }) {
86
- const { nativePaneChrome } = useUiCapabilities();
138
+ const { nativePaneChrome, cellWidthPx = 8 } = useUiCapabilities();
87
139
  const quote = resolveEntryData(quoteEntry) ?? cachedFinancials?.quote;
88
140
  const queriedPriceHistory = resolveEntryData(chartEntry);
89
141
  const barHistory = queriedPriceHistory && queriedPriceHistory.length >= 2
@@ -98,12 +150,17 @@ export function QuoteMonitorCard({
98
150
  [quote],
99
151
  );
100
152
  const flashDirection = useQuoteFlashDirection(flashFinancials, valueFlashingEnabled);
101
- const display = getActiveQuoteDisplay(quote);
153
+ // The headline is the regular session, which holds at its close; a pre-market or
154
+ // after-hours print is its own line, measured from that close, as on the Overview.
155
+ const display = getRegularSessionDisplay(quote);
156
+ const extended = getExtendedSessionDisplay(quote);
157
+ // Once the close is in, a tick moves only the extended print, so only that line flashes.
158
+ const headlineHolds = getCompletedRegularSessionDisplay(quote) != null;
102
159
  const quoteStatus = resolveQuoteStatus(quoteEntry, symbol, quote);
103
160
  const quoteFailed = quoteStatus.failed && !!display;
104
161
  const navAsOf = formatQuoteNavAsOf(quote);
105
162
  const changeColor = quoteFailed ? colors.textDim : priceColor(display?.change ?? 0);
106
- const flashing = !!flashDirection;
163
+ const flashing = !!flashDirection && !headlineHolds;
107
164
  const currency = quote?.currency || ticker?.metadata.currency || "USD";
108
165
  const stacked = width < 31;
109
166
  const compactQuoteFailure = quoteFailed && stacked && height <= 3;
@@ -115,6 +172,16 @@ export function QuoteMonitorCard({
115
172
  const changeValueText = display ? formatSignedMarketPrice(display.change, priceOptions) : "";
116
173
  const priceColumnWidth = Math.max(priceText.length, changePercentText.length + changeValueText.length + 1);
117
174
  const nameMaxWidth = Math.max(10, width - priceColumnWidth - (nativePaneChrome ? 5 : 3));
175
+ // The terminal sets the line beside the change, under the symbol (or after it in a
176
+ // stacked card); the desktop gives it a row of its own under the name.
177
+ const extendedRoom = nativePaneChrome
178
+ ? (width * cellWidthPx - 24) / DESKTOP_DETAIL_CHAR_PX
179
+ : stacked ? width - 3 - displayWidth(symbol) : width - 3 - priceColumnWidth;
180
+ const extendedParts = display && extended
181
+ ? extendedLineParts(extended, formatMarketPriceWithCurrency(extended.price, currency, priceOptions), (text) => displayWidth(text) <= extendedRoom)
182
+ : null;
183
+ const extendedColor = quoteFailed ? colors.textDim : priceColor(extended?.change ?? 0);
184
+ const extendedDim = !!flashDirection && headlineHolds;
118
185
  const sparklineRange = resolvePriceSparklineRange(priceHistory, chartPeriod);
119
186
  // A price in 32nds already has a hyphen (104-16½), so its range reads "to".
120
187
  const rangeSeparator = priceOptions.priceBasis === "thirty-seconds" ? " to " : "-";
@@ -203,7 +270,7 @@ export function QuoteMonitorCard({
203
270
  minWidth={0}
204
271
  style={{
205
272
  gridColumn: "1",
206
- gridRow: "1 / span 2",
273
+ gridRow: extendedParts ? "1" : "1 / span 2",
207
274
  minWidth: 0,
208
275
  overflow: "hidden",
209
276
  }}
@@ -259,6 +326,25 @@ export function QuoteMonitorCard({
259
326
  </Box>
260
327
  </Box>
261
328
 
329
+ {extendedParts && (
330
+ <ExtendedSessionLine
331
+ parts={extendedParts}
332
+ color={extendedColor}
333
+ dim={extendedDim}
334
+ desktopStyle={{
335
+ gridColumn: "1 / -1",
336
+ gridRow: "2",
337
+ justifySelf: "start",
338
+ alignSelf: "start",
339
+ maxWidth: "100%",
340
+ marginTop: 2,
341
+ backgroundColor: colors.bg,
342
+ boxShadow: `0 0 0 2px ${colors.bg}`,
343
+ paddingRight: 2,
344
+ }}
345
+ />
346
+ )}
347
+
262
348
  {perpetuals && <Box style={{ gridColumn: "1 / -1", gridRow: "4", alignSelf: "end" }}>{perpetuals}</Box>}
263
349
  {rangeLabel && (
264
350
  <Text
@@ -289,6 +375,7 @@ export function QuoteMonitorCard({
289
375
  {symbol}
290
376
  </Text>
291
377
  {compactNavAsOf && <Text fg={colors.textDim}>{navAsOf}</Text>}
378
+ {extendedParts && <ExtendedSessionLine parts={extendedParts} color={extendedColor} dim={extendedDim} />}
292
379
  </Box>
293
380
  <Box flexDirection="column">
294
381
  <FigureText fg={changeColor} dim={flashing} style={figureHalo}>
@@ -311,6 +398,7 @@ export function QuoteMonitorCard({
311
398
  <Text attributes={TextAttributes.BOLD} fg={symbolFg} bg={symbolBg} style={desktopSymbolStyle}>
312
399
  {symbol}
313
400
  </Text>
401
+ {extendedParts && <ExtendedSessionLine parts={extendedParts} color={extendedColor} dim={extendedDim} />}
314
402
  {nativePaneChrome && ticker?.metadata.name && width >= 36 && (
315
403
  <Text
316
404
  fg={colors.textDim}
@@ -6,8 +6,11 @@ import type { TreasuryAuction } from "./types";
6
6
 
7
7
  const CACHE_KIND = "treasury-auctions";
8
8
  const CACHE_SOURCE = "treasury-fiscal-data";
9
- /** 3: every reported page was validated before this board was cached. */
10
- const CACHE_SCHEMA_VERSION = 3;
9
+ /**
10
+ * 3: every reported page was validated before this board was cached.
11
+ * 4: boards carry direct takedown and bill discount rates, which a v3 board lacks.
12
+ */
13
+ const CACHE_SCHEMA_VERSION = 4;
11
14
  export const TREASURY_FISCAL_DATA_CONNECTION_ID = "treasury-fiscal-data";
12
15
  /**
13
16
  * Refresh the auction board hourly; the week-long expiry keeps an offline
@@ -19,18 +19,27 @@ const FIELDS = [
19
19
  "bid_to_cover_ratio",
20
20
  "comp_accepted",
21
21
  "indirect_bidder_accepted",
22
+ "direct_bidder_accepted",
22
23
  "primary_dealer_accepted",
23
24
  "total_accepted",
24
25
  "offering_amt",
25
26
  "inflation_index_security",
26
27
  "floating_rate",
27
28
  "high_discnt_margin",
29
+ "high_discnt_rate",
30
+ "avg_med_discnt_rate",
28
31
  ].join(",");
29
32
 
30
33
  export const AUCTION_HISTORY_DAYS = 120;
31
- const PAGE_SIZE = 300;
32
- /** 120 days is ~150 auctions, one page; the bound only exists so bad metadata cannot loop. */
33
- const MAX_PAGES = 5;
34
+ /**
35
+ * The feed holds roughly 400 auctions a year, so the default 120 days is one
36
+ * page and the longest window (10 years, ~4,000 auctions) is four. One large
37
+ * page costs about the same as a small one, so fewer, larger pages keep a
38
+ * ten-year walk to a few seconds inside the throttle.
39
+ */
40
+ const AUCTION_PAGE_SIZE = 1000;
41
+ /** Three times the longest window's pages; the bound only exists so bad metadata cannot loop. */
42
+ export const AUCTION_MAX_PAGES = 12;
34
43
 
35
44
  const TREASURY_FETCH = createThrottledFetch({
36
45
  requestsPerMinute: 20,
@@ -49,8 +58,10 @@ export function buildAuctionsUrl(sinceDays: number, now = Date.now(), page = 1):
49
58
  const params = [
50
59
  `fields=${FIELDS}`,
51
60
  `filter=auction_date:gte:${isoDateDaysAgo(sinceDays, now)}`,
52
- "sort=-auction_date",
53
- `page%5Bsize%5D=${PAGE_SIZE}`,
61
+ // The CUSIP breaks ties inside a date, so a page boundary that falls in the
62
+ // middle of a day's auctions cannot repeat or skip one on the next page.
63
+ "sort=-auction_date,-cusip",
64
+ `page%5Bsize%5D=${AUCTION_PAGE_SIZE}`,
54
65
  `page%5Bnumber%5D=${page}`,
55
66
  ];
56
67
  return `${BASE_URL}?${params.join("&")}`;
@@ -95,6 +106,8 @@ export function normalizeAuction(raw: unknown): TreasuryAuction | null {
95
106
  securityTerm: securityTerm || "—",
96
107
  auctionDate,
97
108
  highInvestmentRate: toNumber(record.high_investment_rate),
109
+ highDiscountRate: toNumber(record.high_discnt_rate),
110
+ avgMedDiscountRate: toNumber(record.avg_med_discnt_rate),
98
111
  highDiscountMargin: toNumber(record.high_discnt_margin),
99
112
  highYield: toNumber(record.high_yield),
100
113
  avgMedYield: toNumber(record.avg_med_yield),
@@ -104,6 +117,7 @@ export function normalizeAuction(raw: unknown): TreasuryAuction | null {
104
117
  bidToCoverRatio: toNumber(record.bid_to_cover_ratio),
105
118
  competitiveAccepted: toNumber(record.comp_accepted),
106
119
  indirectAccepted: toNumber(record.indirect_bidder_accepted),
120
+ directAccepted: toNumber(record.direct_bidder_accepted),
107
121
  primaryDealerAccepted: toNumber(record.primary_dealer_accepted),
108
122
  totalAccepted: toNumber(record.total_accepted),
109
123
  offeringAmount: toNumber(record.offering_amt),
@@ -136,15 +150,15 @@ export async function fetchAuctionPages(
136
150
  const seen = new Set<string>();
137
151
  let pages = 1;
138
152
 
139
- for (let page = 1; page <= Math.min(pages, MAX_PAGES); page += 1) {
153
+ for (let page = 1; page <= Math.min(pages, AUCTION_MAX_PAGES); page += 1) {
140
154
  const body = await loadPage(page);
141
155
  const declaredPages = totalPages(body, page);
142
156
  if (page === 1) pages = declaredPages;
143
157
  else if (declaredPages !== pages) {
144
158
  throw new Error(`Treasury auction page count changed on page ${page}`);
145
159
  }
146
- if (pages > MAX_PAGES) {
147
- throw new Error(`Treasury auction history exceeds the ${MAX_PAGES}-page limit`);
160
+ if (pages > AUCTION_MAX_PAGES) {
161
+ throw new Error(`Treasury auction history exceeds the ${AUCTION_MAX_PAGES}-page limit`);
148
162
  }
149
163
  const data = (body as { data?: unknown } | null)?.data;
150
164
  if (!Array.isArray(data)) {