gloomberb 0.18.4 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (600) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +169 -5
  5. package/src/account/auth-model.ts +7 -3
  6. package/src/api-client/auth.ts +9 -4
  7. package/src/api-client/catalysts.ts +2 -1
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +31 -6
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/index.ts +171 -11
  14. package/src/api-client/live-scan.ts +91 -0
  15. package/src/api-client/market-discovery.ts +10 -0
  16. package/src/api-client/market-movers.ts +7 -0
  17. package/src/api-client/members.ts +13 -1
  18. package/src/api-client/perps.ts +43 -0
  19. package/src/api-client/request.ts +222 -41
  20. package/src/api-client/research-activity.ts +6 -12
  21. package/src/api-client/types.ts +74 -0
  22. package/src/app/keybindings/index.ts +3 -0
  23. package/src/app/keybindings/labels.ts +18 -1
  24. package/src/app/keybindings/resolve.ts +14 -0
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +42 -27
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +1 -1
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/fred-series.ts +5 -0
  35. package/src/cli/commands/keybindings.ts +23 -0
  36. package/src/cli/commands/market.ts +138 -46
  37. package/src/cli/commands/options-chain.ts +107 -10
  38. package/src/cli/commands/options-leaps.ts +9 -3
  39. package/src/cli/commands/overview.ts +176 -39
  40. package/src/cli/commands/search.ts +39 -3
  41. package/src/cli/commands/themes.ts +11 -2
  42. package/src/cli/commands/ticker.ts +57 -35
  43. package/src/cli/company-name-hint.ts +41 -0
  44. package/src/cli/earnings-calendar.ts +1 -1
  45. package/src/cli/errors.ts +10 -2
  46. package/src/cli/export-source.ts +29 -1
  47. package/src/cli/glossary.ts +73 -1
  48. package/src/cli/history-rows.ts +87 -16
  49. package/src/cli/index.ts +2 -0
  50. package/src/cli/known-gaps.ts +5 -2
  51. package/src/cli/listing-arg.ts +195 -56
  52. package/src/cli/output-file.ts +68 -0
  53. package/src/cli/pane-functions/capabilities.ts +3 -0
  54. package/src/cli/pane-functions/catalog.ts +80 -7
  55. package/src/cli/pane-functions/dom.ts +13 -3
  56. package/src/cli/pane-functions/freshness.ts +111 -16
  57. package/src/cli/pane-functions/function-help.ts +23 -13
  58. package/src/cli/pane-functions/headless.ts +56 -12
  59. package/src/cli/pane-functions/index.ts +58 -16
  60. package/src/cli/pane-functions/market-session.ts +81 -9
  61. package/src/cli/pane-functions/options.ts +19 -1
  62. package/src/cli/pane-functions/report.ts +5 -1
  63. package/src/cli/pane-functions/resolver.ts +35 -8
  64. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  65. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  66. package/src/cli/quote-answer.ts +30 -13
  67. package/src/cli/report-tables.ts +18 -7
  68. package/src/cli/restore-plugins.ts +13 -1
  69. package/src/cli/result.ts +57 -5
  70. package/src/cli/ticker-resolution.ts +3 -2
  71. package/src/components/chart/composite/composite-chart.tsx +26 -4
  72. package/src/components/chart/composite/format.ts +27 -0
  73. package/src/components/chart/composite/interactions.ts +6 -0
  74. package/src/components/chart/composite/legend.tsx +45 -7
  75. package/src/components/chart/composite/levels.ts +2 -0
  76. package/src/components/chart/composite/panel-gestures.ts +33 -0
  77. package/src/components/chart/composite/panel-series.ts +1 -0
  78. package/src/components/chart/composite/panel-surface.tsx +61 -5
  79. package/src/components/chart/composite/range-statistics.ts +62 -0
  80. package/src/components/chart/composite/rasterizer.ts +24 -13
  81. package/src/components/chart/composite/scene.ts +46 -7
  82. package/src/components/chart/composite/series-axis-tags.tsx +16 -9
  83. package/src/components/chart/composite/series-cues.ts +14 -0
  84. package/src/components/chart/composite/text-renderer.ts +9 -2
  85. package/src/components/chart/composite/tool-catalog.ts +36 -0
  86. package/src/components/chart/composite/tools.ts +203 -44
  87. package/src/components/chart/composite/types.ts +4 -0
  88. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  89. package/src/components/command-bar/assist/inventory.ts +2 -1
  90. package/src/components/command-bar/commands/direct/index.ts +28 -13
  91. package/src/components/command-bar/pane-templates/items.ts +3 -2
  92. package/src/components/command-bar/panel/prompt-binding.ts +0 -8
  93. package/src/components/command-bar/workflow/alert.ts +1 -1
  94. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  95. package/src/components/layout/header.tsx +26 -40
  96. package/src/components/layout/pane/footer/index.tsx +1 -0
  97. package/src/components/layout/pane/footer/model.ts +27 -0
  98. package/src/components/onboarding/onboarding-frame.tsx +1 -1
  99. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  100. package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
  101. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  102. package/src/components/onboarding/wizard-positions.ts +3 -3
  103. package/src/components/ticker/list-table-view.tsx +4 -1
  104. package/src/components/ui/choice-dialog.tsx +23 -18
  105. package/src/components/ui/choice-groups.ts +18 -0
  106. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  107. package/src/components/ui/data-table/opentui/model.ts +26 -1
  108. package/src/components/ui/data-table/types.ts +2 -0
  109. package/src/components/ui/display.tsx +5 -1
  110. package/src/components/ui/list-view.tsx +8 -4
  111. package/src/components/ui/query-bar-keys.tsx +2 -0
  112. package/src/components/ui/query-bar.tsx +1 -0
  113. package/src/components/ui/select-button.tsx +5 -1
  114. package/src/components/ui/select-field.tsx +1 -0
  115. package/src/components/ui/table-layout.ts +8 -5
  116. package/src/core/app-runtime.ts +2 -0
  117. package/src/core/app-services.ts +2 -2
  118. package/src/core/state/app/layout.ts +2 -0
  119. package/src/core/state/app/state.ts +9 -0
  120. package/src/core/state/app/types.ts +3 -0
  121. package/src/data/config/chart-settings.ts +1 -1
  122. package/src/data/json-ticker-repository.ts +17 -8
  123. package/src/data/saved-copies.ts +55 -16
  124. package/src/data/ticker-repository.ts +16 -9
  125. package/src/data/ticker-writes.ts +81 -0
  126. package/src/data/ttl-cache.ts +165 -0
  127. package/src/docs/snapshot.json +427 -216
  128. package/src/i18n/es.ts +19 -3
  129. package/src/i18n/ja.ts +19 -3
  130. package/src/i18n/ko.ts +19 -3
  131. package/src/i18n/zh-cn.ts +19 -3
  132. package/src/i18n/zh-tw.ts +19 -3
  133. package/src/market-data/coordinator/financials.ts +10 -2
  134. package/src/market-data/coordinator/index.ts +3 -1
  135. package/src/market-data/coordinator/quotes.ts +4 -2
  136. package/src/market-data/depositary-pair.ts +2 -2
  137. package/src/market-data/fund-facts.ts +50 -0
  138. package/src/market-data/market/change-basis.ts +48 -7
  139. package/src/market-data/market/dated-previous-close.ts +203 -0
  140. package/src/market-data/market/format.ts +25 -0
  141. package/src/market-data/market/freshness.ts +2 -1
  142. package/src/market-data/market/futures-week.ts +68 -0
  143. package/src/market-data/market/status.ts +16 -4
  144. package/src/market-data/options-alternatives.ts +141 -4
  145. package/src/market-data/quotes/contributions.ts +20 -2
  146. package/src/market-data/quotes/resolution.ts +3 -0
  147. package/src/market-data/selectors.ts +3 -0
  148. package/src/news/aggregator.ts +113 -9
  149. package/src/news/cloud-session.ts +21 -0
  150. package/src/news/hooks.ts +1 -0
  151. package/src/news/news-model.ts +1 -0
  152. package/src/news/types.ts +11 -0
  153. package/src/notifications/notification-log.ts +6 -4
  154. package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
  155. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  156. package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
  157. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  158. package/src/plugins/builtin/alerts/cli.ts +380 -82
  159. package/src/plugins/builtin/alerts/command.ts +31 -0
  160. package/src/plugins/builtin/alerts/constants.ts +1 -1
  161. package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
  162. package/src/plugins/builtin/alerts/fired-log.ts +113 -8
  163. package/src/plugins/builtin/alerts/format.ts +80 -4
  164. package/src/plugins/builtin/alerts/headless.ts +30 -7
  165. package/src/plugins/builtin/alerts/history-rows.ts +7 -3
  166. package/src/plugins/builtin/alerts/history.ts +2 -0
  167. package/src/plugins/builtin/alerts/index.tsx +135 -22
  168. package/src/plugins/builtin/alerts/live.ts +3 -3
  169. package/src/plugins/builtin/alerts/metric-command.ts +134 -0
  170. package/src/plugins/builtin/alerts/metric-data.ts +111 -0
  171. package/src/plugins/builtin/alerts/metric-model.ts +324 -0
  172. package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
  173. package/src/plugins/builtin/alerts/pane.tsx +90 -39
  174. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  175. package/src/plugins/builtin/alerts/storage.ts +37 -10
  176. package/src/plugins/builtin/alerts/types.ts +7 -0
  177. package/src/plugins/builtin/analytics/index.tsx +2 -1
  178. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  179. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  180. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  181. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  182. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  183. package/src/plugins/builtin/attention/index.tsx +2 -1
  184. package/src/plugins/builtin/attention/listing.ts +14 -0
  185. package/src/plugins/builtin/backtest/headless.ts +10 -5
  186. package/src/plugins/builtin/backtest/pane.tsx +9 -4
  187. package/src/plugins/builtin/backtest/presets.ts +29 -3
  188. package/src/plugins/builtin/broker-manager/index.tsx +10 -5
  189. package/src/plugins/builtin/broker-manager/model.ts +6 -1
  190. package/src/plugins/builtin/broker-trading/controller.ts +22 -12
  191. package/src/plugins/builtin/broker-trading/index.tsx +13 -1
  192. package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
  193. package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
  194. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  195. package/src/plugins/builtin/buildout/format.ts +38 -0
  196. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  197. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  198. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  199. package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
  200. package/src/plugins/builtin/catalysts/model.ts +2 -1
  201. package/src/plugins/builtin/changelog/index.tsx +9 -6
  202. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
  203. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
  204. package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
  205. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
  206. package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
  207. package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
  208. package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
  209. package/src/plugins/builtin/chart-composer/formula.ts +71 -18
  210. package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
  211. package/src/plugins/builtin/chart-composer/headless.ts +19 -3
  212. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  213. package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
  214. package/src/plugins/builtin/chart-composer/presets.ts +50 -15
  215. package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
  216. package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
  217. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  218. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
  219. package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
  220. package/src/plugins/builtin/chat/content/composer.tsx +13 -6
  221. package/src/plugins/builtin/chat/content/footer.tsx +6 -3
  222. package/src/plugins/builtin/chat/content/index.tsx +50 -21
  223. package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
  224. package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
  225. package/src/plugins/builtin/chat/index.tsx +23 -0
  226. package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
  227. package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
  228. package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
  229. package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
  230. package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
  231. package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
  232. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
  233. package/src/plugins/builtin/clinical-trials/model.ts +0 -9
  234. package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
  235. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
  236. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  237. package/src/plugins/builtin/cloud/plugin.tsx +1 -1
  238. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  239. package/src/plugins/builtin/company-attention/client.ts +8 -9
  240. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  241. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  242. package/src/plugins/builtin/company-attention/pane.tsx +9 -2
  243. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  244. package/src/plugins/builtin/correlation/headless.ts +13 -5
  245. package/src/plugins/builtin/correlation/history.ts +22 -0
  246. package/src/plugins/builtin/cot/client.ts +22 -18
  247. package/src/plugins/builtin/cot/headless.ts +15 -10
  248. package/src/plugins/builtin/cot/model.ts +14 -1
  249. package/src/plugins/builtin/cot/pane.tsx +22 -14
  250. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  251. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  252. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  253. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  254. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  255. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  256. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  257. package/src/plugins/builtin/credit-documents/headless.ts +7 -7
  258. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  259. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  260. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  261. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  262. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  263. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  264. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  265. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  266. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  267. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  268. package/src/plugins/builtin/docs/targets.ts +5 -1
  269. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  270. package/src/plugins/builtin/earnings/board.tsx +62 -71
  271. package/src/plugins/builtin/earnings/headless.ts +40 -4
  272. package/src/plugins/builtin/earnings/scope.ts +31 -43
  273. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  274. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  275. package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
  276. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  277. package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
  278. package/src/plugins/builtin/econ/calendar-model.ts +14 -9
  279. package/src/plugins/builtin/econ/figures.ts +72 -0
  280. package/src/plugins/builtin/econ/index.tsx +33 -3
  281. package/src/plugins/builtin/econ/types.ts +4 -0
  282. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  283. package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
  284. package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
  285. package/src/plugins/builtin/econ-statistics/view.ts +3 -0
  286. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  287. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  288. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  289. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  290. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  291. package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
  292. package/src/plugins/builtin/exposure/charts.tsx +2 -2
  293. package/src/plugins/builtin/exposure/model.ts +13 -1
  294. package/src/plugins/builtin/exposure/pane.tsx +4 -4
  295. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  296. package/src/plugins/builtin/futures/index.tsx +1 -0
  297. package/src/plugins/builtin/futures/model.ts +2 -1
  298. package/src/plugins/builtin/futures/table.tsx +2 -2
  299. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  300. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  301. package/src/plugins/builtin/fx-matrix/client.ts +11 -3
  302. package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
  303. package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
  304. package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
  305. package/src/plugins/builtin/gpu/headless.ts +8 -2
  306. package/src/plugins/builtin/help/function-index.ts +4 -1
  307. package/src/plugins/builtin/help/index.tsx +1 -0
  308. package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
  309. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  310. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  311. package/src/plugins/builtin/holders/headless.ts +18 -1
  312. package/src/plugins/builtin/iv-history/client.ts +52 -11
  313. package/src/plugins/builtin/iv-history/format.ts +36 -23
  314. package/src/plugins/builtin/iv-history/headless.ts +36 -18
  315. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  316. package/src/plugins/builtin/iv-history/model.ts +163 -27
  317. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  318. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  319. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  320. package/src/plugins/builtin/jobs/client.ts +25 -29
  321. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  322. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  323. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  324. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  325. package/src/plugins/builtin/macro-day/pane.tsx +7 -5
  326. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  327. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  328. package/src/plugins/builtin/market-movers/client.ts +25 -7
  329. package/src/plugins/builtin/market-movers/footer.ts +2 -2
  330. package/src/plugins/builtin/market-movers/headless.ts +32 -12
  331. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  332. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  333. package/src/plugins/builtin/market-movers/model.ts +4 -1
  334. package/src/plugins/builtin/market-movers/screener.ts +14 -0
  335. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  336. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  337. package/src/plugins/builtin/market-movers/table.ts +17 -5
  338. package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
  339. package/src/plugins/builtin/members/client.ts +19 -4
  340. package/src/plugins/builtin/members/headless.ts +20 -7
  341. package/src/plugins/builtin/members/index.tsx +9 -9
  342. package/src/plugins/builtin/members/model.ts +23 -4
  343. package/src/plugins/builtin/members/pane.tsx +25 -15
  344. package/src/plugins/builtin/news/index.tsx +1 -0
  345. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  346. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  347. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  348. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  349. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  350. package/src/plugins/builtin/openfda/client.ts +14 -2
  351. package/src/plugins/builtin/openfda/index.tsx +3 -2
  352. package/src/plugins/builtin/openfda/model.ts +9 -0
  353. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  354. package/src/plugins/builtin/options/analytics.ts +54 -6
  355. package/src/plugins/builtin/options/enrichment.ts +17 -5
  356. package/src/plugins/builtin/options/model-spot.ts +5 -2
  357. package/src/plugins/builtin/options/strike-window.ts +2 -1
  358. package/src/plugins/builtin/options/table.ts +6 -9
  359. package/src/plugins/builtin/options/types.ts +2 -0
  360. package/src/plugins/builtin/options/view.tsx +42 -12
  361. package/src/plugins/builtin/options-positioning/client.ts +30 -0
  362. package/src/plugins/builtin/options-positioning/headless.ts +74 -21
  363. package/src/plugins/builtin/options-positioning/index.tsx +4 -4
  364. package/src/plugins/builtin/options-positioning/model.ts +122 -14
  365. package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
  366. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  367. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  368. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  369. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  370. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  371. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  372. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  373. package/src/plugins/builtin/pe-band/model.ts +12 -49
  374. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  375. package/src/plugins/builtin/perps/cascade.ts +236 -0
  376. package/src/plugins/builtin/perps/client.ts +56 -2
  377. package/src/plugins/builtin/perps/freshness.ts +12 -10
  378. package/src/plugins/builtin/perps/headless.ts +65 -9
  379. package/src/plugins/builtin/perps/index.tsx +7 -5
  380. package/src/plugins/builtin/perps/live-rows.ts +10 -3
  381. package/src/plugins/builtin/perps/model.ts +18 -4
  382. package/src/plugins/builtin/perps/pane.tsx +32 -31
  383. package/src/plugins/builtin/perps/status-line.ts +25 -0
  384. package/src/plugins/builtin/plugin-module.ts +5 -1
  385. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  386. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  387. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  388. package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
  389. package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
  390. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  391. package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
  392. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  393. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  394. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
  395. package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
  396. package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
  397. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  398. package/src/plugins/builtin/rate-path/pane.tsx +2 -2
  399. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  400. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  401. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  402. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  403. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  404. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  405. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  406. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  407. package/src/plugins/builtin/research/event-model.ts +10 -8
  408. package/src/plugins/builtin/research/headless.ts +1 -1
  409. package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
  410. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  411. package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
  412. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  413. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  414. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  415. package/src/plugins/builtin/scanner/feed.ts +2 -2
  416. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  417. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  418. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  419. package/src/plugins/builtin/scanner/headless.ts +204 -35
  420. package/src/plugins/builtin/scanner/index.ts +40 -6
  421. package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
  422. package/src/plugins/builtin/scanner/live-feed.ts +168 -0
  423. package/src/plugins/builtin/scanner/live-headless.ts +138 -0
  424. package/src/plugins/builtin/scanner/live-model.ts +362 -0
  425. package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
  426. package/src/plugins/builtin/scanner/live-track.ts +186 -0
  427. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  428. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  429. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  430. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  431. package/src/plugins/builtin/sec/headless.ts +2 -1
  432. package/src/plugins/builtin/series-inventory.ts +3 -0
  433. package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
  434. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  435. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  436. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  437. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  438. package/src/plugins/builtin/shared/macro-releases.ts +65 -0
  439. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  440. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  441. package/src/plugins/builtin/shared/report-freshness.ts +3 -2
  442. package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
  443. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  444. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  445. package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
  446. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  447. package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
  448. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  449. package/src/plugins/builtin/themes/headless.ts +6 -4
  450. package/src/plugins/builtin/themes/model.ts +17 -8
  451. package/src/plugins/builtin/themes/pane.tsx +7 -7
  452. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  453. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  454. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  455. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  456. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  457. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  458. package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
  459. package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
  460. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  461. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  462. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  463. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  464. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  465. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  466. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  467. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  468. package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
  469. package/src/plugins/builtin/vol-surface/client.ts +9 -17
  470. package/src/plugins/builtin/vol-surface/headless.ts +63 -14
  471. package/src/plugins/builtin/vol-surface/index.tsx +2 -2
  472. package/src/plugins/builtin/vol-surface/model.ts +22 -7
  473. package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
  474. package/src/plugins/builtin/vol-surface/tables.ts +31 -3
  475. package/src/plugins/builtin/volatility/dvol.ts +46 -0
  476. package/src/plugins/builtin/volatility/index.tsx +2 -2
  477. package/src/plugins/builtin/volatility/pane.tsx +4 -4
  478. package/src/plugins/builtin/volatility/series.ts +26 -0
  479. package/src/plugins/builtin/world-indices/footer.ts +1 -1
  480. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  481. package/src/plugins/catalog-browser.ts +13 -8
  482. package/src/plugins/catalog-manifest.ts +2 -1
  483. package/src/plugins/marketplace/model.ts +18 -5
  484. package/src/portfolio/column-values.ts +5 -5
  485. package/src/portfolio/futures-positions.ts +6 -3
  486. package/src/portfolio/position-quantity.ts +15 -9
  487. package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
  488. package/src/remote/resources.ts +2 -1
  489. package/src/renderers/browser/app-services.ts +1 -7
  490. package/src/renderers/browser/config-host.ts +2 -55
  491. package/src/renderers/browser/main.tsx +7 -57
  492. package/src/renderers/dom/data-table/row.tsx +26 -0
  493. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  494. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  495. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  496. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  497. package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
  498. package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
  499. package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
  500. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  501. package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
  502. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
  503. package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
  504. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  505. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
  506. package/src/renderers/electrobun/bun/index.ts +25 -1
  507. package/src/renderers/electrobun/shared/app-name.ts +30 -0
  508. package/src/renderers/electrobun/shared/protocol.ts +10 -0
  509. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  510. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  511. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
  512. package/src/renderers/electrobun/view/main.tsx +24 -1
  513. package/src/renderers/electrobun/view/news-source.ts +3 -1
  514. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  515. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  516. package/src/renderers/opentui/start.tsx +5 -0
  517. package/src/renderers/opentui/toast-host.tsx +71 -43
  518. package/src/series/registry.ts +1 -0
  519. package/src/series/types.ts +6 -0
  520. package/src/series/validate.ts +1 -0
  521. package/src/sources/gloomberb-cloud/index.ts +1 -0
  522. package/src/sources/gloomberb-cloud/news.ts +47 -7
  523. package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
  524. package/src/sources/provider-router/batches.ts +10 -2
  525. package/src/sources/provider-router/financial-routes.ts +3 -3
  526. package/src/sources/provider-router/financials.ts +15 -3
  527. package/src/sources/provider-router/history.ts +28 -7
  528. package/src/sources/provider-router/index.ts +149 -10
  529. package/src/sources/provider-router/search.ts +13 -2
  530. package/src/state/app/context/index.tsx +3 -0
  531. package/src/state/ticker-cleanup.ts +50 -0
  532. package/src/sync/auth-transition.ts +19 -9
  533. package/src/sync/baseline.ts +10 -4
  534. package/src/sync/controller.ts +70 -19
  535. package/src/sync/core-contributors.ts +90 -31
  536. package/src/sync/react.ts +4 -1
  537. package/src/sync/types.ts +3 -0
  538. package/src/telemetry/stall-reports-dom.ts +38 -0
  539. package/src/telemetry/stall-reports-node.ts +75 -0
  540. package/src/telemetry/stall-reports.ts +214 -0
  541. package/src/telemetry/usage-counts.ts +0 -18
  542. package/src/test-support/preload.ts +14 -0
  543. package/src/theme/colors.ts +8 -0
  544. package/src/theme/themes.ts +10 -0
  545. package/src/tickers/company-name.ts +103 -3
  546. package/src/tickers/listing-venues.ts +38 -3
  547. package/src/tickers/search/bare-symbol.ts +36 -0
  548. package/src/tickers/search/index.ts +36 -7
  549. package/src/tickers/search/ranking.ts +10 -1
  550. package/src/tickers/search/types.ts +5 -0
  551. package/src/tickers/terminal-codes.ts +19 -0
  552. package/src/time-series/alignment.ts +1 -1
  553. package/src/time-series/base-series.ts +13 -5
  554. package/src/time-series/daily-valuation.ts +62 -0
  555. package/src/time-series/economic.ts +18 -4
  556. package/src/time-series/field-catalog.ts +2 -4
  557. package/src/time-series/filed-trailing-eps.ts +197 -0
  558. package/src/time-series/forward-valuation.ts +32 -0
  559. package/src/time-series/fundamentals.ts +198 -7
  560. package/src/time-series/hooks.ts +1 -0
  561. package/src/time-series/ice-credit.ts +3 -0
  562. package/src/time-series/price-comparison.ts +30 -2
  563. package/src/time-series/price-units.ts +72 -2
  564. package/src/time-series/resolve-context.ts +7 -1
  565. package/src/time-series/resolve.ts +116 -15
  566. package/src/time-series/series-presentation.ts +40 -6
  567. package/src/time-series/session-bar.ts +11 -0
  568. package/src/time-series/spec.ts +5 -3
  569. package/src/time-series/studies.ts +278 -74
  570. package/src/time-series/total-return.ts +160 -0
  571. package/src/time-series/transforms.ts +3 -3
  572. package/src/time-series/types.ts +27 -1
  573. package/src/time-series/valuation-releases.ts +39 -0
  574. package/src/types/data-provider.ts +9 -1
  575. package/src/types/desktop-window.ts +2 -0
  576. package/src/types/financials.ts +73 -2
  577. package/src/types/headless.ts +61 -5
  578. package/src/types/instrument.ts +3 -0
  579. package/src/types/news-source.ts +1 -0
  580. package/src/types/plugin.ts +10 -1
  581. package/src/ui/host.tsx +3 -0
  582. package/src/updater/command-names.ts +12 -7
  583. package/src/updater/failure.ts +4 -1
  584. package/src/utils/cli-output.ts +42 -1
  585. package/src/utils/crypto-pair.ts +44 -0
  586. package/src/utils/financial-statements.ts +6 -1
  587. package/src/utils/forward-dividend.ts +2 -0
  588. package/src/utils/fundamentals.ts +24 -3
  589. package/src/utils/fx-market-hours.ts +26 -3
  590. package/src/utils/main-thread-stall.ts +35 -2
  591. package/src/utils/market-capitalization.ts +24 -1
  592. package/src/utils/network-failure.ts +9 -6
  593. package/src/utils/option-expiry.ts +48 -1
  594. package/src/utils/options.ts +10 -0
  595. package/src/utils/plugin-proxy-hosts.json +1 -0
  596. package/src/utils/price-history.ts +12 -1
  597. package/src/utils/shell-word.ts +8 -0
  598. package/src/utils/trailing-dividend.ts +53 -4
  599. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
  600. package/src/renderers/browser/first-screen.ts +0 -193
