gloomberb 0.17.5 → 0.18.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 (922) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/package.json +8 -1
  4. package/patches/electrobun@1.18.1.patch +74 -0
  5. package/src/account/auth-model.ts +3 -2
  6. package/src/account/device-signin-dialog.tsx +38 -13
  7. package/src/account/device-signin.ts +2 -1
  8. package/src/account/upgrade-dialog.tsx +6 -2
  9. package/src/api-client/askg.ts +5 -3
  10. package/src/api-client/bank-figures.ts +73 -0
  11. package/src/api-client/chat.ts +9 -0
  12. package/src/api-client/commodity-balances.ts +77 -0
  13. package/src/api-client/credit-boards.ts +21 -1
  14. package/src/api-client/crypto-markets.ts +88 -1
  15. package/src/api-client/data.ts +135 -6
  16. package/src/api-client/debt-maturities.ts +18 -0
  17. package/src/api-client/earnings.ts +10 -0
  18. package/src/api-client/equity-screener.ts +3 -0
  19. package/src/api-client/errors.ts +13 -0
  20. package/src/api-client/fund-facts.ts +72 -0
  21. package/src/api-client/futures-curve.ts +26 -3
  22. package/src/api-client/geo.ts +11 -1
  23. package/src/api-client/index.ts +30 -2
  24. package/src/api-client/insider-signals.ts +159 -0
  25. package/src/api-client/market-discovery.ts +1 -1
  26. package/src/api-client/normalizers.ts +2 -0
  27. package/src/api-client/paths.ts +28 -0
  28. package/src/api-client/request.ts +11 -3
  29. package/src/api-client/research-activity.ts +15 -8
  30. package/src/api-client/socket.ts +16 -3
  31. package/src/api-client/supply-chain-graph.ts +2 -2
  32. package/src/api-client/supply-chain.ts +8 -0
  33. package/src/api-client/tape.ts +12 -0
  34. package/src/api-client/types.ts +91 -3
  35. package/src/api-client/upgrade-experiment.ts +4 -3
  36. package/src/api-client/wall-experiments.ts +5 -3
  37. package/src/api-client/wall-summary.ts +3 -1
  38. package/src/api-client/web-experiments.ts +5 -2
  39. package/src/api-client/yield-curves.ts +17 -0
  40. package/src/app/command-bar-intro.ts +38 -0
  41. package/src/app/keybindings/actions.ts +1 -1
  42. package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
  43. package/src/app/pane-runtime/plugin-bindings.ts +4 -0
  44. package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
  45. package/src/app/pane-runtime/ticker-open-target.ts +31 -0
  46. package/src/app/runtime/broker-import.ts +23 -21
  47. package/src/app/runtime/desktop-deeplink.ts +3 -4
  48. package/src/app/runtime/plugin-bindings.ts +16 -9
  49. package/src/app/runtime/startup.ts +3 -4
  50. package/src/app/runtime/ticker-refresh.ts +13 -12
  51. package/src/app/runtime/update.ts +17 -3
  52. package/src/architecture/core-builtin-allowlist.json +2 -13
  53. package/src/architecture/silent-catch-allowlist.json +3 -0
  54. package/src/architecture/ui-literals-allowlist.json +186 -0
  55. package/src/architecture/ui-tokens-allowlist.json +40 -0
  56. package/src/ask-gloom/pane-context.ts +3 -1
  57. package/src/brokers/broker-portfolio-sync.ts +66 -0
  58. package/src/brokers/signed-in/catalog.ts +2 -1
  59. package/src/brokers/signed-in/client.ts +7 -2
  60. package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
  61. package/src/brokers/signed-in/sign-in.ts +22 -20
  62. package/src/brokers/sync-broker-instance.ts +77 -42
  63. package/src/capabilities/chart-series.ts +2 -0
  64. package/src/capabilities/registry.ts +1 -3
  65. package/src/cli/bank-section.ts +147 -0
  66. package/src/cli/base-converter.ts +5 -2
  67. package/src/cli/board-coins.ts +70 -0
  68. package/src/cli/commands/fred-series.ts +90 -0
  69. package/src/cli/commands/market.ts +434 -96
  70. package/src/cli/commands/news-search.ts +35 -2
  71. package/src/cli/commands/options-chain.ts +25 -9
  72. package/src/cli/commands/options-leaps.ts +3 -3
  73. package/src/cli/commands/overview.ts +130 -40
  74. package/src/cli/commands/plugins.ts +1 -0
  75. package/src/cli/commands/search.ts +87 -24
  76. package/src/cli/commands/system.ts +5 -2
  77. package/src/cli/commands/ticker.ts +361 -64
  78. package/src/cli/context.ts +15 -1
  79. package/src/cli/desktop-pane-shot.ts +11 -3
  80. package/src/cli/earnings-calendar.ts +2 -1
  81. package/src/cli/errors.ts +25 -2
  82. package/src/cli/export-source.ts +18 -3
  83. package/src/cli/fx-pair.ts +77 -0
  84. package/src/cli/glossary.ts +196 -14
  85. package/src/cli/history-rows.ts +84 -19
  86. package/src/cli/index-roots.ts +26 -0
  87. package/src/cli/index.ts +57 -20
  88. package/src/cli/known-gaps.ts +156 -22
  89. package/src/cli/listing-arg.ts +217 -150
  90. package/src/cli/listing-suggestion.ts +7 -23
  91. package/src/cli/name-entries.ts +76 -0
  92. package/src/cli/not-a-ticker.ts +15 -6
  93. package/src/cli/pane-functions/access-gate.ts +22 -0
  94. package/src/cli/pane-functions/capabilities.ts +62 -4
  95. package/src/cli/pane-functions/catalog.ts +144 -39
  96. package/src/cli/pane-functions/data.ts +6 -5
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +3 -3
  99. package/src/cli/pane-functions/freshness.ts +93 -17
  100. package/src/cli/pane-functions/function-help.ts +20 -17
  101. package/src/cli/pane-functions/headless.ts +187 -40
  102. package/src/cli/pane-functions/index.ts +126 -12
  103. package/src/cli/pane-functions/options.ts +17 -3
  104. package/src/cli/pane-functions/report-notices.ts +17 -3
  105. package/src/cli/pane-functions/resolver.ts +74 -18
  106. package/src/cli/pane-functions/screenshot.ts +126 -23
  107. package/src/cli/pane-functions/shot-options.ts +68 -0
  108. package/src/cli/perpetual-contract.ts +3 -1
  109. package/src/cli/report-tables.ts +21 -6
  110. package/src/cli/result.ts +2 -0
  111. package/src/cli/series-search.ts +6 -1
  112. package/src/cli/staged-tickers.ts +5 -0
  113. package/src/cli/statement-filings.ts +261 -0
  114. package/src/cli/symbol-args.ts +47 -8
  115. package/src/cli/symbol-list.ts +9 -2
  116. package/src/cli/symbol-rows.ts +269 -29
  117. package/src/cli/ticker-resolution.ts +148 -35
  118. package/src/cli/treasury-yields.ts +86 -0
  119. package/src/cli/types.ts +3 -0
  120. package/src/cli/uncovered-instrument.ts +20 -0
  121. package/src/cli/unknown-command.ts +18 -0
  122. package/src/components/chart/composite/axis-overlays.tsx +16 -9
  123. package/src/components/chart/composite/composite-chart.tsx +163 -23
  124. package/src/components/chart/composite/downsample.ts +19 -3
  125. package/src/components/chart/composite/format.ts +13 -0
  126. package/src/components/chart/composite/legend.tsx +161 -68
  127. package/src/components/chart/composite/panel-bitmap.ts +8 -2
  128. package/src/components/chart/composite/panel-labels.ts +38 -0
  129. package/src/components/chart/composite/panel-series.ts +1 -0
  130. package/src/components/chart/composite/panel-surface.tsx +28 -10
  131. package/src/components/chart/composite/price-axis-labels.tsx +16 -3
  132. package/src/components/chart/composite/scene.ts +60 -2
  133. package/src/components/chart/composite/series-axis-tags.tsx +67 -0
  134. package/src/components/chart/composite/toolbar.tsx +12 -7
  135. package/src/components/chart/composite/tools.ts +2 -2
  136. package/src/components/chart/composite/types.ts +34 -0
  137. package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
  138. package/src/components/chart/core/view-reset.ts +46 -0
  139. package/src/components/chart/curve/curve-surface.tsx +1 -1
  140. package/src/components/chart/static/series.ts +39 -0
  141. package/src/components/chart/static/trail-chart-surface.tsx +2 -1
  142. package/src/components/command-bar/commands/collection-list.ts +45 -15
  143. package/src/components/command-bar/commands/collection.ts +11 -4
  144. package/src/components/command-bar/commands/direct/index.ts +1 -1
  145. package/src/components/command-bar/commands/registry.ts +2 -2
  146. package/src/components/command-bar/list/badge.ts +4 -3
  147. package/src/components/command-bar/list/model.ts +12 -0
  148. package/src/components/command-bar/list/view.tsx +1 -1
  149. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  150. package/src/components/command-bar/panel/index.tsx +19 -8
  151. package/src/components/command-bar/panel/native-surface.ts +6 -4
  152. package/src/components/command-bar/panel/prompt-example.ts +22 -0
  153. package/src/components/command-bar/routes/root/command-items.ts +1 -1
  154. package/src/components/command-bar/routes/root/pick-prefetch.ts +38 -0
  155. package/src/components/command-bar/routes/root/provider-search.ts +9 -9
  156. package/src/components/command-bar/routes/root/results.ts +13 -0
  157. package/src/components/command-bar/routes/root/runtime.ts +3 -0
  158. package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -1
  159. package/src/components/command-bar/routes/root/shortcut-items.ts +1 -1
  160. package/src/components/command-bar/routes/root/start-here.ts +90 -0
  161. package/src/components/command-bar/routes/ticker-search/actions.ts +34 -0
  162. package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
  163. package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
  164. package/src/components/command-bar/surface/index.tsx +5 -0
  165. package/src/components/command-bar/theme-picker.tsx +3 -1
  166. package/src/components/command-bar/workflow/builtin.ts +5 -1
  167. package/src/components/command-bar/workflow/collection-actions.ts +23 -13
  168. package/src/components/command-bar/workflow/ops.ts +25 -3
  169. package/src/components/command-bar/workflow/pane-reuse.ts +3 -2
  170. package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
  171. package/src/components/command-bar/workflow/tickers.ts +7 -5
  172. package/src/components/feedback-dialog.tsx +1 -1
  173. package/src/components/form-modal/broker-step.tsx +7 -63
  174. package/src/components/form-modal/content.tsx +7 -2
  175. package/src/components/form-modal/field-row.tsx +6 -8
  176. package/src/components/form-modal/host.tsx +3 -3
  177. package/src/components/form-modal/model.ts +3 -0
  178. package/src/components/form-modal/routes.ts +17 -2
  179. package/src/components/index.ts +6 -6
  180. package/src/components/input-search-bar.tsx +43 -6
  181. package/src/components/layout/detached-pane-shell.tsx +0 -1
  182. package/src/components/layout/floating-pane.tsx +0 -1
  183. package/src/components/layout/header.tsx +8 -7
  184. package/src/components/layout/pane/content.tsx +11 -8
  185. package/src/components/layout/pane/footer/index.tsx +7 -4
  186. package/src/components/layout/pane/footer/model.ts +28 -2
  187. package/src/components/layout/pane/header.tsx +9 -19
  188. package/src/components/layout/pane/index.tsx +0 -1
  189. package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
  190. package/src/components/layout/pane/pane-tabs.tsx +4 -3
  191. package/src/components/layout/pane/sizing.ts +9 -0
  192. package/src/components/layout/pane/ticker-fate-gate.tsx +62 -0
  193. package/src/components/layout/shell/pane/capture.ts +44 -20
  194. package/src/components/layout/status-bar.tsx +3 -6
  195. package/src/components/markdown-text.tsx +17 -2
  196. package/src/components/market-board/index.tsx +19 -8
  197. package/src/components/metric-treemap/heat-surface.tsx +2 -7
  198. package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
  199. package/src/components/onboarding/onboarding-frame.tsx +24 -21
  200. package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
  201. package/src/components/onboarding/wizard-model.ts +1 -1
  202. package/src/components/onboarding/wizard-positions.ts +56 -19
  203. package/src/components/pane-settings-dialog/desktop.tsx +12 -50
  204. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
  205. package/src/components/price-performance.tsx +18 -2
  206. package/src/components/reader/article-reader.tsx +11 -10
  207. package/src/components/reader/detail-reader.tsx +9 -5
  208. package/src/components/reader/reader-text.tsx +51 -24
  209. package/src/components/sign-in-code-panel.tsx +129 -75
  210. package/src/components/ticker/badge/format.ts +64 -15
  211. package/src/components/ticker/badge/index.tsx +33 -11
  212. package/src/components/ticker/badge/list.tsx +4 -3
  213. package/src/components/ticker/badge/text.tsx +2 -2
  214. package/src/components/ticker/list-table-view.tsx +8 -1
  215. package/src/components/ui/action-row.tsx +5 -2
  216. package/src/components/ui/audio-player.tsx +70 -0
  217. package/src/components/ui/card.tsx +75 -0
  218. package/src/components/ui/data-table/index.tsx +4 -2
  219. package/src/components/ui/data-table/opentui/index.tsx +61 -20
  220. package/src/components/ui/data-table/opentui/model.ts +104 -0
  221. package/src/components/ui/data-table/types.ts +11 -0
  222. package/src/components/ui/detail-scroll-body.tsx +1 -1
  223. package/src/components/ui/dialog-actions.tsx +1 -21
  224. package/src/components/ui/display.tsx +13 -5
  225. package/src/components/ui/external-link.tsx +4 -2
  226. package/src/components/ui/fields.tsx +12 -0
  227. package/src/components/ui/figure.tsx +5 -3
  228. package/src/components/ui/frame.tsx +40 -6
  229. package/src/components/ui/icon.tsx +4 -0
  230. package/src/components/ui/index.ts +4 -1
  231. package/src/components/ui/inline-quick-add.tsx +4 -1
  232. package/src/components/ui/list-view.tsx +8 -0
  233. package/src/components/ui/prose.tsx +97 -25
  234. package/src/components/ui/query-bar-keys.tsx +32 -6
  235. package/src/components/ui/query-bar.tsx +35 -4
  236. package/src/components/ui/quote.ts +42 -0
  237. package/src/components/ui/range-track.tsx +15 -2
  238. package/src/components/ui/reveal-in-scroll-box.ts +21 -3
  239. package/src/components/ui/select-button.tsx +38 -3
  240. package/src/components/ui/status.tsx +73 -4
  241. package/src/components/ui/table-layout.ts +20 -0
  242. package/src/components/ui/windowed-list.tsx +27 -0
  243. package/src/components/use-pinned-moment.ts +19 -0
  244. package/src/core/app-runtime.ts +2 -2
  245. package/src/core/state/app/instrument.ts +18 -1
  246. package/src/core/state/app/layout.ts +6 -0
  247. package/src/core/state/app/state.ts +14 -2
  248. package/src/data/cached-query.ts +1 -1
  249. package/src/data/config/chart-settings.ts +3 -1
  250. package/src/data/config/home.ts +4 -1
  251. package/src/data/config/store/node.ts +9 -4
  252. package/src/data/config/store/normalize.ts +9 -0
  253. package/src/data/json-storage.ts +6 -3
  254. package/src/data/json-ticker-repository.ts +3 -1
  255. package/src/data/plugin-cache.ts +5 -1
  256. package/src/data/sqlite/json.ts +3 -1
  257. package/src/data/ticker-repository.ts +5 -2
  258. package/src/docs/snapshot.json +248 -5
  259. package/src/feedback/diagnostics.ts +2 -6
  260. package/src/i18n/es.ts +86 -6
  261. package/src/i18n/index.ts +3 -9
  262. package/src/i18n/ja.ts +86 -6
  263. package/src/i18n/ko.ts +86 -6
  264. package/src/i18n/zh-cn.ts +86 -6
  265. package/src/i18n/zh-tw.ts +86 -6
  266. package/src/layout-marketplace/cloud.ts +16 -5
  267. package/src/layout-marketplace/gallery-desktop.tsx +8 -4
  268. package/src/layout-marketplace/gallery.tsx +4 -4
  269. package/src/layout-marketplace/linked-sync.ts +9 -3
  270. package/src/layout-marketplace/linked.ts +24 -3
  271. package/src/layout-marketplace/model.ts +39 -4
  272. package/src/market-data/bars-per-year.ts +43 -0
  273. package/src/market-data/bond-funds.ts +85 -0
  274. package/src/market-data/coordinator/auxiliary.ts +5 -2
  275. package/src/market-data/coordinator/index.ts +39 -5
  276. package/src/market-data/futures-contract.ts +155 -5
  277. package/src/market-data/hooks.tsx +77 -12
  278. package/src/market-data/market/bar-period.ts +107 -0
  279. package/src/market-data/market/format.ts +18 -14
  280. package/src/market-data/market/freshness.ts +29 -4
  281. package/src/market-data/market/index-level.ts +51 -0
  282. package/src/market-data/market/status.ts +152 -13
  283. package/src/market-data/model-dividend-yield.ts +117 -0
  284. package/src/market-data/quotes/freshness.ts +30 -1
  285. package/src/{plugins/builtin/portfolio-list → market-data/quotes}/live-valuation.ts +52 -6
  286. package/src/market-data/quotes/regular-session-valuation.ts +32 -1
  287. package/src/market-data/realized-volatility.ts +16 -7
  288. package/src/market-data/tax-equivalent.ts +344 -0
  289. package/src/news/aggregator.ts +4 -2
  290. package/src/notifications/notification-log.ts +5 -3
  291. package/src/plugins/auto-update-node.ts +1 -0
  292. package/src/plugins/builtin/account-management/footer.ts +1 -1
  293. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  294. package/src/plugins/builtin/account-management/model.ts +1 -0
  295. package/src/plugins/builtin/account-management/pane.tsx +71 -19
  296. package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
  297. package/src/plugins/builtin/alerts/cli.ts +7 -18
  298. package/src/plugins/builtin/alerts/constants.ts +8 -0
  299. package/src/plugins/builtin/alerts/event-rows.ts +16 -0
  300. package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
  301. package/src/plugins/builtin/alerts/headless.ts +182 -0
  302. package/src/plugins/builtin/alerts/index.tsx +3 -0
  303. package/src/plugins/builtin/alerts/pane.tsx +1 -7
  304. package/src/plugins/builtin/alerts/storage.ts +17 -1
  305. package/src/plugins/builtin/analytics/index.tsx +6 -1
  306. package/src/plugins/builtin/analytics/metrics.ts +55 -13
  307. package/src/plugins/builtin/analytics/risk-headless.ts +26 -7
  308. package/src/plugins/builtin/analytics/risk-math.ts +83 -0
  309. package/src/plugins/builtin/analytics/risk-model.ts +171 -19
  310. package/src/plugins/builtin/analytics/risk-pane.tsx +17 -12
  311. package/src/plugins/builtin/analytics/sector-model.ts +14 -3
  312. package/src/plugins/builtin/awards/headless.ts +6 -5
  313. package/src/plugins/builtin/awards/index.tsx +1 -1
  314. package/src/plugins/builtin/backtest/client.ts +38 -8
  315. package/src/plugins/builtin/backtest/engine.ts +25 -13
  316. package/src/plugins/builtin/backtest/export.ts +27 -0
  317. package/src/plugins/builtin/backtest/format.ts +19 -3
  318. package/src/plugins/builtin/backtest/headless.ts +42 -28
  319. package/src/plugins/builtin/backtest/index.tsx +3 -0
  320. package/src/plugins/builtin/backtest/pane.tsx +39 -10
  321. package/src/plugins/builtin/backtest/report.ts +160 -0
  322. package/src/plugins/builtin/backtest/rules.ts +2 -0
  323. package/src/plugins/builtin/backtest/window.ts +112 -0
  324. package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
  325. package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
  326. package/src/plugins/builtin/builtin-plugin-meta.ts +0 -1
  327. package/src/plugins/builtin/catalysts/headless.ts +24 -13
  328. package/src/plugins/builtin/catalysts/index.tsx +4 -2
  329. package/src/plugins/builtin/catalysts/model.ts +18 -1
  330. package/src/plugins/builtin/catalysts/pane.tsx +2 -2
  331. package/src/plugins/builtin/cds/client.ts +28 -5
  332. package/src/plugins/builtin/cds/headless.ts +183 -31
  333. package/src/plugins/builtin/cds/index.tsx +1 -1
  334. package/src/plugins/builtin/cds/model.ts +229 -53
  335. package/src/plugins/builtin/cds/pane.tsx +82 -26
  336. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
  337. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
  338. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
  339. package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
  340. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
  341. package/src/plugins/builtin/chart-composer/editor/controller.ts +14 -10
  342. package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
  343. package/src/plugins/builtin/chart-composer/formula.ts +233 -0
  344. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
  345. package/src/plugins/builtin/chart-composer/headless.ts +117 -15
  346. package/src/plugins/builtin/chart-composer/index.tsx +73 -15
  347. package/src/plugins/builtin/chart-composer/pane.tsx +86 -23
  348. package/src/plugins/builtin/chart-composer/presets.ts +165 -8
  349. package/src/plugins/builtin/chart-composer/quick-add.tsx +9 -2
  350. package/src/plugins/builtin/chart-composer/series-catalog.ts +85 -16
  351. package/src/plugins/builtin/chart-composer/series-expression.ts +10 -8
  352. package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
  353. package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
  354. package/src/plugins/builtin/chart-composer/settings.ts +16 -4
  355. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  356. package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
  357. package/src/plugins/builtin/chat/content/channel-navigation.ts +17 -6
  358. package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
  359. package/src/plugins/builtin/chat/content/composer.tsx +22 -1
  360. package/src/plugins/builtin/chat/content/index.tsx +47 -10
  361. package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
  362. package/src/plugins/builtin/chat/content/mentions.ts +50 -21
  363. package/src/plugins/builtin/chat/content/scroll.ts +12 -5
  364. package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
  365. package/src/plugins/builtin/chat/content/snapshot.ts +40 -6
  366. package/src/plugins/builtin/chat/content/transcript.tsx +48 -37
  367. package/src/plugins/builtin/chat/content/whole-message-copy.ts +44 -2
  368. package/src/plugins/builtin/chat/controller/index.ts +68 -17
  369. package/src/plugins/builtin/chat/controller/mention-candidates.ts +104 -0
  370. package/src/plugins/builtin/chat/controller/realtime.ts +9 -0
  371. package/src/plugins/builtin/chat/controller/state.ts +10 -0
  372. package/src/plugins/builtin/chat/controller/view.ts +2 -0
  373. package/src/plugins/builtin/chat/conversation-recency.ts +73 -0
  374. package/src/plugins/builtin/chat/conversations.ts +12 -5
  375. package/src/plugins/builtin/chat/ghost-user.ts +11 -3
  376. package/src/plugins/builtin/chat/layout.ts +25 -5
  377. package/src/plugins/builtin/chat/message/desktop.tsx +15 -5
  378. package/src/plugins/builtin/chat/message/header.tsx +26 -4
  379. package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
  380. package/src/plugins/builtin/chat/message/render-state.ts +28 -1
  381. package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
  382. package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
  383. package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
  384. package/src/plugins/builtin/chat/share/flow.tsx +9 -73
  385. package/src/plugins/builtin/chat/sidebar-rows.ts +18 -5
  386. package/src/plugins/builtin/chat/sidebar.tsx +6 -1
  387. package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
  388. package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
  390. package/src/plugins/builtin/cloud/askg/manifest.ts +4 -3
  391. package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
  392. package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
  393. package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
  394. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
  395. package/src/plugins/builtin/cloud/plugin.tsx +2 -0
  396. package/src/plugins/builtin/cloud/team/module.tsx +5 -1
  397. package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
  398. package/src/plugins/builtin/cloud/team/views.ts +11 -4
  399. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
  400. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +16 -5
  401. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
  402. package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
  403. package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
  404. package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
  405. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
  406. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
  407. package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
  408. package/src/plugins/builtin/commodities-energy/index.tsx +31 -9
  409. package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
  410. package/src/plugins/builtin/commodity-balances/client.ts +51 -193
  411. package/src/plugins/builtin/commodity-balances/headless.ts +10 -3
  412. package/src/plugins/builtin/commodity-balances/model.ts +5 -180
  413. package/src/plugins/builtin/commodity-balances/pane.tsx +3 -0
  414. package/src/plugins/builtin/company-attention/headless.ts +15 -7
  415. package/src/plugins/builtin/company-attention/index.tsx +1 -1
  416. package/src/plugins/builtin/company-attention/model.ts +49 -4
  417. package/src/plugins/builtin/company-attention/pane.tsx +11 -9
  418. package/src/plugins/builtin/company-kpis/headless.ts +4 -3
  419. package/src/plugins/builtin/company-kpis/index.tsx +2 -2
  420. package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
  421. package/src/plugins/builtin/congress-trades/headless.ts +23 -2
  422. package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
  423. package/src/plugins/builtin/cot/headless.ts +5 -2
  424. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
  425. package/src/plugins/builtin/credit-boards/client.ts +9 -2
  426. package/src/plugins/builtin/credit-boards/headless.ts +93 -27
  427. package/src/plugins/builtin/credit-boards/index.tsx +15 -4
  428. package/src/plugins/builtin/credit-boards/model.ts +105 -6
  429. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
  430. package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
  431. package/src/plugins/builtin/credit-documents/index.tsx +3 -0
  432. package/src/plugins/builtin/credit-documents/model.ts +3 -10
  433. package/src/plugins/builtin/crypto-board/client.ts +152 -2
  434. package/src/plugins/builtin/crypto-board/headless.ts +138 -19
  435. package/src/plugins/builtin/crypto-board/index.tsx +5 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +210 -12
  437. package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
  438. package/src/plugins/builtin/crypto-board/series.ts +52 -1
  439. package/src/plugins/builtin/crypto-board/supply.ts +123 -0
  440. package/src/plugins/builtin/custom-view/index.tsx +1 -1
  441. package/src/plugins/builtin/custom-view/loader.ts +57 -7
  442. package/src/plugins/builtin/custom-view/sources.ts +146 -0
  443. package/src/plugins/builtin/custom-view/view-spec.ts +37 -1
  444. package/src/plugins/builtin/debt-maturities/client.ts +54 -1
  445. package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
  446. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  447. package/src/plugins/builtin/debt-maturities/model.ts +58 -13
  448. package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
  449. package/src/plugins/builtin/dividend-yield/client.ts +42 -36
  450. package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
  451. package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
  452. package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
  453. package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
  454. package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
  455. package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
  456. package/src/plugins/builtin/dividend-yield/types.ts +6 -1
  457. package/src/plugins/builtin/dividend-yield/view.ts +1 -1
  458. package/src/plugins/builtin/docs/blocks.tsx +54 -8
  459. package/src/plugins/builtin/docs/pane.tsx +8 -13
  460. package/src/plugins/builtin/earnings/board-model.ts +33 -3
  461. package/src/plugins/builtin/earnings/board-table.ts +59 -14
  462. package/src/plugins/builtin/earnings/board.tsx +36 -3
  463. package/src/plugins/builtin/earnings/headless.ts +27 -7
  464. package/src/plugins/builtin/earnings/history-model.ts +9 -1
  465. package/src/plugins/builtin/earnings/history-table.ts +16 -5
  466. package/src/plugins/builtin/earnings/history.tsx +40 -10
  467. package/src/plugins/builtin/earnings/index.tsx +3 -1
  468. package/src/plugins/builtin/earnings/no-earnings.ts +52 -0
  469. package/src/plugins/builtin/earnings-calls/data.ts +51 -0
  470. package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
  471. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  472. package/src/plugins/builtin/earnings-calls/model.ts +97 -0
  473. package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
  474. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
  475. package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
  476. package/src/plugins/builtin/econ/calendar-model.ts +22 -0
  477. package/src/plugins/builtin/econ/calendar-source.ts +35 -0
  478. package/src/plugins/builtin/econ/index.tsx +40 -8
  479. package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
  480. package/src/plugins/builtin/econ-statistics/detail.tsx +11 -1
  481. package/src/plugins/builtin/econ-statistics/headless.ts +31 -5
  482. package/src/plugins/builtin/econ-statistics/pane.tsx +5 -3
  483. package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
  484. package/src/plugins/builtin/econ-statistics/view.ts +23 -0
  485. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  486. package/src/plugins/builtin/equity-screener/model.ts +46 -4
  487. package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
  488. package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
  489. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  490. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
  491. package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
  492. package/src/plugins/builtin/executives/index.tsx +1 -0
  493. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  494. package/src/plugins/builtin/fear-greed/index.tsx +9 -0
  495. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  496. package/src/plugins/builtin/futures/index.tsx +2 -2
  497. package/src/plugins/builtin/futures/model.ts +39 -25
  498. package/src/plugins/builtin/futures/series.ts +38 -1
  499. package/src/plugins/builtin/futures/table.tsx +1 -1
  500. package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
  501. package/src/plugins/builtin/futures-curve/client.ts +4 -4
  502. package/src/plugins/builtin/futures-curve/headless.ts +24 -4
  503. package/src/plugins/builtin/futures-curve/index.tsx +2 -2
  504. package/src/plugins/builtin/futures-curve/model.ts +50 -9
  505. package/src/plugins/builtin/futures-curve/pane.tsx +14 -10
  506. package/src/plugins/builtin/help/index.tsx +55 -8
  507. package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
  508. package/src/plugins/builtin/holders/headless.ts +12 -0
  509. package/src/plugins/builtin/insider/headless.ts +345 -5
  510. package/src/plugins/builtin/insider/index.tsx +304 -36
  511. package/src/plugins/builtin/insider/insiders.tsx +224 -0
  512. package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
  513. package/src/plugins/builtin/insider/signals-client.ts +193 -0
  514. package/src/plugins/builtin/insider/signals-model.ts +718 -0
  515. package/src/plugins/builtin/insider/signals.tsx +376 -0
  516. package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
  517. package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
  518. package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
  519. package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
  520. package/src/plugins/builtin/iv-history/client.ts +41 -2
  521. package/src/plugins/builtin/iv-history/headless.ts +26 -16
  522. package/src/plugins/builtin/iv-history/screen-pane.tsx +25 -7
  523. package/src/plugins/builtin/jobs/headless.ts +1 -0
  524. package/src/plugins/builtin/jobs/index.tsx +1 -0
  525. package/src/plugins/builtin/jodi/client.ts +42 -205
  526. package/src/plugins/builtin/jodi/headless.ts +15 -4
  527. package/src/plugins/builtin/jodi/model.ts +18 -43
  528. package/src/plugins/builtin/jodi/pane.tsx +5 -0
  529. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  530. package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
  531. package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
  532. package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
  533. package/src/plugins/builtin/market-movers/client.ts +32 -6
  534. package/src/plugins/builtin/market-movers/headless.ts +7 -2
  535. package/src/plugins/builtin/market-movers/model.ts +64 -3
  536. package/src/plugins/builtin/market-movers/screener.ts +20 -9
  537. package/src/plugins/builtin/market-valuation/detail.tsx +7 -2
  538. package/src/plugins/builtin/market-valuation/headless.ts +17 -4
  539. package/src/plugins/builtin/market-valuation/pane.tsx +8 -4
  540. package/src/plugins/builtin/market-valuation/view.ts +10 -0
  541. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
  542. package/src/plugins/builtin/mna/distress/headless.ts +2 -1
  543. package/src/plugins/builtin/mna/headless.ts +14 -2
  544. package/src/plugins/builtin/mna/index.tsx +1 -0
  545. package/src/plugins/builtin/news/headless.ts +4 -0
  546. package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
  547. package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
  548. package/src/plugins/builtin/notes/cli.ts +8 -4
  549. package/src/plugins/builtin/notes/files.ts +66 -20
  550. package/src/plugins/builtin/notes/migration.ts +18 -6
  551. package/src/plugins/builtin/notes/owner.tsx +3 -2
  552. package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
  553. package/src/plugins/builtin/options-positioning/client.ts +10 -0
  554. package/src/plugins/builtin/options-positioning/headless.ts +88 -13
  555. package/src/plugins/builtin/options-positioning/index.tsx +2 -2
  556. package/src/plugins/builtin/options-positioning/model.ts +33 -0
  557. package/src/plugins/builtin/options-positioning/pane.tsx +38 -16
  558. package/src/plugins/builtin/options-scenario/charts.tsx +13 -4
  559. package/src/plugins/builtin/options-scenario/client.ts +114 -17
  560. package/src/plugins/builtin/options-scenario/collar.ts +141 -0
  561. package/src/plugins/builtin/options-scenario/csp.ts +1 -1
  562. package/src/plugins/builtin/options-scenario/freshness.ts +27 -0
  563. package/src/plugins/builtin/options-scenario/headless.ts +30 -12
  564. package/src/plugins/builtin/options-scenario/index.tsx +1 -1
  565. package/src/plugins/builtin/options-scenario/model.ts +55 -5
  566. package/src/plugins/builtin/options-scenario/pane.tsx +24 -5
  567. package/src/plugins/builtin/pe-band/index.tsx +1 -1
  568. package/src/plugins/builtin/pe-band/model.ts +1 -15
  569. package/src/plugins/builtin/perps/headless.ts +30 -13
  570. package/src/plugins/builtin/perps/model.ts +45 -3
  571. package/src/plugins/builtin/perps/pane.tsx +7 -7
  572. package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
  573. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
  574. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +144 -63
  575. package/src/plugins/builtin/portfolio-list/cli/render.ts +373 -48
  576. package/src/plugins/builtin/portfolio-list/cli/shared.ts +23 -9
  577. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +25 -35
  578. package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
  579. package/src/plugins/builtin/portfolio-list/headless.ts +161 -32
  580. package/src/plugins/builtin/portfolio-list/income.ts +296 -0
  581. package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
  582. package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
  583. package/src/plugins/builtin/portfolio-list/pane/index.tsx +63 -14
  584. package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +1 -1
  585. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
  586. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +38 -8
  587. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +12 -4
  588. package/src/plugins/builtin/portfolio-list/settings.ts +22 -0
  589. package/src/plugins/builtin/portfolio-list/since-last-seen.ts +197 -0
  590. package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
  591. package/src/plugins/builtin/portfolio-list/summary/totals.ts +32 -4
  592. package/src/plugins/builtin/portfolio-list/table.tsx +39 -7
  593. package/src/plugins/builtin/power/prices-headless.ts +2 -1
  594. package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
  595. package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
  596. package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
  597. package/src/plugins/builtin/realized-vol/headless.ts +6 -4
  598. package/src/plugins/builtin/realized-vol/model.ts +5 -1
  599. package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
  600. package/src/plugins/builtin/realized-vol/settings.ts +1 -1
  601. package/src/plugins/builtin/research/analyst-headless.ts +4 -0
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +16 -5
  603. package/src/plugins/builtin/research/event-model.ts +63 -51
  604. package/src/plugins/builtin/research/headless-schema.ts +1 -0
  605. package/src/plugins/builtin/research/headless.ts +4 -1
  606. package/src/plugins/builtin/research/index.tsx +3 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
  608. package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
  609. package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
  610. package/src/plugins/builtin/reverse-dcf/headless.ts +28 -9
  611. package/src/plugins/builtin/reverse-dcf/index.tsx +5 -2
  612. package/src/plugins/builtin/reverse-dcf/model.ts +87 -6
  613. package/src/plugins/builtin/reverse-dcf/pane.tsx +30 -7
  614. package/src/plugins/builtin/risk-factors/headless.ts +7 -0
  615. package/src/plugins/builtin/risk-factors/index.tsx +1 -0
  616. package/src/plugins/builtin/scanner/feed.ts +5 -3
  617. package/src/plugins/builtin/scanner/headless.ts +9 -57
  618. package/src/plugins/builtin/scanner/index.ts +1 -2
  619. package/src/plugins/builtin/sec/forms.ts +2 -6
  620. package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
  621. package/src/plugins/builtin/sec/fund-facts.ts +328 -0
  622. package/src/plugins/builtin/sec/headless.ts +140 -44
  623. package/src/plugins/builtin/sec/index.tsx +95 -30
  624. package/src/plugins/builtin/sec/model.ts +2 -44
  625. package/src/plugins/builtin/shared/earnings-report.ts +48 -1
  626. package/src/plugins/builtin/shared/headless-options.ts +10 -0
  627. package/src/plugins/builtin/shared/history-span.ts +55 -0
  628. package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
  629. package/src/plugins/builtin/shared/move-rank.ts +108 -0
  630. package/src/plugins/builtin/shared/report-freshness.ts +45 -20
  631. package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
  632. package/src/plugins/builtin/shared/series-detail-chart.tsx +5 -2
  633. package/src/plugins/builtin/shared/series-list-detail.tsx +7 -3
  634. package/src/plugins/builtin/shared/ticker-surface.ts +16 -2
  635. package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
  636. package/src/plugins/builtin/shared/use-quote-board.ts +13 -5
  637. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
  638. package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
  639. package/src/plugins/builtin/short-interest/headless.ts +3 -0
  640. package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
  641. package/src/plugins/builtin/social-mentions/client.ts +8 -1
  642. package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
  643. package/src/plugins/builtin/supply-chain/client.ts +12 -3
  644. package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
  645. package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
  646. package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
  647. package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
  648. package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
  649. package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
  650. package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
  651. package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
  652. package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
  653. package/src/plugins/builtin/supply-chain/headless.ts +69 -27
  654. package/src/plugins/builtin/supply-chain/index.tsx +1 -1
  655. package/src/plugins/builtin/supply-chain/model.ts +76 -15
  656. package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
  657. package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
  658. package/src/plugins/builtin/themes/headless.ts +15 -2
  659. package/src/plugins/builtin/themes/model.ts +2 -5
  660. package/src/plugins/builtin/thirteenf/api.ts +50 -2
  661. package/src/plugins/builtin/thirteenf/data.ts +250 -11
  662. package/src/plugins/builtin/thirteenf/headless.ts +311 -28
  663. package/src/plugins/builtin/thirteenf/index.tsx +4 -1
  664. package/src/plugins/builtin/thirteenf/model.ts +244 -19
  665. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
  666. package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
  667. package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
  668. package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
  669. package/src/plugins/builtin/thirteenf/types.ts +49 -0
  670. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +106 -24
  671. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +108 -0
  672. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +26 -8
  673. package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
  674. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
  675. package/src/plugins/builtin/ticker-detail/headless-schema.ts +4 -2
  676. package/src/plugins/builtin/ticker-detail/headless.ts +109 -23
  677. package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
  678. package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
  679. package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
  680. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
  681. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +37 -4
  682. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
  683. package/src/plugins/builtin/time-sales/client.ts +7 -4
  684. package/src/plugins/builtin/time-sales/headless.ts +71 -22
  685. package/src/plugins/builtin/time-sales/model.ts +12 -0
  686. package/src/plugins/builtin/time-sales/pane.tsx +31 -7
  687. package/src/plugins/builtin/time-sales/window.ts +128 -0
  688. package/src/plugins/builtin/trace-bonds/client.ts +351 -33
  689. package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
  690. package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
  691. package/src/plugins/builtin/trace-bonds/model.ts +27 -25
  692. package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
  693. package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
  694. package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
  695. package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
  696. package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
  697. package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
  698. package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
  699. package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
  700. package/src/plugins/builtin/vol-surface/headless.ts +3 -2
  701. package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
  702. package/src/plugins/builtin/vol-surface/stored.ts +4 -7
  703. package/src/plugins/builtin/volatility/chart-model.ts +16 -15
  704. package/src/plugins/builtin/volatility/charts.tsx +5 -5
  705. package/src/plugins/builtin/volatility/client.ts +73 -2
  706. package/src/plugins/builtin/volatility/evidence.ts +13 -14
  707. package/src/plugins/builtin/volatility/headless.ts +13 -7
  708. package/src/plugins/builtin/volatility/index.tsx +1 -1
  709. package/src/plugins/builtin/volatility/model.ts +104 -38
  710. package/src/plugins/builtin/volatility/pane.tsx +42 -24
  711. package/src/plugins/builtin/volatility/series.ts +22 -13
  712. package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
  713. package/src/plugins/builtin/world-venue-map/headless.ts +25 -8
  714. package/src/plugins/builtin/world-venue-map/index.ts +16 -2
  715. package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
  716. package/src/plugins/builtin/yield-curve/curves.ts +29 -5
  717. package/src/plugins/builtin/yield-curve/headless.ts +41 -8
  718. package/src/plugins/builtin/yield-curve/history.ts +5 -5
  719. package/src/plugins/builtin/yield-curve/index.tsx +30 -13
  720. package/src/plugins/compat.ts +3 -1
  721. package/src/plugins/dependencies.ts +8 -2
  722. package/src/plugins/host-link.ts +12 -5
  723. package/src/plugins/installer.ts +16 -7
  724. package/src/plugins/loader.ts +9 -2
  725. package/src/plugins/marketplace/activation.ts +10 -4
  726. package/src/plugins/registry/host-actions.ts +3 -0
  727. package/src/plugins/registry/index.ts +3 -1
  728. package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +82 -15
  729. package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +143 -55
  730. package/src/portfolio/futures-positions.ts +213 -0
  731. package/src/portfolio/futures-terms.ts +75 -0
  732. package/src/portfolio/manual-lots.ts +78 -0
  733. package/src/{plugins/builtin/portfolio-list → portfolio}/mutations.ts +59 -15
  734. package/src/portfolio/position-metrics.ts +44 -10
  735. package/src/portfolio/valuation-basis.ts +34 -24
  736. package/src/public/__snapshots__/public-api.test.ts.snap +6 -0
  737. package/src/public/time-series.ts +2 -1
  738. package/src/remote/confirm-change.tsx +2 -1
  739. package/src/remote/controller.ts +27 -7
  740. package/src/remote/schema.ts +25 -9
  741. package/src/remote/server.ts +1 -1
  742. package/src/remote/watchlist.ts +9 -1
  743. package/src/renderers/cloudflare/http-proxy.ts +21 -13
  744. package/src/renderers/dom/data-table/row.tsx +1 -1
  745. package/src/renderers/dom/desktop/audio-player.tsx +312 -0
  746. package/src/renderers/dom/desktop/control-styles.ts +17 -82
  747. package/src/renderers/dom/desktop/controls.tsx +39 -63
  748. package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
  749. package/src/renderers/dom/desktop/icons.tsx +8 -0
  750. package/src/renderers/dom/desktop/list-view.tsx +6 -6
  751. package/src/renderers/dom/desktop/popover.tsx +0 -3
  752. package/src/renderers/dom/desktop/query-bar.tsx +7 -5
  753. package/src/renderers/dom/desktop/select-field.tsx +9 -8
  754. package/src/renderers/dom/desktop/stack-header.ts +3 -0
  755. package/src/renderers/dom/dom-ui-host.tsx +5 -2
  756. package/src/renderers/dom/host/chart-surface.tsx +3 -3
  757. package/src/renderers/dom/host/size-observer.ts +33 -4
  758. package/src/renderers/dom/host/surface-3d.tsx +3 -2
  759. package/src/renderers/dom/host/surface-fit.ts +4 -1
  760. package/src/renderers/dom/host/tabs.tsx +22 -132
  761. package/src/renderers/dom/styles.css +358 -160
  762. package/src/renderers/dom/windowed-list.tsx +201 -0
  763. package/src/renderers/dom/windowed-rows.ts +65 -0
  764. package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
  765. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -9
  766. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
  767. package/src/renderers/electrobun/bun/desktop/http-stream.ts +21 -6
  768. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  769. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
  770. package/src/renderers/electrobun/bun/desktop/update.ts +21 -6
  771. package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
  772. package/src/renderers/electrobun/shared/protocol.ts +4 -2
  773. package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
  774. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
  775. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  776. package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
  777. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
  778. package/src/renderers/electrobun/view/http-fetch.ts +12 -1
  779. package/src/series/capability.ts +1 -0
  780. package/src/series/files.ts +9 -3
  781. package/src/series/registry.ts +40 -14
  782. package/src/series/resolve.ts +121 -18
  783. package/src/series/standalone.ts +3 -2
  784. package/src/series/types.ts +55 -1
  785. package/src/series/validate.ts +39 -0
  786. package/src/shares/api.ts +26 -7
  787. package/src/shares/bug-report-dialog.tsx +2 -2
  788. package/src/shares/bug-report-flow.tsx +4 -7
  789. package/src/shares/capture-flow.ts +5 -4
  790. package/src/shares/capture-preview.tsx +2 -2
  791. package/src/shares/chat.ts +7 -27
  792. package/src/shares/flow.tsx +86 -40
  793. package/src/shares/location.ts +1 -3
  794. package/src/shares/pane-image.ts +11 -9
  795. package/src/shares/pane.ts +5 -2
  796. package/src/shares/payload.ts +1 -1
  797. package/src/shares/personal-pane.ts +3 -3
  798. package/src/shares/portable-layout.ts +9 -2
  799. package/src/shares/ready.ts +42 -0
  800. package/src/shares/test-harness.ts +62 -0
  801. package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
  802. package/src/sources/gloomberb-cloud/index.ts +64 -12
  803. package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
  804. package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
  805. package/src/sources/history-omitted.ts +34 -0
  806. package/src/sources/history-result.ts +3 -0
  807. package/src/sources/history-retention.ts +1 -1
  808. package/src/sources/listing-symbols.ts +8 -4
  809. package/src/sources/provider-errors.ts +43 -1
  810. package/src/sources/provider-router/batches.ts +3 -2
  811. package/src/sources/provider-router/cache.ts +2 -1
  812. package/src/sources/provider-router/cached-routes.ts +9 -3
  813. package/src/sources/provider-router/history.ts +9 -8
  814. package/src/sources/provider-router/index.ts +18 -3
  815. package/src/sources/provider-router/primary.ts +5 -4
  816. package/src/sources/provider-router/quote-route.ts +3 -1
  817. package/src/sources/provider-router/routing.ts +2 -1
  818. package/src/sources/provider-router/search.ts +47 -13
  819. package/src/sources/provider-router/streaming.ts +7 -4
  820. package/src/sources/sec-edgar.ts +1 -3
  821. package/src/state/app/context/index.tsx +9 -0
  822. package/src/state/app/tab-cursor.ts +222 -0
  823. package/src/state/hooks/quote-streaming.ts +3 -2
  824. package/src/state/hooks/session-snapshot.ts +3 -2
  825. package/src/state/persist-scheduler.ts +12 -4
  826. package/src/sync/controller.ts +22 -1
  827. package/src/sync/core-contributors.ts +264 -74
  828. package/src/team/collections-sync.ts +14 -4
  829. package/src/team/collections.ts +40 -11
  830. package/src/team/store.ts +5 -4
  831. package/src/telemetry/attention-counts.ts +8 -2
  832. package/src/telemetry/crash-reports-dom.ts +3 -0
  833. package/src/telemetry/crash-reports-node.ts +7 -4
  834. package/src/telemetry/crash-reports.ts +11 -11
  835. package/src/telemetry/usage-counts.ts +16 -12
  836. package/src/test-support/config-store.ts +31 -0
  837. package/src/test-support/moved-symbols.ts +64 -0
  838. package/src/theme/colors.ts +32 -0
  839. package/src/theme/font-scale.ts +2 -0
  840. package/src/theme/ui-tokens.ts +71 -0
  841. package/src/tickers/batched-lookups.ts +186 -0
  842. package/src/tickers/company-name-listing.ts +97 -0
  843. package/src/tickers/company-name.ts +13 -0
  844. package/src/tickers/instrument-coverage.ts +112 -0
  845. package/src/tickers/listing-fate.ts +282 -0
  846. package/src/tickers/listing-venues.ts +24 -5
  847. package/src/tickers/open-target.ts +13 -5
  848. package/src/tickers/search/index.ts +156 -37
  849. package/src/tickers/search/ranking.ts +68 -3
  850. package/src/tickers/search/types.ts +3 -0
  851. package/src/tickers/search/upsert.ts +22 -7
  852. package/src/tickers/terminal-codes.ts +79 -0
  853. package/src/tickers/ticker-changes.ts +100 -12
  854. package/src/tickers/us-listing.ts +2 -1
  855. package/src/time-series/bar-spacing.ts +19 -0
  856. package/src/time-series/chart-data.ts +116 -21
  857. package/src/time-series/field-catalog.ts +5 -2
  858. package/src/time-series/forward-valuation.ts +10 -7
  859. package/src/time-series/fundamentals.ts +103 -17
  860. package/src/time-series/hooks.ts +2 -0
  861. package/src/time-series/ice-credit.ts +17 -2
  862. package/src/time-series/live-bars.ts +24 -9
  863. package/src/time-series/live-quotes.ts +2 -3
  864. package/src/time-series/parsed-history-cache.ts +2 -0
  865. package/src/time-series/price-comparison.ts +6 -1
  866. package/src/time-series/price-units.ts +69 -0
  867. package/src/time-series/resolve.ts +114 -14
  868. package/src/time-series/session-bar.ts +138 -0
  869. package/src/time-series/spec.ts +14 -4
  870. package/src/time-series/studies.ts +214 -15
  871. package/src/time-series/transforms.ts +2 -0
  872. package/src/time-series/types.ts +33 -1
  873. package/src/time-series/use-chart-resolution.ts +2 -1
  874. package/src/time-series/valuation-price.ts +13 -0
  875. package/src/types/config.ts +17 -1
  876. package/src/types/data-provider.ts +20 -0
  877. package/src/types/financials.ts +2 -0
  878. package/src/types/headless.ts +169 -1
  879. package/src/types/instrument.ts +6 -0
  880. package/src/types/plugin.ts +39 -9
  881. package/src/types/price-history.ts +5 -0
  882. package/src/types/ticker-changes.ts +19 -0
  883. package/src/types/ticker.ts +10 -2
  884. package/src/ui/context-menu.tsx +2 -0
  885. package/src/ui/host.tsx +45 -4
  886. package/src/ui/measured-cells.ts +13 -7
  887. package/src/ui/qr.ts +7 -5
  888. package/src/updater/index.ts +8 -6
  889. package/src/utils/asset-category.ts +20 -0
  890. package/src/utils/calendar-date.ts +86 -27
  891. package/src/utils/currency-units.ts +17 -0
  892. package/src/utils/debug-log.ts +1 -1
  893. package/src/utils/decimal-comma.ts +83 -3
  894. package/src/utils/dividend-cash.ts +48 -0
  895. package/src/utils/dom-screenshot-rasters.ts +3 -1
  896. package/src/utils/dom-screenshot.ts +14 -10
  897. package/src/utils/exchange-rate-snapshot.ts +9 -4
  898. package/src/utils/external-url.ts +1 -0
  899. package/src/utils/fiscal-period.ts +47 -0
  900. package/src/utils/format.ts +5 -2
  901. package/src/utils/forward-dividend.ts +160 -11
  902. package/src/utils/fundamentals.ts +7 -2
  903. package/src/utils/futures-generic.ts +34 -8
  904. package/src/utils/http-proxy-response.ts +67 -9
  905. package/src/utils/local-utc-offset.ts +2 -0
  906. package/src/utils/log-failure.ts +31 -0
  907. package/src/utils/option-expiry.ts +12 -12
  908. package/src/utils/plugin-proxy-hosts.json +1 -5
  909. package/src/utils/price-history-integrity.ts +56 -0
  910. package/src/utils/price-history.ts +13 -1
  911. package/src/utils/sec-filings.ts +245 -0
  912. package/src/utils/selection-clipboard.ts +5 -2
  913. package/src/utils/throttled-fetch.ts +1 -0
  914. package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
  915. package/src/utils/typo-distance.ts +22 -0
  916. package/src/utils/url.ts +1 -0
  917. package/src/utils/utc-time.ts +11 -0
  918. package/src/plugins/builtin/chat/share/targets.ts +0 -27
  919. package/src/plugins/builtin/shared/zip-text.ts +0 -46
  920. package/src/renderers/dom/host/top-surface.ts +0 -119
  921. package/src/shares/dialog.tsx +0 -221
  922. package/src/shares/live.ts +0 -19
