@gloom-sh/terminal 0.18.5 → 0.19.1

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 (570) 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 +163 -8
  5. package/src/api-client/app-attention.ts +3 -0
  6. package/src/api-client/beneficial-owners.ts +3 -0
  7. package/src/api-client/company-kpis.ts +3 -0
  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 +19 -7
  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/hiring.ts +3 -0
  14. package/src/api-client/index.ts +15 -0
  15. package/src/api-client/ipo.ts +24 -1
  16. package/src/api-client/market-discovery.ts +10 -0
  17. package/src/api-client/market-movers.ts +7 -0
  18. package/src/api-client/members.ts +1 -1
  19. package/src/api-client/mna.ts +3 -0
  20. package/src/api-client/paths.ts +9 -1
  21. package/src/api-client/perps.ts +43 -0
  22. package/src/api-client/research-activity.ts +3 -2
  23. package/src/api-client/roadmap.ts +85 -0
  24. package/src/api-client/types.ts +82 -2
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +0 -2
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +2 -2
  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/market.ts +107 -38
  35. package/src/cli/commands/news-search.ts +22 -0
  36. package/src/cli/commands/options-chain.ts +48 -7
  37. package/src/cli/commands/options-leaps.ts +9 -3
  38. package/src/cli/commands/overview.ts +147 -32
  39. package/src/cli/commands/search.ts +21 -1
  40. package/src/cli/commands/themes.ts +11 -2
  41. package/src/cli/commands/ticker.ts +87 -36
  42. package/src/cli/company-name-hint.ts +41 -0
  43. package/src/cli/earnings-calendar.ts +1 -1
  44. package/src/cli/errors.ts +25 -2
  45. package/src/cli/glossary.ts +31 -2
  46. package/src/cli/history-rows.ts +57 -9
  47. package/src/cli/index-roots.ts +0 -7
  48. package/src/cli/index.ts +2 -0
  49. package/src/cli/listing-arg.ts +235 -60
  50. package/src/cli/output-file.ts +68 -0
  51. package/src/cli/pane-functions/capabilities.ts +4 -0
  52. package/src/cli/pane-functions/catalog.ts +80 -7
  53. package/src/cli/pane-functions/discovery.ts +21 -1
  54. package/src/cli/pane-functions/dom.ts +13 -3
  55. package/src/cli/pane-functions/freshness.ts +93 -4
  56. package/src/cli/pane-functions/function-help.ts +20 -10
  57. package/src/cli/pane-functions/headless.ts +17 -7
  58. package/src/cli/pane-functions/index.ts +54 -16
  59. package/src/cli/pane-functions/market-session.ts +66 -4
  60. package/src/cli/pane-functions/options.ts +19 -1
  61. package/src/cli/pane-functions/resolver.ts +19 -8
  62. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  63. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  64. package/src/cli/quote-answer.ts +22 -11
  65. package/src/cli/saved-copy-persistence.ts +88 -0
  66. package/src/cli/ticker-resolution.ts +3 -2
  67. package/src/components/chart/composite/composite-chart.tsx +4 -0
  68. package/src/components/chart/composite/interactions.ts +6 -0
  69. package/src/components/chart/composite/legend.tsx +45 -7
  70. package/src/components/chart/composite/panel-gestures.ts +33 -0
  71. package/src/components/chart/composite/panel-surface.tsx +52 -3
  72. package/src/components/chart/composite/rasterizer.ts +24 -13
  73. package/src/components/chart/composite/scene.ts +39 -4
  74. package/src/components/chart/composite/series-cues.ts +14 -0
  75. package/src/components/chart/composite/text-renderer.ts +9 -2
  76. package/src/components/chart/composite/tool-catalog.ts +36 -0
  77. package/src/components/chart/composite/tools.ts +203 -44
  78. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  79. package/src/components/command-bar/assist/inventory.ts +2 -1
  80. package/src/components/command-bar/commands/direct/index.ts +28 -13
  81. package/src/components/command-bar/list/model.ts +5 -0
  82. package/src/components/command-bar/pane-templates/items.ts +3 -2
  83. package/src/components/command-bar/routes/root/runtime.ts +6 -7
  84. package/src/components/command-bar/selection-runtime.ts +52 -3
  85. package/src/components/command-bar/surface/index.tsx +2 -0
  86. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  87. package/src/components/layout/pane/capture-stamp.ts +2 -2
  88. package/src/components/layout/pane/footer/index.tsx +22 -6
  89. package/src/components/layout/pane/footer/model.ts +83 -15
  90. package/src/components/layout/pane/status-footer.ts +35 -2
  91. package/src/components/layout/status-widget-space.ts +9 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  93. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  94. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  95. package/src/components/onboarding/wizard-positions.ts +3 -3
  96. package/src/components/ticker/list-table-view.tsx +8 -21
  97. package/src/components/ui/choice-dialog.tsx +23 -18
  98. package/src/components/ui/choice-groups.ts +18 -0
  99. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  100. package/src/components/ui/data-table/opentui/model.ts +26 -1
  101. package/src/components/ui/data-table/types.ts +2 -0
  102. package/src/components/ui/display.tsx +18 -3
  103. package/src/components/ui/icon.tsx +4 -1
  104. package/src/components/ui/list-view.tsx +8 -4
  105. package/src/components/ui/query-bar-keys.tsx +2 -0
  106. package/src/components/ui/query-bar.tsx +1 -0
  107. package/src/components/ui/select-button.tsx +5 -1
  108. package/src/components/ui/select-field.tsx +1 -0
  109. package/src/components/ui/table-layout.ts +8 -5
  110. package/src/core/app-runtime.ts +2 -0
  111. package/src/core/app-services.ts +2 -2
  112. package/src/data/config/chart-settings.ts +1 -1
  113. package/src/data/json-ticker-repository.ts +17 -8
  114. package/src/data/plugin-cache.ts +5 -3
  115. package/src/data/saved-copies.ts +8 -0
  116. package/src/data/ticker-repository.ts +16 -9
  117. package/src/data/ticker-writes.ts +81 -0
  118. package/src/data/ttl-cache.ts +165 -0
  119. package/src/docs/snapshot.json +103 -142
  120. package/src/i18n/es.ts +20 -6
  121. package/src/i18n/ja.ts +20 -6
  122. package/src/i18n/ko.ts +20 -6
  123. package/src/i18n/zh-cn.ts +20 -6
  124. package/src/i18n/zh-tw.ts +20 -6
  125. package/src/market-data/depositary-pair.ts +27 -3
  126. package/src/market-data/fund-facts.ts +50 -0
  127. package/src/market-data/market/change-basis.ts +16 -3
  128. package/src/market-data/market/format.ts +25 -0
  129. package/src/market-data/market/freshness.ts +2 -1
  130. package/src/market-data/options-alternatives.ts +81 -4
  131. package/src/market-data/quotes/metadata.ts +7 -0
  132. package/src/news/aggregator.ts +119 -9
  133. package/src/news/cloud-session.ts +21 -0
  134. package/src/news/hooks.ts +1 -0
  135. package/src/news/news-model.ts +1 -0
  136. package/src/news/types.ts +17 -0
  137. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  138. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  139. package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
  140. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  141. package/src/plugins/builtin/alerts/cli.ts +362 -80
  142. package/src/plugins/builtin/alerts/command.ts +25 -1
  143. package/src/plugins/builtin/alerts/event-rows.ts +24 -3
  144. package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
  145. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  146. package/src/plugins/builtin/alerts/format.ts +75 -5
  147. package/src/plugins/builtin/alerts/headless.ts +34 -12
  148. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  149. package/src/plugins/builtin/alerts/history.ts +1 -1
  150. package/src/plugins/builtin/alerts/index.tsx +79 -16
  151. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  152. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  153. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  154. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  155. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  156. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  157. package/src/plugins/builtin/alerts/storage.ts +12 -8
  158. package/src/plugins/builtin/alerts/types.ts +4 -0
  159. package/src/plugins/builtin/analytics/index.tsx +2 -1
  160. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  161. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  162. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  163. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  164. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  165. package/src/plugins/builtin/attention/index.tsx +2 -1
  166. package/src/plugins/builtin/attention/listing.ts +14 -0
  167. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  168. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  169. package/src/plugins/builtin/buildout/format.ts +38 -0
  170. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  171. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  172. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  173. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  174. package/src/plugins/builtin/cds/client.ts +142 -30
  175. package/src/plugins/builtin/cds/headless.ts +35 -8
  176. package/src/plugins/builtin/cds/model.ts +145 -18
  177. package/src/plugins/builtin/cds/pane.tsx +16 -5
  178. package/src/plugins/builtin/changelog/index.tsx +9 -6
  179. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  180. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  181. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  182. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  183. package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
  184. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  185. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  186. package/src/plugins/builtin/chart-composer/index.tsx +5 -6
  187. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  188. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  189. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  190. package/src/plugins/builtin/chat/controller/index.ts +5 -1
  191. package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
  192. package/src/plugins/builtin/chat/controller/state.ts +25 -2
  193. package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
  194. package/src/plugins/builtin/chat/controller/utils.ts +7 -2
  195. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  196. package/src/plugins/builtin/chat/status-preview.ts +79 -0
  197. package/src/plugins/builtin/chat/status-widget.tsx +147 -10
  198. package/src/plugins/builtin/chat/unread-badge.ts +51 -10
  199. package/src/plugins/builtin/clinical-trials/client.ts +49 -4
  200. package/src/plugins/builtin/clinical-trials/model.ts +86 -31
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
  202. package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
  203. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
  204. package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
  205. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  206. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  207. package/src/plugins/builtin/company-attention/client.ts +8 -9
  208. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  209. package/src/plugins/builtin/company-attention/headless.ts +2 -1
  210. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  211. package/src/plugins/builtin/company-attention/pane.tsx +12 -2
  212. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  213. package/src/plugins/builtin/company-kpis/headless.ts +8 -3
  214. package/src/plugins/builtin/company-kpis/model.ts +73 -5
  215. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  216. package/src/plugins/builtin/composite-plugins.ts +2 -1
  217. package/src/plugins/builtin/correlation/headless.ts +13 -5
  218. package/src/plugins/builtin/correlation/history.ts +22 -0
  219. package/src/plugins/builtin/cot/client.ts +22 -18
  220. package/src/plugins/builtin/cot/headless.ts +15 -10
  221. package/src/plugins/builtin/cot/model.ts +14 -1
  222. package/src/plugins/builtin/cot/pane.tsx +22 -14
  223. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  224. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  225. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  226. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  227. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  228. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  229. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  230. package/src/plugins/builtin/credit-documents/headless.ts +10 -10
  231. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  232. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  233. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  234. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  235. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  236. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  237. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  238. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  239. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  240. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  241. package/src/plugins/builtin/docs/targets.ts +5 -1
  242. package/src/plugins/builtin/doe/headless.ts +3 -2
  243. package/src/plugins/builtin/doe/pane.tsx +13 -3
  244. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  245. package/src/plugins/builtin/earnings/board.tsx +62 -71
  246. package/src/plugins/builtin/earnings/headless.ts +40 -4
  247. package/src/plugins/builtin/earnings/scope.ts +31 -43
  248. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  249. package/src/plugins/builtin/earnings-calls/data.ts +18 -6
  250. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  251. package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
  252. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  253. package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
  254. package/src/plugins/builtin/econ/calendar-model.ts +48 -1
  255. package/src/plugins/builtin/econ/calendar-source.ts +4 -2
  256. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  257. package/src/plugins/builtin/econ/figures.ts +48 -0
  258. package/src/plugins/builtin/econ/index.tsx +14 -5
  259. package/src/plugins/builtin/econ/print-watch.ts +3 -1
  260. package/src/plugins/builtin/econ/types.ts +2 -0
  261. package/src/plugins/builtin/econ-statistics/client.ts +11 -0
  262. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  263. package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
  264. package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
  265. package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
  266. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  267. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  268. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  269. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  270. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  271. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  272. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  273. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  274. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  275. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  276. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  277. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  278. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  279. package/src/plugins/builtin/gpu/headless.ts +8 -2
  280. package/src/plugins/builtin/help/function-index.ts +4 -1
  281. package/src/plugins/builtin/help/index.tsx +62 -59
  282. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  283. package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
  284. package/src/plugins/builtin/help/table-cells.tsx +13 -2
  285. package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
  286. package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
  287. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  288. package/src/plugins/builtin/holders/headless.ts +22 -3
  289. package/src/plugins/builtin/holders/pane.tsx +3 -0
  290. package/src/plugins/builtin/holders/report-rows.ts +4 -1
  291. package/src/plugins/builtin/insider/headless.ts +2 -2
  292. package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
  293. package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
  294. package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
  295. package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
  296. package/src/plugins/builtin/iv-history/client.ts +52 -11
  297. package/src/plugins/builtin/iv-history/format.ts +36 -23
  298. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  299. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  300. package/src/plugins/builtin/iv-history/model.ts +163 -27
  301. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  302. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  303. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  304. package/src/plugins/builtin/jobs/client.ts +25 -29
  305. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  306. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  307. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  308. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  309. package/src/plugins/builtin/macro-day/headless.ts +12 -5
  310. package/src/plugins/builtin/macro-day/index.tsx +1 -1
  311. package/src/plugins/builtin/macro-day/model.ts +39 -5
  312. package/src/plugins/builtin/macro-day/pane.tsx +27 -19
  313. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  314. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  315. package/src/plugins/builtin/market-movers/client.ts +27 -7
  316. package/src/plugins/builtin/market-movers/headless.ts +34 -12
  317. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  318. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  319. package/src/plugins/builtin/market-movers/model.ts +4 -1
  320. package/src/plugins/builtin/market-movers/screener.ts +22 -4
  321. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  322. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  323. package/src/plugins/builtin/market-movers/table.ts +17 -5
  324. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  325. package/src/plugins/builtin/mna/headless.ts +2 -0
  326. package/src/plugins/builtin/mna/pane.tsx +3 -1
  327. package/src/plugins/builtin/news/headless.ts +3 -1
  328. package/src/plugins/builtin/news/index.tsx +4 -0
  329. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  330. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  331. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  332. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  333. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  334. package/src/plugins/builtin/openfda/client.ts +14 -2
  335. package/src/plugins/builtin/openfda/index.tsx +3 -2
  336. package/src/plugins/builtin/openfda/model.ts +9 -0
  337. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  338. package/src/plugins/builtin/options/analytics.ts +29 -5
  339. package/src/plugins/builtin/options/enrichment.ts +17 -5
  340. package/src/plugins/builtin/options/table.ts +5 -2
  341. package/src/plugins/builtin/options/types.ts +2 -0
  342. package/src/plugins/builtin/options/view.tsx +17 -4
  343. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  344. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  345. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  346. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  347. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  348. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  349. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  350. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  351. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  352. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  353. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  354. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  355. package/src/plugins/builtin/pe-band/model.ts +12 -49
  356. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  357. package/src/plugins/builtin/perps/cascade.ts +236 -0
  358. package/src/plugins/builtin/perps/client.ts +56 -2
  359. package/src/plugins/builtin/perps/headless.ts +65 -9
  360. package/src/plugins/builtin/perps/index.tsx +7 -5
  361. package/src/plugins/builtin/perps/model.ts +18 -4
  362. package/src/plugins/builtin/perps/pane.tsx +19 -22
  363. package/src/plugins/builtin/perps/status-line.ts +25 -0
  364. package/src/plugins/builtin/plugin-module.ts +5 -1
  365. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  366. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  367. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  368. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  369. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  370. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
  371. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  372. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  373. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
  374. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
  375. package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
  376. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  377. package/src/plugins/builtin/realized-vol/client.ts +13 -5
  378. package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
  379. package/src/plugins/builtin/realized-vol/headless.ts +8 -4
  380. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  381. package/src/plugins/builtin/realized-vol/model.ts +37 -6
  382. package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
  383. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  384. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  385. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  386. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  387. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  388. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  389. package/src/plugins/builtin/research/event-model.ts +40 -12
  390. package/src/plugins/builtin/research/headless.ts +1 -1
  391. package/src/plugins/builtin/research/index.tsx +7 -3
  392. package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
  393. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  394. package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
  395. package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
  396. package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
  397. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  398. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  399. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  400. package/src/plugins/builtin/risk-factors/data.ts +4 -2
  401. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  402. package/src/plugins/builtin/roadmap/index.tsx +28 -0
  403. package/src/plugins/builtin/roadmap/model.ts +67 -0
  404. package/src/plugins/builtin/roadmap/pane.tsx +244 -0
  405. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  406. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  407. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  408. package/src/plugins/builtin/scanner/headless.ts +204 -35
  409. package/src/plugins/builtin/scanner/index.ts +16 -5
  410. package/src/plugins/builtin/scanner/live-headless.ts +48 -5
  411. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  412. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  413. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  414. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  415. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  416. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  417. package/src/plugins/builtin/sec/headless.ts +4 -2
  418. package/src/plugins/builtin/sec/index.tsx +3 -0
  419. package/src/plugins/builtin/series-inventory.ts +2 -0
  420. package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
  421. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  422. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  423. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  424. package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
  425. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  426. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  427. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  428. package/src/plugins/builtin/shared/report-freshness.ts +11 -2
  429. package/src/plugins/builtin/shared/research-cells.tsx +1 -1
  430. package/src/plugins/builtin/shared/series-cache.ts +4 -4
  431. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  432. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  433. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  434. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  435. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  436. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  437. package/src/plugins/builtin/themes/model.ts +16 -7
  438. package/src/plugins/builtin/themes/pane.tsx +5 -5
  439. package/src/plugins/builtin/thirteenf/api.ts +12 -1
  440. package/src/plugins/builtin/thirteenf/data.ts +7 -7
  441. package/src/plugins/builtin/thirteenf/headless.ts +31 -4
  442. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  443. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  444. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  445. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  446. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  447. package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
  448. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
  449. package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
  450. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  451. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  452. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  453. package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
  454. package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
  455. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  456. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  457. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  458. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  459. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  460. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  461. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  462. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  463. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  464. package/src/plugins/builtin/volatility/index.tsx +2 -2
  465. package/src/plugins/builtin/volatility/pane.tsx +7 -4
  466. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  467. package/src/plugins/builtin/yield-curve/client.ts +61 -1
  468. package/src/plugins/builtin/yield-curve/headless.ts +9 -6
  469. package/src/plugins/builtin/yield-curve/index.tsx +34 -10
  470. package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
  471. package/src/plugins/catalog-browser.ts +2 -1
  472. package/src/plugins/catalog-manifest.ts +2 -1
  473. package/src/plugins/host-link.ts +1 -1
  474. package/src/plugins/marketplace/model.ts +18 -5
  475. package/src/portfolio/column-values.ts +11 -7
  476. package/src/portfolio/futures-positions.ts +6 -3
  477. package/src/portfolio/position-quantity.ts +15 -9
  478. package/src/remote/resources.ts +2 -1
  479. package/src/renderers/dom/data-table/row.tsx +26 -0
  480. package/src/renderers/dom/desktop/icons.tsx +7 -0
  481. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  482. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  483. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  484. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  485. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  486. package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
  487. package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
  488. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  489. package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
  490. package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
  491. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  492. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  493. package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
  494. package/src/renderers/electrobun/bun/index.ts +26 -2
  495. package/src/renderers/electrobun/bun/window/create.ts +43 -4
  496. package/src/renderers/electrobun/bun/window/events.ts +20 -0
  497. package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
  498. package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
  499. package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
  500. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  501. package/src/renderers/electrobun/shared/protocol.ts +12 -0
  502. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  503. package/src/renderers/electrobun/view/main.tsx +24 -1
  504. package/src/renderers/electrobun/view/news-source.ts +5 -1
  505. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  506. package/src/renderers/electrobun/view/ui-host.tsx +8 -0
  507. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  508. package/src/renderers/opentui/start.tsx +5 -0
  509. package/src/series/registry.ts +1 -0
  510. package/src/series/types.ts +6 -0
  511. package/src/series/validate.ts +1 -0
  512. package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
  513. package/src/sources/gloomberb-cloud/index.ts +43 -8
  514. package/src/sources/gloomberb-cloud/news.ts +47 -7
  515. package/src/sources/provider-router/financials.ts +15 -3
  516. package/src/sources/provider-router/index.ts +8 -5
  517. package/src/state/ticker-cleanup.ts +50 -0
  518. package/src/sync/controller.ts +4 -3
  519. package/src/sync/core-contributors.ts +90 -31
  520. package/src/telemetry/stall-reports-dom.ts +38 -0
  521. package/src/telemetry/stall-reports-node.ts +75 -0
  522. package/src/telemetry/stall-reports.ts +214 -0
  523. package/src/test-support/preload.ts +14 -0
  524. package/src/theme/themes.ts +10 -0
  525. package/src/tickers/company-name-listing.ts +53 -13
  526. package/src/tickers/company-name.ts +50 -4
  527. package/src/tickers/index-roots.ts +7 -0
  528. package/src/tickers/listing-fate.ts +2 -2
  529. package/src/tickers/listing-venues.ts +15 -3
  530. package/src/tickers/search/index.ts +6 -2
  531. package/src/tickers/search/ranking.ts +5 -1
  532. package/src/tickers/search/upsert.ts +13 -0
  533. package/src/tickers/terminal-codes.ts +19 -0
  534. package/src/tickers/ticker-changes.ts +8 -4
  535. package/src/time-series/alignment.ts +1 -1
  536. package/src/time-series/economic.ts +18 -4
  537. package/src/time-series/field-catalog.ts +1 -1
  538. package/src/time-series/filed-trailing-eps.ts +197 -0
  539. package/src/time-series/fundamentals.ts +44 -1
  540. package/src/time-series/ice-credit.ts +3 -0
  541. package/src/time-series/price-comparison.ts +30 -2
  542. package/src/time-series/resolve-context.ts +3 -1
  543. package/src/time-series/resolve.ts +56 -4
  544. package/src/time-series/session-bar.ts +11 -0
  545. package/src/time-series/spec.ts +3 -2
  546. package/src/time-series/studies.ts +8 -2
  547. package/src/time-series/total-return.ts +160 -0
  548. package/src/time-series/transforms.ts +2 -3
  549. package/src/time-series/types.ts +22 -1
  550. package/src/types/company-from.ts +40 -0
  551. package/src/types/data-provider.ts +7 -0
  552. package/src/types/financials.ts +59 -1
  553. package/src/types/headless.ts +27 -3
  554. package/src/types/news-source.ts +1 -0
  555. package/src/types/plugin.ts +9 -0
  556. package/src/ui/host.tsx +3 -0
  557. package/src/updater/failure.ts +4 -1
  558. package/src/utils/cli-output.ts +42 -1
  559. package/src/utils/crypto-pair.ts +165 -7
  560. package/src/utils/declared-dividends.ts +1 -1
  561. package/src/utils/forward-dividend.ts +2 -0
  562. package/src/utils/fundamentals.ts +60 -8
  563. package/src/utils/fx-market-hours.ts +26 -3
  564. package/src/utils/main-thread-stall.ts +35 -2
  565. package/src/utils/market-capitalization.ts +24 -1
  566. package/src/utils/price-history.ts +7 -1
  567. package/src/utils/shell-word.ts +8 -0
  568. package/src/utils/trailing-dividend.ts +53 -4
  569. package/src/version.ts +2 -2
  570. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