@@ -1,3 +1,5 @@
1
+ import { currencyPairSpelling } from "../utils/currency-pair";
2
+
1
3
  /**
2
4
  * What a price counts, read from its unit: the currency it is in and the
3
5
  * quantity one unit of that currency buys ("USD / gallon"), as futures
@@ -7,9 +9,22 @@
7
9
  /** Gallons in one US oil barrel: heating oil and gasoline quote per gallon, crude per barrel. */
8
10
  export const GALLONS_PER_BARREL = 42;
9
11
 
12
+ /**
13
+ * Barrels in a metric tonne of a refined product, the conversions a formula
14
+ * knows by their number: gasoil 7.45 bbl/t (ICE gasoil's own), gasoline 8.45.
15
+ * Times one, a price per barrel is a price per tonne.
16
+ */
17
+ const BARRELS_PER_TONNE = { gasoil: 7.45, gasoline: 8.45 } as const;
18
+
19
+ /** Whether a formula's multiplier is the barrels in a tonne of a product (7.45 or 8.45). */
20
+ export function isBarrelsPerTonne(value: number): boolean {
21
+ return Object.values(BARRELS_PER_TONNE).includes(value as 7.45 | 8.45);
22
+ }
23
+
10
24
  const US_CENTS = /^us\s*cents?$/i;
