gloomberb 0.19.1 → 0.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (554) hide show
  1. package/README.md +7 -1
  2. package/package.json +2 -1
  3. package/patches/electrobun@1.18.1.patch +108 -1
  4. package/src/account/upgrade-dialog.tsx +4 -23
  5. package/src/api-client/coalesce.ts +118 -0
  6. package/src/api-client/credit-boards.ts +13 -0
  7. package/src/api-client/crypto-etf-flows.ts +6 -0
  8. package/src/api-client/crypto-markets.ts +40 -0
  9. package/src/api-client/data.ts +201 -14
  10. package/src/api-client/equity-screener.ts +10 -0
  11. package/src/api-client/errors.ts +58 -0
  12. package/src/api-client/gpu.ts +29 -0
  13. package/src/api-client/index.ts +5 -2
  14. package/src/api-client/plan-access.ts +10 -1
  15. package/src/api-client/plan-rules.ts +35 -0
  16. package/src/api-client/research-activity.ts +1 -32
  17. package/src/api-client/types.ts +21 -1
  18. package/src/app/attention-notice.tsx +94 -0
  19. package/src/app/runtime/startup.ts +3 -1
  20. package/src/app.tsx +23 -1
  21. package/src/architecture/core-builtin-allowlist.json +2 -1
  22. package/src/capabilities/chart-series.ts +2 -1
  23. package/src/capabilities/factories.ts +1 -0
  24. package/src/cli/ascii-output.ts +42 -0
  25. package/src/cli/base-converter.ts +10 -2
  26. package/src/cli/command-options.ts +16 -1
  27. package/src/cli/commands/crypto-hints.ts +23 -4
  28. package/src/cli/commands/market.ts +101 -27
  29. package/src/cli/commands/news-search.ts +24 -5
  30. package/src/cli/commands/overview.ts +14 -10
  31. package/src/cli/commands/search.ts +84 -25
  32. package/src/cli/commands/system.ts +22 -30
  33. package/src/cli/commands/ticker.ts +49 -9
  34. package/src/cli/commands/version.ts +33 -0
  35. package/src/cli/context.ts +6 -9
  36. package/src/cli/data-dir.ts +7 -0
  37. package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
  38. package/src/cli/desktop-pane-shot-browser.ts +320 -0
  39. package/src/cli/desktop-pane-shot-routes.ts +26 -1
  40. package/src/cli/desktop-pane-shot.ts +196 -256
  41. package/src/cli/early-command.ts +42 -0
  42. package/src/cli/earnings-calendar.ts +9 -0
  43. package/src/cli/entry.ts +19 -13
  44. package/src/cli/errors.ts +12 -2
  45. package/src/cli/glossary.ts +2 -1
  46. package/src/cli/help.ts +1 -1
  47. package/src/cli/history-export.ts +22 -2
  48. package/src/cli/history-rows.ts +189 -11
  49. package/src/cli/index.ts +18 -1
  50. package/src/cli/known-gaps.ts +51 -5
  51. package/src/cli/listing-arg.ts +63 -10
  52. package/src/cli/options.ts +1 -1
  53. package/src/cli/output-file.ts +60 -13
  54. package/src/cli/pane-functions/catalog.ts +14 -3
  55. package/src/cli/pane-functions/data.ts +1 -1
  56. package/src/cli/pane-functions/dom.ts +45 -0
  57. package/src/cli/pane-functions/freshness.ts +46 -9
  58. package/src/cli/pane-functions/function-help.ts +17 -9
  59. package/src/cli/pane-functions/headless.ts +22 -14
  60. package/src/cli/pane-functions/index.ts +6 -2
  61. package/src/cli/pane-functions/options.ts +6 -2
  62. package/src/cli/pane-functions/resolver.ts +27 -5
  63. package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
  64. package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
  65. package/src/cli/pane-functions/screenshot-http.ts +70 -0
  66. package/src/cli/pane-functions/screenshot-render.ts +19 -5
  67. package/src/cli/quote-answer.ts +49 -6
  68. package/src/cli/registry.ts +41 -13
  69. package/src/cli/result.ts +12 -6
  70. package/src/cli/ticker-resolution.ts +2 -1
  71. package/src/cli/types.ts +2 -0
  72. package/src/components/chart/composite/format.ts +3 -0
  73. package/src/components/chart-table/header.tsx +1 -1
  74. package/src/components/chart-table/layout.ts +28 -11
  75. package/src/components/command-bar/action-runtime.ts +4 -0
  76. package/src/components/command-bar/commands/direct/index.ts +18 -33
  77. package/src/components/command-bar/commands/direct/runtime.ts +5 -0
  78. package/src/components/command-bar/commands/registry.ts +10 -1
  79. package/src/components/command-bar/routes/root/command-items.ts +9 -4
  80. package/src/components/command-bar/routes/root/runtime.ts +51 -6
  81. package/src/components/command-bar/selection-runtime.ts +12 -29
  82. package/src/components/command-bar/surface/index.tsx +9 -3
  83. package/src/components/company-logo.tsx +2 -1
  84. package/src/components/layout/detached-pane-shell.tsx +2 -2
  85. package/src/components/layout/header.tsx +10 -5
  86. package/src/components/layout/market-summary.ts +25 -7
  87. package/src/components/layout/pane/footer/index.tsx +8 -2
  88. package/src/components/layout/pane/footer/model.ts +73 -6
  89. package/src/components/layout/pane/header.tsx +2 -2
  90. package/src/components/layout/pane/status-footer.ts +2 -1
  91. package/src/components/market-board/index.tsx +3 -2
  92. package/src/components/onboarding/desks-step.tsx +12 -4
  93. package/src/components/onboarding/first-run-workspace.ts +81 -4
  94. package/src/components/onboarding/onboarding-frame.tsx +4 -2
  95. package/src/components/onboarding/onboarding-keyboard.ts +8 -5
  96. package/src/components/onboarding/onboarding-wizard.tsx +60 -26
  97. package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
  98. package/src/components/onboarding/run-setup-again.ts +24 -0
  99. package/src/components/onboarding/use-onboarding-progress.ts +33 -15
  100. package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
  101. package/src/components/onboarding/wizard-model.ts +57 -3
  102. package/src/components/onboarding/wizard-positions.ts +1 -1
  103. package/src/components/ui/delayed-mark.tsx +56 -0
  104. package/src/components/ui/display.tsx +16 -5
  105. package/src/components/ui/external-link.tsx +3 -6
  106. package/src/components/ui/icon.tsx +16 -3
  107. package/src/components/ui/logo-mark.tsx +8 -5
  108. package/src/components/ui/stat-grid.tsx +263 -19
  109. package/src/core/app-services.ts +11 -2
  110. package/src/data/app-persistence.ts +9 -7
  111. package/src/data/config/store/node.ts +11 -5
  112. package/src/data/config/store/normalize.ts +16 -3
  113. package/src/data/plugin-cache.ts +15 -0
  114. package/src/data/plugin-state-store.ts +81 -24
  115. package/src/data/profile-permissions.ts +47 -0
  116. package/src/data/resource-store.ts +165 -90
  117. package/src/data/session-store.ts +31 -8
  118. package/src/data/sqlite/access.ts +324 -0
  119. package/src/data/sqlite/checkpointer.ts +285 -0
  120. package/src/data/sqlite/database.ts +80 -4
  121. package/src/data/sqlite/retry.ts +50 -9
  122. package/src/data/ticker-store.ts +57 -23
  123. package/src/docs/snapshot.json +191 -78
  124. package/src/i18n/es.ts +18 -2
  125. package/src/i18n/ja.ts +18 -2
  126. package/src/i18n/ko.ts +18 -2
  127. package/src/i18n/zh-cn.ts +18 -2
  128. package/src/i18n/zh-tw.ts +18 -2
  129. package/src/layout/desk-keys.ts +4 -1
  130. package/src/layout/desks.ts +38 -5
  131. package/src/layout-marketplace/gallery-desktop.tsx +45 -16
  132. package/src/market-data/batched-reads.ts +16 -0
  133. package/src/market-data/coordinator/auxiliary.ts +4 -2
  134. package/src/market-data/coordinator/index.ts +11 -2
  135. package/src/market-data/futures-board.ts +1 -1
  136. package/src/market-data/futures-contract.ts +3 -1
  137. package/src/market-data/market/index-level.ts +6 -4
  138. package/src/market-data/published-apac-sessions.ts +6 -0
  139. package/src/market-data/realized-volatility.ts +4 -2
  140. package/src/news/aggregator.ts +7 -4
  141. package/src/news/news-model.ts +11 -0
  142. package/src/news/story-id.ts +22 -0
  143. package/src/news/ticker-symbols.ts +17 -1
  144. package/src/notifications/app-notifier.ts +35 -14
  145. package/src/plugins/auto-update-node.ts +10 -6
  146. package/src/plugins/builtin/account-management/form-components.tsx +1 -1
  147. package/src/plugins/builtin/account-management/index.tsx +2 -3
  148. package/src/plugins/builtin/account-management/model.ts +1 -1
  149. package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
  150. package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
  151. package/src/plugins/builtin/alerts/events.ts +10 -2
  152. package/src/plugins/builtin/alerts/index.tsx +5 -5
  153. package/src/plugins/builtin/alerts/research-builder.ts +5 -1
  154. package/src/plugins/builtin/alerts/research-rules.ts +18 -0
  155. package/src/plugins/builtin/alerts/targets.ts +4 -1
  156. package/src/plugins/builtin/attention/headless.ts +1 -1
  157. package/src/plugins/builtin/attention/index.tsx +2 -4
  158. package/src/plugins/builtin/attention/listing.ts +1 -1
  159. package/src/plugins/builtin/attention/series.ts +1 -1
  160. package/src/plugins/builtin/awards/index.tsx +1 -2
  161. package/src/plugins/builtin/backtest/client.ts +1 -0
  162. package/src/plugins/builtin/backtest/index.tsx +1 -2
  163. package/src/plugins/builtin/backtest/rules.ts +3 -1
  164. package/src/plugins/builtin/backtest/window.ts +8 -7
  165. package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
  166. package/src/plugins/builtin/broker-manager/index.tsx +1 -1
  167. package/src/plugins/builtin/buildout/cells.tsx +5 -0
  168. package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
  169. package/src/plugins/builtin/buildout/company/id.ts +2 -0
  170. package/src/plugins/builtin/buildout/company/model.ts +247 -0
  171. package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
  172. package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
  173. package/src/plugins/builtin/buildout/facilities.ts +309 -0
  174. package/src/plugins/builtin/buildout/format.ts +9 -0
  175. package/src/plugins/builtin/buildout/index.tsx +51 -5
  176. package/src/plugins/builtin/buildout/model/index.ts +62 -10
  177. package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
  178. package/src/plugins/builtin/buildout/model/types.ts +11 -2
  179. package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
  180. package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
  181. package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
  182. package/src/plugins/builtin/buildout/table-model.ts +26 -2
  183. package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
  184. package/src/plugins/builtin/cds/headless.ts +10 -2
  185. package/src/plugins/builtin/cds/model.ts +7 -3
  186. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
  187. package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
  188. package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
  189. package/src/plugins/builtin/changelog/index.tsx +3 -2
  190. package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
  191. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
  192. package/src/plugins/builtin/chart-composer/headless.ts +88 -21
  193. package/src/plugins/builtin/chart-composer/index.tsx +2 -3
  194. package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
  195. package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
  196. package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
  197. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  198. package/src/plugins/builtin/chat/controller/index.ts +1 -1
  199. package/src/plugins/builtin/chat/controller/messages.ts +15 -4
  200. package/src/plugins/builtin/chat/ghost-user.ts +8 -0
  201. package/src/plugins/builtin/chat/index.tsx +5 -5
  202. package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
  203. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  204. package/src/plugins/builtin/cloud/index.tsx +1 -2
  205. package/src/plugins/builtin/cloud/plugin.tsx +6 -4
  206. package/src/plugins/builtin/cloud/team/module.tsx +1 -1
  207. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
  208. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
  209. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
  210. package/src/plugins/builtin/comment-letters/index.tsx +1 -2
  211. package/src/plugins/builtin/commodity-balances/client.ts +4 -1
  212. package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
  213. package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
  214. package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
  215. package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
  216. package/src/plugins/builtin/congress-trades/headless.ts +2 -0
  217. package/src/plugins/builtin/congress-trades/model.ts +92 -64
  218. package/src/plugins/builtin/congress-trades/table.tsx +14 -2
  219. package/src/plugins/builtin/connections/index.ts +2 -3
  220. package/src/plugins/builtin/cot/client.ts +28 -2
  221. package/src/plugins/builtin/cot/headless.ts +4 -1
  222. package/src/plugins/builtin/cot/index.tsx +5 -4
  223. package/src/plugins/builtin/cot/model.ts +58 -7
  224. package/src/plugins/builtin/cot/pane.tsx +6 -2
  225. package/src/plugins/builtin/cpi/client.ts +6 -5
  226. package/src/plugins/builtin/cpi/index.tsx +1 -2
  227. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  228. package/src/plugins/builtin/credit-boards/client.ts +7 -2
  229. package/src/plugins/builtin/credit-boards/headless.ts +25 -12
  230. package/src/plugins/builtin/credit-boards/model.ts +29 -0
  231. package/src/plugins/builtin/credit-boards/series.ts +3 -4
  232. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
  233. package/src/plugins/builtin/credit-conditions/client.ts +22 -14
  234. package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
  235. package/src/plugins/builtin/crypto-board/client.ts +5 -5
  236. package/src/plugins/builtin/crypto-board/headless.ts +4 -3
  237. package/src/plugins/builtin/crypto-board/index.tsx +4 -2
  238. package/src/plugins/builtin/crypto-board/series.ts +6 -4
  239. package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
  240. package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
  241. package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
  242. package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
  243. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
  244. package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
  245. package/src/plugins/builtin/debt-maturities/model.ts +17 -1
  246. package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
  247. package/src/plugins/builtin/debt-maturities/series.ts +3 -3
  248. package/src/plugins/builtin/debug/index.tsx +1 -1
  249. package/src/plugins/builtin/dividend-yield/client.ts +32 -2
  250. package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
  251. package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
  252. package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
  253. package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
  254. package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
  255. package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
  256. package/src/plugins/builtin/dividend-yield/view.ts +21 -0
  257. package/src/plugins/builtin/docs/index.tsx +2 -3
  258. package/src/plugins/builtin/doe/client.ts +6 -5
  259. package/src/plugins/builtin/doe/headless.ts +16 -3
  260. package/src/plugins/builtin/doe/index.tsx +5 -4
  261. package/src/plugins/builtin/doe/prices.ts +2 -3
  262. package/src/plugins/builtin/doe/series.ts +4 -4
  263. package/src/plugins/builtin/earnings/board.tsx +2 -1
  264. package/src/plugins/builtin/earnings/history.tsx +2 -1
  265. package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
  266. package/src/plugins/builtin/earnings-calls/data.ts +19 -11
  267. package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
  268. package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
  269. package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
  270. package/src/plugins/builtin/econ/calendar-model.ts +29 -6
  271. package/src/plugins/builtin/econ/detail-view.tsx +16 -2
  272. package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
  273. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  274. package/src/plugins/builtin/equity-screener/headless.ts +1 -0
  275. package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
  276. package/src/plugins/builtin/equity-screener/index.tsx +1 -2
  277. package/src/plugins/builtin/equity-screener/model.ts +28 -4
  278. package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
  279. package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
  280. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
  281. package/src/plugins/builtin/executives/data.ts +6 -1
  282. package/src/plugins/builtin/executives/pane.tsx +9 -21
  283. package/src/plugins/builtin/exposure/holdings.ts +35 -13
  284. package/src/plugins/builtin/exposure/index.tsx +1 -2
  285. package/src/plugins/builtin/fear-greed/cache.ts +4 -0
  286. package/src/plugins/builtin/fear-greed/cli.ts +44 -0
  287. package/src/plugins/builtin/fear-greed/index.tsx +5 -53
  288. package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
  289. package/src/plugins/builtin/fear-greed/series.ts +6 -4
  290. package/src/plugins/builtin/fixed-income/index.tsx +1 -2
  291. package/src/plugins/builtin/futures/board-reads.ts +25 -0
  292. package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
  293. package/src/plugins/builtin/futures/index.tsx +15 -0
  294. package/src/plugins/builtin/futures/model.ts +14 -0
  295. package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
  296. package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
  297. package/src/plugins/builtin/futures-curve/headless.ts +6 -1
  298. package/src/plugins/builtin/futures-curve/index.tsx +1 -2
  299. package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
  300. package/src/plugins/builtin/gpu/board.tsx +51 -12
  301. package/src/plugins/builtin/gpu/client.ts +19 -3
  302. package/src/plugins/builtin/gpu/equities.tsx +5 -2
  303. package/src/plugins/builtin/gpu/headless.ts +14 -2
  304. package/src/plugins/builtin/gpu/index.tsx +1 -2
  305. package/src/plugins/builtin/gpu/model.ts +20 -2
  306. package/src/plugins/builtin/gpu/pane.tsx +1 -1
  307. package/src/plugins/builtin/gpu/series.ts +2 -3
  308. package/src/plugins/builtin/help/index.tsx +1 -1
  309. package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
  310. package/src/plugins/builtin/holders/index-holders.ts +24 -12
  311. package/src/plugins/builtin/holders/index.tsx +2 -3
  312. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
  313. package/src/plugins/builtin/iv-history/index.tsx +1 -2
  314. package/src/plugins/builtin/jodi/client.ts +4 -1
  315. package/src/plugins/builtin/jodi/index.tsx +1 -2
  316. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
  317. package/src/plugins/builtin/layout-manager/index.tsx +9 -8
  318. package/src/plugins/builtin/macro-day/pane.tsx +4 -2
  319. package/src/plugins/builtin/market-halts/index.tsx +1 -2
  320. package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
  321. package/src/plugins/builtin/market-movers/client.ts +1 -0
  322. package/src/plugins/builtin/market-movers/index.tsx +16 -5
  323. package/src/plugins/builtin/market-movers/screener.ts +23 -6
  324. package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
  325. package/src/plugins/builtin/market-valuation/index.tsx +1 -2
  326. package/src/plugins/builtin/members/index.tsx +1 -2
  327. package/src/plugins/builtin/money-markets/index.tsx +1 -2
  328. package/src/plugins/builtin/money-markets/pane.tsx +2 -1
  329. package/src/plugins/builtin/money-markets/series.ts +3 -5
  330. package/src/plugins/builtin/news/headless.ts +7 -0
  331. package/src/plugins/builtin/news/wire/index.tsx +1 -1
  332. package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
  333. package/src/plugins/builtin/notes/index.tsx +3 -6
  334. package/src/plugins/builtin/openfda/index.tsx +1 -2
  335. package/src/plugins/builtin/options/view.tsx +6 -5
  336. package/src/plugins/builtin/options-calculator/index.tsx +1 -2
  337. package/src/plugins/builtin/options-positioning/index.tsx +1 -2
  338. package/src/plugins/builtin/options-scenario/index.tsx +2 -4
  339. package/src/plugins/builtin/perps/index.tsx +1 -2
  340. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
  341. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
  342. package/src/plugins/builtin/plugin-module.ts +16 -2
  343. package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
  344. package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
  345. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
  346. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
  347. package/src/plugins/builtin/power/gas-client.ts +2 -1
  348. package/src/plugins/builtin/power/index.tsx +1 -2
  349. package/src/plugins/builtin/power/prices-client.ts +3 -2
  350. package/src/plugins/builtin/rate-path/index.tsx +1 -2
  351. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  352. package/src/plugins/builtin/realized-vol/index.tsx +1 -2
  353. package/src/plugins/builtin/realized-vol/model.ts +16 -2
  354. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  355. package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
  356. package/src/plugins/builtin/research/analyst-headless.ts +33 -6
  357. package/src/plugins/builtin/research/analyst-model.ts +37 -2
  358. package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
  359. package/src/plugins/builtin/research/index.tsx +5 -8
  360. package/src/plugins/builtin/research-search/index.tsx +1 -2
  361. package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
  362. package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
  363. package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
  364. package/src/plugins/builtin/risk-factors/data.ts +8 -4
  365. package/src/plugins/builtin/risk-factors/index.tsx +3 -1
  366. package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
  367. package/src/plugins/builtin/roadmap/index.tsx +2 -3
  368. package/src/plugins/builtin/roadmap/pane.tsx +49 -2
  369. package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
  370. package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
  371. package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
  372. package/src/plugins/builtin/scanner/lock-note.ts +26 -3
  373. package/src/plugins/builtin/sec/filing-content.ts +38 -6
  374. package/src/plugins/builtin/sec/headless.ts +5 -1
  375. package/src/plugins/builtin/sec/index.tsx +4 -2
  376. package/src/plugins/builtin/sec/model.ts +2 -0
  377. package/src/plugins/builtin/sectors/index.tsx +11 -5
  378. package/src/plugins/builtin/series-inventory.ts +2 -0
  379. package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
  380. package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
  381. package/src/plugins/builtin/shared/forming-bars.ts +29 -0
  382. package/src/plugins/builtin/shared/fred-points.ts +6 -1
  383. package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
  384. package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
  385. package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
  386. package/src/plugins/builtin/shared/series-warning.ts +13 -4
  387. package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
  388. package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
  389. package/src/plugins/builtin/short-volume/pane.tsx +2 -1
  390. package/src/plugins/builtin/short-volume/series.ts +2 -3
  391. package/src/plugins/builtin/social-mentions/client.ts +15 -10
  392. package/src/plugins/builtin/social-mentions/index.tsx +1 -2
  393. package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
  394. package/src/plugins/builtin/social-mentions/series.ts +2 -3
  395. package/src/plugins/builtin/supply-chain/client.ts +22 -1
  396. package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
  397. package/src/plugins/builtin/supply-chain/index.tsx +1 -2
  398. package/src/plugins/builtin/thirteenf/api.ts +127 -29
  399. package/src/plugins/builtin/thirteenf/data.ts +165 -35
  400. package/src/plugins/builtin/thirteenf/index.tsx +2 -4
  401. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  402. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
  403. package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
  404. package/src/plugins/builtin/thirteenf/types.ts +14 -0
  405. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
  406. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
  407. package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
  408. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
  409. package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
  410. package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
  411. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
  412. package/src/plugins/builtin/time-sales/index.tsx +1 -2
  413. package/src/plugins/builtin/time-sales/pane.tsx +3 -1
  414. package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
  415. package/src/plugins/builtin/trace-bonds/model.ts +44 -10
  416. package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
  417. package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
  418. package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
  419. package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
  420. package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
  421. package/src/plugins/builtin/vol-surface/client.ts +18 -3
  422. package/src/plugins/builtin/vol-surface/index.tsx +1 -2
  423. package/src/plugins/builtin/volatility/client.ts +43 -16
  424. package/src/plugins/builtin/volatility/headless.ts +5 -2
  425. package/src/plugins/builtin/volatility/index.tsx +1 -2
  426. package/src/plugins/builtin/volatility/model.ts +17 -6
  427. package/src/plugins/builtin/volatility/pane.tsx +6 -2
  428. package/src/plugins/builtin/volatility/series.ts +2 -1
  429. package/src/plugins/builtin/world-indices/footer.ts +6 -1
  430. package/src/plugins/builtin/world-venue-map/client.ts +10 -1
  431. package/src/plugins/builtin/world-venue-map/index.ts +6 -2
  432. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
  433. package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
  434. package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
  435. package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
  436. package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
  437. package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
  438. package/src/plugins/builtin/yield-curve/client.ts +4 -6
  439. package/src/plugins/builtin/yield-curve/curves.ts +16 -2
  440. package/src/plugins/builtin/yield-curve/headless.ts +3 -3
  441. package/src/plugins/bundle.ts +52 -2
  442. package/src/plugins/catalog-browser.ts +2 -2
  443. package/src/plugins/catalog-cli.ts +20 -0
  444. package/src/plugins/host-link.ts +13 -1
  445. package/src/plugins/installer.ts +64 -42
  446. package/src/plugins/loader.ts +43 -11
  447. package/src/plugins/peer-imports.ts +201 -0
  448. package/src/plugins/peer-retry.ts +26 -29
  449. package/src/plugins/plugin-process.ts +128 -0
  450. package/src/plugins/redirection-guard.ts +45 -0
  451. package/src/remote/endpoint.ts +16 -3
  452. package/src/renderers/browser/main.tsx +3 -2
  453. package/src/renderers/cloudflare/http-proxy.ts +7 -123
  454. package/src/renderers/dom/desktop/icons.tsx +5 -2
  455. package/src/renderers/dom/host/image-surface.tsx +46 -21
  456. package/src/renderers/dom/styles.css +7 -0
  457. package/src/renderers/dom/toast-host.tsx +2 -0
  458. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  459. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
  460. package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
  461. package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
  462. package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
  463. package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
  464. package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
  465. package/src/renderers/electrobun/bun/window/attention.ts +54 -0
  466. package/src/renderers/electrobun/bun/window/create.ts +6 -2
  467. package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
  468. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
  469. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
  470. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
  471. package/src/renderers/electrobun/view/main.tsx +7 -2
  472. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  473. package/src/renderers/electrobun/view/ui-host.tsx +9 -2
  474. package/src/renderers/opentui/browser-open.ts +10 -4
  475. package/src/renderers/opentui/image/surface.tsx +1 -1
  476. package/src/renderers/opentui/start.tsx +12 -6
  477. package/src/renderers/opentui/toast-host.tsx +2 -1
  478. package/src/series/registry.ts +18 -0
  479. package/src/series/resolve.ts +2 -1
  480. package/src/series/standalone.ts +10 -1
  481. package/src/series/types.ts +12 -0
  482. package/src/series/validate.ts +11 -2
  483. package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
  484. package/src/sources/gloomberb-cloud/index.ts +28 -5
  485. package/src/sources/gloomberb-cloud/news.ts +24 -3
  486. package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
  487. package/src/sources/provider-errors.ts +26 -3
  488. package/src/sources/provider-router/history.ts +5 -0
  489. package/src/sources/provider-router/index.ts +13 -0
  490. package/src/sync/core-contributors.ts +32 -0
  491. package/src/telemetry/attention-counts.ts +64 -6
  492. package/src/telemetry/attention-setting.ts +33 -0
  493. package/src/telemetry/crash-reports-node.ts +3 -1
  494. package/src/telemetry/stall-clocks.ts +93 -0
  495. package/src/telemetry/stall-reports-node.ts +25 -9
  496. package/src/telemetry/stall-reports.ts +218 -13
  497. package/src/test-support/outage.ts +8 -0
  498. package/src/test-support/preload.ts +9 -0
  499. package/src/tickers/company-name-listing.ts +59 -29
  500. package/src/tickers/company-name.ts +84 -1
  501. package/src/tickers/listing-fate.ts +14 -3
  502. package/src/tickers/saved-instrument.ts +17 -0
  503. package/src/tickers/search/home-listing.ts +104 -8
  504. package/src/tickers/search/index.ts +5 -2
  505. package/src/tickers/search/types.ts +6 -1
  506. package/src/time-series/bar-aggregation.ts +7 -1
  507. package/src/time-series/base-series.ts +3 -3
  508. package/src/time-series/chart-bounds.ts +1 -1
  509. package/src/time-series/chart-data.ts +5 -2
  510. package/src/time-series/chart-seed.ts +1 -1
  511. package/src/time-series/coin-session-close.ts +211 -0
  512. package/src/time-series/live-bars.ts +13 -2
  513. package/src/time-series/live-quotes.ts +1 -1
  514. package/src/time-series/market.ts +5 -3
  515. package/src/time-series/official-close.ts +159 -0
  516. package/src/time-series/price-comparison.ts +75 -8
  517. package/src/time-series/resolve.ts +32 -4
  518. package/src/time-series/series-presentation.ts +1 -1
  519. package/src/time-series/studies/formula.ts +205 -0
  520. package/src/time-series/studies/indicators.ts +299 -0
  521. package/src/time-series/studies/leverage.ts +75 -0
  522. package/src/time-series/studies/pair.ts +138 -0
  523. package/src/time-series/studies/resolve.ts +333 -0
  524. package/src/time-series/studies/shared.ts +141 -0
  525. package/src/time-series/studies/units.ts +243 -0
  526. package/src/time-series/studies/vwap.ts +274 -0
  527. package/src/time-series/types.ts +7 -0
  528. package/src/types/config.ts +18 -2
  529. package/src/types/data-provider.ts +23 -0
  530. package/src/types/financials.ts +40 -3
  531. package/src/types/headless.ts +42 -3
  532. package/src/types/plugin.ts +32 -3
  533. package/src/ui/host.tsx +17 -1
  534. package/src/ui/toast.tsx +26 -1
  535. package/src/utils/ascii-text.ts +140 -0
  536. package/src/utils/cli-output.ts +91 -18
  537. package/src/utils/concurrency.ts +24 -0
  538. package/src/utils/exchanges.ts +6 -0
  539. package/src/utils/external-url.ts +20 -7
  540. package/src/utils/financial-statements.ts +73 -16
  541. package/src/utils/fundamentals.ts +17 -5
  542. package/src/utils/futures-generic.ts +4 -4
  543. package/src/utils/http-proxy-upstream.ts +124 -0
  544. package/src/utils/lease-liabilities.ts +54 -0
  545. package/src/utils/main-thread-stall.ts +122 -5
  546. package/src/utils/perf-marks.ts +58 -12
  547. package/src/utils/price-history-integrity.ts +47 -1
  548. package/src/utils/private-files.ts +141 -0
  549. package/src/utils/public-address.ts +99 -0
  550. package/src/account/upgrade-personalization.ts +0 -57
  551. package/src/account/upgrade-tickers.ts +0 -124
  552. package/src/api-client/upgrade-experiment.ts +0 -91
  553. package/src/plugins/builtin/cloud/browser.tsx +0 -3
  554. package/src/time-series/studies.ts +0 -1628