@@ -2,6 +2,7 @@ import { publishedWeekClose } from "../analytics/sharpe-cadence";
2
2
  import { historyStatistics } from "../../../components/chart/curve/model";
3
3
  import type { CloudPricePointPayload } from "../../../api-client/types";
4
4
  import { getSectorCollection } from "../sectors/sector-data";
5
+ import { rotationQuadrant, rotationSeries, type RotationQuadrantName } from "../alerts/metric-model";
5
6
  import {
6
7
  canonicalExchange,
7
8
  normalizeSymbol,
@@ -11,6 +12,8 @@ import {
11
12
  const DAY = 86_400_000;
12
13
  const WEEK = 7 * DAY;
13
14
  export const ROTATION_LIMIT = 24;
15
+ /** RRG's benchmark when none is chosen; RRG alerts default to it too. */
16
+ export const DEFAULT_ROTATION_BENCHMARK = "SPY:NYSEARCA";
14
17
  export interface RotationInstrument {
15
18
  symbol: string;
16
19
  exchange: string;
@@ -46,8 +49,7 @@ export interface RotationRow {
46
49
  asOf: string | null;
47
50
  strength: number | null;
48
51
  momentum: number | null;
49
- quadrant:
50
- "Leading" | "Weakening" | "Lagging" | "Improving" | "Neutral" | null;
52
+ quadrant: RotationQuadrantName | null;
51
53
  strengthRank: RotationRank;
52
54
  momentumRank: RotationRank;
53
55
  relativeReturn13w: number | null;
@@ -149,21 +151,8 @@ function rotationRank(
149
151
  lastDate: stats.endDate,
150
152
  };
151
153
  }
152
- export function rotationQuadrant(
153
- strength: number | null,
154
- momentum: number | null,
155
- ): RotationRow["quadrant"] {
156
- if (strength == null || momentum == null) return null;
157
- if (Math.abs(strength - 100) < 1e-9 || Math.abs(momentum - 100) < 1e-9)
158
- return "Neutral";
159
- return strength > 100
160
- ? momentum > 100
161
- ? "Leading"
162
- : "Weakening"
163
- : momentum > 100
164
- ? "Improving"
165
- : "Lagging";
166
- }
154
+ /** The weekly strength, momentum and quadrant math is the RRG alert's, shared with Gloom Cloud's delivery. */
155
+ export { rotationQuadrant };
167
156
  export function buildRotation(
168
157
  benchmark: RotationHistory,
169
158
  instruments: RotationHistory[],
@@ -219,10 +208,12 @@ export function buildRotation(
219
208
  const benchmarkUsable = !!benchmark.currency && !!asOf;
220
209
  const rows = instruments.map((source): RotationRow => {
221
210
  const closes = rotationCloses(source.points, through);
211
+ // A benchmark with no usable weeks says so once in `gaps`; its rows are not to blame for it.
222
212
  const gaps = [
223
213
  source.error,
224
214
  source.stale ? "History is stale." : null,
225
- source.currency !== benchmark.currency || !source.currency
215
+ benchmarkUsable &&
216
+ (source.currency !== benchmark.currency || !source.currency)
226
217
  ? "Price currency does not match the benchmark."
227
218
  : null,
228
219
  ].filter((x): x is string => !!x);
@@ -240,34 +231,16 @@ export function buildRotation(
240
231
  ? ratio
241
232
  : null;
242
233
  });
243
- const strengths = ratios.map((ratio, index) => {
244
- const window = ratios.slice(Math.max(0, index - 12), index + 1);
245
- if (
246
- ratio == null ||
247
- window.length !== 13 ||
248
- window.some((value) => value == null)
249
- )
250
- return null;
251
- const mean =
252
- (window as number[]).reduce((sum, value) => sum + value, 0) / 13;
253
- return (100 * ratio) / mean;
254
- });
234
+ const series = rotationSeries(ratios);
255
235
  const history = weeks.map((week, index): RotationPoint => {
256
- const strength = strengths[index] ?? null,
257
- previous = index >= 4 ? (strengths[index - 4] ?? null) : null;
258
- const contiguous =
259
- index >= 4 &&
260
- strengths.slice(index - 4, index + 1).every((value) => value != null);
236
+ const { strength, momentum } = series[index]!;
261
237
  const priorRatio = index >= 13 ? (ratios[index - 13] ?? null) : null,
262
238
  ratio = ratios[index] ?? null;
263
239
  return {
264
240
  date: weekly.get(week)?.date ?? week,
265
241
  week,
266
242
  strength,
267
- momentum:
268
- strength != null && previous != null && contiguous
269
- ? (100 * strength) / previous
270
- : null,
243
+ momentum,
271
244
  relativeReturn13w:
272
245
  ratio != null &&
273
246
  priorRatio != null &&
@@ -284,7 +257,10 @@ export function buildRotation(
284
257
  Number.isFinite(latest.strength) &&
285
258
  latest.momentum != null &&
286
259
  Number.isFinite(latest.momentum);
287
- if (!latest || latest.strength == null || latest.momentum == null)
260
+ if (
261
+ benchmarkUsable &&
262
+ (!latest || latest.strength == null || latest.momentum == null)
263
+ )
288
264
  gaps.push(
289
265
  "At least 17 contiguous matched weekly closes are required; a missing week breaks the trail.",
290
266
  );
@@ -36,6 +36,7 @@ import {
36
36
  import { useResearchCloudSession } from "../shared/research-cloud-session";
37
37
  import { cachedRotation, loadRotation } from "./client";
38
38
  import {
39
+ DEFAULT_ROTATION_BENCHMARK,
39
40
  ROTATION_LIMIT,
40
41
  rotationId,
41
42
  rotationTrailWeeks,
@@ -140,13 +141,13 @@ function RotationDetail({
140
141
  export function RelativeRotationPane(props: PaneProps) {
141
142
  const [scope] = usePaneSettingValue("scope", "sectors");
142
143
  const [symbols] = usePaneSettingValue("symbols", "");
143
- const [benchmarkText] = usePaneSettingValue("benchmark", "SPY:NYSEARCA");
144
+ const [benchmarkText] = usePaneSettingValue("benchmark", DEFAULT_ROTATION_BENCHMARK);
144
145
  const [trailText] = usePaneSettingValue("trail", "6");
145
146
  const { collectionId } = usePaneCollection();
146
147
  const tickers = useTickers();
147
148
  const context = useMemo(() => {
148
149
  try {
149
- const references = rotationInstruments(benchmarkText || "SPY:NYSEARCA");
150
+ const references = rotationInstruments(benchmarkText || DEFAULT_ROTATION_BENCHMARK);
150
151
  if (references.length !== 1)
151
152
  throw new Error("Choose one benchmark in pane settings.");
152
153
  const instruments =
@@ -446,6 +446,7 @@ export function CorporateActionsView({
446
446
  const financialCurrency = tickerFinancials?.financialCurrency;
447
447
  const trailingEps = tickerFinancials?.fundamentals?.eps;
448
448
  const trailingEpsCurrency = tickerFinancials?.fundamentals?.epsCurrency;
449
+ const trailingEpsSource = tickerFinancials?.fundamentals?.trailingEpsSource;
449
450
  const fundamentalsCurrency = tickerFinancials?.fundamentals?.financialCurrency;
450
451
  const marketCapCurrency = tickerFinancials?.fundamentals?.marketCapCurrency;
451
452
  const quoteCurrency = tickerFinancials?.quote?.currency;
@@ -453,10 +454,10 @@ export function CorporateActionsView({
453
454
  () => (quarterlyStatements || financialCurrency || trailingEps != null ? {
454
455
  quarterlyStatements: quarterlyStatements ?? [],
455
456
  financialCurrency,
456
- fundamentals: { eps: trailingEps, epsCurrency: trailingEpsCurrency, financialCurrency: fundamentalsCurrency, marketCapCurrency },
457
+ fundamentals: { eps: trailingEps, epsCurrency: trailingEpsCurrency, financialCurrency: fundamentalsCurrency, marketCapCurrency, trailingEpsSource },
457
458
  quote: quoteCurrency ? { currency: quoteCurrency } : null,
458
459
  } : null),
459
- [financialCurrency, fundamentalsCurrency, marketCapCurrency, quarterlyStatements, quoteCurrency, trailingEps, trailingEpsCurrency],
460
+ [financialCurrency, fundamentalsCurrency, marketCapCurrency, quarterlyStatements, quoteCurrency, trailingEps, trailingEpsCurrency, trailingEpsSource],
460
461
  );
461
462
  const actionsLoader = useCallback((nextSymbol: string, nextExchange: string, forceRefresh: boolean) => {
462
463
  if (!dataProvider?.getCorporateActions) throw new Error("Corporate actions source unavailable");
@@ -15,7 +15,7 @@ import {
15
15
  } from "../../../utils/format";
16
16
  import { computeTTM } from "../ticker-detail/financials/aggregation";
17
17
  import { declaredDividends } from "../../../utils/declared-dividends";
18
- import { fundamentalsEpsCurrency } from "../../../utils/fundamentals";
18
+ import { fundamentalsEpsCurrency, SERVED_TRAILING_EPS } from "../../../utils/fundamentals";
19
19
  import {
20
20
  earningsDateStatus,
21
21
  earningsDetail,
@@ -175,11 +175,11 @@ function estimateValue(pair: EstimatePair): string {
175
175
  }
176
176
 
177
177
  /**
178
- * What the TTM row reads: the statements for its four quarters and revenue, and the trailing EPS Gloom Cloud serves
179
- * with the fundamentals, the figure `ticker` prints as EPS (TTM).
178
+ * What the TTM row reads: the statements for its four quarters and revenue, and the trailing EPS of the fundamentals,
179
+ * the figure `ticker` prints as EPS (TTM) (the filed quarters' when they give one, see time-series/filed-trailing-eps).
180
180
  */
181
181
  export type EventFinancials = Pick<TickerFinancials, "quarterlyStatements" | "financialCurrency"> & {
182
- fundamentals?: Pick<NonNullable<TickerFinancials["fundamentals"]>, "eps" | "epsCurrency" | "financialCurrency" | "marketCapCurrency"> | null;
182
+ fundamentals?: Pick<NonNullable<TickerFinancials["fundamentals"]>, "eps" | "epsCurrency" | "financialCurrency" | "marketCapCurrency" | "trailingEpsSource"> | null;
183
183
  /** The listing's currency, which says whether an unlabelled EPS can be in the statements' (fundamentalsCurrency). */
184
184
  quote?: Pick<NonNullable<TickerFinancials["quote"]>, "currency"> | null;
185
185
  };
@@ -204,9 +204,10 @@ function adjustedQuarterSum(
204
204
  }
205
205
 
206
206
  /**
207
- * The TTM row's EPS is the GAAP diluted trailing figure, the one `ticker` prints as EPS (TTM). Without it, the four
208
- * statement quarters' diluted EPS summed. The reported rows above it are often adjusted, so they are never added to
209
- * it: only when all four are adjusted does the detail also give their sum.
207
+ * The TTM row's EPS is the GAAP diluted trailing figure `ticker` prints as EPS (TTM): the last four filed quarters',
208
+ * or Gloom Cloud's trailing figure, named so, when they give none. Without either, the four statement quarters'
209
+ * diluted EPS summed. The reported rows above it are often adjusted, so they are never added to it: only when all
210
+ * four are adjusted does the detail also give their sum.
210
211
  */
211
212
  function ttmRow(
212
213
  quarterlyStatements: readonly FinancialStatement[],
@@ -226,7 +227,8 @@ function ttmRow(
226
227
  date: latest?.date ?? "",
227
228
  status: "TTM",
228
229
  period: "4 qtrs",
229
- detail: [eps == null ? "statement sum" : served ? "GAAP diluted" : "GAAP diluted (statements)",
230
+ detail: [eps == null ? "statement sum" : !served ? "GAAP diluted (statements)"
231
+ : financials?.fundamentals?.trailingEpsSource === "served" ? `GAAP diluted (${SERVED_TRAILING_EPS})` : "GAAP diluted",
230
232
  adjusted == null ? null : `adj sum ${formatNumber(adjusted, 2)}`].filter(Boolean).join(", "),
231
233
  epsCurrency: served ? fundamentalsEpsCurrency(financials) : ttm.currency ?? financials?.financialCurrency,
232
234
  revenueCurrency: ttm.currency ?? financials?.financialCurrency,
@@ -420,7 +422,8 @@ export function buildEventRows(
420
422
  date: dividend.exDate,
421
423
  status: "Dividend",
422
424
  period: "-",
423
- detail: dividend.payDate ? `Declared, payable ${dividend.payDate}` : "Declared",
425
+ // A one-off beside the regular payments says so: "Declared special, payable 2026-11-25".
426
+ detail: `Declared${dividend.special ? " special" : ""}${dividend.payDate ? `, payable ${dividend.payDate}` : ""}`,
424
427
  value: formatDistributionAmount(dividend.amount, data?.currency ?? currency),
425
428
  tone: "positive",
426
429
  });
@@ -478,9 +481,14 @@ export function eventSourceNotice(state: EventSourceState): EventSourceNotice |
478
481
  const unavailableSections = Object.entries(state.actions?.coverage ?? {})
479
482
  .filter(([section, status]) => status === "unavailable" && (state.variant !== "earnings-estimates" || section === "earnings"))
480
483
  .map(([section]) => section);
484
+ // Sections and estimates that a source failed to give share one line: "Unavailable: earnings, analyst estimates (the source did not answer)".
485
+ const estimatesCode = !!state.estimatesError && REASON_CODE.test(state.estimatesError.trim());
486
+ const sharedLine = !state.actionsError && unavailableSections.length > 0 && estimatesCode;
481
487
 
482
488
  if (state.actionsError) {
483
- notices.push(`${actionsLabel} unavailable: ${state.actionsError}`);
489
+ notices.push(`${actionsLabel} unavailable: ${sourceErrorText(state.actionsError)}`);
490
+ } else if (sharedLine) {
491
+ notices.push(`Unavailable: ${unavailableSections.join(", ")}, analyst estimates (${sourceErrorText(state.estimatesError!)})`);
484
492
  } else if (unavailableSections.length > 0) {
485
493
  notices.push(`Unavailable: ${unavailableSections.join(", ")}`);
486
494
  } else if (state.actions && !(state.variant === "earnings-estimates"
@@ -495,7 +503,7 @@ export function eventSourceNotice(state: EventSourceState): EventSourceNotice |
495
503
  if (state.estimates?.stale) notices.push(`Analyst estimates stale${state.estimates.fetchedAt ? ` (fetched ${state.estimates.fetchedAt})` : ""}`);
496
504
 
497
505
  if (state.estimatesError) {
498
- notices.push(`Analyst estimates unavailable: ${state.estimatesError}`);
506
+ if (!sharedLine) notices.push(`Analyst estimates unavailable: ${sourceErrorText(state.estimatesError)}`);
499
507
  } else if (state.estimates && !hasAnalystEstimates(state.estimates)) {
500
508
  notices.push(`No analyst estimates for ${state.symbol}`);
501
509
  }
@@ -507,8 +515,28 @@ export function eventSourceNotice(state: EventSourceState): EventSourceNotice |
507
515
  };
508
516
  }
509
517
 
518
+ /** A data service's reason code (UPSTREAM_ERROR) rather than words. */
519
+ const REASON_CODE = /^[A-Z][A-Z0-9_]+$/;
520
+
521
+ /**
522
+ * A source's failure in words: a reason code reads as what happened
523
+ * ("UPSTREAM_ERROR" is "the source did not answer"); a sentence stands.
524
+ */
525
+ export function sourceErrorText(error: string): string {
526
+ const text = error.trim();
527
+ if (!REASON_CODE.test(text)) return text;
528
+ switch (text) {
529
+ case "TIMEOUT": return "the source timed out";
530
+ case "NOT_FOUND":
531
+ case "NO_DATA": return "the source has none for this symbol";
532
+ case "RATE_LIMITED": return "the source is busy, try again shortly";
533
+ default: return "the source did not answer";
534
+ }
535
+ }
536
+
510
537
  function hasCorporateActionRows(data: CorporateActionsData): boolean {
511
- return data.dividends.length > 0 || data.splits.length > 0 || data.earnings.length > 0;
538
+ // A declared dividend is a row too: AIOK's $3.50 special with nothing paid yet.
539
+ return data.dividends.length > 0 || data.splits.length > 0 || data.earnings.length > 0 || declaredDividends(data).length > 0;
512
540
  }
513
541
 
514
542
  function hasAnalystEstimates(data: AnalystResearchData): boolean {
@@ -83,7 +83,7 @@ function projectEarningsEstimatesHeadless(
83
83
  returned: rows.length,
84
84
  truncated: rows.length < matching.length,
85
85
  coverageNote: notice?.text,
86
- epsBasis: `${EPS_BASIS_NOTE} The TTM row's EPS is the GAAP diluted trailing figure ticker shows as EPS (TTM), or the four statement quarters' diluted EPS when none is served; reported quarters of different bases are never summed, and the row adds an adjusted sum only when all four quarters are adjusted.`,
86
+ epsBasis: `${EPS_BASIS_NOTE} The TTM row's EPS is the GAAP diluted trailing figure ticker shows as EPS (TTM): the last four filed quarters', or Gloom Cloud's TTM (named so) when they give none, or the four statement quarters' diluted EPS when neither is there; reported quarters of different bases are never summed, and the row adds an adjusted sum only when all four quarters are adjusted.`,
87
87
  dateNote: "Fiscal period dates are not announcement dates. Revenue is attached only to an identified statement period.",
88
88
  unmatchedReportedPeriods,
89
89
  },
@@ -6,6 +6,8 @@ import { CorporateActionsView } from "./corporate-actions-pane";
6
6
  import { EquityDiagnosticView } from "./equity-diagnostic-pane";
7
7
  import { RelativeValuationPane } from "./relative-valuation-pane";
8
8
  import { relativeValuationHeadless } from "./relative-valuation-headless";
9
+ import { AUTO_PEERS_SETTING, relativeValuationTitle } from "./relative-valuation-peers";
10
+ import { buildRelativeValuationSettingsDef, MAX_RELATIVE_VALUATION_TICKERS } from "./relative-valuation-settings";
9
11
  import { analystResearchHeadless } from "./analyst-headless";
10
12
  import { eventsHeadless } from "./events-headless";
11
13
  import { earningsEstimatesHeadless } from "./headless";
@@ -95,6 +97,7 @@ export const researchModule: PluginModule = {
95
97
  defaultMode: "floating",
96
98
  defaultFloatingSize: { width: 104, height: 24 },
97
99
  tableExport: true,
100
+ settings: buildRelativeValuationSettingsDef,
98
101
  },
99
102
  {
100
103
  id: "earnings-estimates",
@@ -168,14 +171,15 @@ export const researchModule: PluginModule = {
168
171
  try {
169
172
  symbols = options?.symbols?.length
170
173
  ? options.symbols
171
- : parseTickerListInput(options?.arg ?? context.activeTicker ?? "", 12);
174
+ : parseTickerListInput(options?.arg ?? context.activeTicker ?? "", MAX_RELATIVE_VALUATION_TICKERS);
172
175
  } catch {
173
176
  return null;
174
177
  }
175
178
  return {
176
- title: `RV ${formatTickerListInput(symbols)}`,
179
+ title: relativeValuationTitle(symbols, null),
177
180
  placement: "floating",
178
- settings: { symbols, symbolsText: formatTickerListInput(symbols) },
181
+ // One ticker is compared with the peers `--peers` picks, which the pane finds once it opens.
182
+ settings: { symbols, symbolsText: formatTickerListInput(symbols), ...(symbols.length === 1 ? { [AUTO_PEERS_SETTING]: true } : {}) },
179
183
  };
180
184
  },
181
185
  },
@@ -8,11 +8,10 @@ import {
8
8
  RELATIVE_VALUATION_STALE_FUNDAMENTALS_NOTICE, RELATIVE_VALUATION_STALE_QUOTE_NOTICE, relativeValuationValues, type RelativeValuationGapKey,
9
9
  } from "./relative-valuation-model";
10
10
  import { paneSchemas } from "./headless-schema";
11
- import { isRecord } from "../../../utils/guards";
12
- import { errorMessage } from "../../../utils/errors";
13
- import { notATickerSymbol } from "../../../sources/provider-errors";
14
11
  import { resolveCurrencyUnit } from "../../../utils/currency-units";
15
- import { pickPeers, peerMethodTitle, type PeerSelection } from "./relative-valuation-peers";
12
+ import {
13
+ AUTO_PEERS_SETTING, findPeers, peerMethodTitle, peerSelectionOf, PEER_SELECTION_SETTING, relativeValuationTitle, type PeerSelection,
14
+ } from "./relative-valuation-peers";
16
15
  import { extraColumns, extraColumnsBasis, loadExtraColumns, parseExtraColumns } from "./relative-valuation-columns";
17
16
  import { fundamentalsBasisParts } from "./relative-valuation-basis";
18
17
  import { conversionNote, convertMarketCaps, loadFxLegs } from "./relative-valuation-currency";
@@ -96,37 +95,21 @@ const VALUATION_COLUMNS: HeadlessPaneColumn[] = [
96
95
  ...([["fcfYield", "FCF Yield"], ["revenueGrowth", REVENUE_GROWTH_HEADER], ["operatingMargin", "TTM Op Margin"]] as const).map(([key, header]) => ({
97
96
  key, header, align: "right" as const,
98
97
  format: (value: unknown, row: Record<string, unknown>) => value == null ? gapText(row, key)
99
- : key === "revenueGrowth" ? formatPercent(Number(value)) : formatLevelPercent(Number(value)),
98
+ : key === "revenueGrowth" ? formatPercent(Number(value))
99
+ // A margin of the latest fiscal year, where no trailing quarters were served, says which year.
100
+ : key === "operatingMargin" && typeof row.operatingMarginPeriod === "string"
101
+ ? `${formatLevelPercent(Number(value))} ${row.operatingMarginPeriod}` : formatLevelPercent(Number(value)),
100
102
  })),
101
103
  ];
102
104
 
103
- /** Peers `--peers` chose, as the expansion left them in the settings or the load picked them. */
104
- function peerSelectionOf(value: unknown, symbols: readonly string[]): PeerSelection | null {
105
- if (!isRecord(value) || typeof value.subject !== "string" || !Array.isArray(value.peers)) return null;
106
- const selection = value as unknown as PeerSelection;
107
- const expected = [selection.subject, ...selection.peers];
108
- return expected.length === symbols.length && expected.every((symbol, index) => symbol.toUpperCase() === symbols[index]?.toUpperCase())
109
- ? selection : null;
110
- }
111
-
112
105
  /** The peers of the one symbol `--peers` was given; several symbols are already a peer list. */
113
106
  async function choosePeers(symbols: readonly string[], ctx: HeadlessPaneContext): Promise<PeerSelection> {
114
107
  if (symbols.length !== 1) {
115
108
  throw new Error(`--peers picks the peers of one symbol, such as RV ${symbols[0] ?? "DUOL"} --peers; with several symbols, leave it out.`);
116
109
  }
117
- const subject = symbols[0]!;
118
- const financials = await loadHeadlessFinancials(ctx, subject).catch((error: unknown) => {
119
- ctx.signal.throwIfAborted();
120
- // A word that is no ticker is said as `fn RV duolingo lrn` says it, which the CLI follows with the search to run.
121
- if (notATickerSymbol(errorMessage(error))) throw error;
122
- // Any other failure leaves the peers to what the symbol alone places (a curated core's member).
123
- return null;
124
- });
125
- return pickPeers(subject, financials, ctx);
110
+ return findPeers(symbols[0]!, ctx);
126
111
  }
127
112
 
128
- const PEER_SELECTION_SETTING = "peerSelection";
129
-
130
113
  export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
131
114
  ...paneSchemas["relative-valuation-pane"],
132
115
  // What Ask Gloom reads. The template's "across peers" never said this is where a list of holdings' P/E is.
@@ -146,9 +129,12 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
146
129
  return {
147
130
  argument: [selection.subject, ...selection.peers].join(", "),
148
131
  settings: { [PEER_SELECTION_SETTING]: selection },
149
- title: `RV ${selection.subject} + ${peerMethodTitle(selection, { automatic: true })}`,
132
+ title: relativeValuationTitle([], selection),
150
133
  };
151
134
  },
135
+ // The app adds the peers of a pane opened on one ticker; a capture draws the tickers given, as the report does,
136
+ // unless --peers widened them.
137
+ shotState: (args) => args.options.peers === true ? {} : { settings: { [AUTO_PEERS_SETTING]: false } },
152
138
  async load(args, ctx) {
153
139
  // --peers picked in advance (fn and shot) reads the same peers; any other caller picks them here.
154
140
  const preset = args.options.peers === true ? peerSelectionOf(ctx.settings?.[PEER_SELECTION_SETTING], args.symbols) : null;
@@ -226,6 +212,7 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
226
212
  ...rows.flatMap((row) => row.evSalesUnavailable ? [`${row.symbol}: ${row.evSalesUnavailable}`] : []),
227
213
  ...rows.flatMap((row) => row.evSalesNote ? [`${row.symbol}: ${row.evSalesNote}`] : []),
228
214
  ...rows.flatMap((row) => row.marketCapNote ? [`${row.symbol}: ${row.marketCapNote}`] : []),
215
+ ...rows.flatMap((row) => row.marketCapWithheld ? [`${row.symbol}: ${row.marketCapWithheld}`] : []),
229
216
  ],
230
217
  multipleBasis: "Comparable P/E fields require a positive finite multiple over positive EPS, and evSales an enterprise value above zero; notMeaningful says why one is null, and reportedMultiples preserves the finite provider P/E values.",
231
218
  marketCapBasis: "Market caps retain their own currency and source. Fundamentals retrieval time is not a valuation date; quote timestamps do not date fallback caps.",
@@ -1,9 +1,10 @@
1
1
  import { formatCurrency } from "../../../utils/format";
2
2
  import { comparablePriceEarnings, notMeaningfulMultiples } from "../../../utils/price-earnings";
3
- import { marketCapBasisNote, viewMarketCapitalization } from "../../../utils/market-capitalization";
3
+ import { marketCapBasisNote, viewMarketCapitalization, withheldMarketCap } from "../../../utils/market-capitalization";
4
4
  import { enterpriseValueNotMeaningful, enterpriseValueReconciliationNote, enterpriseValueWithdrawn, reportedEnterpriseValue } from "../../../utils/fundamentals";
5
5
  import { getExtendedSessionDisplay, getQuoteSessionFields } from "../../../market-data/market/status";
6
6
  import { atRegularSessionPrice, movePriceDerivedFigures, regularSessionValuationPrice } from "../../../market-data/quotes/regular-session-valuation";
7
+ import { fiscalPeriodLabel, fiscalYearEndMonth } from "../../../utils/fiscal-period";
7
8
  import { latestFinancialPeriod, latestReportedPeriod } from "../../../utils/latest-financial-period";
8
9
  import type { Quote, TickerFinancials } from "../../../types/financials";
9
10
 
@@ -50,6 +51,25 @@ function fcfYieldGap(fundamentals: TickerFinancials["fundamentals"], capCurrency
50
51
  }
51
52
  export const RELATIVE_VALUATION_STALE_FUNDAMENTALS_NOTICE = "Fundamentals stale: retained values may be out of date";
52
53
 
54
+ /**
55
+ * The operating margin of the latest fiscal year, for a row whose trailing
56
+ * twelve months are not whole (a quarter the source has not carried yet):
57
+ * that year's operating income over its revenue, named for the year ("FY25")
58
+ * so a cell never passes it off as trailing. Null when the latest year lacks
59
+ * either figure; an older year is never stood in for it.
60
+ */
61
+ function fiscalYearOperatingMargin(annualStatements: TickerFinancials["annualStatements"] | undefined) {
62
+ const latest = latestFinancialPeriod(annualStatements, (row) => row.date);
63
+ if (!latest || !finiteNumber(latest.operatingIncome) || !finiteNumber(latest.totalRevenue) || latest.totalRevenue <= 0) return null;
64
+ const year = /^FY\d{2}(\d{2})$/.exec(fiscalPeriodLabel(latest.date, "annual", fiscalYearEndMonth(annualStatements ?? [])))?.[1];
65
+ return {
66
+ value: latest.operatingIncome / latest.totalRevenue,
67
+ // The fiscal year's own name; the calendar year of its end where the year cannot be named.
68
+ label: `FY${year ?? latest.date.slice(2, 4)}`,
69
+ periodEnd: latest.date.slice(0, 10),
70
+ };
71
+ }
72
+
53
73
  /**
54
74
  * A peer's row. Last, its change and the extended print are the quote's
55
75
  * sessions as `quote` reads them (getQuoteSessionFields): after the close,
@@ -97,14 +117,17 @@ export function relativeValuationValues(snapshot: TickerFinancials | null) {
97
117
  && fundamentals.freeCashFlow != null && Number.isFinite(fundamentals.freeCashFlow) && capitalization.value > 0
98
118
  ? fundamentals.freeCashFlow / capitalization.value : null;
99
119
  const revenueGrowth = fundamentals?.revenueGrowth ?? fundamentals?.lastQuarterGrowth ?? null;
100
- const operatingMargin = fundamentals?.operatingMargin ?? null;
120
+ const trailingOperatingMargin = fundamentals?.operatingMargin ?? null;
121
+ const fiscalYearMargin = trailingOperatingMargin == null ? fiscalYearOperatingMargin(financials?.annualStatements) : null;
122
+ const operatingMargin = trailingOperatingMargin ?? fiscalYearMargin?.value ?? null;
101
123
  const trailingPE = trailingNotMeaningful ? null : comparablePriceEarnings(reportedMultiples.trailingPE);
102
124
  const forwardPE = forwardNotMeaningful ? null : comparablePriceEarnings(reportedMultiples.forwardPE);
103
125
  // Every figure a row lacks says why, so an export's blank cell is never silent: N/M multiples say so themselves.
104
126
  const gaps: Partial<Record<RelativeValuationGapKey, string>> = {};
105
127
  if (session.price == null) gaps.price = quoteStale ? QUOTE_STALE : NOT_REPORTED;
106
128
  if (session.changePercent == null) gaps.changePercent = quoteStale ? QUOTE_STALE : NOT_REPORTED;
107
- if (capitalization == null) gaps.marketCap = quoteStale && !fundamentals?.marketCap ? QUOTE_STALE : NOT_REPORTED;
129
+ const withheld = withheldMarketCap(fundamentals);
130
+ if (capitalization == null) gaps.marketCap = withheld?.gap ?? (quoteStale && !fundamentals?.marketCap ? QUOTE_STALE : NOT_REPORTED);
108
131
  if (trailingPE == null && !trailingNotMeaningful && reportedMultiples.trailingPE == null) gaps.trailingPE = NOT_REPORTED;
109
132
  if (forwardPE == null && !forwardNotMeaningful && reportedMultiples.forwardPE == null) gaps.forwardPE = NOT_REPORTED;
110
133
  if (evSales == null && !evSalesNotMeaningful) gaps.evSales = evSalesGap(fundamentals, quote?.currency, evSalesWithdrawn, enterpriseValueGap);
@@ -136,6 +159,8 @@ export function relativeValuationValues(snapshot: TickerFinancials | null) {
136
159
  marketCapProvenance: capitalization?.provenance ?? null,
137
160
  /** valuation's note when the cap is the share count times the price: the provider's figure it replaced. */
138
161
  marketCapNote: marketCapBasisNote(capitalization) ?? null,
162
+ /** Why Gloom Cloud withheld the cap (a receipt priced as ordinary shares), as `valuation` and EQS say it; null otherwise. */
163
+ marketCapWithheld: withheld?.note ?? null,
139
164
  trailingPE,
140
165
  forwardPE,
141
166
  reportedMultiples,
@@ -151,6 +176,9 @@ export function relativeValuationValues(snapshot: TickerFinancials | null) {
151
176
  fcfYield,
152
177
  revenueGrowth,
153
178
  operatingMargin,
179
+ /** The fiscal year the operating margin is of ("FY25") when no trailing figure was served; null when it is trailing twelve months. */
180
+ operatingMarginPeriod: fiscalYearMargin?.label ?? null,
181
+ operatingMarginPeriodEnd: fiscalYearMargin?.periodEnd ?? null,
154
182
  /** Why each missing figure is missing, in a few words the row draws in its place. */
155
183
  gaps,
156
184
  };