11
25
  const GALLON = /^(?:gallons?|gal)$/i;
12
26
  const BARREL = /^(?:barrels?|bbl)$/i;
27
+ const TONNE = /^(?:tonnes?|metric tons?|mt|t)$/i;
13
28
 
14
29
  /**
15
30
  * The currency code a unit's leading factor names: `USD` for "USD", `USX`
@@ -41,10 +56,43 @@ export function contractPriceUnit(unit: string | null | undefined, currency: str
41
56
  return parts.currency.toUpperCase() === currency.trim().toUpperCase() ? unit!.trim() : null;
42
57
  }
43
58
 
44
- /** One name for a quantity however it is written: "gal" and "gallons" are a gallon, "bbl" a barrel. */
59
+ /** One name for a quantity however it is written: "gal" and "gallons" are a gallon, "bbl" a barrel, "t" a tonne. */
45
60
  export function quantityIdentity(quantity: string): string {
46
61
  const token = quantity.trim().toLowerCase();
47
- return GALLON.test(token) ? "gallon" : BARREL.test(token) ? "barrel" : token.replace(/^shares$/, "share");
62
+ return GALLON.test(token) ? "gallon" : BARREL.test(token) ? "barrel" : TONNE.test(token) ? "tonne"
63
+ : token.replace(/^shares$/, "share");
64
+ }
65
+
66
+ /**
67
+ * The physical quantities a price can count, each by one name: what sets a
68
+ * price's scale, so a per-gallon and a per-barrel price never share an axis.
69
+ * A share, a unit or a currency is none of them.
70
+ */
71
+ const PHYSICAL_QUANTITIES: ReadonlyArray<readonly [RegExp, string]> = [
72
+ [GALLON, "gallon"],
73
+ [BARREL, "barrel"],
74
+ [TONNE, "tonne"],
75
+ [/^(?:dry\s+metric\s+tons?|dmtu?)$/i, "dry metric ton"],
76
+ [/^(?:short\s+)?tons?$/i, "short ton"],
77
+ [/^(?:bushels?|bu)$/i, "bushel"],
78
+ [/^(?:pounds?|lbs?)$/i, "pound"],
79
+ [/^(?:troy\s+)?(?:ounces?|oz)$/i, "troy ounce"],
80
+ [/^(?:hundredweight|cwt)$/i, "hundredweight"],
81
+ [/^(?:kilograms?|kg)$/i, "kilogram"],
82
+ [/^mmbtu$/i, "MMBtu"],
83
+ [/^therms?$/i, "therm"],
84
+ [/^gj$/i, "GJ"],
85
+ [/^mwh$/i, "MWh"],
86
+ [/^kwh$/i, "kWh"],
87
+ [/^(?:board\s+feet|mbf)$/i, "board feet"],
88
+ [/^(?:litres?|liters?)$/i, "litre"],
89
+ [/^(?:cubic\s+met(?:er|re)s?|m3)$/i, "cubic metre"],
90
+ ];
91
+
92
+ /** The physical quantity a unit's denominator names ("barrel" for "bbl"), or null for a share, a unit or a currency. */
93
+ export function physicalQuantity(quantity: string): string | null {
94
+ const token = quantity.trim();
95
+ return PHYSICAL_QUANTITIES.find(([pattern]) => pattern.test(token))?.[1] ?? null;
48
96
  }