@@ -358,6 +358,33 @@ export function latestRegularSessionClose(
358
358
  return null;
359
359
  }
360
360
 
361
+ /**
362
+ * When the venue's regular session on `date` (its local `YYYY-MM-DD`) closed,
363
+ * a published early close included. Null when the venue had no session that
364
+ * day or its hours are unknown.
365
+ */
366
+ export function regularSessionCloseOn(exchange: string | undefined, date: string): number | null {
367
+ const timeZone = sessionCalendarTimeZone(exchange);
368
+ if (!timeZone || isoLocalDateToUtcDay(date) == null) return null;
369
+ const endOfDay = zonedWallClockToUtcMs(timeZone, Number(date.slice(0, 4)), Number(date.slice(5, 7)), Number(date.slice(8, 10)), 23, 59, 59);
370
+ const close = latestRegularSessionClose(exchange, endOfDay);
371
+ return close?.date === date ? close.close : null;
372
+ }
373
+
374
+ /** The US pre-market opens at 04:00 New York. */
375
+ const US_PRE_MARKET_OPEN_MINUTES = 4 * 60;
376
+
377
+ /**
378
+ * When a US listing's pre-market opened on `date` (its New York
379
+ * `YYYY-MM-DD`): 04:00 there. Null for any other venue, whose pre-market
380
+ * hours are not known, and for a date that does not parse.
381
+ */
382
+ export function preMarketOpenOn(exchange: string | undefined, date: string): number | null {
383
+ if (!isUsListingExchange(canonicalExchange(exchange)) || isoLocalDateToUtcDay(date) == null) return null;
384
+ return zonedWallClockToUtcMs("America/New_York", Number(date.slice(0, 4)), Number(date.slice(5, 7)), Number(date.slice(8, 10)),
385
+ Math.floor(US_PRE_MARKET_OPEN_MINUTES / 60), US_PRE_MARKET_OPEN_MINUTES % 60, 0);
386
+ }
387
+
361
388
  /**
362
389
  * The UTC minute of the day the venue's regular session closed, for the latest
363
390
  * session at or before `time` (20:00 UTC is 1200 while New York is on daylight
@@ -518,7 +545,7 @@ function formatUsSessionState(timestampMs: number): UsSessionState {
518
545
  const minute = Number(parts.find((part) => part.type === "minute")?.value ?? "0");
519
546
  const totalMinutes = hour * 60 + minute;
520
547
 
521
- if (totalMinutes < 4 * 60) return "PREPRE";
548
+ if (totalMinutes < US_PRE_MARKET_OPEN_MINUTES) return "PREPRE";
522
549
  if (totalMinutes < 9 * 60 + 30) return "PRE";
523
550
  if (totalMinutes < 16 * 60) return "REGULAR";
524
551
  if (totalMinutes < 20 * 60) return "POST";
@@ -633,9 +660,7 @@ export function isTimestampStaleForExchangeSession(
633
660
  ): boolean {
634
661
  try {
635
662
  return isTimestampStaleForExchangeSessionUnsafe(timestampMs, exchange, now, marketState, weekendDays);
636
- } catch {
637
- return true;
638
- }
663
+ } catch { /* Intentional ignore: Invalid session dates are conservatively stale, never fresh. */ return true; }
639
664
  }
