gloomberb 0.18.4 → 0.19.0

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 (600) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +169 -5
  5. package/src/account/auth-model.ts +7 -3
  6. package/src/api-client/auth.ts +9 -4
  7. package/src/api-client/catalysts.ts +2 -1
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +31 -6
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/index.ts +171 -11
  14. package/src/api-client/live-scan.ts +91 -0
  15. package/src/api-client/market-discovery.ts +10 -0
  16. package/src/api-client/market-movers.ts +7 -0
  17. package/src/api-client/members.ts +13 -1
  18. package/src/api-client/perps.ts +43 -0
  19. package/src/api-client/request.ts +222 -41
  20. package/src/api-client/research-activity.ts +6 -12
  21. package/src/api-client/types.ts +74 -0
  22. package/src/app/keybindings/index.ts +3 -0
  23. package/src/app/keybindings/labels.ts +18 -1
  24. package/src/app/keybindings/resolve.ts +14 -0
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +42 -27
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +1 -1
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/fred-series.ts +5 -0
  35. package/src/cli/commands/keybindings.ts +23 -0
  36. package/src/cli/commands/market.ts +138 -46
  37. package/src/cli/commands/options-chain.ts +107 -10
  38. package/src/cli/commands/options-leaps.ts +9 -3
  39. package/src/cli/commands/overview.ts +176 -39
  40. package/src/cli/commands/search.ts +39 -3
  41. package/src/cli/commands/themes.ts +11 -2
  42. package/src/cli/commands/ticker.ts +57 -35
  43. package/src/cli/company-name-hint.ts +41 -0
  44. package/src/cli/earnings-calendar.ts +1 -1
  45. package/src/cli/errors.ts +10 -2
  46. package/src/cli/export-source.ts +29 -1
  47. package/src/cli/glossary.ts +73 -1
  48. package/src/cli/history-rows.ts +87 -16
  49. package/src/cli/index.ts +2 -0
  50. package/src/cli/known-gaps.ts +5 -2
  51. package/src/cli/listing-arg.ts +195 -56
  52. package/src/cli/output-file.ts +68 -0
  53. package/src/cli/pane-functions/capabilities.ts +3 -0
  54. package/src/cli/pane-functions/catalog.ts +80 -7
  55. package/src/cli/pane-functions/dom.ts +13 -3
  56. package/src/cli/pane-functions/freshness.ts +111 -16
  57. package/src/cli/pane-functions/function-help.ts +23 -13
  58. package/src/cli/pane-functions/headless.ts +56 -12
  59. package/src/cli/pane-functions/index.ts +58 -16
  60. package/src/cli/pane-functions/market-session.ts +81 -9
  61. package/src/cli/pane-functions/options.ts +19 -1
  62. package/src/cli/pane-functions/report.ts +5 -1
  63. package/src/cli/pane-functions/resolver.ts +35 -8
  64. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  65. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  66. package/src/cli/quote-answer.ts +30 -13
  67. package/src/cli/report-tables.ts +18 -7
  68. package/src/cli/restore-plugins.ts +13 -1
  69. package/src/cli/result.ts +57 -5
  70. package/src/cli/ticker-resolution.ts +3 -2
  71. package/src/components/chart/composite/composite-chart.tsx +26 -4
  72. package/src/components/chart/composite/format.ts +27 -0
  73. package/src/components/chart/composite/interactions.ts +6 -0
  74. package/src/components/chart/composite/legend.tsx +45 -7
  75. package/src/components/chart/composite/levels.ts +2 -0
  76. package/src/components/chart/composite/panel-gestures.ts +33 -0
  77. package/src/components/chart/composite/panel-series.ts +1 -0
  78. package/src/components/chart/composite/panel-surface.tsx +61 -5
  79. package/src/components/chart/composite/range-statistics.ts +62 -0
  80. package/src/components/chart/composite/rasterizer.ts +24 -13
  81. package/src/components/chart/composite/scene.ts +46 -7
  82. package/src/components/chart/composite/series-axis-tags.tsx +16 -9
  83. package/src/components/chart/composite/series-cues.ts +14 -0
  84. package/src/components/chart/composite/text-renderer.ts +9 -2
  85. package/src/components/chart/composite/tool-catalog.ts +36 -0
  86. package/src/components/chart/composite/tools.ts +203 -44
  87. package/src/components/chart/composite/types.ts +4 -0
  88. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  89. package/src/components/command-bar/assist/inventory.ts +2 -1
  90. package/src/components/command-bar/commands/direct/index.ts +28 -13
  91. package/src/components/command-bar/pane-templates/items.ts +3 -2
  92. package/src/components/command-bar/panel/prompt-binding.ts +0 -8
  93. package/src/components/command-bar/workflow/alert.ts +1 -1
  94. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  95. package/src/components/layout/header.tsx +26 -40
  96. package/src/components/layout/pane/footer/index.tsx +1 -0
  97. package/src/components/layout/pane/footer/model.ts +27 -0
  98. package/src/components/onboarding/onboarding-frame.tsx +1 -1
  99. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  100. package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
  101. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  102. package/src/components/onboarding/wizard-positions.ts +3 -3
  103. package/src/components/ticker/list-table-view.tsx +4 -1
  104. package/src/components/ui/choice-dialog.tsx +23 -18
  105. package/src/components/ui/choice-groups.ts +18 -0
  106. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  107. package/src/components/ui/data-table/opentui/model.ts +26 -1
  108. package/src/components/ui/data-table/types.ts +2 -0
  109. package/src/components/ui/display.tsx +5 -1
  110. package/src/components/ui/list-view.tsx +8 -4
  111. package/src/components/ui/query-bar-keys.tsx +2 -0
  112. package/src/components/ui/query-bar.tsx +1 -0
  113. package/src/components/ui/select-button.tsx +5 -1
  114. package/src/components/ui/select-field.tsx +1 -0
  115. package/src/components/ui/table-layout.ts +8 -5
  116. package/src/core/app-runtime.ts +2 -0
  117. package/src/core/app-services.ts +2 -2
  118. package/src/core/state/app/layout.ts +2 -0
  119. package/src/core/state/app/state.ts +9 -0
  120. package/src/core/state/app/types.ts +3 -0
  121. package/src/data/config/chart-settings.ts +1 -1
  122. package/src/data/json-ticker-repository.ts +17 -8
  123. package/src/data/saved-copies.ts +55 -16
  124. package/src/data/ticker-repository.ts +16 -9
  125. package/src/data/ticker-writes.ts +81 -0
  126. package/src/data/ttl-cache.ts +165 -0
  127. package/src/docs/snapshot.json +427 -216
  128. package/src/i18n/es.ts +19 -3
  129. package/src/i18n/ja.ts +19 -3
  130. package/src/i18n/ko.ts +19 -3
  131. package/src/i18n/zh-cn.ts +19 -3
  132. package/src/i18n/zh-tw.ts +19 -3
  133. package/src/market-data/coordinator/financials.ts +10 -2
  134. package/src/market-data/coordinator/index.ts +3 -1
  135. package/src/market-data/coordinator/quotes.ts +4 -2
  136. package/src/market-data/depositary-pair.ts +2 -2
  137. package/src/market-data/fund-facts.ts +50 -0
  138. package/src/market-data/market/change-basis.ts +48 -7
  139. package/src/market-data/market/dated-previous-close.ts +203 -0
  140. package/src/market-data/market/format.ts +25 -0
  141. package/src/market-data/market/freshness.ts +2 -1
  142. package/src/market-data/market/futures-week.ts +68 -0
  143. package/src/market-data/market/status.ts +16 -4
  144. package/src/market-data/options-alternatives.ts +141 -4
  145. package/src/market-data/quotes/contributions.ts +20 -2
  146. package/src/market-data/quotes/resolution.ts +3 -0
  147. package/src/market-data/selectors.ts +3 -0
  148. package/src/news/aggregator.ts +113 -9
  149. package/src/news/cloud-session.ts +21 -0
  150. package/src/news/hooks.ts +1 -0
  151. package/src/news/news-model.ts +1 -0
  152. package/src/news/types.ts +11 -0
  153. package/src/notifications/notification-log.ts +6 -4
  154. package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
  155. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  156. package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
  157. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  158. package/src/plugins/builtin/alerts/cli.ts +380 -82
  159. package/src/plugins/builtin/alerts/command.ts +31 -0
  160. package/src/plugins/builtin/alerts/constants.ts +1 -1
  161. package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
  162. package/src/plugins/builtin/alerts/fired-log.ts +113 -8
  163. package/src/plugins/builtin/alerts/format.ts +80 -4
  164. package/src/plugins/builtin/alerts/headless.ts +30 -7
  165. package/src/plugins/builtin/alerts/history-rows.ts +7 -3
  166. package/src/plugins/builtin/alerts/history.ts +2 -0
  167. package/src/plugins/builtin/alerts/index.tsx +135 -22
  168. package/src/plugins/builtin/alerts/live.ts +3 -3
  169. package/src/plugins/builtin/alerts/metric-command.ts +134 -0
  170. package/src/plugins/builtin/alerts/metric-data.ts +111 -0
  171. package/src/plugins/builtin/alerts/metric-model.ts +324 -0
  172. package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
  173. package/src/plugins/builtin/alerts/pane.tsx +90 -39
  174. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  175. package/src/plugins/builtin/alerts/storage.ts +37 -10
  176. package/src/plugins/builtin/alerts/types.ts +7 -0
  177. package/src/plugins/builtin/analytics/index.tsx +2 -1
  178. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  179. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  180. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  181. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  182. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  183. package/src/plugins/builtin/attention/index.tsx +2 -1
  184. package/src/plugins/builtin/attention/listing.ts +14 -0
  185. package/src/plugins/builtin/backtest/headless.ts +10 -5
  186. package/src/plugins/builtin/backtest/pane.tsx +9 -4
  187. package/src/plugins/builtin/backtest/presets.ts +29 -3
  188. package/src/plugins/builtin/broker-manager/index.tsx +10 -5
  189. package/src/plugins/builtin/broker-manager/model.ts +6 -1
  190. package/src/plugins/builtin/broker-trading/controller.ts +22 -12
  191. package/src/plugins/builtin/broker-trading/index.tsx +13 -1
  192. package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
  193. package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
  194. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  195. package/src/plugins/builtin/buildout/format.ts +38 -0
  196. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  197. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  198. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  199. package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
  200. package/src/plugins/builtin/catalysts/model.ts +2 -1
  201. package/src/plugins/builtin/changelog/index.tsx +9 -6
  202. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
  203. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
  204. package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
  205. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
  206. package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
  207. package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
  208. package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
  209. package/src/plugins/builtin/chart-composer/formula.ts +71 -18
  210. package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
  211. package/src/plugins/builtin/chart-composer/headless.ts +19 -3
  212. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  213. package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
  214. package/src/plugins/builtin/chart-composer/presets.ts +50 -15
  215. package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
  216. package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
  217. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  218. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
  219. package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
  220. package/src/plugins/builtin/chat/content/composer.tsx +13 -6
  221. package/src/plugins/builtin/chat/content/footer.tsx +6 -3
  222. package/src/plugins/builtin/chat/content/index.tsx +50 -21
  223. package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
  224. package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
  225. package/src/plugins/builtin/chat/index.tsx +23 -0
  226. package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
  227. package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
  228. package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
  229. package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
  230. package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
  231. package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
  232. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
  233. package/src/plugins/builtin/clinical-trials/model.ts +0 -9
  234. package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
  235. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
  236. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  237. package/src/plugins/builtin/cloud/plugin.tsx +1 -1
  238. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  239. package/src/plugins/builtin/company-attention/client.ts +8 -9
  240. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  241. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  242. package/src/plugins/builtin/company-attention/pane.tsx +9 -2
  243. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  244. package/src/plugins/builtin/correlation/headless.ts +13 -5
  245. package/src/plugins/builtin/correlation/history.ts +22 -0
  246. package/src/plugins/builtin/cot/client.ts +22 -18
  247. package/src/plugins/builtin/cot/headless.ts +15 -10
  248. package/src/plugins/builtin/cot/model.ts +14 -1
  249. package/src/plugins/builtin/cot/pane.tsx +22 -14
  250. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  251. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  252. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  253. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  254. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  255. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  256. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  257. package/src/plugins/builtin/credit-documents/headless.ts +7 -7
  258. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  259. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  260. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  261. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  262. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  263. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  264. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  265. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  266. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  267. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  268. package/src/plugins/builtin/docs/targets.ts +5 -1
  269. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  270. package/src/plugins/builtin/earnings/board.tsx +62 -71
  271. package/src/plugins/builtin/earnings/headless.ts +40 -4
  272. package/src/plugins/builtin/earnings/scope.ts +31 -43
  273. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  274. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  275. package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
  276. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  277. package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
  278. package/src/plugins/builtin/econ/calendar-model.ts +14 -9
  279. package/src/plugins/builtin/econ/figures.ts +72 -0
  280. package/src/plugins/builtin/econ/index.tsx +33 -3
  281. package/src/plugins/builtin/econ/types.ts +4 -0
  282. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  283. package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
  284. package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
  285. package/src/plugins/builtin/econ-statistics/view.ts +3 -0
  286. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  287. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  288. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  289. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  290. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  291. package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
  292. package/src/plugins/builtin/exposure/charts.tsx +2 -2
  293. package/src/plugins/builtin/exposure/model.ts +13 -1
  294. package/src/plugins/builtin/exposure/pane.tsx +4 -4
  295. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  296. package/src/plugins/builtin/futures/index.tsx +1 -0
  297. package/src/plugins/builtin/futures/model.ts +2 -1
  298. package/src/plugins/builtin/futures/table.tsx +2 -2
  299. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  300. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  301. package/src/plugins/builtin/fx-matrix/client.ts +11 -3
  302. package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
  303. package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
  304. package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
  305. package/src/plugins/builtin/gpu/headless.ts +8 -2
  306. package/src/plugins/builtin/help/function-index.ts +4 -1
  307. package/src/plugins/builtin/help/index.tsx +1 -0
  308. package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
  309. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  310. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  311. package/src/plugins/builtin/holders/headless.ts +18 -1
  312. package/src/plugins/builtin/iv-history/client.ts +52 -11
  313. package/src/plugins/builtin/iv-history/format.ts +36 -23
  314. package/src/plugins/builtin/iv-history/headless.ts +36 -18
  315. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  316. package/src/plugins/builtin/iv-history/model.ts +163 -27
  317. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  318. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  319. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  320. package/src/plugins/builtin/jobs/client.ts +25 -29
  321. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  322. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  323. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  324. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  325. package/src/plugins/builtin/macro-day/pane.tsx +7 -5
  326. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  327. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  328. package/src/plugins/builtin/market-movers/client.ts +25 -7
  329. package/src/plugins/builtin/market-movers/footer.ts +2 -2
  330. package/src/plugins/builtin/market-movers/headless.ts +32 -12
  331. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  332. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  333. package/src/plugins/builtin/market-movers/model.ts +4 -1
  334. package/src/plugins/builtin/market-movers/screener.ts +14 -0
  335. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  336. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  337. package/src/plugins/builtin/market-movers/table.ts +17 -5
  338. package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
  339. package/src/plugins/builtin/members/client.ts +19 -4
  340. package/src/plugins/builtin/members/headless.ts +20 -7
  341. package/src/plugins/builtin/members/index.tsx +9 -9
  342. package/src/plugins/builtin/members/model.ts +23 -4
  343. package/src/plugins/builtin/members/pane.tsx +25 -15
  344. package/src/plugins/builtin/news/index.tsx +1 -0
  345. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  346. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  347. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  348. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  349. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  350. package/src/plugins/builtin/openfda/client.ts +14 -2
  351. package/src/plugins/builtin/openfda/index.tsx +3 -2
  352. package/src/plugins/builtin/openfda/model.ts +9 -0
  353. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  354. package/src/plugins/builtin/options/analytics.ts +54 -6
  355. package/src/plugins/builtin/options/enrichment.ts +17 -5
  356. package/src/plugins/builtin/options/model-spot.ts +5 -2
  357. package/src/plugins/builtin/options/strike-window.ts +2 -1
  358. package/src/plugins/builtin/options/table.ts +6 -9
  359. package/src/plugins/builtin/options/types.ts +2 -0
  360. package/src/plugins/builtin/options/view.tsx +42 -12
  361. package/src/plugins/builtin/options-positioning/client.ts +30 -0
  362. package/src/plugins/builtin/options-positioning/headless.ts +74 -21
  363. package/src/plugins/builtin/options-positioning/index.tsx +4 -4
  364. package/src/plugins/builtin/options-positioning/model.ts +122 -14
  365. package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
  366. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  367. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  368. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  369. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  370. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  371. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  372. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  373. package/src/plugins/builtin/pe-band/model.ts +12 -49
  374. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  375. package/src/plugins/builtin/perps/cascade.ts +236 -0
  376. package/src/plugins/builtin/perps/client.ts +56 -2
  377. package/src/plugins/builtin/perps/freshness.ts +12 -10
  378. package/src/plugins/builtin/perps/headless.ts +65 -9
  379. package/src/plugins/builtin/perps/index.tsx +7 -5
  380. package/src/plugins/builtin/perps/live-rows.ts +10 -3
  381. package/src/plugins/builtin/perps/model.ts +18 -4
  382. package/src/plugins/builtin/perps/pane.tsx +32 -31
  383. package/src/plugins/builtin/perps/status-line.ts +25 -0
  384. package/src/plugins/builtin/plugin-module.ts +5 -1
  385. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  386. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  387. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  388. package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
  389. package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
  390. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  391. package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
  392. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  393. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  394. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
  395. package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
  396. package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
  397. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  398. package/src/plugins/builtin/rate-path/pane.tsx +2 -2
  399. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  400. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  401. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  402. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  403. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  404. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  405. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  406. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  407. package/src/plugins/builtin/research/event-model.ts +10 -8
  408. package/src/plugins/builtin/research/headless.ts +1 -1
  409. package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
  410. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  411. package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
  412. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  413. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  414. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  415. package/src/plugins/builtin/scanner/feed.ts +2 -2
  416. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  417. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  418. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  419. package/src/plugins/builtin/scanner/headless.ts +204 -35
  420. package/src/plugins/builtin/scanner/index.ts +40 -6
  421. package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
  422. package/src/plugins/builtin/scanner/live-feed.ts +168 -0
  423. package/src/plugins/builtin/scanner/live-headless.ts +138 -0
  424. package/src/plugins/builtin/scanner/live-model.ts +362 -0
  425. package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
  426. package/src/plugins/builtin/scanner/live-track.ts +186 -0
  427. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  428. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  429. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  430. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  431. package/src/plugins/builtin/sec/headless.ts +2 -1
  432. package/src/plugins/builtin/series-inventory.ts +3 -0
  433. package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
  434. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  435. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  436. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  437. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  438. package/src/plugins/builtin/shared/macro-releases.ts +65 -0
  439. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  440. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  441. package/src/plugins/builtin/shared/report-freshness.ts +3 -2
  442. package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
  443. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  444. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  445. package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
  446. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  447. package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
  448. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  449. package/src/plugins/builtin/themes/headless.ts +6 -4
  450. package/src/plugins/builtin/themes/model.ts +17 -8
  451. package/src/plugins/builtin/themes/pane.tsx +7 -7
  452. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  453. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  454. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  455. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  456. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  457. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  458. package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
  459. package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
  460. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  461. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  462. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  463. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  464. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  465. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  466. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  467. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  468. package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
  469. package/src/plugins/builtin/vol-surface/client.ts +9 -17
  470. package/src/plugins/builtin/vol-surface/headless.ts +63 -14
  471. package/src/plugins/builtin/vol-surface/index.tsx +2 -2
  472. package/src/plugins/builtin/vol-surface/model.ts +22 -7
  473. package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
  474. package/src/plugins/builtin/vol-surface/tables.ts +31 -3
  475. package/src/plugins/builtin/volatility/dvol.ts +46 -0
  476. package/src/plugins/builtin/volatility/index.tsx +2 -2
  477. package/src/plugins/builtin/volatility/pane.tsx +4 -4
  478. package/src/plugins/builtin/volatility/series.ts +26 -0
  479. package/src/plugins/builtin/world-indices/footer.ts +1 -1
  480. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  481. package/src/plugins/catalog-browser.ts +13 -8
  482. package/src/plugins/catalog-manifest.ts +2 -1
  483. package/src/plugins/marketplace/model.ts +18 -5
  484. package/src/portfolio/column-values.ts +5 -5
  485. package/src/portfolio/futures-positions.ts +6 -3
  486. package/src/portfolio/position-quantity.ts +15 -9
  487. package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
  488. package/src/remote/resources.ts +2 -1
  489. package/src/renderers/browser/app-services.ts +1 -7
  490. package/src/renderers/browser/config-host.ts +2 -55
  491. package/src/renderers/browser/main.tsx +7 -57
  492. package/src/renderers/dom/data-table/row.tsx +26 -0
  493. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  494. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  495. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  496. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  497. package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
  498. package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
  499. package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
  500. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  501. package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
  502. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
  503. package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
  504. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  505. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
  506. package/src/renderers/electrobun/bun/index.ts +25 -1
  507. package/src/renderers/electrobun/shared/app-name.ts +30 -0
  508. package/src/renderers/electrobun/shared/protocol.ts +10 -0
  509. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  510. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  511. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
  512. package/src/renderers/electrobun/view/main.tsx +24 -1
  513. package/src/renderers/electrobun/view/news-source.ts +3 -1
  514. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  515. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  516. package/src/renderers/opentui/start.tsx +5 -0
  517. package/src/renderers/opentui/toast-host.tsx +71 -43
  518. package/src/series/registry.ts +1 -0
  519. package/src/series/types.ts +6 -0
  520. package/src/series/validate.ts +1 -0
  521. package/src/sources/gloomberb-cloud/index.ts +1 -0
  522. package/src/sources/gloomberb-cloud/news.ts +47 -7
  523. package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
  524. package/src/sources/provider-router/batches.ts +10 -2
  525. package/src/sources/provider-router/financial-routes.ts +3 -3
  526. package/src/sources/provider-router/financials.ts +15 -3
  527. package/src/sources/provider-router/history.ts +28 -7
  528. package/src/sources/provider-router/index.ts +149 -10
  529. package/src/sources/provider-router/search.ts +13 -2
  530. package/src/state/app/context/index.tsx +3 -0
  531. package/src/state/ticker-cleanup.ts +50 -0
  532. package/src/sync/auth-transition.ts +19 -9
  533. package/src/sync/baseline.ts +10 -4
  534. package/src/sync/controller.ts +70 -19
  535. package/src/sync/core-contributors.ts +90 -31
  536. package/src/sync/react.ts +4 -1
  537. package/src/sync/types.ts +3 -0
  538. package/src/telemetry/stall-reports-dom.ts +38 -0
  539. package/src/telemetry/stall-reports-node.ts +75 -0
  540. package/src/telemetry/stall-reports.ts +214 -0
  541. package/src/telemetry/usage-counts.ts +0 -18
  542. package/src/test-support/preload.ts +14 -0
  543. package/src/theme/colors.ts +8 -0
  544. package/src/theme/themes.ts +10 -0
  545. package/src/tickers/company-name.ts +103 -3
  546. package/src/tickers/listing-venues.ts +38 -3
  547. package/src/tickers/search/bare-symbol.ts +36 -0
  548. package/src/tickers/search/index.ts +36 -7
  549. package/src/tickers/search/ranking.ts +10 -1
  550. package/src/tickers/search/types.ts +5 -0
  551. package/src/tickers/terminal-codes.ts +19 -0
  552. package/src/time-series/alignment.ts +1 -1
  553. package/src/time-series/base-series.ts +13 -5
  554. package/src/time-series/daily-valuation.ts +62 -0
  555. package/src/time-series/economic.ts +18 -4
  556. package/src/time-series/field-catalog.ts +2 -4
  557. package/src/time-series/filed-trailing-eps.ts +197 -0
  558. package/src/time-series/forward-valuation.ts +32 -0
  559. package/src/time-series/fundamentals.ts +198 -7
  560. package/src/time-series/hooks.ts +1 -0
  561. package/src/time-series/ice-credit.ts +3 -0
  562. package/src/time-series/price-comparison.ts +30 -2
  563. package/src/time-series/price-units.ts +72 -2
  564. package/src/time-series/resolve-context.ts +7 -1
  565. package/src/time-series/resolve.ts +116 -15
  566. package/src/time-series/series-presentation.ts +40 -6
  567. package/src/time-series/session-bar.ts +11 -0
  568. package/src/time-series/spec.ts +5 -3
  569. package/src/time-series/studies.ts +278 -74
  570. package/src/time-series/total-return.ts +160 -0
  571. package/src/time-series/transforms.ts +3 -3
  572. package/src/time-series/types.ts +27 -1
  573. package/src/time-series/valuation-releases.ts +39 -0
  574. package/src/types/data-provider.ts +9 -1
  575. package/src/types/desktop-window.ts +2 -0
  576. package/src/types/financials.ts +73 -2
  577. package/src/types/headless.ts +61 -5
  578. package/src/types/instrument.ts +3 -0
  579. package/src/types/news-source.ts +1 -0
  580. package/src/types/plugin.ts +10 -1
  581. package/src/ui/host.tsx +3 -0
  582. package/src/updater/command-names.ts +12 -7
  583. package/src/updater/failure.ts +4 -1
  584. package/src/utils/cli-output.ts +42 -1
  585. package/src/utils/crypto-pair.ts +44 -0
  586. package/src/utils/financial-statements.ts +6 -1
  587. package/src/utils/forward-dividend.ts +2 -0
  588. package/src/utils/fundamentals.ts +24 -3
  589. package/src/utils/fx-market-hours.ts +26 -3
  590. package/src/utils/main-thread-stall.ts +35 -2
  591. package/src/utils/market-capitalization.ts +24 -1
  592. package/src/utils/network-failure.ts +9 -6
  593. package/src/utils/option-expiry.ts +48 -1
  594. package/src/utils/options.ts +10 -0
  595. package/src/utils/plugin-proxy-hosts.json +1 -0
  596. package/src/utils/price-history.ts +12 -1
  597. package/src/utils/shell-word.ts +8 -0
  598. package/src/utils/trailing-dividend.ts +53 -4
  599. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
  600. package/src/renderers/browser/first-screen.ts +0 -193