@@ -15,6 +15,10 @@ import type { DataProvider } from "../../../../types/data-provider";
15
15
  import type { PricePoint } from "../../../../types/financials";
16
16
  import { isRoundTheClockCoin } from "../../../../utils/crypto-pair";
17
17
  import { isCurrencyPairListing } from "../../../../utils/fx-market-hours";
18
+ import { latestRegularSessionClose } from "../../../../market-data/market/freshness";
19
+ import { futuresSessionOpenMinute } from "../../../../market-data/market/trading-sessions";
20
+ import { calendarBarStart } from "../../../../time-series/chart-data";
21
+ import { newestClosedSession, newestSessionClose, withOfficialCloses } from "../../../../time-series/official-close";
18
22
  import {
19
23
  effectiveReturnResolution,
20
24
  endsAtLatestBar,
@@ -39,6 +43,8 @@ export interface ReturnSeries {
39
43
  lastSession?: string;
40
44
  /** The venue whose sessions the window follows; empty when none is known. */
41
45
  venue: string;
46
+ /** The local date of the newest session, closed but without its official close yet: its last bar ends on the last trade. */
47
+ closingPrintPending?: string;
42
48
  }
43
49
 
44
50
  export interface ReturnSeriesRequest {
@@ -66,20 +72,76 @@ function historyErrorMessage(error: unknown, resolution: ManualChartResolution):
66
72
  /**
67
73
  * The venue whose sessions set where the window ends: the one the ticker
68
74
  * names, else the listing its quote names, so a bare VTI ends at the NYSE
69
- * Arca close on a weekend. Coins and currency pairs need none.
75
+ * Arca close on a weekend, with the listing's currency when that was read.
76
+ * Coins and currency pairs need none.
70
77
  */
71
- async function sessionVenue(provider: DataProvider, symbol: string, exchange: string): Promise<string> {
72
- if (exchange || isRoundTheClockCoin(symbol) || isCurrencyPairListing(symbol, exchange)) return exchange;
78
+ async function sessionVenue(provider: DataProvider, symbol: string, exchange: string): Promise<{ venue: string; currency?: string }> {
79
+ if (exchange || isRoundTheClockCoin(symbol) || isCurrencyPairListing(symbol, exchange)) return { venue: exchange };
73
80
  try {
74
81
  const metadata = provider.getQuoteMetadata
75
82
  ? await provider.getQuoteMetadata(symbol, exchange)
76
83
  : await provider.getQuote(symbol, exchange);
77
- return metadata?.listingExchangeName ?? "";
84
+ return { venue: metadata?.listingExchangeName ?? "", ...(metadata?.currency ? { currency: metadata.currency } : {}) };
78
85
  } catch (error) {
79
- return logFailure("ticker-detail.data-panes.returns-history", "Read return session venue failed", "")(error);
86
+ return { venue: logFailure("ticker-detail.data-panes.returns-history", "Read return session venue failed", "")(error) };
80
87
  }
81
88
  }
82
89
 
90
+ const DAY_MS = 86_400_000;
91
+
92
+ /**
93
+ * Intraday bars with each closed session's last bar on its official close
94
+ * (withOfficialCloses), so every total and the base they are measured from
95
+ * are the closes `history` and CMP give, not the last trade before the
96
+ * closing auction. Earlier sessions take their daily bar's close, dated by
97
+ * the venue's local date; the newest closed session the close
98
+ * newestSessionClose picks from the quote and its daily bar, or the note
99
+ * that its print is to come. Daily and longer bars, futures (which settle
100
+ * rather than close on a print) and listings without sessions are returned
101
+ * as loaded.
102
+ */
103
+ async function withSessionCloses(
104
+ provider: DataProvider,
105
+ target: { symbol: string; exchange: string; venue: string; currency?: string },
106
+ interval: ManualChartResolution,
107
+ points: PricePoint[],
108
+ now: number,
109
+ context: { cacheMode: "refresh" } | undefined,
110
+ ): Promise<{ points: PricePoint[]; closingPrintPending?: string }> {
111
+ const venue = target.venue || undefined;
112
+ if (!isIntradayResolution(interval) || !points.length || futuresSessionOpenMinute(target.symbol, target.venue || target.exchange) != null
113
+ || !latestRegularSessionClose(venue, now)) return { points };
114
+ const newest = newestClosedSession(points, { venue, now });
115
+ // Whole UTC days, so the same window asks the same read (and its cached copy) all day.
116
+ const first = Math.floor(new Date(points[0]!.date).getTime() / DAY_MS) * DAY_MS - 2 * DAY_MS;
117
+ const end = Math.floor(Math.min(now, new Date(points.at(-1)!.date).getTime() + DAY_MS) / DAY_MS) * DAY_MS + DAY_MS - 1;
118
+ const [daily, quote] = await Promise.all([
119
+ fetchHistoryResult(provider, target.symbol, target.exchange, { kind: "detail", start: new Date(first), end: new Date(end), interval: "1d" }, context)
120
+ .catch(logFailure("ticker-detail.data-panes.returns-history", "Read the daily closes of a return window failed", null)),
121
+ // Only the newest closed session needs the quote's word on its close.
122
+ newest
123
+ ? Promise.resolve(provider.getQuote(target.symbol, target.exchange, context))
124
+ .catch(logFailure("ticker-detail.data-panes.returns-history", "Read the official close of a return window failed", null))
125
+ : null,
126
+ ]);
127
+ const dailyCloses = new Map<string, number>();
128
+ for (const bar of daily?.points ?? []) {
129
+ const time = new Date(bar.date).getTime();
130
+ if (!bar.partial && Number.isFinite(time) && Number.isFinite(bar.close) && bar.close > 0) {
131
+ dailyCloses.set(calendarBarStart(time, "1d", venue), bar.close);
132
+ }
133
+ }
134
+ const closes = new Map([...dailyCloses].filter(([date]) => date !== newest?.date));
135
+ const decided = newest
136
+ ? newestSessionClose(quote, { ...newest, venue, currency: target.currency ?? null, dailyClose: dailyCloses.get(newest.date) ?? null })
137
+ : null;
138
+ if (newest && decided && "close" in decided) closes.set(newest.date, decided.close);
139
+ return {
140
+ points: withOfficialCloses(points, closes, { venue, now }),
141
+ ...(newest && decided && "pending" in decided ? { closingPrintPending: newest.date } : {}),
142
+ };
143
+ }
144
+
83
145
  /**
84
146
  * The bars of a return window and where it starts and ends, as RETURN, its
85
147
  * report and its capture all load them: the window ends now while the
@@ -91,7 +153,7 @@ export async function loadReturnSeries(provider: DataProvider, request: ReturnSe
91
153
  if (request.window) return loadReturnWindow(provider, request, request.window);
92
154
  const { symbol, exchange, range, support, now } = request;
93
155
  const interval = effectiveReturnResolution(range, request.resolution, support);
94
- const venue = await sessionVenue(provider, symbol, exchange);
156
+ const { venue, currency } = await sessionVenue(provider, symbol, exchange);
95
157
  const windowEnd = returnWindowEnd({ symbol, exchange: venue }, now);
96
158
  const visibleFrom = returnVisibleFrom(range, interval, windowEnd.end, venue || undefined);
97
159
  const start = new Date(visibleFrom - returnLookbackMs(range, interval, support));
@@ -100,18 +162,24 @@ export async function loadReturnSeries(provider: DataProvider, request: ReturnSe
100
162
  let result;
101
163
  try {
102
164
  // Exact bounds first, then the interval's range history, as charts do
103
- // when a source keeps no bars for the exact window.
104
- result = await fetchHistoryResult(provider, symbol, exchange, { kind: "detail", start, end, interval }, context);
165
+ // when a source keeps no bars for the exact window or failed to load it
166
+ // (the range history may answer from a saved copy); the failure stands when that has none either.
167
+ let detailFailure: unknown = null;
168
+ result = await fetchHistoryResult(provider, symbol, exchange, { kind: "detail", start, end, interval }, context)
169
+ .catch((error: unknown) => { detailFailure = error; return null; });
105
170
  if (!result?.points.length) {
106
171
  result = await fetchHistoryResult(provider, symbol, exchange, { kind: "resolution", range: returnSupportRange(range), resolution: interval }, context) ?? result;
107
172
  }
173
+ if (!result?.points.length && detailFailure) throw detailFailure;
108
174
  } catch (error) {
109
175
  throw historyErrorMessage(error, interval);
110
176
  }
111
177
  if (!result) throw new Error("Price history unavailable");
112
178
  const latest = latestBarTime(result.points);
179
+ const closed = await withSessionCloses(provider, { symbol, exchange, venue, currency }, interval, result.points, now, context);
113
180
  return {
114
- points: result.points,
181
+ points: closed.points,
182
+ ...(closed.closingPrintPending ? { closingPrintPending: closed.closingPrintPending } : {}),
115
183
  // A 1W to 5Y window's rows start after its base close, found among the bars by their session days.
116
184
  visibleFrom: endsAtLatestBar(range) && latest !== null && latest < windowEnd.end
117
185
  ? returnVisibleFrom(range, interval, latest, venue || undefined)
@@ -142,7 +210,7 @@ async function loadReturnWindow(provider: DataProvider, request: ReturnSeriesReq
142
210
  const step = CHART_RESOLUTION_STEP_MS[request.resolution];
143
211
  let interval = available.includes(request.resolution) ? request.resolution
144
212
  : available.find((candidate) => CHART_RESOLUTION_STEP_MS[candidate] >= step) ?? available.at(-1) ?? "1d";
145
- const venue = await sessionVenue(provider, symbol, exchange);
213
+ const { venue, currency } = await sessionVenue(provider, symbol, exchange);
146
214
  const context = request.forceRefresh ? { cacheMode: "refresh" as const } : undefined;
147
215
  const load = async (resolution: ManualChartResolution) => {
148
216
  // A daily or longer bar is stamped at midnight UTC, so the window takes every bar from its first date's day, week or month on.
@@ -167,8 +235,10 @@ async function loadReturnWindow(provider: DataProvider, request: ReturnSeriesReq
167
235
  }
168
236
  }
169
237
  if (!loaded?.result) throw new Error("Price history unavailable");
238
+ const closed = await withSessionCloses(provider, { symbol, exchange, venue, currency }, interval, loaded.result.points, now, context);
170
239
  return {
171
- points: loaded.result.points,
240
+ points: closed.points,
241
+ ...(closed.closingPrintPending ? { closingPrintPending: closed.closingPrintPending } : {}),
172
242
  visibleFrom: loaded.visibleFrom,
173
243
  visibleTo: Math.min(last, now),
174
244
  ...(loaded.result.coverageStart ? { coverageStart: loaded.result.coverageStart } : {}),
@@ -505,13 +505,46 @@ function coversLatestYear(ttm: FinancialTableStatement, latestYear: FinancialSta
505
505
  return days <= 7;
506
506
  }
507
507
 
508
+ /** Counts of the shares a company has issued or has outstanding, which are never zero; treasury shares can be. */
509
+ const OUTSTANDING_COUNT_FIELDS = ["basicShares", "dilutedShares", "shareIssued", "ordinarySharesNumber"] as const;
510
+
511
+ /**
512
+ * Share counts that cannot be the company's: zero, or under a thousandth of its
513
+ * typical count (IAG's 4,642 for Q3 2025, millions read as shares, which shows
514
+ * as 0.00 bn and a fall of 100%). They are missing, not a value to grow from.
515
+ */
516
+ function withoutImpossibleShareCounts(
517
+ annualStatements: FinancialStatement[],
518
+ quarterlyStatements: FinancialStatement[],
519
+ ): { annualStatements: FinancialStatement[]; quarterlyStatements: FinancialStatement[] } {
520
+ const all = [...annualStatements, ...quarterlyStatements];
521
+ const floors = new Map<string, number>();
522
+ for (const field of OUTSTANDING_COUNT_FIELDS) {
523
+ const counts = all.map((row) => row[field]).filter((value): value is number => typeof value === "number" && value > 0).sort((a, b) => a - b);
524
+ floors.set(field, counts.length ? counts[Math.floor(counts.length / 2)]! / 1000 : 0);
525
+ }
526
+ const impossible = (row: FinancialStatement, field: typeof OUTSTANDING_COUNT_FIELDS[number]) => {
527
+ const value = row[field];
528
+ return typeof value === "number" && (value <= 0 || value < floors.get(field)!);
529
+ };
530
+ const clean = (rows: FinancialStatement[]) => rows.map((row) => {
531
+ const drop = OUTSTANDING_COUNT_FIELDS.filter((field) => impossible(row, field));
532
+ if (!drop.length) return row;
533
+ const next = { ...row };
534
+ for (const field of drop) delete next[field];
535
+ return next;
536
+ });
537
+ return { annualStatements: clean(annualStatements), quarterlyStatements: clean(quarterlyStatements) };
538
+ }
539
+
508
540
  export function selectFinancialStatements(
509
541
  period: FinancialPeriod,
510
542
  statement: string,
511
- annualStatements: FinancialStatement[],
512
- quarterlyStatements: FinancialStatement[],
543
+ servedAnnual: FinancialStatement[],
544
+ servedQuarterly: FinancialStatement[],
513
545
  limit = period === "annual" ? 5 : 6,
514
546
  ) {
547
+ const { annualStatements, quarterlyStatements } = withoutImpossibleShareCounts(servedAnnual, servedQuarterly);
515
548
  // A period with nothing on this statement (a fiscal year-end balance point on
516
549
  // the income statement) would render as an all-dash column.
517
550
  const tabRows = FINANCIAL_SUB_TABS.find((tab) => tab.key === statement)?.rows;
@@ -4,6 +4,7 @@ import { formatNumber, pickUnit } from "../../../../utils/format";
4
4
  import { pricePointIntegrity } from "../../../../utils/price-history-integrity";
5
5
  import { formatPerShareNumber } from "../../../../utils/reported-money";
6
6
  import { hasStatementGap } from "../../../../utils/statement-gaps";
7
+ import { debtAndLeases, leaseBasis, type LeaseBasis } from "../../../../utils/lease-liabilities";
7
8
  import { cashAndShortTermInvestments, excludesCashRunway, formatRunwayMonths, SHORT_RUNWAY_HISTORY, monthlyOperatingBurn, QUARTER_MONTHS, reportsShortTermTotal, runwayMonths, YEAR_MONTHS } from "../../../../utils/cash-runway";
8
9
  import {
9
10
  buildPreviousStatementMap,
@@ -58,6 +59,8 @@ export interface RatioPeriod {
58
59
  price: RatioAmount;
59
60
  /** The filer reports cash and short-term investments as a total in some period on file (utils/cash-runway). */
60
61
  shortTermTotalOnFile?: boolean;
62
+ /** How net debt treats the lease liabilities the listing's balance sheets report; absent when none do. */
63
+ leaseBasis?: LeaseBasis;
61
64
  }
62
65
 
63
66
  interface RatioInputDef {
@@ -75,6 +78,13 @@ interface RatioInputDef {
75
78
  trailing?: boolean;
76
79
  /** A shorter name for quarterly columns, so the label, TTM and its unit fit the label column. */
77
80
  trailingLabel?: string;
81
+ /** Debt net of cash: where the listing reports lease liabilities, a row naming them follows it (withLeaseRow). */
82
+ netDebt?: boolean;
83
+ /**
84
+ * Not an operand: a line the input before it includes or leaves out, shown under it. Neither its value nor
85
+ * its gap enters the ratio.
86
+ */
87
+ memo?: boolean;
78
88
  value: (period: RatioPeriod) => RatioAmount;
79
89
  }
80
90
 
@@ -245,12 +255,41 @@ function marketCap(period: RatioPeriod): RatioAmount {
245
255
  return period.price * shares;
246
256
  }
247
257
 
258
+ /**
259
+ * Total debt less cash and short-term investments, with the balance sheet's lease liabilities where the listing's
260
+ * are included (utils/lease-liabilities). A balance sheet without its lease line then has no net debt.
261
+ */
262
+ function netDebt({ statement, leaseBasis: basis }: RatioPeriod): RatioAmount {
263
+ const cash = cashAndShortTerm(statement);
264
+ if (basis === "included") {
265
+ const lines = debtAndLeases(statement);
266
+ return lines === undefined || cash === undefined ? "not-reported" : lines.totalDebt + lines.leases - cash;
267
+ }
268
+ const debt = finite(statement.totalDebt);
269
+ return debt === undefined || cash === undefined ? "not-reported" : debt - cash;
270
+ }
271
+
248
272
  function enterpriseValue(period: RatioPeriod): RatioAmount {
249
- const debt = finite(period.statement.totalDebt);
250
- const cash = cashAndShortTerm(period.statement);
251
- if (debt === undefined || cash === undefined) return "not-reported";
273
+ const debt = netDebt(period);
274
+ if (isGap(debt)) return debt;
252
275
  const cap = marketCap(period);
253
- return isGap(cap) ? cap : cap + debt - cash;
276
+ return isGap(cap) ? cap : cap + debt;
277
+ }
278
+
279
+ /** The lease liabilities net debt includes, or a US GAAP filer's operating leases it leaves out. */
280
+ const LEASE_ROW_LABEL: Record<LeaseBasis, string> = { included: "incl. leases", excluded: "excl. op. leases" };
281
+
282
+ /** The ratio with a row naming the listing's lease liabilities under its net debt, when it reports any. */
283
+ function withLeaseRow(def: RatioDef, basis: LeaseBasis | undefined): RatioDef {
284
+ const at = def.inputs.findIndex((input) => input.netDebt);
285
+ if (!basis || at < 0) return def;
286
+ const row: RatioInputDef = {
287
+ label: LEASE_ROW_LABEL[basis],
288
+ format: "money",
289
+ memo: true,
290
+ value: ({ statement }) => reported(finite(statement.capitalLeaseObligations)),
291
+ };
292
+ return { ...def, inputs: [...def.inputs.slice(0, at + 1), row, ...def.inputs.slice(at + 1)] };
254
293
  }
255
294
 
256
295
  const DSO: RatioDef = {
@@ -378,15 +417,7 @@ export const RATIO_TABS: RatioTabDef[] = [
378
417
  label: "Net Debt / EBITDA",
379
418
  format: "multiple",
380
419
  inputs: [
381
- {
382
- label: "Net Debt",
383
- format: "money",
384
- value: ({ statement }) => {
385
- const debt = finite(statement.totalDebt);
386
- const cash = cashAndShortTerm(statement);
387
- return debt === undefined || cash === undefined ? "not-reported" : debt - cash;
388
- },
389
- },
420
+ { label: "Net Debt", format: "money", netDebt: true, value: netDebt },
390
421
  trailingFlow("EBITDA", line("ebitda"), "÷"),
391
422
  ],
392
423
  compute: ([debt, ebitda]) => over(debt!, ebitda!),
@@ -517,7 +548,7 @@ export const RATIO_TABS: RatioTabDef[] = [
517
548
  label: "EV / EBITDA",
518
549
  format: "multiple",
519
550
  inputs: [
520
- { label: "Enterprise Value", format: "money", value: enterpriseValue },
551
+ { label: "Enterprise Value", format: "money", netDebt: true, value: enterpriseValue },
521
552
  trailingFlow("EBITDA", line("ebitda"), "÷"),
522
553
  ],
523
554
  // A negative enterprise value makes the multiple meaningless too.
@@ -568,10 +599,11 @@ function inputAmount(input: RatioInputDef, period: RatioPeriod): RatioAmount {
568
599
 
569
600
  export function evaluateRatio(def: RatioDef, period: RatioPeriod): RatioCell {
570
601
  const inputs = def.inputs.map((input) => inputAmount(input, period));
571
- const gap = GAP_ORDER.find((candidate) => inputs.includes(candidate));
602
+ const operands = inputs.filter((_, index) => !def.inputs[index]!.memo);
603
+ const gap = GAP_ORDER.find((candidate) => operands.includes(candidate));
572
604
  if (gap) return { value: gap, inputs };
573
- const values = inputs.map((amount, index) => (
574
- def.inputs[index]!.annualized && period.quarterly ? (amount as number) * QUARTERS_PER_YEAR : amount as number
605
+ const values = def.inputs.flatMap((input, index) => (
606
+ input.memo ? [] : [input.annualized && period.quarterly ? (inputs[index] as number) * QUARTERS_PER_YEAR : inputs[index] as number]
575
607
  ));
576
608
  const value = def.compute(values, period);
577
609
  return { value: isGap(value) || Number.isFinite(value) ? value : "not-meaningful", inputs };
@@ -702,6 +734,7 @@ export function ratioPeriods(
702
734
  onFile: { annual: readonly FinancialStatement[]; quarterly: readonly FinancialStatement[] } = { annual: [], quarterly: [] },
703
735
  ): RatioPeriod[] {
704
736
  const shortTermTotalOnFile = reportsShortTermTotal([...onFile.annual, ...onFile.quarterly, ...statements]);
737
+ const leases = leaseBasis([...onFile.annual, ...onFile.quarterly, ...statements]);
705
738
  return statements.map((statement) => {
706
739
  const quarterly = period === "quarterly" && statement.date !== "TTM";
707
740
  return {
@@ -711,6 +744,7 @@ export function ratioPeriods(
711
744
  ...(quarterly ? { trailing: trailingYear(statement, onFile.annual, onFile.quarterly) } : {}),
712
745
  price: price(statement),
713
746
  shortTermTotalOnFile,
747
+ ...(leases ? { leaseBasis: leases } : {}),
714
748
  };
715
749
  });
716
750
  }
@@ -758,11 +792,12 @@ export function buildRatioTableModel(
758
792
  ): RatioTableModel {
759
793
  const quarterly = periods.some((period) => period.quarterly);
760
794
  const totalOnFile = periods.some((period) => period.shortTermTotalOnFile);
795
+ const leases = periods.find((period) => period.leaseBasis)?.leaseBasis;
761
796
  const ratios = visibleRatios(tab, quarterly)
762
797
  .filter((def) => def.format !== "months" || !excludesCashRunway(profile))
763
798
  .map((def) => def.format === "months" && !totalOnFile
764
799
  ? { ...def, inputs: def.inputs.map((input, index) => index === 0 ? { ...input, label: "Cash" } : input) }
765
- : def);
800
+ : withLeaseRow(def, leases));
766
801
  const cells = new Map(ratios.map((def) => [def.id, periods.map((period) => evaluateRatio(def, period))]));
767
802
  const inputs = ratios.flatMap((def) => def.inputs.map((input, index) => {
768
803
  const values = cells.get(def.id)!.map((cell) => cell.inputs[index]).filter((value) => typeof value === "number");
@@ -1,4 +1,7 @@
1
- import { FINANCIAL_VINTAGE_NOTICE, filledPeriodsNote, stalePeriodNote, statementHistoryNotes } from "../../../utils/financial-statements";
1
+ import {
2
+ FINANCIAL_VINTAGE_NOTICE, filledPeriodsNote, hasOlderStatements, reportsHalfYearly, stalePeriodNote, statementHistoryNotes,
3
+ } from "../../../utils/financial-statements";
4
+ import { canonicalExchange } from "../../../utils/exchanges";
2
5
  import { hasValidQuoteObservationTime } from "../../../market-data/quotes/freshness";
3
6
  import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, quoteHeadlineTime, type ExtendedSession } from "../../../market-data/market/status";
4
7
  import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneFreshness, HeadlessPaneOptionValues } from "../../../types/headless";
@@ -6,7 +9,8 @@ import type { TimeRange } from "../../../time-series/range";
6
9
  import { formatNumber, formatPercentRaw } from "../../../utils/format";
7
10
  import { formatMarketPrice, formatMarketPriceWithCurrency, formatPriceObservation, penceFigure, quoteFormatOptions, withCurrencyMinorDigits, type MarketFormatOptions } from "../../../market-data/market/format";
8
11
  import {
9
- MISSING_VOLUME_TEXT, missingVolumeCount, missingVolumeNotice, pricePointValues, priceHistoryIntegrityNotice,
12
+ MISSING_VOLUME_TEXT, missingVolumeCount, missingVolumeNotice, pricePointValues, pricePointWickFlags, priceHistoryIntegrityNotice,
13
+ priceHistoryWickNotice,
10
14
  } from "../../../utils/price-history-integrity";
11
15
  import {
12
16
  buildFinancialTableModel, financialStatementCurrency, financialStatementDateNotice, financialStatementLimitations,
@@ -48,6 +52,7 @@ import { DAY_SO_FAR_TEXT, withSessionBar } from "../../../time-series/session-ba
48
52
  import { buildReturnRows, closedMarketNote, isTodayReturnRow, returnBaseCloseDay, type ReturnRange } from "./data-panes/returns-model";
49
53
  import { periodBaseText } from "../../../market-data/market/period-return";
50
54
  import { loadReturnSeries } from "./data-panes/returns-history";
55
+ import { CLOSING_PRINT_PENDING_TEXT } from "../../../time-series/official-close";
51
56
  import {
52
57
  closedHistoryBarDay, formingHistoryBar, HISTORY_DEFAULT_RANGE, historyBarDate, historyBarSize, historyBarSizeWord, historyDateHeader, isDailyOrLongerBar,
53
58
  type HistoryBarSize,
@@ -151,18 +156,23 @@ export const financialStatementsHeadless: HeadlessPaneDefinition<"rows"> = {
151
156
  const stale = table ? stalePeriodNote(financialStatementPeriods(table.statements, table.subTab)) : null;
152
157
  // Years filled from the listing's exchange, with the lines this table shows.
153
158
  const filled = table ? filledPeriodsNote(table.statements, financialSubTabFields(table.subTab)) : null;
159
+ const periodDates = dates.map(({ date }) => date);
160
+ const missing = rows.length ? null : await missingPeriodsError(ctx, symbol, financials, requestedPeriod, hasRequestedStatements, options.statement);
154
161
  return {
155
162
  rows, columns, unavailableSymbols: rows.length ? [] : [symbol],
156
- ...(!hasRequestedStatements ? { errors: [`${symbol}: No ${requestedPeriod} financial statement coverage.`] } : {}),
163
+ ...(missing ? { errors: [missing] } : {}),
157
164
  // The report closes with how old its latest period is, how far back its periods go, and how to read further.
158
- notes: [...stale ? [stale] : [], ...filled ? [filled] : [], ...offering ? [offering] : [], ...statementHistoryNotes(requestedPeriod, dates.map(({ date }) => date), symbol)],
165
+ notes: [...stale ? [stale] : [], ...filled ? [filled] : [], ...offering ? [offering] : [],
166
+ ...statementHistoryNotes(requestedPeriod, periodDates, symbol, await reportOlderStatements(ctx, symbol, requestedPeriod, periodDates))],
159
167
  metadata: {
160
168
  symbol, name: financials.quote?.name ?? symbol, currency: statementCurrency ?? null, unit: table?.unit ?? null, quoteCurrency: financials.quote?.currency ?? null,
161
169
  statement: table?.subTab.key ?? options.statement, statementLabel: table?.subTab.name ?? null,
162
170
  period: requestedPeriod, growthBasis: requestedPeriod === "quarterly" ? "QoQ" : "YoY", columns: dates,
163
171
  shareBasis: shareBases.size === 1 ? [...shareBases][0] ?? null : null,
164
172
  ...(receiptPeriods.length > 0 ? { adrRatio: financials.fundamentals?.adrRatio ?? financials.quote?.adrRatio ?? null } : {}),
165
- notices: rows.length ? [FINANCIAL_VINTAGE_NOTICE, ...(shareBasisNotice ? [shareBasisNotice] : [])] : [],
173
+ // A quarter's change is against the quarter before it, which a reader of a table of quarters must be told.
174
+ notices: rows.length ? [FINANCIAL_VINTAGE_NOTICE, ...requestedPeriod === "quarterly" ? [QUARTERLY_GROWTH_NOTICE] : [],
175
+ ...(shareBasisNotice ? [shareBasisNotice] : [])] : [],
166
176
  limitations: financialStatementLimitations(financials),
167
177
  dateProvenance: financialStatementDateNotice(table?.statements ?? []),
168
178
  },
@@ -172,6 +182,44 @@ export const financialStatementsHeadless: HeadlessPaneDefinition<"rows"> = {
172
182
 
173
183
  const reportAmount = (amount: RatioAmount) => (typeof amount === "number" ? amount : null);
174
184
 
185
+ /** What a table of quarters says its growth is measured against. */
186
+ const QUARTERLY_GROWTH_NOTICE = "Growth is vs prior quarter.";
187
+
188
+ type HeadlessContext = Parameters<typeof financialStatementsHeadless.load>[1];
189
+
190
+ /** Whether GF, reading the listing's extended statement history, goes back further than the report (statementHistoryNotes). */
191
+ function reportOlderStatements(ctx: HeadlessContext, symbol: string, period: "annual" | "quarterly", dates: readonly string[]) {
192
+ return hasOlderStatements(async () => {
193
+ const { symbol: bare, exchange } = await resolveHeadlessInstrument(ctx, symbol);
194
+ return ctx.marketData.getTickerFinancials(bare, exchange ?? "", { statementHistory: "extended" });
195
+ }, period, dates);
196
+ }
197
+
198
+ /**
199
+ * Why a statements report has no rows, and what to read instead: a listing
200
+ * with years but no quarters names its years, and says so of a company that
201
+ * reports half-yearly (Qantas on ASX). Null when it has years and quarters,
202
+ * whatever this table lacks.
203
+ */
204
+ async function missingPeriodsError(
205
+ ctx: HeadlessContext,
206
+ symbol: string,
207
+ financials: Awaited<ReturnType<typeof loadHeadlessFinancials>>,
208
+ period: "annual" | "quarterly",
209
+ hasRequestedStatements: boolean,
210
+ statement: unknown,
211
+ ): Promise<string | null> {
212
+ const hasYears = financials.annualStatements.length > 0;
213
+ if (period === "annual" || !hasYears) return hasRequestedStatements ? null : `${symbol}: No ${period} financial statement coverage.`;
214
+ const exchange = await resolveHeadlessInstrument(ctx, symbol).then(({ exchange }) => canonicalExchange(exchange ?? ""))
215
+ .catch(logFailure("ticker-detail.headless", "Resolving the listing of a statements report failed", ""));
216
+ const halfYearly = reportsHalfYearly(exchange, financials.quarterlyStatements);
217
+ if (hasRequestedStatements && !halfYearly) return null;
218
+ const tab = typeof statement === "string" && statement && statement !== "income" ? ` --statement ${statement}` : "";
219
+ const years = `For its years: gloom fn FA ${symbol} --period annual${tab}`;
220
+ return halfYearly ? `${symbol} reports half-yearly, not quarterly. ${years}` : `${symbol}: No quarterly financial statement coverage. ${years}`;
221
+ }
222
+
175
223
  /** The note that an offering filed after the latest balance sheet is not in its cash yet; a failed read leaves it out. */
176
224
  async function reportOfferingNote(
177
225
  ctx: Parameters<typeof financialStatementsHeadless.load>[1],
@@ -240,16 +288,18 @@ async function loadRatioReport(
240
288
  const runwayShown = liquidity && table.ratios.some((def) => def.format === "months"
241
289
  && table.cells.get(def.id)!.some((cell) => typeof cell.value === "number"));
242
290
  const offering = liquidity ? await reportOfferingNote(ctx, symbol, financials) : null;
291
+ const periodDates = dates.map(({ date }) => date);
292
+ const missing = rows.length ? null : await missingPeriodsError(ctx, symbol, financials, period, hasRequestedStatements, tab.key);
243
293
  return {
244
294
  rows, columns, unavailableSymbols: rows.length ? [] : [symbol],
245
- ...(!hasRequestedStatements ? { errors: [`${symbol}: No ${period} financial statement coverage.`] } : {}),
295
+ ...(missing ? { errors: [missing] } : {}),
246
296
  ...(tab.key === "valuation" && hasRequestedStatements && !history?.length ? { complete: false } : {}),
247
297
  notes: [
248
298
  ...[stalePeriodNote(dates.map(({ periodEnd }) => ({ date: periodEnd, lead: true })))].filter((note): note is string => !!note),
249
299
  ...[filledPeriodsNote(table?.periods.map(({ statement }) => statement) ?? [])].filter((note): note is string => !!note),
250
300
  ...offering ? [offering] : [],
251
301
  ...runwayShown ? [runwayMethod(reportsShortTermTotal([...financials.quarterlyStatements, ...financials.annualStatements]))] : [],
252
- ...statementHistoryNotes(period, dates.map(({ date }) => date), symbol),
302
+ ...statementHistoryNotes(period, periodDates, symbol, await reportOlderStatements(ctx, symbol, period, periodDates)),
253
303
  ],
254
304
  metadata: {
255
305
  symbol, name: financials.quote?.name ?? symbol, currency: statementCurrency ?? null,
@@ -453,7 +503,9 @@ export const historicalPricesHeadless: HeadlessPaneDefinition<"rows"> = {
453
503
  const drawdown = maxDrawdown(shown);
454
504
  const drawdownLine = drawdown ? maxDrawdownText(drawdown, { basis: `${historyBarSizeWord(size)} closes`, price: (value) => formatBarPrice(value, decimals),
455
505
  date: (value) => isDailyOrLongerBar(size) ? value.slice(0, 10) : formatUtcTime(value) }) : null;
456
- const reportNotices = [drawdownLine, notice].filter((line): line is string => line != null);
506
+ // A wick left out of the range is said, though the bar's close still counts.
507
+ const wickNotice = priceHistoryWickNotice(points.filter((point) => pricePointWickFlags(point).length > 0).length);
508
+ const reportNotices = [drawdownLine, notice, wickNotice].filter((line): line is string => line != null);
457
509
  return {
458
510
  rows: shown,
459
511
  columns: historyColumns(size, marked, false, false, decimals),
@@ -545,7 +597,10 @@ export const returnsHeadless: HeadlessPaneDefinition<"rows"> = {
545
597
  ...figures,
546
598
  ...forming || dayToDate ? [{ key: "partial", header: "Partial", format: (value: unknown) => value === true ? "true" : "" }] : [],
547
599
  ];
548
- const textColumns: HeadlessPaneColumn[] = [timeColumn(true), ...figures];
600
+ // A narrow table drops the dollar moves, the interval's first, rather than putting Total % on a line under every row.
601
+ const textFigures = figures.map((column) => column.key === "intervalChange" ? { ...column, optional: true, dropPriority: 2 }
602
+ : column.key === "totalChange" ? { ...column, optional: true, dropPriority: 1 } : column);
603
+ const textColumns: HeadlessPaneColumn[] = [timeColumn(true), ...textFigures];
549
604
  // An interval the range is too long for is served in the nearest longer bar; say so rather than switch silently.
550
605
  const notices = [
551
606
  maxDrawdownLine,
@@ -568,8 +623,12 @@ export const returnsHeadless: HeadlessPaneDefinition<"rows"> = {
568
623
  oldest: null,
569
624
  ...(forming ? { periodToDate: forming } : closedDay ? { asOf: closedDay } : {}),
570
625
  ...(series.venue ? { cadenceExchange: series.venue } : {}),
571
- // The window ends at the last session while the market is shut, which the status line says.
572
- ...(series.lastSession ? { note: closedMarketNote(series.lastSession, series.visibleTo) } : {}),
626
+ // The window ends at the last session while the market is shut, which the status line says, and whether its
627
+ // last bar ends on the official close yet.
628
+ ...(series.lastSession || series.closingPrintPending ? { note: [
629
+ series.lastSession ? closedMarketNote(series.lastSession, series.visibleTo) : null,
630
+ series.closingPrintPending ? CLOSING_PRINT_PENDING_TEXT : null,
631
+ ].filter(Boolean).join(", ") } : {}),
573
632
  ...(dayToDate ? { basis: "daily bars", note: `the newest bar is ${DAY_SO_FAR_TEXT}` } : {}),
574
633
  },
575
634
  metadata: {
@@ -4,14 +4,9 @@ import { holdsActiveTicker } from "../../../shares/pane-image";
4
4
  import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
5
5
  import { normalizeTickerInput } from "../../../tickers/search";
6
6
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
7
- import { FinancialAnalysisPane } from "./financials/pane";
8
- import { HistoricalPricesPane } from "./data-panes/historical-prices";
9
- import { ReturnsPane } from "./data-panes/returns-pane";
10
- import { TickerResearchPane } from "./pane";
11
7
  import { futuresContractCache } from "./overview/futures-contract";
12
8
  import { offeringFilingsCache } from "./financials/offerings";
13
9
  import { TICKER_RESEARCH_BUILTIN_TABS } from "./research-tabs";
14
- import { QuoteMonitorPane } from "./quote-monitor";
15
10
  import { financialStatementsHeadless, historicalPricesHeadless, quoteComparisonHeadless, returnsHeadless } from "./headless";
16
11
  import {
17
12
  buildQuoteMonitorSettingsDef,
@@ -47,7 +42,7 @@ export const tickerDetailModule: PluginModule = {
47
42
  id: TICKER_RESEARCH_PANE_ID,
48
43
  name: "Research",
49
44
  icon: "D",
50
- component: TickerResearchPane,
45
+ get component(): typeof import("./pane").TickerResearchPane { const { TickerResearchPane } = require("./pane"); return TickerResearchPane; },
51
46
  defaultPosition: "right",
52
47
  tickerFollower: true,
53
48
  // DES and ADR open this pane; ADR's window is named by its params (the tab it asked for), so settings never say whose a window is.
@@ -70,7 +65,7 @@ export const tickerDetailModule: PluginModule = {
70
65
  id: "financial-analysis",
71
66
  name: "Financials",
72
67
  icon: "F",
73
- component: FinancialAnalysisPane,
68
+ get component(): typeof import("./financials/pane").FinancialAnalysisPane { const { FinancialAnalysisPane } = require("./financials/pane"); return FinancialAnalysisPane; },
74
69
  headless: financialStatementsHeadless,
75
70
  defaultPosition: "right",
76
71
  tickerFollower: true,
@@ -82,7 +77,7 @@ export const tickerDetailModule: PluginModule = {
82
77
  id: "quote-monitor",
83
78
  name: "Quote Monitor",
84
79
  icon: "Q",
85
- component: QuoteMonitorPane,
80
+ get component(): typeof import("./quote-monitor").QuoteMonitorPane { const { QuoteMonitorPane } = require("./quote-monitor"); return QuoteMonitorPane; },
86
81
  headless: quoteComparisonHeadless,
87
82
  defaultPosition: "right",
88
83
  defaultMode: "floating",
@@ -97,7 +92,7 @@ export const tickerDetailModule: PluginModule = {
97
92
  id: "historical-prices",
98
93
  name: "Historical Prices",
99
94
  icon: "H",
100
- component: HistoricalPricesPane,
95
+ get component(): typeof import("./data-panes/historical-prices").HistoricalPricesPane { const { HistoricalPricesPane } = require("./data-panes/historical-prices"); return HistoricalPricesPane; },
101
96
  headless: historicalPricesHeadless,
102
97
  defaultPosition: "right",
103
98
  tickerFollower: true,
@@ -110,7 +105,7 @@ export const tickerDetailModule: PluginModule = {
110
105
  reportFreshness: { status: "not-a-feed", basis: "price history" },
111
106
  name: "Returns",
112
107
  icon: "R",
113
- component: ReturnsPane,
108
+ get component(): typeof import("./data-panes/returns-pane").ReturnsPane { const { ReturnsPane } = require("./data-panes/returns-pane"); return ReturnsPane; },
114
109
  headless: returnsHeadless,
115
110
  defaultPosition: "right",
116
111
  tickerFollower: true,