640
665
 
641
666
  function isTimestampStaleForExchangeSessionUnsafe(
@@ -0,0 +1,51 @@
1
+ import { classifyInstrumentType, isCryptoVenuePerpetual } from "../../tickers/instrument-kind";
2
+
3
+ /**
4
+ * The unit an index level is in, as a chart's axis and series name it. A
5
+ * generic future on an index reads in the same points.
6
+ */
7
+ export const INDEX_POINTS_UNIT = "points";
8
+
9
+ /** How a report line names that unit: `history` heads an index's bars with it, `ticker` its quote. */
10
+ export const INDEX_POINTS_LABEL = "Index points";
11
+
12
+ /** How an export names it, in `currency_or_unit` and a `unit` field. */
13
+ export const INDEX_POINTS_EXPORT_UNIT = "index points";
14
+
15
+ /**
16
+ * An index level is points, not money: ^GSPC at 7,805.45 or ^VIX at 19.99 has
17
+ * no currency to name, wherever it is shown, though its quote names the
18
+ * currency its members trade in.
19
+ */
20
+ export function isIndexLevel(instrumentType: string | null | undefined): boolean {
21
+ return classifyInstrumentType(instrumentType) === "index";
22
+ }
23
+
24
+ /** The currency a quote's price is in: none for an index level, nor for a quote that names none. */
25
+ export function quotePriceCurrency(quote: { currency?: string | null; instrumentType?: string | null } | null | undefined): string | null {
26
+ if (!quote || isIndexLevel(quote.instrumentType)) return null;
27
+ return quote.currency?.trim() || null;
28
+ }
29
+
30
+ /**
31
+ * What a price chart of the instrument reads in: points for an index level,
32
+ * otherwise its currency, so an axis never puts a dollar sign on ^VIX.
33
+ */
34
+ export function priceSeriesUnit(currency: string, instrumentType: string | null | undefined): string {
35
+ return isIndexLevel(instrumentType) ? INDEX_POINTS_UNIT : currency;
36
+ }
37
+
38
+ /**
39
+ * A quote's volume where it counts one. An index and a crypto venue's
40
+ * perpetual price carry 0 for a volume nobody counts, which is not a session
41
+ * without trades: null for those, as for a quote with no volume. A broad
42
+ * index whose source sums its members' volume (^GSPC) keeps it.
43
+ */
44
+ export function reportedQuoteVolume(
45
+ quote: { volume?: number | null; instrumentType?: string | null; name?: string | null } | null | undefined,
46
+ ): number | null {
47
+ const volume = quote?.volume;
48
+ if (volume == null || !Number.isFinite(volume)) return null;
49
+ if (volume === 0 && (isIndexLevel(quote!.instrumentType) || isCryptoVenuePerpetual(quote!.instrumentType, quote!.name))) return null;
50
+ return volume;
51
+ }
@@ -3,6 +3,8 @@ import { blendHex, colors, priceColor, type ThemeColors } from "../../theme/colo
3
3
  import { isFiniteNumber } from "../../utils/guards";
4
4
  import { canonicalExchange, isKnownExchangeCode } from "../../utils/exchanges";
5
5
  import { quoteTradingDay } from "../quotes/day-range";
6
+ import { isQuoteBeforeLatestSession } from "../quotes/freshness";
7
+ import { preMarketOpenOn, regularSessionCloseOn } from "./freshness";
6
8
 
7
9
  const CLOSED_CHANGE_MUTING_RATIO = 0.55;
8
10
  const US_SESSION_TIME = new Intl.DateTimeFormat("en-US", {
@@ -60,6 +62,28 @@ export function marketStateLabel(state: MarketState): string {
60
62
  }
61
63
  }
62
64
 
65
+ /** What a quote's session reads as once its last print is older than its venue's latest completed session. */
66
+ const STALE_SESSION_LABEL = "STALE";
67
+
68
+ const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as const;
69
+
70
+ /** `Thu close`: the weekday of the session whose close a quote still stands at. */
71
+ function closeLabel(date: string): string {
72
+ return `${WEEKDAYS[new Date(`${date}T00:00:00Z`).getUTCDay()]} close`;
73
+ }
74
+
75
+ /**
76
+ * A quote's market state as a status cell names it (marketStateLabel), STALE
77
+ * for a print older than its venue's latest session, and the close it stands
78
+ * at (`Thu close`) in a pre-market nothing has traded in yet.
79
+ */
80
+ export function quoteMarketStateLabel(quote: Quote | null | undefined, now = Date.now()): string | null {
81
+ if (!quote?.marketState) return null;
82
+ if (isQuoteBeforeLatestSession(quote, now)) return STALE_SESSION_LABEL;
83
+ const awaiting = preMarketCloseAwaitingPrint(quote);
84
+ return awaiting ? closeLabel(awaiting.date) : marketStateLabel(quote.marketState);
85
+ }
86
+
63
87
  /** An extended session's column, named as marketStateLabel names its state. */
64
88
  export function extendedSessionStateLabel(session: ExtendedSession): string {
65
89
  return session === "OVERNIGHT" ? "OVERNIGHT" : marketStateLabel(session);
@@ -136,19 +160,53 @@ function overnightSessionDisplay(quote: Quote): ExtendedSessionDisplay | null {
136
160
  }
137
161
 
138
162
  /**
139
- * A quote's session as a status cell names it: the overnight session while
140
- * the quote is priced by it, else its market state (marketStateLabel). The
141
- * exchange stays closed overnight, so the state alone reads CLOSED.
163
+ * A quote's session as a status cell names it: STALE when its last print is
164
+ * older than its venue's latest completed session (isQuoteBeforeLatestSession),
165
+ * the overnight session while the quote is priced by it, the close it stands
166
+ * at (`Thu close`) in a pre-market nothing has traded in yet, else its market
167
+ * state (marketStateLabel). The exchange stays closed overnight, so the state
168
+ * alone reads CLOSED.
142
169
  */
143
- export function quoteSessionLabel(quote: Quote | null | undefined): string | null {
170
+ export function quoteSessionLabel(quote: Quote | null | undefined, now = Date.now()): string | null {
171
+ return sessionLabel(quote, now, true);
172
+ }
173
+
174
+ /**
175
+ * quoteSessionLabel as an export keeps it, one of a fixed set: the market
176
+ * state (`PRE-MKT`) in a pre-market nothing has traded in yet, where the
177
+ * status cell names the close (`Thu close`).
178
+ */
179
+ export function quoteSessionStateLabel(quote: Quote | null | undefined, now = Date.now()): string | null {
180
+ return sessionLabel(quote, now, false);
181
+ }
182
+
183
+ function sessionLabel(quote: Quote | null | undefined, now: number, standingClose: boolean): string | null {
144
184
  if (!quote?.marketState) return null;
145
- return overnightSessionDisplay(quote) ? "OVERNIGHT" : marketStateLabel(quote.marketState);
185
+ if (isQuoteBeforeLatestSession(quote, now)) return STALE_SESSION_LABEL;
186
+ if (overnightSessionDisplay(quote)) return "OVERNIGHT";
187
+ const awaiting = standingClose ? preMarketCloseAwaitingPrint(quote) : null;
188
+ return awaiting ? closeLabel(awaiting.date) : marketStateLabel(quote.marketState);
146
189
  }
147
190
 
148
- /** The color of quoteSessionLabel: an extended session's while the overnight session prices the quote. */
149
- export function quoteSessionColor(quote: Quote | null | undefined, palette: ThemeColors = colors): string {
191
+ /**
192
+ * The color of quoteSessionLabel: STALE in the warning color every stale
193
+ * mark uses, an extended session's while the overnight session prices the
194
+ * quote, a closed session's for the close a pre-market still stands at.
195
+ */
196
+ export function quoteSessionColor(quote: Quote | null | undefined, palette: ThemeColors = colors, now = Date.now()): string {
150
197
  if (!quote?.marketState) return palette.textDim;
151
- return overnightSessionDisplay(quote) ? marketStateColor("POST", palette) : marketStateColor(quote.marketState, palette);
198
+ if (isQuoteBeforeLatestSession(quote, now)) return palette.warning;
199
+ if (overnightSessionDisplay(quote)) return marketStateColor("POST", palette);
200
+ return marketStateColor(preMarketCloseAwaitingPrint(quote) ? "CLOSED" : quote.marketState, palette);
201
+ }
202
+
203
+ /**
204
+ * The market state a session dot pictures for a quote: none for one whose
205
+ * last print is older than its venue's latest session, so it draws as a quote
206
+ * with no known session rather than an open market.
207
+ */
208
+ export function quoteDotMarketState(quote: Quote | null | undefined, now = Date.now()): MarketState | undefined {
209
+ return quote && !isQuoteBeforeLatestSession(quote, now) ? quote.marketState : undefined;
152
210
  }
153
211
 
154
212
  /** Closed prices are final snapshots, so they should not look live or directional. */
@@ -181,7 +239,8 @@ export function exchangeShortName(exchangeName?: string, fullExchangeName?: stri
181
239
  */
182
240
  export function getActiveQuoteDisplay(quote: Quote | null | undefined): ActiveQuoteDisplay | null {
183
241
  if (!quote) return null;
184
- if ((quote.marketState === "PRE" || quote.marketState === "PREPRE") && quote.preMarketPrice != null) {
242
+ // A pre-market price of 0 is no print.
243
+ if ((quote.marketState === "PRE" || quote.marketState === "PREPRE") && isPositive(quote.preMarketPrice)) {
185
244
  return { price: quote.preMarketPrice, change: quote.preMarketChange, changePercent: quote.preMarketChangePercent };
186
245
  }
187
246
  const extendedPrice = (quote.marketState === "POST" || quote.marketState === "POSTPOST") ? quote.postMarketPrice : undefined;
@@ -205,6 +264,8 @@ interface CompletedRegularSession {
205
264
  close: number;
206
265
  /** The close's move from the session before it, when the quote reports it with the close. */
207
266
  reported: { change: number; changePercent: number } | null;
267
+ /** The session's exchange-local date when the quote reports it with the close; otherwise the quote's own day. */
268
+ date?: string;
208
269
  }
209
270
 
210
271
  function reportedRegularMove(quote: Quote, close: number): CompletedRegularSession["reported"] {
@@ -236,9 +297,11 @@ function completedRegularSession(quote: Quote): CompletedRegularSession | null {
236
297
  if (state === "PRE") {
237
298
  // The quote's own day is today; the close must be an earlier one, with its move.
238
299
  const today = quoteTradingDay(quote);
239
- return reported && closeDate && today && closeDate < today ? { close, reported } : null;
300
+ return reported && closeDate && today && closeDate < today ? { close, reported, date: closeDate } : null;
301
+ }
302
+ if (closeDate ? closeDate === quoteTradingDay(quote) : state === "POST") {
303
+ return closeDate ? { close, reported, date: closeDate } : { close, reported };
240
304
  }
241
- if (closeDate ? closeDate === quoteTradingDay(quote) : state === "POST") return { close, reported };
242
305
  }
243
306
  if (state === "POST" && isFiniteNumber(quote.postMarketPrice) && isFiniteNumber(quote.postMarketChange)) {
244
307
  const derived = quote.postMarketPrice - quote.postMarketChange;
@@ -251,6 +314,58 @@ function completedRegularClose(quote: Quote): number | null {
251
314
  return completedRegularSession(quote)?.close ?? null;
252
315
  }
253
316
 
317
+ /**
318
+ * Whether a pre-market quote's pre-market price is a trade: not missing or
319
+ * 0, not the regular close carried forward, and, when the source projects
320
+ * its last print into the pre-market fields (`projected`), not the quote's
321
+ * own price.
322
+ */
323
+ function hasPreMarketPrint(quote: Quote, close: number, projected: boolean): boolean {
324
+ const price = quote.preMarketPrice;
325
+ if (!isPositive(price) || price === close) return false;
326
+ return !(projected && price === quote.price);
327
+ }
328
+
329
+ /**
330
+ * The completed regular session a pre-market quote's headline still shows
331
+ * while nothing has traded in the pre-market, its exchange-local date and
332
+ * when it closed, so the quote reads as that close (`Thu close`, at 20:00
333
+ * UTC), not as the pre-market or the after-hours print before it. Only when
334
+ * the quote cannot hold a pre-market trade: never when the source says it
335
+ * does (extendedSessionPrint true); when it says nothing has traded (false),
336
+ * while its pre-market price is no trade (hasPreMarketPrint), its own price
337
+ * included while that was last updated before the pre-market opened; when it
338
+ * does not say, while there is no pre-market price other than the close and
339
+ * its last update is from before the venue's pre-market opened today. Null in
340
+ * any other session, and while the quote cannot say which close its headline
341
+ * is or its venue's hours are unknown.
342
+ */
343
+ function preMarketCloseAwaitingPrint(quote: Quote | null | undefined): { date: string; closedAt: number } | null {
344
+ if (quote?.marketState !== "PRE" || quote.extendedSessionPrint === true) return null;
345
+ const session = completedRegularSession(quote);
346
+ const today = quoteTradingDay(quote);
347
+ if (!session?.date || !today) return null;
348
+ const exchange = quote.listingExchangeName || quote.exchangeName;
349
+ const opened = preMarketOpenOn(exchange, today);
350
+ // Last updated before today's pre-market opened: the price is carried from the last session.
351
+ const carried = opened != null && Number.isFinite(quote.lastUpdated) && quote.lastUpdated < opened;
352
+ const saysNoTrade = quote.extendedSessionPrint === false;
353
+ if (!saysNoTrade && !carried) return null;
354
+ if (hasPreMarketPrint(quote, session.close, saysNoTrade && carried)) return null;
355
+ const closedAt = regularSessionCloseOn(exchange, session.date);
356
+ return closedAt == null ? null : { date: session.date, closedAt };
357
+ }
358
+
359
+ /**
360
+ * When the price a quote's headline shows was observed: the regular close a
361
+ * pre-market with no trade yet still stands at (preMarketCloseAwaitingPrint),
362
+ * otherwise the quote's last update. What an Updated cell and a report's
363
+ * as-of date the quote by.
364
+ */
365
+ export function quoteHeadlineTime(quote: Quote): number {
366
+ return preMarketCloseAwaitingPrint(quote)?.closedAt ?? quote.lastUpdated;
367
+ }
368
+
254
369
  /**
255
370
  * The day's headline: the regular session, which stops at its close. Once it
256
371
  * is over, and in the pre-market while the quote carries the last session's
@@ -286,6 +401,27 @@ export function getCompletedRegularSessionDisplay(quote: Quote | null | undefine
286
401
  return quote && completedRegularClose(quote) != null ? getRegularSessionDisplay(quote) : null;
287
402
  }
288
403
 
404
+ /**
405
+ * The price a daily, weekly or monthly bar takes from a quote: the regular
406
+ * session's, never an extended print, so a closed market's last bar is the
407
+ * close `history` and `portfolio show --close` give. While the session
408
+ * trades, or when the quote does not say its session, the quote's price. Once
409
+ * it is over, its close as the headline reads it, with that session's date
410
+ * when the quote reports one (in the pre-market it is an earlier day's).
411
+ * Null in the pre-market while the quote cannot say which close that is: a
412
+ * pre-market print is no regular session's price, and today's bar has not
413
+ * opened.
414
+ */
415
+ export function getRegularSessionBarClose(quote: Quote | null | undefined): { price: number; sessionDate: string | null } | null {
416
+ if (!quote) return null;
417
+ const state = quote.marketState;
418
+ if (state == null || state === "REGULAR") return { price: quote.price, sessionDate: null };
419
+ const session = completedRegularSession(quote);
420
+ if (session) return { price: session.close, sessionDate: session.date ?? null };
421
+ if (state === "PRE" || (state === "PREPRE" && quote.preMarketPrice != null)) return null;
422
+ return { price: quote.price, sessionDate: null };
423
+ }
424
+
289
425
  /**
290
426
  * The pre-market or after-hours print, measured from the regular close before
291
427
  * it, while the quote has one: the pre-market until the open, the after-hours
@@ -298,9 +434,12 @@ export function getExtendedSessionDisplay(quote: Quote | null | undefined): Exte
298
434
  if (overnight) return overnight;
299
435
  const state = quote.marketState;
300
436
  if (state === "PRE" || state === "PREPRE") {
301
- if (isFiniteNumber(quote.preMarketPrice)) {
437
+ // Nothing has traded in the pre-market: its figures are the close carried forward, no print.
438
+ if (state === "PRE" && preMarketCloseAwaitingPrint(quote)) return null;
439
+ // A pre-market price of 0 is no print.
440
+ if (isPositive(quote.preMarketPrice)) {
302
441
  // Against the close the headline shows, so the two cannot disagree about it.
303
- const close = state === "PRE" && quote.preMarketPrice > 0 ? completedRegularClose(quote) : null;
442
+ const close = state === "PRE" ? completedRegularClose(quote) : null;
304
443
  if (close != null) {
305
444
  const change = quote.preMarketPrice - close;
306
445
  return { session: "PRE", price: quote.preMarketPrice, change, changePercent: (change / close) * 100 };
@@ -0,0 +1,117 @@
1
+ import type { CorporateActionsData, Fundamentals } from "../types/financials";
2
+ import { abortable, settleWithin } from "../utils/async-deadline";
3
+ import { distributionHistory, forwardYieldMayHaveLapsed, lapsedDividend, lastPaidNote, NO_CURRENT_DIVIDEND } from "../utils/forward-dividend";
4
+
5
+ /**
6
+ * How long a distribution history must go without a dividend before it says a
7
+ * security pays none: fifteen months, since an annual payer's next ex-date can
8
+ * slip a few weeks past a year (Gloom Cloud withdraws a lapsed yield the same
9
+ * way). Gloom Cloud's history reaches back ten years (SAP's and Novartis's
10
+ * annual dividends from 2017 on), well past this window.
11
+ */
12
+ const NO_DIVIDEND_WINDOW_MS = 455 * 86_400_000;
13
+
14
+ /** How long the history may take before the yield is left unknown. */
15
+ const HISTORY_DEADLINE_MS = 5_000;
16
+
17
+ /**
18
+ * The continuous dividend yield an option model values an underlying at, read
19
+ * the same way by OSA and the options chain's Greeks
20
+ * (docs/research-data.md#greeks-spot-and-time):
21
+ *
22
+ * - `stated`: the yield the underlying's fundamentals state, which the caller
23
+ * reads (0 for a company whose quote affirms it pays none);
24
+ * - `none-paid`: 0, when no yield is stated but its distribution history,
25
+ * current and covering dividends, has none paid or declared in the last
26
+ * fifteen months (a fund that pays none, such as GLD);
27
+ * - `stopped`: 0, when the dividend has stopped by the issuer's own cadence
28
+ * (lapsedDividend, as every pane reads a lapsed forward yield): WHR, last
29
+ * paid 2026-02-27, whose stated 12.91% annualized a payment that did not
30
+ * recur. `lastPaid` dates it.
31
+ *
32
+ * Null when none says: an unknown yield is never taken as 0.
33
+ */
34
+ export interface ModelDividendYield {
35
+ /** Decimal annual yield. */
36
+ value: number;
37
+ basis: "stated" | "none-paid" | "stopped";
38
+ /** The last distribution's ex-date (YYYY-MM-DD) of a `stopped` dividend; null when unknown. */
39
+ lastPaid?: string | null;
40
+ }
41
+
42
+ /** What the yield is read from: the fundamentals' yield, its basis, and the ex-date of the distribution it annualizes. */
43
+ type StatedDividend = Pick<Fundamentals, "dividendYield" | "dividendYieldBasis" | "exDividendDate">;
44
+
45
+ const symbolKey = (value: string | null | undefined) => (value ?? "").trim().toUpperCase().replace(/[^A-Z0-9]/g, "");
46
+
47
+ /** Whether a history is `symbol`'s own and current, covering dividends. */
48
+ function currentHistory(actions: CorporateActionsData | null | undefined, symbol: string): actions is CorporateActionsData {
49
+ return !!actions && !actions.stale && actions.coverage?.dividends === "available" && symbolKey(actions.symbol) === symbolKey(symbol);
50
+ }
51
+
52
+ /** Whether a current history that covers dividends shows none paid or declared for `symbol` in the last fifteen months. */
53
+ function paysNoDividend(actions: CorporateActionsData, now: number): boolean {
54
+ const since = now - NO_DIVIDEND_WINDOW_MS;
55
+ // A date that cannot be read counts as recent: it cannot show the dividend stopped.
56
+ const recent = (exDate: unknown) => {
57
+ const time = typeof exDate === "string" ? Date.parse(`${exDate.slice(0, 10)}T00:00:00Z`) : Number.NaN;
58
+ return !Number.isFinite(time) || time >= since;
59
+ };
60
+ return !actions.dividends.some((dividend) => recent(dividend.exDate))
61
+ && !(actions.declaredDividends ?? []).some((dividend) => recent(dividend.exDate));
62
+ }
63
+
64
+ /** 0, dated by the last distribution, when the current history shows the dividend dated `asOf` stopped (lapsedDividend). */
65
+ function stoppedDividend(asOf: string | undefined, actions: CorporateActionsData, now: number): ModelDividendYield | null {
66
+ const lapse = lapsedDividend(asOf, distributionHistory(actions, new Date(now)), new Date(now));
67
+ return lapse ? { value: 0, basis: "stopped", lastPaid: lapse.lastPaid } : null;
68
+ }
69
+
70
+ interface DividendHistoryOptions {
71
+ now?: number;
72
+ /** Stops waiting for the history; the request itself is shared and keeps running. */
73
+ signal?: AbortSignal;
74
+ timeoutMs?: number;
75
+ }
76
+
77
+ /**
78
+ * The yield to value `symbol` at: 0 when its distribution history shows the
79
+ * dividend stopped, else `stated` when it is a yield, else 0 when the history
80
+ * shows none paid, else null. The history is read when no yield is stated,
81
+ * and for a forward yield old enough to have lapsed; one that fails, is stale
82
+ * or takes over five seconds leaves a stated yield as it is and an unstated
83
+ * one unknown.
84
+ */
85
+ export async function loadModelDividendYield(
86
+ stated: StatedDividend | null | undefined,
87
+ symbol: string,
88
+ loadActions: (() => Promise<CorporateActionsData | null | undefined>) | null | undefined,
89
+ { now = Date.now(), signal, timeoutMs = HISTORY_DEADLINE_MS }: DividendHistoryOptions = {},
90
+ ): Promise<ModelDividendYield | null> {
91
+ const value = stated?.dividendYield;
92
+ const statedYield: ModelDividendYield | null = typeof value === "number" && Number.isFinite(value) && value >= 0
93
+ ? { value, basis: "stated" } : null;
94
+ const mayHaveLapsed = !!statedYield && statedYield.value > 0 && forwardYieldMayHaveLapsed(stated, new Date(now));
95
+ if ((statedYield && !mayHaveLapsed) || !loadActions) return statedYield;
96
+ const read = await abortable(settleWithin(Promise.resolve().then(loadActions), timeoutMs), signal, "Dividend history read was cancelled");
97
+ const actions = currentHistory(read, symbol) ? read : null;
98
+ if (!actions) return statedYield;
99
+ if (statedYield) return stoppedDividend(stated?.exDividendDate, actions, now) ?? statedYield;
100
+ if (paysNoDividend(actions, now)) return { value: 0, basis: "none-paid" };
101
+ return stoppedDividend(stated?.exDividendDate, actions, now);
102
+ }
103
+
104
+ /** What a yield the history shows none paid is, beside the 0%: `no dividend in the last 15 months`. */
105
+ const NO_DIVIDEND_PAID = "no dividend in the last 15 months";
106
+
107
+ /**
108
+ * What a yield read as 0 is, beside the 0%: `no dividend in the last 15
109
+ * months`, or `no current dividend, last paid 2026-02-27` in the words every
110
+ * pane reads a stopped dividend in. Null for a stated yield.
111
+ */
112
+ export function modelDividendYieldNote(dividend: Pick<ModelDividendYield, "basis" | "lastPaid"> | null | undefined): string | null {
113
+ if (dividend?.basis === "none-paid") return NO_DIVIDEND_PAID;
114
+ if (dividend?.basis !== "stopped") return null;
115
+ const lastPaid = lastPaidNote({ lastPaid: dividend.lastPaid ?? null });
116
+ return lastPaid ? `${NO_CURRENT_DIVIDEND}, ${lastPaid}` : NO_CURRENT_DIVIDEND;
117
+ }
@@ -1,13 +1,16 @@
1
1
  import type { Quote } from "../../types/financials";
2
- import { isUsListingExchange } from "../../utils/exchanges";
2
+ import { canonicalExchange, isUsListingExchange } from "../../utils/exchanges";
3
3
  import { zonedDateKey } from "../../utils/zoned-date-time";
4
4
  import { getPublishedUsEquitySession } from "../published-us-sessions";
5
5
  import { quoteFutureToleranceMs } from "./clock";
6
6
  import {
7
7
  activeUsExtendedHoursSession,
8
+ isAlwaysOpenExchange,
8
9
  isTimestampStaleForExchangeSession,
9
10
  isUsPriorSessionPremarketQuote,
10
11
  isUsRegularCloseQuoteInPostSession,
12
+ latestRegularSessionClose,
13
+ latestRegularSessionOpen,
11
14
  } from "../market/freshness";
12
15
 
13
16
  // Give the next regular session's NAV time to publish after its actual close.
@@ -90,6 +93,32 @@ export function isQuoteStaleForCurrentSession(quote: Quote | null | undefined, n
90
93
  );
91
94
  }
92
95
 
96
+ /** Spot FX: its week is in `fx-market-hours`, not a session calendar. */
97
+ const FX_VENUE = "CCY";
98
+ /** Every venue with sessions has had one within this long; Spring Festival can close TWSE for twelve days. */
99
+ const LONGEST_SESSION_GAP_MS = 14 * 24 * 60 * 60_000;
100
+
101
+ /**
102
+ * Whether a quote's last print is older than its venue's latest completed
103
+ * regular session: a whole session traded since, so the state its source
104
+ * reports says where the market is, not where this price is (a delisted
105
+ * line read as OPEN). Only a quote already stale for its session can be;
106
+ * the last session's close before the open, an after-hours print after it,
107
+ * and round-the-clock venues and FX keep their state. A venue without a
108
+ * calendar counts a print older than any venue goes between sessions.
109
+ */
110
+ export function isQuoteBeforeLatestSession(quote: Quote | null | undefined, now = Date.now()): boolean {
111
+ if (!quote || !isQuoteStaleForCurrentSession(quote, now)) return false;
112
+ const venue = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
113
+ if (venue === FX_VENUE || isAlwaysOpenExchange(venue)) return false;
114
+ const printed = quote.lastUpdated;
115
+ if (!Number.isFinite(printed) || printed <= 0 || printed > now) return false;
116
+ const latest = latestRegularSessionClose(venue, now);
117
+ if (!latest) return now - printed > LONGEST_SESSION_GAP_MS;
118
+ const open = latestRegularSessionOpen(venue, latest.close);
119
+ return open != null ? printed < open : zonedDateKey(printed, latest.timeZone) < latest.date;
120
+ }
121
+
93
122
  export function hasFreshQuoteForCurrentSession(
94
123
  quotes: Iterable<Quote | null | undefined>,
95
124
  now = Date.now(),
@@ -1,9 +1,11 @@
1
- import type { AnalystResearchData, Fundamentals, Quote } from "../../../types/financials";
2
- import { atRegularSessionPrice, regularSessionValuationPrice } from "../../../market-data/quotes/regular-session-valuation";
3
- import { hasLikelyQuoteUnitMismatch } from "../../../utils/currency-units";
4
- import { priceEarningsEps } from "../../../utils/fundamentals";
5
- import { selectMarketCapitalization, type MarketCapitalization } from "../../../utils/market-capitalization";
6
- import { priceEarningsOnEarnings, type PriceEarnings } from "../../../utils/price-earnings";
1
+ import type { AnalystResearchData, CorporateActionsData, Fundamentals, Quote } from "../../types/financials";
2
+ import { atRegularSessionPrice, regularSessionValuationPrice } from "./regular-session-valuation";
3
+ import { hasLikelyQuoteUnitMismatch, resolveCurrencyUnit } from "../../utils/currency-units";
4
+ import { cashInMajorUnits } from "../../utils/dividend-cash";
5
+ import { cashDistribution, dividendReferencePrice, trailingDividendYield, type CashDistribution } from "../../utils/trailing-dividend";
6
+ import { priceEarningsEps } from "../../utils/fundamentals";
7
+ import { selectMarketCapitalization, type MarketCapitalization } from "../../utils/market-capitalization";
8
+ import { priceEarningsOnEarnings, type PriceEarnings } from "../../utils/price-earnings";
7
9
 
8
10
  /**
9
11
  * Price-derived statistics repriced at the quote's regular session price
@@ -89,6 +91,50 @@ export function liveDividendYield(quote: Quote | null | undefined, fundamentals:
89
91
  return rate / price;
90
92
  }
91
93
 
94
+ /** Whether a listing's yield is a trailing one, which is measured from its cash history (listingDividendYield). */
95
+ export function measuresTrailingYield(fundamentals: Pick<Fundamentals, "dividendYield" | "dividendYieldBasis"> | null | undefined): boolean {
96
+ return fundamentals?.dividendYieldBasis === "trailing" && fundamentals.dividendYield != null;
97
+ }
98
+
99
+ /**
100
+ * A trailing dividend yield as DVD measures it (trailingDividendYield): the
101
+ * cash history's trailing twelve months over the quote's regular session
102
+ * price, in the cash's currency. Null when the history cannot be read in full
103
+ * (a row that is no paid distribution, no currency, dividends not covered) or
104
+ * the price is in another currency, where DVD leaves its own blank.
105
+ */
106
+ export function historyTrailingDividendYield(
107
+ sourceActions: CorporateActionsData,
108
+ quote: Quote | null | undefined,
109
+ now = new Date(),
110
+ ): number | null {
111
+ const actions = cashInMajorUnits(sourceActions);
112
+ const currency = resolveCurrencyUnit(actions.currency).currency;
113
+ if (!currency || !Array.isArray(actions.dividends) || actions.coverage?.dividends === "unavailable") return null;
114
+ if (actions.coverage?.dividends !== "available" && actions.dividends.length === 0) return null;
115
+ const payments = actions.dividends.map((row) => cashDistribution(row.exDate, row.amount));
116
+ if (payments.some((payment) => payment == null)) return null;
117
+ const price = dividendReferencePrice(regularSessionValuationPrice(quote), quote?.currency, currency);
118
+ return trailingDividendYield(payments as CashDistribution[], price, now);
119
+ }
120
+
121
+ /**
122
+ * The dividend yield a listing shows at its regular session price. A trailing
123
+ * one is DVD's, measured from `actions`, the listing's cash history
124
+ * (historyTrailingDividendYield), so the overview, DIV% and DVD read one
125
+ * figure; until that history is read, or when it cannot be read in full, it
126
+ * is Gloom Cloud's, as a forward one is (liveDividendYield).
127
+ */
128
+ export function listingDividendYield(
129
+ quote: Quote | null | undefined,
130
+ fundamentals: Fundamentals | undefined,
131
+ actions: CorporateActionsData | null | undefined,
132
+ now = new Date(),
133
+ ): number | undefined {
134
+ const measured = actions && measuresTrailingYield(fundamentals) ? historyTrailingDividendYield(actions, quote, now) : null;
135
+ return measured ?? liveDividendYield(quote, fundamentals);
136
+ }
137
+
92
138
  /**
93
139
  * Whether a capitalization's implied share price is one this listing traded
94
140
  * at: the prior close (a source capitalization) or anywhere in the session
@@ -1,6 +1,9 @@
1
1
  import type { EnterpriseValueCheck, Fundamentals, Quote, TickerFinancials } from "../../types/financials";
2
2
  import { priceEarningsEps } from "../../utils/fundamentals";
3
- import { getRegularSessionDisplay } from "../market/status";
3
+ import type { ReportTime } from "../../utils/utc-time";
4
+ import { latestRegularSessionClose } from "../market/freshness";
5
+ import { getCompletedRegularSessionDisplay, getRegularSessionDisplay } from "../market/status";
6
+ import { quoteTradingDay } from "./day-range";
4
7
 
5
8
  function positive(value: number | null | undefined): value is number {
6
9
  return typeof value === "number" && Number.isFinite(value) && value > 0;
@@ -19,6 +22,34 @@ export function regularSessionValuationPrice(quote: Quote | null | undefined): n
19
22
  return positive(price) ? price : null;
20
23
  }
21
24
 
25
+ /**
26
+ * When the completed regular session the quote reports closed: the venue's
27
+ * close on that day from its calendar, or the day alone when the calendar does
28
+ * not give the hour.
29
+ */
30
+ function regularCloseTime(quote: Quote): ReportTime | null {
31
+ const date = quote.regularCloseSessionDate ?? quoteTradingDay(quote);
32
+ const exchange = quote.listingExchangeName || quote.exchangeName;
33
+ const close = exchange && Number.isFinite(quote.lastUpdated) ? latestRegularSessionClose(exchange, quote.lastUpdated) : null;
34
+ if (close && (!date || close.date === date)) return { time: close.close, dateOnly: false };
35
+ const day = date ? Date.parse(`${date}T00:00:00Z`) : Number.NaN;
36
+ return Number.isFinite(day) ? { time: day, dateOnly: true } : null;
37
+ }
38
+
39
+ /**
40
+ * When the price regularSessionValuationPrice reads is from: once the regular
41
+ * session is over (and in the pre-market while the quote carries the last
42
+ * session's close), that session's close; while it trades, the quote's own
43
+ * time. A figure measured at that price is dated by it, never by an
44
+ * after-hours or pre-market print. Null without a dated quote.
45
+ */
46
+ export function regularSessionValuationTime(quote: Quote | null | undefined): ReportTime | null {
47
+ if (!quote) return null;
48
+ const close = getCompletedRegularSessionDisplay(quote) ? regularCloseTime(quote) : null;
49
+ if (close) return close;
50
+ return Number.isFinite(quote.lastUpdated) && quote.lastUpdated > 0 ? { time: quote.lastUpdated, dateOnly: false } : null;
51
+ }
52
+
22
53
  function scaled(value: number | undefined, ratio: number): number | undefined {
23
54
  return value != null && Number.isFinite(value) ? value * ratio : value;
24
55
  }