@@ -14,9 +14,10 @@ import {
14
14
  } from "./client";
15
15
  import { historyStartNotice } from "../shared/history-span";
16
16
  import { formatMoveRank } from "../shared/move-rank";
17
+ import type { StatPoint } from "../shared/stat-transform";
17
18
  import { formatSigma } from "../shared/trend";
18
19
  import { STAT_CATEGORIES } from "./defs";
19
- import { DEFAULT_STAT_ID, resolveStatArg, STATS } from "./stats";
20
+ import { DEFAULT_STAT_ID, readStatArg, STATS } from "./stats";
20
21
  import { selectStatViews, type StatRangeId, type StatViewModel } from "./view";
21
22
 
22
23
  /** How a report reads a statistic: a spread in basis points, everything else as the pane does. */
@@ -24,20 +25,26 @@ function reportFormat(stat: StatDef): (value: number) => string {
24
25
  return stat.basisPoints ? (value) => formatBasisPoints(value) : stat.formatValue;
25
26
  }
26
27
 
27
- /** `cutoffShown`: the status line names when in the day the reading was taken, so the period does not repeat it. */
28
- function detailEntries(view: StatViewModel, cutoffShown: boolean): HeadlessPaneEntry[] {
28
+ /**
29
+ * `cutoffShown`: the status line names when in the day the reading was taken, so the period does not repeat it.
30
+ * `text` is the report's text lines, where Previous says its period beside it; exports give the period a line of its own.
31
+ */
32
+ function detailEntries(view: StatViewModel, cutoffShown: boolean, text: boolean): HeadlessPaneEntry[] {
29
33
  const format = reportFormat(view.stat);
30
34
  return [
31
35
  { label: "Latest", value: view.latest.value, formatted: format(view.latest.value) },
32
36
  {
33
37
  label: "Previous",
34
38
  value: view.previous?.value ?? null,
35
- formatted: view.previous ? format(view.previous.value) : "-",
39
+ formatted: view.previous ? `${format(view.previous.value)}${text && view.previousReading ? ` (${view.previousReading})` : ""}` : "-",
40
+ ...earlierDating(view, view.previous),
36
41
  },
42
+ ...!text && view.previous ? [{ label: "Previous period", value: view.previous.date, formatted: view.previousReading ?? view.previous.date, ...earlierDating(view, view.previous) }] : [],
37
43
  {
38
44
  label: "1Y ago",
39
45
  value: view.yearAgo?.value ?? null,
40
46
  formatted: view.yearAgo ? format(view.yearAgo.value) : "-",
47
+ ...earlierDating(view, view.yearAgo),
41
48
  },
42
49
  { label: "Mean", value: view.mean, formatted: format(view.mean) },
43
50
  {
@@ -84,6 +91,29 @@ const PERCENTILE_WINDOW: Record<StatRangeId, string> = {
84
91
  ALL: "the full history",
85
92
  };
86
93
 
94
+ /**
95
+ * An earlier reading's own dating (previous, a year ago): its period, no release (the server dates only the
96
+ * latest print's), and its own day for as-of when the series is observed daily or weekly. A reading there is
97
+ * none of is of no period, never the latest's.
98
+ */
99
+ function earlierDating(view: StatViewModel, point: StatPoint | null): Pick<HeadlessPaneEntry, "period" | "released" | "asOf"> {
100
+ return { period: point?.date ?? null, released: null, asOf: point && !view.periodicity ? point.date : null };
101
+ }
102
+
103
+ /** The period a statistic's lines cover and the day they were released, for the `period` and `released` of each exported line. */
104
+ function exportDating(view: StatViewModel): { period: string; released?: string; asOf: string | null } {
105
+ return { period: view.latest.date, ...(view.releasedOn ? { released: view.releasedOn } : {}), asOf: exportAsOf(view) };
106
+ }
107
+
108
+ /**
109
+ * What an exported row is as of: the day its figure was released, else its own
110
+ * day when it is observed daily or weekly (a Treasury yield), and nothing when
111
+ * the date only starts the period it covers.
112
+ */
113
+ function exportAsOf(view: StatViewModel): string | null {
114
+ return view.releasedOn ?? (view.periodicity ? null : view.latest.date);
115
+ }
116
+
87
117
  export function projectStatsHeadlessBundle(
88
118
  bundle: StatsBundle,
89
119
  range: StatRangeId,
@@ -94,35 +124,45 @@ export function projectStatsHeadlessBundle(
94
124
  const sections = STAT_CATEGORIES.flatMap((category) => {
95
125
  const categoryViews = views.filter((view) => view.stat.category === category.id);
96
126
  if (categoryViews.length === 0) return [];
127
+ // The text table keeps the columns a terminal has room for; exports add what each figure is of.
128
+ const columns = (text: boolean) => [
129
+ { key: "indicator", header: "Indicator" },
130
+ {
131
+ key: "latest",
132
+ header: "Latest",
133
+ align: "right" as const,
134
+ format: (_value: unknown, row: Record<string, unknown>) => String(row.formattedLatest),
135
+ },
136
+ {
137
+ key: "previous",
138
+ header: "Previous",
139
+ align: "right" as const,
140
+ format: (_value: unknown, row: Record<string, unknown>) => String(row.formattedPrevious),
141
+ },
142
+ // Which period Previous is: exports carry it beside the figure, the text table has its Period column for the latest.
143
+ ...text ? [] : [{ key: "previousPeriod", header: "Previous period" }],
144
+ {
145
+ key: "percentile",
146
+ header: "%ile",
147
+ align: "right" as const,
148
+ format: (value: unknown) => formatNumber(Number(value), 0),
149
+ },
150
+ // `period` and `released` are the names every dated export gives these (`econ`, `fred`).
151
+ { key: "asOf", header: "period", literalHeader: true, textHeader: "Period", format: (_value: unknown, row: Record<string, unknown>) => String(row.reading) },
152
+ {
153
+ key: "releasedOn",
154
+ header: "released",
155
+ literalHeader: true,
156
+ textHeader: "Released",
157
+ format: (_value: unknown, row: Record<string, unknown>) => typeof row.released === "string" ? row.released.replace(/^released /, "") : "",
158
+ },
159
+ ];
97
160
  return [{
98
161
  title: category.label,
99
- columns: [
100
- { key: "indicator", header: "Indicator" },
101
- {
102
- key: "latest",
103
- header: "Latest",
104
- align: "right" as const,
105
- format: (_value: unknown, row: Record<string, unknown>) => String(row.formattedLatest),
106
- },
107
- {
108
- key: "previous",
109
- header: "Previous",
110
- align: "right" as const,
111
- format: (_value: unknown, row: Record<string, unknown>) => String(row.formattedPrevious),
112
- },
113
- {
114
- key: "percentile",
115
- header: "%ile",
116
- align: "right" as const,
117
- format: (value: unknown) => formatNumber(Number(value), 0),
118
- },
119
- { key: "asOf", header: "Period", format: (_value: unknown, row: Record<string, unknown>) => String(row.reading) },
120
- {
121
- key: "releasedOn",
122
- header: "Released",
123
- format: (_value: unknown, row: Record<string, unknown>) => typeof row.released === "string" ? row.released.replace(/^released /, "") : "",
124
- },
125
- ],
162
+ columns: columns(false),
163
+ textColumns: columns(true),
164
+ // `as_of` is the release; a figure with none stated is as of its own day when it is observed daily or weekly, never its period's first day.
165
+ rowAsOfKey: "exportAsOf",
126
166
  rows: categoryViews.map((view) => ({
127
167
  id: view.stat.id,
128
168
  indicator: view.stat.shortLabel,
@@ -130,6 +170,7 @@ export function projectStatsHeadlessBundle(
130
170
  formattedLatest: reportFormat(view.stat)(view.latest.value),
131
171
  previous: view.previous?.value ?? null,
132
172
  formattedPrevious: view.previous ? reportFormat(view.stat)(view.previous.value) : "-",
173
+ previousPeriod: view.previous?.date ?? null,
133
174
  // A spread's values stay in percentage points; these carry the figures its text shows.
134
175
  ...(view.stat.basisPoints ? {
135
176
  latestBasisPoints: toBasisPoints(view.latest.value),
@@ -139,6 +180,7 @@ export function projectStatsHeadlessBundle(
139
180
  // The period the figure covers; `releasedOn` is when it was published, which the report is as of.
140
181
  asOf: view.latest.date,
141
182
  releasedOn: view.releasedOn,
183
+ exportAsOf: exportAsOf(view),
142
184
  ...(view.periodicity ? { periodicity: view.periodicity } : {}),
143
185
  reading: view.reading,
144
186
  released: view.released,
@@ -159,7 +201,12 @@ export function projectStatsHeadlessBundle(
159
201
  ...(cutoff ? { freshness: { ...ECST_FRESHNESS, schedule: cutoff } } : {}),
160
202
  sections: [
161
203
  ...sections,
162
- ...(selected ? [{ title: selected.stat.label, entries: detailEntries(selected, cutoff != null) }] : []),
204
+ ...(selected ? [{
205
+ title: selected.stat.label,
206
+ entries: detailEntries(selected, cutoff != null, false),
207
+ textEntries: detailEntries(selected, cutoff != null, true),
208
+ ...exportDating(selected),
209
+ }] : []),
163
210
  ],
164
211
  errors: bundle.errors,
165
212
  notes: [
@@ -187,10 +234,11 @@ const ECST_FRESHNESS = { source: "FRED", status: "not-a-feed", basis: "published
187
234
  export const econStatisticsHeadless: HeadlessPaneDefinition<"bundle"> = {
188
235
  shape: "bundle",
189
236
  freshness: ECST_FRESHNESS,
237
+ exportDated: true,
190
238
  argument: {
191
239
  kind: "free-text",
192
240
  placeholder: "statistic",
193
- description: "Optional statistic id, FRED series id, label, or prefix.",
241
+ description: "Optional statistic id, name (PCE, GDP), label, FRED series id, or prefix.",
194
242
  optional: true,
195
243
  },
196
244
  options: [{
@@ -202,7 +250,8 @@ export const econStatisticsHeadless: HeadlessPaneDefinition<"bundle"> = {
202
250
  }],
203
251
  describe: (args) => `Economic Statistics | ${String(args.options.range)}`,
204
252
  async load(args, ctx) {
205
- const requested = typeof args.argument === "string" ? resolveStatArg(args.argument) : null;
253
+ const read = typeof args.argument === "string" ? readStatArg(args.argument) : null;
254
+ const requested = read?.stat ?? null;
206
255
  if (args.argument && !requested) {
207
256
  throw new Error(
208
257
  `Unknown economic statistic "${String(args.argument)}". Use one of: ${STATS.map(({ id }) => id).join(", ")}.`,
@@ -213,6 +262,7 @@ export const econStatisticsHeadless: HeadlessPaneDefinition<"bundle"> = {
213
262
  loader: createStatSeriesLoader(ctx.apiClient),
214
263
  ...(requested ? { stats: [requested] } : {}),
215
264
  });
216
- return projectStatsHeadlessBundle(bundle, range, requested?.id ?? DEFAULT_STAT_ID);
265
+ const report = projectStatsHeadlessBundle(bundle, range, requested?.id ?? DEFAULT_STAT_ID);
266
+ return read?.readAs ? { ...report, warnings: [read.readAs] } : report;
217
267
  },
218
268
  };
@@ -68,6 +68,7 @@ export const STATS: readonly StatDef[] = [
68
68
  }),
69
69
  stat({
70
70
  id: "pce-yoy",
71
+ aliases: ["pce", "pce inflation", "pce price index"],
71
72
  label: "PCE Prices",
72
73
  shortLabel: "PCE y/y",
73
74
  category: "inflation",
@@ -179,6 +180,7 @@ export const STATS: readonly StatDef[] = [
179
180
  // ---- Growth ----------------------------------------------------------
180
181
  stat({
181
182
  id: "real-gdp",
183
+ aliases: ["gdp", "gdp growth"],
182
184
  measurementBasis: "q/q annualized",
183
185
  label: "Real GDP",
184
186
  shortLabel: "Real GDP q/q",
@@ -270,6 +272,7 @@ export const STATS: readonly StatDef[] = [
270
272
  reference: { value: 0, label: "flat" },
271
273
  staleAfterMs: NOWCAST_QUARTERLY,
272
274
  note: "Atlanta Fed model estimate of real GDP growth, updated within the quarter. Already an annualized percentage change, not a GDP level.",
275
+ keywords: ["nowcast", "GDP nowcast"],
273
276
  }),
274
277
  stat({
275
278
  id: "nfci",
@@ -636,15 +639,40 @@ export const STATS: readonly StatDef[] = [
636
639
 
637
640
  export const DEFAULT_STAT_ID = STATS[0]!.id;
638
641
 
639
- /** Resolves shortcut arguments like `ECST cpi` onto a statistic. */
640
- export function resolveStatArg(arg: string | null | undefined): StatDef | null {
642
+ /** A statistic an argument names, and what to say when it had to be read. */
643
+ interface StatArgRead {
644
+ stat: StatDef;
645
+ /** "Read "PCE" as PCE Prices (PCE y/y)." when the argument is a name or a prefix, not the statistic's id, label or FRED series; null when it spelled one. */
646
+ readAs: string | null;
647
+ }
648
+
649
+ /**
650
+ * Reads a shortcut argument like `ECST cpi` as a statistic. What it is called
651
+ * comes before what FRED files it under: a statistic's id, its label, one of
652
+ * its common names (`PCE`, `gdp`), then a FRED series id, then the start of an
653
+ * id or a label. A name or a start says what it read, and a name that is also
654
+ * another statistic's FRED series (`PCE` is personal spending there) points at it.
655
+ */
656
+ export function readStatArg(arg: string | null | undefined): StatArgRead | null {
641
657
  const query = arg?.trim().toLowerCase();
642
658
  if (!query) return null;
643
- return STATS.find((entry) => entry.id === query)
644
- ?? STATS.find((entry) => entry.seriesId.toLowerCase() === query)
645
- ?? STATS.find((entry) =>
646
- entry.id.startsWith(query)
647
- || entry.label.toLowerCase().startsWith(query)
648
- || entry.shortLabel.toLowerCase().startsWith(query))
649
- ?? null;
659
+ const exact = STATS.find((entry) => entry.id === query)
660
+ ?? STATS.find((entry) => entry.label.toLowerCase() === query || entry.shortLabel.toLowerCase() === query);
661
+ if (exact) return { stat: exact, readAs: null };
662
+ const named = STATS.find((entry) => entry.aliases?.some((alias) => alias.toLowerCase() === query));
663
+ const series = STATS.find((entry) => entry.seriesId.toLowerCase() === query);
664
+ if (!named && series) return { stat: series, readAs: null };
665
+ const stat = named ?? STATS.find((entry) =>
666
+ entry.id.startsWith(query)
667
+ || entry.label.toLowerCase().startsWith(query)
668
+ || entry.shortLabel.toLowerCase().startsWith(query));
669
+ if (!stat) return null;
670
+ const otherSeries = series && series !== stat
671
+ ? ` FRED's series ${series.seriesId} is ${series.label.toLowerCase()}: type ${series.id} for it.` : "";
672
+ return { stat, readAs: `Read "${arg!.trim()}" as ${stat.label} (${stat.shortLabel}).${otherSeries}` };
673
+ }
674
+
675
+ /** Resolves shortcut arguments like `ECST cpi` onto a statistic. */
676
+ export function resolveStatArg(arg: string | null | undefined): StatDef | null {
677
+ return readStatArg(arg)?.stat ?? null;
650
678
  }
@@ -34,6 +34,8 @@ export interface StatViewModel {
34
34
  periodicity: "monthly" | "quarterly" | "annual" | null;
35
35
  /** The print before this one, for the direction of the last move. */
36
36
  previous: StatPoint | null;
37
+ /** What that print is a reading of, as `reading` says the latest's: "Aug 2026"; null with no previous print. */
38
+ previousReading: string | null;
37
39
  yearAgo: StatPoint | null;
38
40
  changeOnPrevious: number | null;
39
41
  /** Where the current reading sits within the selected range. */
@@ -153,6 +155,7 @@ export function projectStat(
153
155
  readingReleased: periodReleasedText(reading, releasedOn, latest.date),
154
156
  periodicity: periodStartPeriodicity(latest.date, perYear),
155
157
  previous,
158
+ previousReading: previous ? formatStatReading(previous.date, perYear) : null,
156
159
  yearAgo,
157
160
  changeOnPrevious: previous ? latest.value - previous.value : null,
158
161
  percentile: visible.length === 0 ? 0 : (100 * atOrBelow) / visible.length,
@@ -0,0 +1,179 @@
1
+ import type { ScreenDefinition, ScreenPayload, ScreenRow } from "../../../api-client/equity-screener";
2
+
3
+ /**
4
+ * The sector and industry names a screen's `sector` and `industry` criteria
5
+ * match, as the screener's rows carry them (read from the covered universe on
6
+ * 2026-10-28): the eleven sectors and the industries under them. A criterion
7
+ * matches a name exactly, so `Utility` or `utilities` matches nothing.
8
+ */
9
+ export const SCREEN_SECTORS = [
10
+ "Basic Materials", "Communication Services", "Consumer Cyclical", "Consumer Defensive", "Energy",
11
+ "Financial Services", "Healthcare", "Industrials", "Real Estate", "Technology", "Utilities",
12
+ ] as const;
13
+
14
+ export const SCREEN_INDUSTRIES = [
15
+ "Advertising Agencies", "Aerospace & Defense", "Agricultural Inputs", "Airlines", "Airports & Air Services",
16
+ "Aluminum", "Apparel Manufacturing", "Apparel Retail", "Asset Management", "Auto Manufacturers", "Auto Parts",
17
+ "Auto & Truck Dealerships", "Banks - Diversified", "Banks - Regional", "Beverages - Brewers",
18
+ "Beverages - Non-Alcoholic", "Beverages - Wineries & Distilleries", "Biotechnology", "Broadcasting",
19
+ "Building Materials", "Building Products & Equipment", "Business Equipment & Supplies", "Capital Markets",
20
+ "Chemicals", "Coking Coal", "Communication Equipment", "Computer Hardware", "Confectioners", "Conglomerates",
21
+ "Consulting Services", "Consumer Electronics", "Copper", "Credit Services", "Department Stores",
22
+ "Diagnostics & Research", "Discount Stores", "Drug Manufacturers - General",
23
+ "Drug Manufacturers - Specialty & Generic", "Education & Training Services", "Electrical Equipment & Parts",
24
+ "Electronic Components", "Electronic Gaming & Multimedia", "Electronics & Computer Distribution",
25
+ "Engineering & Construction", "Entertainment", "Farm & Heavy Construction Machinery", "Farm Products",
26
+ "Financial Conglomerates", "Financial Data & Stock Exchanges", "Food Distribution", "Footwear & Accessories",
27
+ "Furnishings, Fixtures & Appliances", "Gambling", "Gold", "Grocery Stores", "Healthcare Plans",
28
+ "Health Information Services", "Home Improvement Retail", "Household & Personal Products",
29
+ "Industrial Distribution", "Information Technology Services", "Infrastructure Operations", "Insurance Brokers",
30
+ "Insurance - Diversified", "Insurance - Life", "Insurance - Property & Casualty", "Insurance - Reinsurance",
31
+ "Insurance - Specialty", "Integrated Freight & Logistics", "Internet Content & Information", "Internet Retail",
32
+ "Leisure", "Lodging", "Lumber & Wood Production", "Luxury Goods", "Marine Shipping", "Medical Care Facilities",
33
+ "Medical Devices", "Medical Distribution", "Medical Instruments & Supplies", "Metal Fabrication",
34
+ "Mortgage Finance", "Oil & Gas Drilling", "Oil & Gas E&P", "Oil & Gas Equipment & Services",
35
+ "Oil & Gas Integrated", "Oil & Gas Midstream", "Oil & Gas Refining & Marketing",
36
+ "Other Industrial Metals & Mining", "Other Precious Metals & Mining", "Packaged Foods",
37
+ "Packaging & Containers", "Paper & Paper Products", "Personal Services", "Pharmaceutical Retailers",
38
+ "Pollution & Treatment Controls", "Publishing", "Railroads", "Real Estate - Development",
39
+ "Real Estate Services", "Recreational Vehicles", "REIT - Diversified", "REIT - Healthcare Facilities",
40
+ "REIT - Hotel & Motel", "REIT - Industrial", "REIT - Mortgage", "REIT - Office", "REIT - Residential",
41
+ "REIT - Retail", "REIT - Specialty", "Rental & Leasing Services", "Residential Construction",
42
+ "Resorts & Casinos", "Restaurants", "Scientific & Technical Instruments", "Security & Protection Services",
43
+ "Semiconductor Equipment & Materials", "Semiconductors", "Shell Companies", "Silver",
44
+ "Software - Application", "Software - Infrastructure", "Solar", "Specialty Business Services",
45
+ "Specialty Chemicals", "Specialty Industrial Machinery", "Specialty Retail", "Staffing & Employment Services",
46
+ "Steel", "Telecom Services", "Textile Manufacturing", "Thermal Coal", "Tobacco", "Tools & Accessories",
47
+ "Travel Services", "Trucking", "Uranium", "Utilities - Diversified", "Utilities - Independent Power Producers",
48
+ "Utilities - Regulated Electric", "Utilities - Regulated Gas", "Utilities - Regulated Water",
49
+ "Utilities - Renewable", "Waste Management",
50
+ ] as const;
51
+
52
+ /** GICS sector names, which these sectors are not, as the sector each means. */
53
+ const GICS_SECTORS: Record<string, (typeof SCREEN_SECTORS)[number]> = {
54
+ informationtechnology: "Technology",
55
+ financials: "Financial Services",
56
+ healthcare: "Healthcare",
57
+ consumerstaples: "Consumer Defensive",
58
+ consumerdiscretionary: "Consumer Cyclical",
59
+ materials: "Basic Materials",
60
+ telecommunicationservices: "Communication Services",
61
+ };
62
+
63
+ /** The category fields whose names a screen can be checked against. A symbol has no list to check. */
64
+ type NamedField = "sector" | "industry" | "exchange" | "currency";
65
+ const NAMED_FIELDS: readonly NamedField[] = ["sector", "industry", "exchange", "currency"];
66
+
67
+ /** A criterion's value that names nothing the screen covers, with the names it was likely meant as. */
68
+ interface UnmatchedCategoryValue {
69
+ field: NamedField;
70
+ value: string;
71
+ closest: string[];
72
+ /** The value is a name of the other of sector and industry: `Solar` given as a sector. */
73
+ otherField?: "sector" | "industry";
74
+ }
75
+
76
+ /** Names compared without case, spaces or punctuation: `Oil and Gas E&P` beside `oil & gas e&p`. */
77
+ const fold = (name: string) => name.toLowerCase().replace(/\band\b/g, "&").replace(/[^a-z0-9&]+/g, "");
78
+
79
+ function editDistance(left: string, right: string): number {
80
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
81
+ for (let row = 1; row <= left.length; row += 1) {
82
+ const current = [row];
83
+ for (let column = 1; column <= right.length; column += 1) {
84
+ const cost = left[row - 1] === right[column - 1] ? 0 : 1;
85
+ current[column] = Math.min(previous[column]! + 1, current[column - 1]! + 1, previous[column - 1]! + cost);
86
+ }
87
+ previous = current;
88
+ }
89
+ return previous[right.length]!;
90
+ }
91
+
92
+ /** How far a typed name is from a candidate, 0 the same and 1 nothing alike: against the whole name or one of its parts. */
93
+ function nameDistance(typed: string, candidate: string): number {
94
+ const needle = fold(typed);
95
+ if (!needle) return 1;
96
+ const parts = [candidate, ...candidate.split(/\s+-\s+|\s+/)].map(fold).filter(Boolean);
97
+ return Math.min(...parts.map((part) => {
98
+ if (part === needle) return 0;
99
+ // "Bank" in "Banks - Regional", "Regulated" in "Utilities - Regulated Gas".
100
+ if (needle.length >= 4 && part.startsWith(needle)) return 0.2;
101
+ return editDistance(needle, part) / Math.max(needle.length, part.length);
102
+ }));
103
+ }
104
+
105
+ /** Names further than this from what was typed are not offered. */
106
+ const CLOSEST_DISTANCE = 0.4;
107
+ const CLOSEST_LIMIT = 5;
108
+
109
+ /** The names a typed one was likely meant as, closest first. */
110
+ function closestCategoryNames(typed: string, names: Iterable<string>, limit = CLOSEST_LIMIT): string[] {
111
+ const scored = [...new Set(names)].map((name) => ({ name, distance: nameDistance(typed, name) }));
112
+ // The name typed in another case or spacing is the one meant, whatever else is near it.
113
+ const same = scored.filter(({ name }) => fold(name) === fold(typed));
114
+ return (same.length > 0 ? same : scored)
115
+ .filter(({ distance }) => distance <= CLOSEST_DISTANCE)
116
+ .sort((a, b) => a.distance - b.distance || a.name.localeCompare(b.name))
117
+ .slice(0, limit)
118
+ .map(({ name }) => name);
119
+ }
120
+
121
+ /**
122
+ * The names each category field matches: the sector and industry lists above
123
+ * with those the rows carry, and the exchanges and currencies the covered
124
+ * universe reports. A field with no list (an older server's universe) is left
125
+ * out, so nothing is said of its values.
126
+ */
127
+ function knownNames(payload: Pick<ScreenPayload, "universe">, rows: readonly Pick<ScreenRow, NamedField>[]): Record<NamedField, Set<string> | null> {
128
+ const seen = (field: NamedField) => rows.flatMap((row) => row[field] ? [row[field]!] : []);
129
+ const served = (list: readonly string[] | undefined, field: NamedField) =>
130
+ list?.length ? new Set([...list, ...seen(field)]) : null;
131
+ return {
132
+ sector: new Set([...SCREEN_SECTORS, ...payload.universe.sectors ?? [], ...seen("sector")]),
133
+ industry: new Set([...SCREEN_INDUSTRIES, ...seen("industry")]),
134
+ exchange: served(payload.universe.exchanges, "exchange"),
135
+ currency: served(payload.universe.currencies, "currency"),
136
+ };
137
+ }
138
+
139
+ /**
140
+ * The values of a screen's category criteria that name nothing the screen
141
+ * covers (`Utility` for the Utilities sector), so the screen reads why it
142
+ * matched nothing, or less than it should, rather than "No data".
143
+ */
144
+ export function unmatchedCategoryValues(
145
+ definition: ScreenDefinition,
146
+ payload: Pick<ScreenPayload, "universe">,
147
+ rows: readonly Pick<ScreenRow, NamedField>[] = [],
148
+ ): UnmatchedCategoryValue[] {
149
+ const known = knownNames(payload, rows);
150
+ const unmatched: UnmatchedCategoryValue[] = [];
151
+ for (const criterion of definition.criteria) {
152
+ if (criterion.op !== "in" || !(NAMED_FIELDS as readonly string[]).includes(criterion.field)) continue;
153
+ const field = criterion.field as NamedField;
154
+ const names = known[field];
155
+ if (!names) continue;
156
+ for (const value of criterion.value) {
157
+ if (names.has(value)) continue;
158
+ const other = field === "sector" ? "industry" : field === "industry" ? "sector" : null;
159
+ const otherNames = other ? known[other] : null;
160
+ const otherField = other && otherNames && [...otherNames].some((name) => fold(name) === fold(value)) ? other : undefined;
161
+ const gics = field === "sector" ? GICS_SECTORS[fold(value)] : undefined;
162
+ const closest = gics ? [gics] : closestCategoryNames(value, names);
163
+ unmatched.push({ field, value, closest, ...(otherField ? { otherField } : {}) });
164
+ }
165
+ }
166
+ return unmatched;
167
+ }
168
+
169
+ const quoted = (value: string) => JSON.stringify(value);
170
+
171
+ /** What a screen says of a value that names nothing: `No sector is named "Utility". Closest: Utilities.` */
172
+ export function unmatchedCategoryText(entry: UnmatchedCategoryValue): string {
173
+ const head = `No ${entry.field} is named ${quoted(entry.value)}`;
174
+ if (entry.otherField) return `${head}; it is ${entry.otherField === "industry" ? "an industry" : "a sector"} name, so use "field": ${quoted(entry.otherField)}.`;
175
+ return entry.closest.length > 0 ? `${head}. Closest: ${entry.closest.join(", ")}.` : `${head}.`;
176
+ }
177
+
178
+ /** Where every sector and industry name is listed, said once after the values that name nothing. */
179
+ export const CATEGORY_NAMES_POINTER = "gloom catalog EQS lists every sector and industry.";
@@ -21,7 +21,7 @@ const savedPath = (id: string) => `saved/${encodeURIComponent(id)}`;
21
21
  export const screenerApi = {
22
22
  query: (query: ScreenQuery, signal?: AbortSignal, client: ScreenerClient = apiClient) =>
23
23
  client.equityScreener<ScreenPayload>("query", json("POST", query, { signal })),
24
- fields: (signal?: AbortSignal) => apiClient.equityScreener<ScreenFieldsResponse>("fields?include=social,research,attention,hiring-apps,kpis,volatility,dividends", { signal }),
24
+ fields: (signal?: AbortSignal) => apiClient.equityScreener<ScreenFieldsResponse>("fields?include=social,research,attention,hiring-apps,kpis,volatility,dividends,technical,option-volatility,ownership", { signal }),
25
25
  export: (definition: ScreenDefinition, snapshotId: string) =>
26
26
  apiClient.equityScreener<ScreenExportResponse>("export", json("POST", { ...definition, snapshotId }, { headers: { Accept: "application/json" } })),
27
27
  saved: async (signal?: AbortSignal, client: ScreenerClient = apiClient) =>
@@ -40,10 +40,12 @@ export async function fetchScreen(
40
40
  cursor: string | null = null,
41
41
  signal?: AbortSignal,
42
42
  client: ScreenerClient = apiClient,
43
+ /** Rows a page holds, at most the server's 200. */
44
+ limit = 100,
43
45
  ): Promise<ScreenPayload> {
44
46
  const payload = validateScreenPayload(
45
47
  await screenerApi.query(
46
- { ...parseScreenDefinition(definition), limit: 100, cursor },
48
+ { ...parseScreenDefinition(definition), limit, cursor },
47
49
  signal,
48
50
  client,
49
51
  ),