49
97
 
50
98
  /** A price per gallon: "USD / gallon", "USD/gallon". */
@@ -67,3 +115,25 @@ export function isPerBarrelUnit(unit: string): boolean {
67
115
  export function perBarrelUnit(unit: string): string {
68
116
  return isPerGallonUnit(unit) ? unit.replace(/(\/\s*)\S.*$/, "$1barrel") : unit;
69
117
  }
118
+
119
+ /**
120
+ * The per-tonne unit a per-barrel price takes once multiplied by the barrels
121
+ * in a tonne (7.45 for gasoil), in the same wording: "USD / barrel" is
122
+ * "USD / tonne". Any other unit is returned as it is.
123
+ */
124
+ export function perTonneUnit(unit: string): string {
125
+ return isPerBarrelUnit(unit) ? unit.replace(/(\/\s*)\S.*$/, "$1tonne") : unit;
126
+ }
127
+
128
+ /**
129
+ * What a currency pair's price counts: the quote currency one unit of the
130
+ * base buys, so EURUSD=X is USD / EUR and USDJPY=X JPY / USD. Null for a
131
+ * listing that is not a pair, or whose quote is in another currency.
132
+ */
133
+ export function currencyPairPriceUnit(symbol: string, exchange: string | undefined, currency: string | null | undefined): string | null {
134
+ const pair = currencyPairSpelling(symbol, exchange);
135
+ if (!pair || pair.ambiguous || !currency) return null;
136
+ const base = pair.symbol.slice(0, 3);
137
+ const quote = pair.symbol.slice(3, 6);
138
+ return quote === currency.trim().toUpperCase() ? `${quote} / ${base}` : null;
139
+ }
@@ -3,7 +3,8 @@ import type { PriceHistoryResult } from "../types/price-history";
3
3
  import { LiveBarAccumulator } from "./live-bars";
4
4
  import type { ChartResolutionSupport, ManualChartResolution } from "./resolution";
5
5
  import type { DataProvider, MarketDataRequestContext } from "../types/data-provider";
6
- import type { Quote, QuoteMetadata, TickerFinancials } from "../types/financials";
6
+ import type { CorporateActionsData, EarningsAction, Quote, QuoteMetadata, TickerFinancials } from "../types/financials";
7
+ import type { EarningsHistoryPayload } from "../api-client/earnings";
7
8
  import type { FredSeriesLoadResult, FredSeriesRequest } from "../sources/gloomberb-cloud/fred-series";
8
9
  import { chartQuoteOverrideKeyForSource } from "./live-quotes";
9
10
  import { publicTickerKey } from "../utils/exchanges";
@@ -16,6 +17,8 @@ import type {
16
17
  export interface ChartResolveSources {
17
18
  dataProvider: DataProvider | null;
18
19
  loadFredSeries: (request: FredSeriesRequest) => Promise<FredSeriesLoadResult>;
20
+ /** Dated releases from the same history the earnings calendar reads. US listings only. */
21
+ loadEarningsHistory?: (symbol: string) => Promise<EarningsHistoryPayload>;
19
22
  now?: Date;
20
23
  /** Latest streamed quote per security identity, layered over snapshot data. */
21
24
  quoteOverrides?: ReadonlyMap<string, Quote>;
@@ -74,6 +77,9 @@ export interface LiveTailRequest {
74
77
  /** Raw source data retained while live quotes recompute the chart tail. */
75
78
  export class ChartResolveCache {
76
79
  readonly financialsByInstrument = new Map<string, Promise<TickerFinancials | null>>();
80
+ readonly earningsReleasesByInstrument = new Map<string, Promise<EarningsAction[]>>();
81
+ /** Each listing's cash distribution history, for the charts that draw total returns. */
82
+ readonly distributionsByInstrument = new Map<string, Promise<CorporateActionsData | null>>();
77
83
  readonly quoteMetadataByInstrument = new Map<string, Promise<QuoteMetadata | null>>();
78
84
  /** Each futures symbol's contract price unit, asked once per chart once known. */
79
85
  readonly futuresPriceUnits = new Map<string, Promise<string | null>>();
@@ -23,7 +23,7 @@ import {
23
23
  import { aggregatePriceBars, futuresOpenMinuteOf, withBuiltBarSize } from "./bar-aggregation";
24
24
  import { TIME_RANGES, type TimeRange } from "./range";
25
25
  import type { DataProvider } from "../types/data-provider";
26
- import type { Quote, TickerFinancials } from "../types/financials";
26
+ import type { CorporateActionsData, EarningsAction, Quote, TickerFinancials } from "../types/financials";
27
27
  import { mergeQuoteMetadata, quoteMetadataFromQuote, quoteMetadataMatchesTarget } from "../market-data/quotes/metadata";
28
28
  import type { FredSeriesRequest } from "../sources/gloomberb-cloud/fred-series";
29
29
  import { fredCreditCoverageNotice } from "./economic";
@@ -44,12 +44,14 @@ import { VALUATION_CURRENCY_WARNING_PREFIX } from "./valuation-currency";
44
44
  import { chartPriceHistoryIntegrityNotices } from "./market";
45
45
  import { maxStudyWarmupPoints, resolveStudies } from "./studies";
46
46
  import { clipSeriesToWindow } from "./alignment";
47
+ import { totalReturnSeries } from "./total-return";
47
48
  import {
48
49
  bufferPriceComparison,
49
50
  clipPriceComparison,
50
51
  priceComparisonBoundsForSeries,
51
52
  priceComparisonSeriesIds,
52
53
  resolvePriceComparison,
54
+ totalReturnLevelsNotice,
53
55
  } from "./price-comparison";
54
56
  import { chartQuoteOverrideKeyForSource } from "./live-quotes";
55
57
  import { chartSeriesSourceKey } from "../capabilities/chart-series";
@@ -266,6 +268,13 @@ export async function resolveChartSpecData(
266
268
  return result;
267
269
  }
268
270
 
271
+ /** The price fields whose returns a distribution adds to. */
272
+ function totalReturnFieldId(fieldId: string): boolean {
273
+ const canonical = getTimeSeriesField(fieldId)?.id ?? fieldId;
274
+ return canonical === "market.ohlcv" || canonical === "market.close" || canonical === "market.open"
275
+ || canonical === "market.high" || canonical === "market.low";
276
+ }
277
+
269
278
  async function resolveChartSpecPass(
270
279
  spec: ChartSpec,
271
280
  sources: ChartResolveSources,
@@ -277,6 +286,7 @@ async function resolveChartSpecPass(
277
286
  && entry.source.kind === "security" && isFundamentalFieldId(entry.source.fieldId))
278
287
  ? [FINANCIAL_VINTAGE_NOTICE] : [];
279
288
  const priorityWarnings: string[] = [];
289
+ const coverageNotices: string[] = [];
280
290
  const delistings = new Map<string, { symbol: string; label: string; notice: string }>();
281
291
  const baseSeriesIds = new Set(spec.series.map((entry) => entry.id));
282
292
  const visibleSeriesIds = new Set(spec.series
@@ -300,9 +310,9 @@ async function resolveChartSpecPass(
300
310
  const fieldId = getTimeSeriesField(source.fieldId)?.id ?? source.fieldId;
301
311
  // Calendar-window research needs the same full source bundle as a period
302
312
  // count. The extended bundle also carries the estimate history behind the
303
- // forward multiples; PEG alone has no historical series.
313
+ // forward multiples; daily PEG also needs consecutive trailing years.
304
314
  const statementHistory = /^(fundamental|valuation)\./.test(fieldId)
305
- && fieldId !== "valuation.pegRatio"
315
+ && (fieldId !== "valuation.pegRatio" || source.period === "daily")
306
316
  ? "extended" as const : undefined;
307
317
  const key = `${instrumentKey(source)}|history:${statementHistory ?? "default"}`;
308
318
  let pending = cache.financialsByInstrument.get(key);
@@ -330,6 +340,53 @@ async function resolveChartSpecPass(
330
340
  }
331
341
  return pending;
332
342
  };
343
+ const loadEarningsReleases = (source: Extract<ChartSeriesSpec["source"], { kind: "security" }>) => {
344
+ const key = instrumentKey(source);
345
+ let pending = cache.earningsReleasesByInstrument.get(key);
346
+ if (!pending) {
347
+ pending = (async () => {
348
+ const [events, history] = await Promise.allSettled([
349
+ sources.dataProvider?.getCorporateActions?.(source.instrument.symbol, source.instrument.exchange, requestContext(source)),
350
+ isUsListingExchange(source.instrument.exchange) ? sources.loadEarningsHistory?.(source.instrument.symbol) : undefined,
351
+ ]);
352
+ const releases: EarningsAction[] = [];
353
+ if (history.status === "fulfilled") for (const report of history.value?.reports ?? []) {
354
+ if (report.dateStatus !== "reported") continue;
355
+ releases.push({ date: report.date, dateType: "announcement", dateStatus: "reported",
356
+ fiscalPeriod: report.fiscalPeriod ?? undefined, reportedAt: report.reportedAt ?? undefined,
357
+ time: report.timingSource !== "history" ? report.timing ?? undefined : undefined });
358
+ }
359
+ else logFailure("time-series-resolve", "Loading earnings release history failed")(history.reason);
360
+ if (events.status === "fulfilled") releases.push(...events.value?.earnings ?? []);
361
+ else logFailure("time-series-resolve", "Loading earnings release events failed")(events.reason);
362
+ if (events.status === "rejected" || history.status === "rejected") cache.earningsReleasesByInstrument.delete(key);
363
+ return releases;
364
+ })();
365
+ cache.earningsReleasesByInstrument.set(key, pending);
366
+ cache.revision += 1;
367
+ }
368
+ return pending;
369
+ };
370
+ // Total returns reinvest the cash a listing paid; each listing's history is read once per chart.
371
+ const loadDistributions = (source: Extract<ChartSeriesSpec["source"], { kind: "security" }>) => {
372
+ const key = instrumentKey(source);
373
+ let pending = cache.distributionsByInstrument.get(key);
374
+ if (!pending) {
375
+ pending = (async (): Promise<CorporateActionsData | null> => {
376
+ const read = sources.dataProvider?.getCorporateActions;
377
+ if (!read) return null;
378
+ try {
379
+ return await read.call(sources.dataProvider, source.instrument.symbol, source.instrument.exchange, requestContext(source));
380
+ } catch (error) {
381
+ cache.distributionsByInstrument.delete(key);
382
+ throw error;
383
+ }
384
+ })();
385
+ cache.distributionsByInstrument.set(key, pending);
386
+ cache.revision += 1;
387
+ }
388
+ return pending;
389
+ };
333
390
  const loadQuoteMetadata = (source: Extract<ChartSeriesSpec["source"], { kind: "security" }>) => {
334
391
  const key = instrumentKey(source);
335
392
  let pending = cache.quoteMetadataByInstrument.get(key);
@@ -496,8 +553,16 @@ async function resolveChartSpecPass(
496
553
  all = false,
497
554
  ) => {
498
555
  const support = await loadResolutionSupport(source, priceOnly);
499
- const maxRange = getSupportMaxRange(support, initialResolution);
556
+ const dailyValuation = source.period === "daily" && source.fieldId.startsWith("valuation.");
557
+ const sourceResolution = dailyValuation ? "1d" : initialResolution;
558
+ const maxRange = getSupportMaxRange(support, sourceResolution);
500
559
  const ahead = movedWindow ? barsAhead(requestVisibleBounds, maxRange, referenceNow.getTime()) : 0;
560
+ // Bars ahead that reach the present end wherever the clock is on this pass.
561
+ // Such a window is named by its start and the present, so every pass shares
562
+ // one acquisition and the stale-copy refresh below keeps it current. Named
563
+ // by the clock, each live pass fetched and kept another copy of the history.
564
+ const toPresent = ahead > 0 && requestVisibleBounds.end !== null
565
+ && requestVisibleBounds.end + ahead >= referenceNow.getTime();
501
566
  const durationEnd = rangeDurationBounds(spec, initialCalculationBounds).end;
502
567
  const historyBounds = clampHistoryBoundsToSupport(
503
568
  ahead > 0 && initialCalculationBounds.end !== null
@@ -517,8 +582,8 @@ async function resolveChartSpecPass(
517
582
  explicitWindow: hasExplicitWindow,
518
583
  authoredWindow: hasExplicitWindow && !movedWindow,
519
584
  fallbackRange,
520
- resolution: initialResolution,
521
- allowProviderDefaultFallback: spec.viewport.resolution === "auto",
585
+ resolution: sourceResolution,
586
+ allowProviderDefaultFallback: !dailyValuation && spec.viewport.resolution === "auto",
522
587
  support,
523
588
  requiredWarmupPoints: maxStudyWarmupPoints(spec.studies),
524
589
  ...(extendedHoursFor(source, initialResolution) ? { historySession: "extended" as const } : {}),
@@ -531,7 +596,9 @@ async function resolveChartSpecPass(
531
596
  request.allowProviderDefaultFallback ? "auto" : "manual",
532
597
  isMarketFieldId(source.fieldId) ? source.period ?? "auto" : "auto",
533
598
  ...(request.explicitWindow
534
- ? [request.bounds.start ?? "open", request.bounds.end ?? "open"]
599
+ ? toPresent
600
+ ? [initialCalculationBounds.start ?? "open", "present"]
601
+ : [request.bounds.start ?? "open", request.bounds.end ?? "open"]
535
602
  : []),
536
603
  // A moved view can chart retained bars that the same authored dates cannot.
537
604
  ...(request.explicitWindow && !request.authoredWindow && !request.allowProviderDefaultFallback ? ["moved"] : []),
@@ -774,7 +841,10 @@ async function resolveChartSpecPass(
774
841
  const result = baseEconomicSeries(seriesSpec, fred, index);
775
842
  const coverageNotice = fredCreditCoverageNotice(seriesSpec.source.seriesId, fred.data.info,
776
843
  requestVisibleBounds.start, fred.data.observations);
777
- if (result && coverageNotice) priorityWarnings.push(`${result.label}: ${coverageNotice}`);
844
+ if (result && coverageNotice) {
845
+ priorityWarnings.push(`${result.label}: ${coverageNotice}`);
846
+ coverageNotices.push(`${result.label}: ${coverageNotice}`);
847
+ }
778
848
  const freshnessWarning = staleFredWarning(fred);
779
849
  if (result && freshnessWarning) priorityWarnings.push(`${result.label}: ${freshnessWarning}`);
780
850
  return result;
@@ -782,8 +852,9 @@ async function resolveChartSpecPass(
782
852
 
783
853
  const source = seriesSpec.source;
784
854
  const marketField = isMarketFieldId(source.fieldId);
855
+ const dailyValuation = source.period === "daily" && source.fieldId.startsWith("valuation.");
785
856
  const quoteDerivedValuation = valuationSeriesUsesLiveQuote(source.fieldId);
786
- const historyPricedValuation = valuationSeriesUsesPriceHistory(source.fieldId);
857
+ const historyPricedValuation = valuationSeriesUsesPriceHistory(source.fieldId, source.period);
787
858
  // Forward P/E only prices a history when the bundle carries estimates;
788
859
  // otherwise the provider's single figure stands and history is not required.
789
860
  const forwardPE = getTimeSeriesField(source.fieldId)?.id === "valuation.forwardPE";
@@ -833,6 +904,10 @@ async function resolveChartSpecPass(
833
904
  let merged = financials ?? (history ? emptyFinancials() : null);
834
905
  if (merged && quoteOverride) merged = { ...merged, quote: latestQuote(merged.quote, quoteOverride) };
835
906
  if (!merged) throw new Error(`No financial data is available for ${instrumentLabel(source)}.`);
907
+ if (dailyValuation && source.fieldId !== "valuation.forwardPE" && source.fieldId !== "valuation.realizedNtmPE"
908
+ && merged.earningsReleases === undefined) {
909
+ merged = { ...merged, earningsReleases: await loadEarningsReleases(resolvedSource) };
910
+ }
836
911
  // Historical labels can retain listing facts after the live price is
837
912
  // unavailable. Reuse existing facts before requesting a price-free snapshot.
838
913
  const existingMetadata = merged.quoteMetadata && quoteMetadataMatchesTarget(merged.quoteMetadata, resolvedSource.instrument.symbol, resolvedSource.instrument.exchange)
@@ -883,17 +958,23 @@ async function resolveChartSpecPass(
883
958
  // Keep provider history separate from quote-extended display bars. A live
884
959
  // mark is not a daily closing observation, including on snapshot replay.
885
960
  const historical = history ? { ...merged, priceHistory: history.points } : merged;
886
- sources.onSecurityData?.(resolvedSpec, history?.session || realizedVolInputs.has(seriesSpec.id) ? historical : sourceBars, history !== null);
961
+ sources.onSecurityData?.(resolvedSpec, dailyValuation || history?.session || realizedVolInputs.has(seriesSpec.id) ? historical : sourceBars, history !== null);
887
962
  const base = baseSecuritySeries(
888
963
  resolvedSpec,
889
- merged,
964
+ dailyValuation ? historical : merged,
890
965
  index,
891
966
  history?.resolution,
892
967
  quoteMetadata,
893
968
  contractUnit,
894
969
  );
895
970
  if (!base) throw new Error(`Unknown field ${source.fieldId}.`);
896
- const result = builtBars ? withBuiltBarSize(base, builtBars) : base;
971
+ let result = builtBars ? withBuiltBarSize(base, builtBars) : base;
972
+ if (spec.viewport.totalReturn === true && totalReturnFieldId(source.fieldId)) {
973
+ const outcome = await totalReturnSeries(result, () => loadDistributions(resolvedSource),
974
+ merged.quote?.currency || quoteMetadata?.currency, referenceNow);
975
+ result = outcome.series;
976
+ if (outcome.notice) warnings.push(`${instrumentLabel(resolvedSource)}: ${outcome.notice}`);
977
+ }
897
978
  if (marketField && history?.historyStart !== undefined && Number.isFinite(history.historyStart)) {
898
979
  result.historyStart = new Date(history.historyStart);
899
980
  }
@@ -909,7 +990,7 @@ async function resolveChartSpecPass(
909
990
  if (historicalResult) historicalPriceSeries.set(seriesSpec.id, historicalResult);
910
991
  }
911
992
  const reportedForwardPE = merged.fundamentals?.forwardPE;
912
- if (source.fieldId === "valuation.forwardPE" && merged.epsEstimates) {
993
+ if (source.fieldId === "valuation.forwardPE" && merged.epsEstimates && !dailyValuation) {
913
994
  warnings.push(FORWARD_PE_BASIS_NOTICE);
914
995
  } else if (source.fieldId === "valuation.forwardPE" && reportedForwardPE != null
915
996
  && Number.isFinite(reportedForwardPE) && reportedForwardPE <= 0) {
@@ -918,7 +999,7 @@ async function resolveChartSpecPass(
918
999
  if (source.fieldId === "valuation.realizedNtmPE" && merged.epsEstimates) {
919
1000
  warnings.push(REALIZED_NTM_PE_BASIS_NOTICE);
920
1001
  }
921
- if (isFundamentalFieldId(source.fieldId) && fundamentalSeriesUsesAvailabilityFallback(merged, source)) {
1002
+ if (!dailyValuation && isFundamentalFieldId(source.fieldId) && fundamentalSeriesUsesAvailabilityFallback(merged, source)) {
922
1003
  result.warning = [result.warning, "Publication dates are unavailable for some observations; period-end dates are used as a fallback."].filter(Boolean).join(" ");
923
1004
  }
924
1005
  return result;
@@ -943,9 +1024,24 @@ async function resolveChartSpecPass(
943
1024
  ? latestSessionView(primaryRaw, rawSeries.filter((entry) => entry.observationKind === "market"),
944
1025
  extendedHistories.has(primaryRaw?.id ?? ""), quoteReferences.get(primaryRaw?.id ?? ""))
945
1026
  : null;
1027
+ const dailyValuationView = spec.series.filter((entry) => entry.visible !== false);
1028
+ const shortDailyValuation = !hasExplicitWindow && (spec.viewport.range === "1D" || spec.viewport.range === "1W")
1029
+ && rawSeries.every(series => series.nativeFrequency === "daily")
1030
+ && dailyValuationView.length > 0 && dailyValuationView.every(({ source }) => source.kind === "security"
1031
+ && source.period === "daily" && source.fieldId.startsWith("valuation."));
1032
+ const dailyTimes = shortDailyValuation ? [...new Set(rawSeries.flatMap((entry) => entry.points.map((point) => point.date.getTime())))]
1033
+ .filter(Number.isFinite).sort((a, b) => a - b) : [];
1034
+ const latestDaily = dailyTimes.at(-1);
1035
+ // A daily multiple has no intraday observations. The day tab compares its
1036
+ // last two closes; the week tab ends at the last close even on a weekend.
1037
+ const dailyBounds = latestDaily === undefined ? null : {
1038
+ start: spec.viewport.range === "1D" ? dailyTimes.at(-2) ?? latestDaily - CHART_RESOLUTION_STEP_MS["1d"]
1039
+ : latestDaily - 7 * CHART_RESOLUTION_STEP_MS["1d"],
1040
+ end: latestDaily,
1041
+ };
946
1042
  const bounds = hasExplicitWindow
947
1043
  ? requestVisibleBounds
948
- : sessionView?.bounds ?? (isDatasetChart(spec)
1044
+ : dailyBounds ?? sessionView?.bounds ?? (isDatasetChart(spec)
949
1045
  ? endAtNewestObservation(initialVisibleBounds, rawSeries)
950
1046
  : followLatestMarketObservation(initialVisibleBounds, rawSeries));
951
1047
  if (sessionView?.priorClose != null && primaryRaw?.transform === "raw"
@@ -1024,6 +1120,9 @@ async function resolveChartSpecPass(
1024
1120
  bounds, comparisonResolution, requestBounds ? null : spec.viewport.dateWindow ?? null,
1025
1121
  );
1026
1122
  if (priceComparison) warnings.push(priceComparison.notice);
1123
+ // A raw-price chart of total returns is no comparison, and its levels say what they are instead.
1124
+ const returnBasisNotice = priceComparison ? null : totalReturnLevelsNotice(spec, rawSeries);
1125
+ if (returnBasisNotice) warnings.push(returnBasisNotice);
1027
1126
  warnings.push(...chartPriceHistoryIntegrityNotices(priceHistoryIntegrity));
1028
1127
  warnings.push(...financialPeriodCoverageWarnings(financialPeriodCoverage(spec, resolved)));
1029
1128
  const resolvedById = new Map(resolved.map((entry) => [entry.id, entry] as const));
@@ -1081,6 +1180,8 @@ async function resolveChartSpecPass(
1081
1180
  loading: false,
1082
1181
  errors,
1083
1182
  warnings: [...new Set([...priorityWarnings, ...warnings])],
1183
+ ...(coverageNotices.length ? { coverageNotices: [...new Set(coverageNotices)] } : {}),
1184
+ ...(returnBasisNotice ? { returnBasisNotice } : {}),
1084
1185
  ...(delistings.size ? { delistings: [...delistings.values()] } : {}),
1085
1186
  viewport,
1086
1187
  resolution,
@@ -13,6 +13,8 @@ import {
13
13
  type PriceComparison,
14
14
  } from "./price-comparison";
15
15
  import { parsePublicTickerKey, publicTickerKey } from "../utils/exchanges";
16
+ import { physicalQuantity } from "./price-units";
17
+ import { isCurrencyCode } from "../utils/currency-pair";
16
18
  import type {
17
19
  ChartSeriesSpec,
18
20
  ChartSpec,
@@ -40,33 +42,65 @@ export function resolvePriceHistoryIntegrity(
40
42
  ];
41
43
  }
42
44
 
45
+ /**
46
+ * What sets a price's scale beyond its currency: the physical quantity it
47
+ * counts ("gallon" for USD / gallon, "barrel" for USD/bbl), or "currency" for
48
+ * an FX rate (USD / EUR). Null for a bare currency, a share or a unit, which
49
+ * scale as any price in their currency does.
50
+ */
51
+ function scaleQuantity(unit: string): string | null {
52
+ const slash = unit.indexOf("/");
53
+ if (slash < 0) return null;
54
+ const per = unit.slice(slash + 1).trim();
55
+ return physicalQuantity(per) ?? (/^[A-Z]{3}$/.test(per) && isCurrencyCode(per) ? "currency" : null);
56
+ }
57
+
58
+ /**
59
+ * Each series' axis: one a spec names, else the side whose series count the
60
+ * same unit. Prices in one currency per different physical quantities
61
+ * (heating oil USD / gallon beside Brent USD / barrel), or a price beside an
62
+ * FX rate, scale on axes of their own; per-share and per-unit prices, and FX
63
+ * rates quoted in one currency, share one as any prices in that currency do.
64
+ */
43
65
  export function assignAxes(
44
66
  series: ResolvedSeries[],
45
67
  specs: readonly { id: string; axis: ChartSeriesSpec["axis"] }[],
46
68
  warnings: string[],
47
69
  ): ResolvedSeries[] {
70
+ type AxisUnit = { group: string; quantity: string | null };
48
71
  const requested = new Map(specs.map((spec) => [spec.id, spec.axis] as const));
49
- const groupsByPanel = new Map<string, Partial<Record<"left" | "right", string>>>();
72
+ const groupsByPanel = new Map<string, Partial<Record<"left" | "right", AxisUnit>>>();
50
73
  return series.map((entry) => {
51
74
  const groups = groupsByPanel.get(entry.panelId) ?? {};
52
75
  groupsByPanel.set(entry.panelId, groups);
76
+ const quantity = scaleQuantity(entry.unit);
77
+ const sameGroup = (side: "left" | "right") => groups[side]?.group === entry.unitGroup;
78
+ const fits = (side: "left" | "right") => {
79
+ const held = groups[side];
80
+ return sameGroup(side) && (!held!.quantity || !quantity || held!.quantity === quantity);
81
+ };
53
82
  const preferred = requested.get(entry.id);
54
83
  let axis: "left" | "right";
55
84
  if (preferred === "left" || preferred === "right") {
56
85
  axis = preferred;
57
- } else if (groups.left === entry.unitGroup) {
86
+ } else if (fits("left")) {
58
87
  axis = "left";
59
- } else if (groups.right === entry.unitGroup) {
88
+ } else if (fits("right")) {
60
89
  axis = "right";
61
90
  } else if (!groups.left) {
62
91
  axis = "left";
63
- } else {
92
+ } else if (!groups.right || !sameGroup("left")) {
64
93
  axis = "right";
94
+ } else {
95
+ // Both axes taken: a third quantity in the same currency shares as it would without them.
96
+ axis = "left";
65
97
  }
66
- if (groups[axis] && groups[axis] !== entry.unitGroup) {
98
+ const held = groups[axis];
99
+ if (held && held.group !== entry.unitGroup) {
67
100
  warnings.push(`${entry.label} shares the ${axis} axis with a different unit; choose an explicit panel for independent scaling.`);
68
101
  }
69
- groups[axis] ??= entry.unitGroup;
102
+ if (!held) groups[axis] = { group: entry.unitGroup, quantity };
103
+ else if (!held.quantity && quantity && fits(axis)) held.quantity = quantity;
70
104
  return { ...entry, axis };
71
105
  });
72
106
  }
@@ -136,3 +136,14 @@ export function withSessionBar(
136
136
  }
137
137
  return points;
138
138
  }
139
+
140
+ /**
141
+ * The daily bars `history` shows as final, which alerts on a daily close read
142
+ * too: the bars as served, less the bar of a session still trading
143
+ * (withSessionBar, which `history` rebuilds from the quote and marks partial)
144
+ * and any bar the source marks partial. A close `history` prints without
145
+ * `partial` is final here, and one it prints with it is not.
146
+ */
147
+ export function finalDailyBars(points: PricePoint[], options: SessionBarOptions): PricePoint[] {
148
+ return withSessionBar(points, null, options).filter((point) => !point.partial);
149
+ }
@@ -37,7 +37,7 @@ import { isLeverageFactor } from "./leverage";
37
37
  const RANGE_SET = new Set<TimeRange>(TIME_RANGES);
38
38
  const PERIODS = new Set<SeriesPeriod>(["auto", "daily", "weekly", "monthly", "quarterly", "annual", "ttm"]);
39
39
  // `band` only stays on a capability series: a field's styles and an economic series' leave it out.
40
- const STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc", "band"]);
40
+ const STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points", "candles", "hollow", "heikin-ashi", "ohlc", "hlc", "band"]);
41
41
  const ECONOMIC_STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points"]);
42
42
  const TRANSFORMS = new Set<SeriesTransform>(["raw", "percent", "index100", "yoy", "qoq", "log"]);
43
43
  const AXES = new Set<SeriesAxis>(["auto", "left", "right"]);
@@ -71,7 +71,7 @@ export function isDatasetChart(spec: Pick<ChartSpec, "series">): boolean {
71
71
  }
72
72
 
73
73
  export function isOhlcSeriesStyle(style: SeriesStyle): boolean {
74
- return style === "candles" || style === "ohlc" || style === "hlc";
74
+ return style === "candles" || style === "hollow" || style === "heikin-ashi" || style === "ohlc" || style === "hlc";
75
75
  }
76
76
 
77
77
  export function coerceSeriesTransformForStyle(
@@ -333,6 +333,7 @@ export function normalizeChartSpec(value: unknown, fallback: ChartSpec = DEFAULT
333
333
  ? Math.min(10_000, Math.floor(viewport.maxPoints))
334
334
  : undefined,
335
335
  ...(viewport?.extendedHours === true ? { extendedHours: true } : {}),
336
+ ...(viewport?.totalReturn === true ? { totalReturn: true } : {}),
336
337
  },
337
338
  panels,
338
339
  series,
@@ -448,7 +449,8 @@ export function validateChartSpec(spec: ChartSpec): ChartSpecValidationResult {
448
449
  ));
449
450
  }
450
451
  if ((definition.id.startsWith("fundamental.") || definition.id.startsWith("valuation."))
451
- && (entry.source.period === "daily" || entry.source.period === "weekly" || entry.source.period === "monthly")) {
452
+ && ((entry.source.period === "daily" && !definition.id.startsWith("valuation."))
453
+ || entry.source.period === "weekly" || entry.source.period === "monthly")) {
452
454
  errors.push(issue(`${path}.source.period`, "unsupported-period", "Financial fields support automatic, quarterly, annual, or TTM periods."));
453
455
  }
454
456
  const groups = unitGroupsByPanel.get(entry.panelId) ?? new Set<string>();