gloomberb 0.18.2 → 0.18.4

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 (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -4,7 +4,11 @@
4
4
  * convert both ways, and two stamps on the same UTC date are the same expiry.
5
5
  */
6
6
 
7
- import { readCalendarDate } from "./calendar-date";
7
+ import type { OptionsChain } from "../types/financials";
8
+ import { hasRegularCloseHour, latestRegularSessionClose, sessionCalendarTimeZone } from "../market-data/market/freshness";
9
+ import { dateOptionHint, readCalendarDate } from "./calendar-date";
10
+ import { canonicalExchange, isUsListingExchange } from "./exchanges";
11
+ import { zonedWallClockToUtcMs } from "./zoned-date-time";
8
12
 
9
13
  // 9999-12-31 in Unix seconds; a longer number is milliseconds or a typo.
10
14
  const MAX_UNIX_SECONDS = 253_402_300_799;
@@ -40,13 +44,23 @@ export function parseOptionExpiration(raw: string): number | null {
40
44
  return seconds > 0 && seconds <= MAX_UNIX_SECONDS ? seconds : null;
41
45
  }
42
46
 
47
+ /**
48
+ * The sentence that follows an unreadable expiry: the date to type for one
49
+ * written another way that plainly names it (`Type --expiration 2026-10-22 for
50
+ * 22/10/2026.`), led by a space, or nothing for anything else.
51
+ */
52
+ export function expirationHint(value: string, flag = "expiration"): string {
53
+ const hint = dateOptionHint(value, `--${flag}`);
54
+ return hint ? ` ${hint[0]!.toUpperCase()}${hint.slice(1)}.` : "";
55
+ }
56
+
43
57
  /**
44
58
  * A pane function's typed expiry flag as the Unix seconds `parseOptionExpiration`
45
59
  * reads; throws an Error naming both accepted forms.
46
60
  */
47
61
  export function expirationOptionSeconds(value: string, flag = "expiration"): number {
48
62
  const expiration = parseOptionExpiration(value);
49
- if (expiration == null) throw new Error(`Invalid --${flag} "${value}". Use ${OPTION_EXPIRATION_FORMAT}.`);
63
+ if (expiration == null) throw new Error(`Invalid --${flag} "${value}". Use ${OPTION_EXPIRATION_FORMAT}.${expirationHint(value, flag)}`);
50
64
  return expiration;
51
65
  }
52
66
 
@@ -70,10 +84,128 @@ export function daysToExpiry(expiration: number, asOfMs: number): number {
70
84
  return Math.round((expiry - asOf) / DAY_MS);
71
85
  }
72
86
 
73
- /** `469d` to expiry from the as-of date; `expired` once it has passed. */
74
- export function formatDaysToExpiry(expiration: number, asOfMs: number): string {
75
- const days = daysToExpiry(expiration, asOfMs);
76
- return days < 0 ? "expired" : `${days}d`;
87
+ /** `469d` to expiry from the as-of date; `expired` once its last session has closed (`isLiveOptionExpiry`). */
88
+ export function formatDaysToExpiry(expiration: number, asOfMs: number, exchange?: string): string {
89
+ return isLiveOptionExpiry(expiration, asOfMs, exchange) ? `${Math.max(0, daysToExpiry(expiration, asOfMs))}d` : "expired";
90
+ }
91
+
92
+ /** Listed US options trade on the US equity calendar, NYSE's published one. */
93
+ const US_OPTIONS_CALENDAR = "NYSE";
94
+ const lastSessions = new Map<string, { date: string; close: number } | null>();
95
+
96
+ /**
97
+ * The venue whose calendar an expiry's last session is read on: a listing's
98
+ * own venue abroad when its zone and close hour are known (XETRA, JPX), else
99
+ * the US one listed options trade on, which also stands for a chain read
100
+ * without a venue.
101
+ */
102
+ function expiryCalendarVenue(exchange: string | undefined): string {
103
+ const venue = canonicalExchange(exchange);
104
+ return venue && !isUsListingExchange(venue) && hasRegularCloseHour(venue) && sessionCalendarTimeZone(venue)
105
+ ? venue : US_OPTIONS_CALENDAR;
106
+ }
107
+
108
+ /** The expiry's last trading session, on or before its date, on its venue's calendar: the date and when it closed. */
109
+ function expiryLastSession(expiration: number, exchange: string | undefined): { date: string; close: number } | null {
110
+ const date = expiryIsoDate(expiration);
111
+ if (!date) return null;
112
+ const venue = expiryCalendarVenue(exchange);
113
+ const key = `${venue}:${date}`;
114
+ const known = lastSessions.get(key);
115
+ if (known !== undefined) return known;
116
+ const timeZone = sessionCalendarTimeZone(venue)!;
117
+ // Midnight ending the expiry's date on the venue's clock: its session that day, else the latest before it.
118
+ const dayEnd = zonedWallClockToUtcMs(timeZone, Number(date.slice(0, 4)), Number(date.slice(5, 7)), Number(date.slice(8, 10)) + 1, 0, 0, 0);
119
+ const session = latestRegularSessionClose(venue, dayEnd);
120
+ const last = session ? { date: session.date, close: session.close } : null;
121
+ // The calendar is static, and every list and picker asks about the same few dates.
122
+ if (lastSessions.size < 4096) lastSessions.set(key, last);
123
+ return last;
124
+ }
125
+
126
+ /**
127
+ * When an expiry stops trading: the regular close of its last session, the
128
+ * listing venue's last trading day on or before the expiry's date, on that
129
+ * venue's clock and calendar. A US expiry closes at 16:00 New York, at 13:00
130
+ * on a published early close, and one dated on a holiday or a weekend closed
131
+ * with the session before it. Null for a stamp that is not a date.
132
+ */
133
+ export function optionExpiryCloseMs(expiration: number, exchange?: string): number | null {
134
+ return expiryLastSession(expiration, exchange)?.close ?? null;
135
+ }
136
+
137
+ /**
138
+ * The rule every expiry list, picker and default chain follows: an expiry is
139
+ * live until its last session has closed (`optionExpiryCloseMs`). After
140
+ * Friday's close Friday's expiry is gone, over the weekend and on Monday
141
+ * before the open too, while on its own day it stays until the close.
142
+ */
143
+ export function isLiveOptionExpiry(expiration: number, now: number, exchange?: string): boolean {
144
+ if (!Number.isFinite(expiration) || expiration <= 0) return false;
145
+ const close = optionExpiryCloseMs(expiration, exchange);
146
+ return close != null && now < close;
147
+ }
148
+
149
+ /** The listed expiries still live (`isLiveOptionExpiry`), once each, in date order. */
150
+ export function liveOptionExpiries(expirations: readonly number[], now: number, exchange?: string): number[] {
151
+ return [...new Set(expirations)].filter((expiration) => isLiveOptionExpiry(expiration, now, exchange)).sort((a, b) => a - b);
152
+ }
153
+
154
+ /** The chain with only its live expiries listed; the contracts are left as they are. */
155
+ export function withLiveExpiryList<T extends Pick<OptionsChain, "expirationDates">>(chain: T, now: number, exchange?: string): T {
156
+ const live = liveOptionExpiries(chain.expirationDates, now, exchange);
157
+ return live.length === chain.expirationDates.length && live.every((expiration, index) => expiration === chain.expirationDates[index])
158
+ ? chain : { ...chain, expirationDates: live };
159
+ }
160
+
161
+ /**
162
+ * The expiry a chain asked for without a date shows instead of the one its
163
+ * source sent, by the same rule: undefined when the source's default is live
164
+ * or has no contracts, else the nearest live expiry it lists, to ask for by
165
+ * date, or null when none of them is live. A source can still answer with
166
+ * Friday's chain after Friday's close, all weekend.
167
+ */
168
+ export function liveDefaultExpiry(
169
+ chain: Pick<OptionsChain, "calls" | "puts" | "expirationDates">, now: number, exchange?: string,
170
+ ): number | null | undefined {
171
+ const contracts = [...chain.calls, ...chain.puts];
172
+ if (contracts.length === 0 || contracts.some((contract) => isLiveOptionExpiry(contract.expiration, now, exchange))) return undefined;
173
+ return liveOptionExpiries(chain.expirationDates, now, exchange)[0] ?? null;
174
+ }
175
+
176
+ /** The first live listed expiry dated after `expiration`, for a chain that had nothing to offer. */
177
+ export function nextLiveExpiry(expiration: number, expirations: readonly number[], now: number, exchange?: string): number | undefined {
178
+ const date = expiryIsoDate(expiration);
179
+ return liveOptionExpiries(expirations, now, exchange).find((listed) => expiryIsoDate(listed) > date);
180
+ }
181
+
182
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
183
+
184
+ /** An expiry's date as a sentence names it: `23 Oct`, with the year when it is not the current one (`21 Jan 2028`). */
185
+ export function expiryDayLabel(expiration: number | string, now: number): string {
186
+ const date = typeof expiration === "string" ? expiration : expiryIsoDate(expiration);
187
+ if (!date) return "";
188
+ const label = `${Number(date.slice(8, 10))} ${MONTHS[Number(date.slice(5, 7)) - 1]}`;
189
+ return date.slice(0, 4) === new Date(now).toISOString().slice(0, 4) ? label : `${label} ${date.slice(0, 4)}`;
190
+ }
191
+
192
+ /** An expiry to pick, and with a flag how to ask for it: `30 Oct (--expiration 2026-10-30)`. */
193
+ export function expiryChoiceText(expiration: number, now: number, flag?: string): string {
194
+ return `${expiryDayLabel(expiration, now)}${flag ? ` (--${flag} ${expiryIsoDate(expiration)})` : ""}`;
195
+ }
196
+
197
+ /**
198
+ * Why an expired date shows no chain, naming the day its last session closed
199
+ * and the nearest live expiry, with the flag that asks for it when given:
200
+ * `expired on 23 Oct; nearest live: 30 Oct (--expiration 2026-10-30)`.
201
+ */
202
+ export function expiredExpiryText(
203
+ requested: number, expirations: readonly number[], now: number, { exchange, flag }: { exchange?: string; flag?: string } = {},
204
+ ): string {
205
+ const last = expiryLastSession(requested, exchange);
206
+ const nearest = liveOptionExpiries(expirations, now, exchange)[0];
207
+ return `expired on ${expiryDayLabel(last?.date ?? requested, now)}; `
208
+ + (nearest == null ? "the chain lists no live expiry" : `nearest live: ${expiryChoiceText(nearest, now, flag)}`);
77
209
  }
78
210
 
79
211
  const NEAREST_EXPIRIES = 4;
@@ -137,6 +269,6 @@ export function missingExpiryMessage(
137
269
  ): { message: string; details: string } {
138
270
  return {
139
271
  message: missingExpiryText(requested, expirations, symbol),
140
- details: `Run gloomberb options ${symbol} to list every expiry (--json carries them too).`,
272
+ details: `Run gloom options ${symbol} to list every expiry (--json carries them too).`,
141
273
  };
142
274
  }
@@ -1,5 +1,6 @@
1
1
  import type { Fundamentals } from "../types/financials";
2
2
  import { formatNumber } from "./format";
3
+ import { enterpriseValueNotMeaningful } from "./fundamentals";
3
4
 
4
5
  export const PRICE_EARNINGS_NOTICE = "N/M = not meaningful. Non-positive P/E values are excluded from ranking.";
5
6
 
@@ -61,16 +62,19 @@ export function notMeaningfulMultiples(
61
62
 
62
63
  /**
63
64
  * The fundamentals block as JSON exports it: a multiple that is not meaningful
64
- * reads null, with the reason under `notMeaningful`, never as a number.
65
+ * reads null, with the reason under `notMeaningful`, never as a number. An
66
+ * enterprise value at or below zero does too, with EV/revenue over it.
65
67
  */
66
68
  export function exportedFundamentals<T extends Fundamentals>(
67
69
  fundamentals: T | undefined,
68
- ): (Omit<T, PriceEarningsField> & Partial<Record<PriceEarningsField, number | null>> & {
69
- notMeaningful?: Partial<Record<PriceEarningsField, string>>;
70
+ ): (Omit<T, PriceEarningsField | EnterpriseValueField> & Partial<Record<PriceEarningsField | EnterpriseValueField, number | null>> & {
71
+ notMeaningful?: Partial<Record<PriceEarningsField | EnterpriseValueField, string>>;
70
72
  }) | undefined {
71
73
  if (!fundamentals) return fundamentals;
72
- const reasons = notMeaningfulMultiples(fundamentals);
73
- const fields = Object.keys(reasons) as PriceEarningsField[];
74
+ const reasons: Partial<Record<PriceEarningsField | EnterpriseValueField, string>> = notMeaningfulMultiples(fundamentals);
75
+ const enterpriseValue = enterpriseValueNotMeaningful(fundamentals);
76
+ if (enterpriseValue) reasons.enterpriseValue = reasons.enterpriseToRevenue = enterpriseValue;
77
+ const fields = Object.keys(reasons) as Array<PriceEarningsField | EnterpriseValueField>;
74
78
  if (fields.length === 0) return fundamentals;
75
79
  return {
76
80
  ...fundamentals,
@@ -78,3 +82,5 @@ export function exportedFundamentals<T extends Fundamentals>(
78
82
  notMeaningful: reasons,
79
83
  };
80
84
  }
85
+
86
+ type EnterpriseValueField = "enterpriseValue" | "enterpriseToRevenue";
@@ -313,6 +313,8 @@ function zonedWeekday(time: number, timeZone: string): number {
313
313
  }
314
314
 
315
315
  const CRYPTO_BAR_MAX_AGE_MS = 60 * 60 * 1000;
316
+ // A coin's UTC day is served whole this long after midnight: the delay on bars that are not live.
317
+ const CRYPTO_DAY_ROLL_MS = 15 * 60 * 1000;
316
318
  // A copy missing a settled session is asked again at most this often, and less
317
319
  // often as that close recedes, so a halted, delisted or late listing costs a
318
320
  // handful of requests per session rather than one per request.
@@ -404,12 +406,15 @@ function intradayHistoryFetchState(
404
406
  }
405
407
 
406
408
  /**
407
- * A daily or coarser series changes at every close. A copy fetched before the
408
- * latest settled close is outdated whatever its cache TTL. A copy fetched
409
- * after it but without its bar is behind only where the venue's closures are
410
- * published (hasPublishedSessionCalendar): elsewhere, and for a bare symbol whose venue is
411
- * unknown, a local holiday would read as a missing session. A copy of a 24/7
412
- * series goes out of date within the hour.
409
+ * A daily or coarser series changes at every close, and gains a bar for the
410
+ * day once a session opens. A copy fetched before the latest settled close is
411
+ * outdated whatever its cache TTL, and so is one fetched before a session that
412
+ * has opened since: it lacks today's bar, so a morning copy would answer for
413
+ * yesterday all day. A copy fetched after the latest settled close but without
414
+ * its bar is behind only where the venue's closures are published
415
+ * (hasPublishedSessionCalendar): elsewhere, and for a bare symbol whose venue
416
+ * is unknown, a local holiday would read as a missing session. A copy of a
417
+ * 24/7 series goes out of date within the hour.
413
418
  */
414
419
  export function calendarHistoryFetchState(
415
420
  points: PricePoint[],
@@ -424,13 +429,18 @@ export function calendarHistoryFetchState(
424
429
  if (intervalMs < DAY_MS) return intradayHistoryFetchState(normalized, fetchedAt, now, intervalMs, options);
425
430
  const exchange = canonicalExchange(options.exchange);
426
431
  if (isRoundTheClockCoin(options.symbol, options.exchange)) {
427
- return now - fetchedAt > CRYPTO_BAR_MAX_AGE_MS
428
- ? unsettledState(fetchedAt + CRYPTO_BAR_MAX_AGE_MS, options.checkedAt, now) : "current";
432
+ // A coin's day closes at UTC midnight, and the service has it by the delay after:
433
+ // a copy from before then holds that day as still forming.
434
+ const roll = Math.floor((now - CRYPTO_DAY_ROLL_MS) / DAY_MS) * DAY_MS + CRYPTO_DAY_ROLL_MS;
435
+ const settledAt = Math.min(fetchedAt + CRYPTO_BAR_MAX_AGE_MS, fetchedAt < roll ? roll : Number.POSITIVE_INFINITY);
436
+ return now >= settledAt ? unsettledState(settledAt, options.checkedAt, now) : "current";
429
437
  }
430
438
  // Bare symbols resolve to their US listing at the sources.
431
439
  const session = latestRegularSessionClose(exchange || "NYSE", now - SESSION_BAR_SETTLE_MS);
432
440
  if (!session) return "current";
433
441
  if (fetchedAt < session.close + SESSION_BAR_SETTLE_MS) return unsettledState(session.close + SESSION_BAR_SETTLE_MS, options.checkedAt, now);
442
+ const open = latestRegularSessionOpen(exchange || "NYSE", now);
443
+ if (open !== null && open > session.close && fetchedAt < open) return unsettledState(open, options.checkedAt, now);
434
444
  if (!exchange || !hasPublishedSessionCalendar(exchange, session.date)) return "current";
435
445
  const latest = normalized.findLast(hasFiniteClose);
436
446
  if (!latest || !isBarBeforeSession(getPricePointTimestamp(latest), session.date, intervalMs, session.timeZone)) return "current";
@@ -0,0 +1,131 @@
1
+ import { dateOptionError, isoDateToday, readCalendarDate } from "./calendar-date";
2
+ import { fetchHistoryResult } from "../sources/history-result";
3
+ import type { DataProvider, MarketDataRequestContext } from "../types/data-provider";
4
+ import type { PriceHistoryResult } from "../types/price-history";
5
+ import { getPricePointTimestamp } from "./price-history";
6
+ import { zonedDateKey } from "./zoned-date-time";
7
+
8
+ /**
9
+ * A price-history window typed as `--from` and `--to`: a year (2007) or a date
10
+ * (2008-09-15, or any form `readCalendarDate` reads). A year runs from 1 Jan
11
+ * as a start and to 31 Dec as an end. Without `--to` the window runs to
12
+ * today; `--to` needs a `--from`. Dates are YYYY-MM-DD, both inclusive.
13
+ */
14
+ export interface PriceWindow {
15
+ from: string;
16
+ to: string;
17
+ }
18
+
19
+ const YEAR = /^\d{4}$/;
20
+ const DAY_MS = 86_400_000;
21
+
22
+ /**
23
+ * Today as the window reads it: the reader's date (in `timeZone`, the host's
24
+ * when not given), or UTC's when that is later, so a date that has begun for
25
+ * the reader (east of UTC, or in UTC already) is never refused as still ahead.
26
+ */
27
+ function priceWindowToday(now: Date, timeZone?: string): string {
28
+ const pad = (value: number) => String(value).padStart(2, "0");
29
+ const local = timeZone ? zonedDateKey(now.getTime(), timeZone) : `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
30
+ const utc = isoDateToday(now);
31
+ return local > utc ? local : utc;
32
+ }
33
+
34
+ /** Whether the window closed before today in UTC: its bars are history, with no session still trading in them. */
35
+ export function isPastPriceWindow(window: PriceWindow, now = new Date()): boolean {
36
+ return window.to < isoDateToday(now);
37
+ }
38
+
39
+ const FORMATS = "a year (2007) or a date (2008-09-15)";
40
+
41
+ function readBound(value: string, edge: "start" | "end", flag: string, now: Date): string {
42
+ const text = value.trim();
43
+ if (YEAR.test(text)) return edge === "start" ? `${text}-01-01` : `${text}-12-31`;
44
+ const date = text ? readCalendarDate(text) : null;
45
+ if (date) return date;
46
+ throw new Error(`Invalid ${flag} "${value}". ${dateOptionError(text, flag, { now, subject: `the window's ${edge}`, formats: FORMATS, example: edge === "start" ? "2007" : "2009" })}`);
47
+ }
48
+
49
+ /**
50
+ * The window `--from` and `--to` name, or null when neither was given. Throws
51
+ * a message naming the option for a value that is no year or date, a `--to`
52
+ * alone, a start after the end, or a start still ahead. An end past today
53
+ * stops at today.
54
+ */
55
+ export function readPriceWindow(
56
+ from: string | undefined,
57
+ to: string | undefined,
58
+ now = new Date(),
59
+ timeZone?: string,
60
+ /** The options as typed, for the messages: `--start` and `--end` where a command takes those too. */
61
+ flags: { from: string; to: string } = { from: "--from", to: "--to" },
62
+ ): PriceWindow | null {
63
+ if (from == null && to == null) return null;
64
+ const { from: fromFlag, to: toFlag } = flags;
65
+ if (from == null) throw new Error(`${toFlag} needs a ${fromFlag}: ${fromFlag} ${YEAR.test(to!.trim()) ? Number(to!.trim()) - 2 : 2007} ${toFlag} ${to!.trim()}.`);
66
+ const today = priceWindowToday(now, timeZone);
67
+ const start = readBound(from, "start", fromFlag, now);
68
+ const typedEnd = to == null ? today : readBound(to, "end", toFlag, now);
69
+ if (start > today) throw new Error(`${fromFlag} ${from.trim()} has not started yet. Use a start on or before ${today}.`);
70
+ if (start > typedEnd) {
71
+ throw new Error(`${fromFlag} ${from.trim()} is after ${toFlag} ${to!.trim()}. The window runs from the earlier to the later, such as ${fromFlag} ${to!.trim()} ${toFlag} ${from.trim()}.`);
72
+ }
73
+ return { from: start, to: typedEnd > today ? today : typedEnd };
74
+ }
75
+
76
+ /** The window in words: `2007 to 2009`, `2008-09-15 to 2009-03-31`, `2007 to today`. */
77
+ export function priceWindowText(window: PriceWindow, now = new Date(), timeZone?: string): string {
78
+ const year = (date: string, edge: "start" | "end") => date.slice(5) === (edge === "start" ? "01-01" : "12-31") ? date.slice(0, 4) : date;
79
+ const end = window.to === priceWindowToday(now, timeZone) ? "today" : year(window.to, "end");
80
+ return `${year(window.from, "start")} to ${end}`;
81
+ }
82
+
83
+ /** Calendar days the window spans, both ends counted. */
84
+ function priceWindowDays(window: PriceWindow): number {
85
+ return Math.round((Date.parse(`${window.to}T00:00:00Z`) - Date.parse(`${window.from}T00:00:00Z`)) / 86_400_000) + 1;
86
+ }
87
+
88
+ /** Daily bars up to five years, weekly up to twenty, monthly beyond: the sizes `history`'s 1Y, 5Y and ALL come in. */
89
+ export function priceWindowInterval(window: PriceWindow): "1d" | "1wk" | "1mo" {
90
+ const days = priceWindowDays(window);
91
+ return days <= 5 * 366 ? "1d" : days <= 20 * 366 ? "1wk" : "1mo";
92
+ }
93
+
94
+ /**
95
+ * Where the bar of this size that holds `date` starts, at midnight UTC: the
96
+ * day itself, its week's Monday (weekly bars are dated by the Monday) or its
97
+ * month's 1st. A window keeps the week or month its first date falls in.
98
+ */
99
+ export function priceWindowBarStart(date: string, interval: string): number {
100
+ const day = Date.parse(`${date}T00:00:00Z`);
101
+ if (interval === "1wk") return day - ((new Date(day).getUTCDay() + 6) % 7) * DAY_MS;
102
+ if (interval === "1mo") return Date.parse(`${date.slice(0, 7)}-01T00:00:00Z`);
103
+ return day;
104
+ }
105
+
106
+ /**
107
+ * The bars of a window, in the size `priceWindowInterval` picks: the source's
108
+ * bars for those dates, else, from a source that serves no dated request, its
109
+ * whole history in that size. Either way only bars inside the window are
110
+ * kept, with the week or month its first date falls in (`--from 2008-09-15`
111
+ * in monthly bars starts with September 2008's).
112
+ */
113
+ export async function loadPriceWindowHistory(
114
+ provider: DataProvider, symbol: string, exchange: string, window: PriceWindow, context?: MarketDataRequestContext,
115
+ ): Promise<PriceHistoryResult> {
116
+ const interval = priceWindowInterval(window);
117
+ const start = new Date(priceWindowBarStart(window.from, interval));
118
+ const end = new Date(`${window.to}T23:59:59Z`);
119
+ const result = await fetchHistoryResult(provider, symbol, exchange, { kind: "detail", start, end, interval }, context)
120
+ ?? await fetchHistoryResult(provider, symbol, exchange, { kind: "resolution", range: "ALL", resolution: interval }, context)
121
+ ?? await fetchHistoryResult(provider, symbol, exchange, { kind: "range", range: "ALL" }, context);
122
+ const points = (result?.points ?? []).filter((point) => {
123
+ const time = getPricePointTimestamp(point);
124
+ return Number.isFinite(time) && time >= start.getTime() && time <= end.getTime();
125
+ });
126
+ return { ...(result ?? { resolution: null }), points };
127
+ }
128
+
129
+ /** Shared help text for `--from` and `--to`. */
130
+ export const PRICE_WINDOW_FROM_HELP = `First year or date of the window, as ${FORMATS}; replaces --range`;
131
+ export const PRICE_WINDOW_TO_HELP = "Last year or date of the window, with --from (default today)";
@@ -0,0 +1,47 @@
1
+ import { parseReportTime } from "./utc-time";
2
+
3
+ const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as const;
4
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] as const;
5
+
6
+ /**
7
+ * When a statistic was released, said after the period it covers:
8
+ * `released Tue 20 Oct`, with the year when it is not the year of the period
9
+ * (`released Fri 15 Jan 2027` for December 2026's figure), and the time in
10
+ * UTC for a release known to the minute (`released Wed 14 Oct 12:30 UTC`).
11
+ * `periodStart` is the date the figure is dated by (`2026-09-01`). Null when
12
+ * `released` is not a date.
13
+ */
14
+ export function releasedText(released: unknown, periodStart?: string | null): string | null {
15
+ const time = parseReportTime(released);
16
+ if (!time) return null;
17
+ const day = new Date(time.time);
18
+ const periodYear = periodStart ? Number(periodStart.slice(0, 4)) : Number.NaN;
19
+ const year = day.getUTCFullYear() === periodYear ? "" : ` ${day.getUTCFullYear()}`;
20
+ const date = `${WEEKDAYS[day.getUTCDay()]} ${day.getUTCDate()} ${MONTHS[day.getUTCMonth()]}${year}`;
21
+ return `released ${date}${time.dateOnly ? "" : ` ${day.toISOString().slice(11, 16)} UTC`}`;
22
+ }
23
+
24
+ /**
25
+ * The period a figure covers and when it was released, as every macro pane,
26
+ * report and footer says it: `Sep 2026, released Tue 20 Oct`. The period
27
+ * alone when the release is not known.
28
+ */
29
+ export function periodReleasedText(period: string, released: unknown, periodStart?: string | null): string {
30
+ const text = releasedText(released, periodStart);
31
+ return text ? `${period}, ${text}` : period;
32
+ }
33
+
34
+ /**
35
+ * How often a figure dated by the first day of its period publishes, from the
36
+ * prints a year holds (12 for monthly): `monthly` for `2026-09-01`, `quarterly`
37
+ * for `2026-07-01`. Null for a weekly or daily print, or a date that starts no
38
+ * such period.
39
+ */
40
+ export function periodStartPeriodicity(date: string, perYear: number): "monthly" | "quarterly" | "annual" | null {
41
+ const match = /^\d{4}-(\d{2})-01/.exec(date);
42
+ if (!match || perYear > 12) return null;
43
+ const month = Number(match[1]) - 1;
44
+ if (perYear <= 1) return month === 0 ? "annual" : null;
45
+ if (perYear <= 4) return month % 3 === 0 ? "quarterly" : null;
46
+ return "monthly";
47
+ }
package/src/utils/sec.ts CHANGED
@@ -141,6 +141,9 @@ export function listingAbroadVenue(key: string): string | null {
141
141
  return venue && zone && zone !== "UTC" && !US_TIME_ZONES.has(zone) ? venue : null;
142
142
  }
143
143
 
144
+ /** What `ticker` and ETF say of a fund listed abroad: the facts Gloom collects are the US-listed funds'. */
145
+ export const US_FUND_FACTS_ONLY = "Fund facts (fee, NAV, premium) cover US-listed funds only.";
146
+
144
147
  /**
145
148
  * The symbol data that covers US listings only reads a listing by: AAPL for
146
149
  * AAPL:NASDAQ, ^SPX as it is. A listing abroad throws, naming what covers US
@@ -163,6 +166,74 @@ export function noSecReportsNotice(listing: { symbol: string; exchange: string;
163
166
  return `${shortListingTitle(listing)} files no SEC reports.`;
164
167
  }
165
168
 
169
+ /** The country of a venue abroad, ISO 3166 alpha-2, for the companies an SEC report may list there. */
170
+ const VENUE_COUNTRY: Readonly<Record<string, string>> = {
171
+ TSX: "CA", TSXV: "CA", CSE: "CA", NEO: "CA",
172
+ LSE: "GB",
173
+ ASX: "AU", CXA: "AU", NZX: "NZ",
174
+ SWX: "CH",
175
+ XETRA: "DE", FWB2: "DE", EPA: "FR", AMS: "NL", BRU: "BE", LIS: "PT", BIT: "IT", MCE: "ES", ISE: "IE",
176
+ SFB: "SE", HEL: "FI", CPH: "DK", OSL: "NO", ICEX: "IS", WSE: "PL", PSE: "CZ", VIE: "AT", WBDM: "AT",
177
+ JPX: "JP", HKEX: "HK", KRX: "KR", KOSDAQ: "KR", TWSE: "TW", TPEX: "TW", SSE: "CN", SZSE: "CN",
178
+ NSE: "IN", BSE: "IN", SGX: "SG", SET: "TH",
179
+ BMV: "MX", B3: "BR", BYMA: "AR", JSE: "ZA", TASE: "IL", BIST: "TR", TADAWUL: "SA",
180
+ };
181
+
182
+ /** The European Economic Area, whose issuers report managers' dealings under the Market Abuse Regulation. */
183
+ const EEA = new Set(["AT", "BE", "CZ", "DE", "DK", "ES", "FI", "FR", "IE", "IS", "IT", "NL", "NO", "PL", "PT", "SE"]);
184
+
185
+ /** Insider reports outside the SEC by the name they go by, by the issuer's country. */
186
+ const HOME_INSIDER_REPORTS: Readonly<Record<string, string>> = {
187
+ CA: "Canadian insider reports (SEDI)",
188
+ GB: "UK insider reports (PDMR notifications)",
189
+ AU: "Australian insider reports (Appendix 3Y)",
190
+ };
191
+
192
+ /** The country of a venue abroad that a company's insiders may report in (TSX is CA); null for a US venue or one not placed. */
193
+ export function venueHomeCountry(exchange: string | null | undefined): string | null {
194
+ return VENUE_COUNTRY[canonicalExchange(exchange ?? "")] ?? null;
195
+ }
196
+
197
+ /** A country's English name ("Japan"), or null when the runtime has none. */
198
+ function countryName(code: string): string | null {
199
+ try {
200
+ const name = new Intl.DisplayNames(["en"], { type: "region" }).of(code);
201
+ return name && name !== code ? name : null;
202
+ } catch {
203
+ // Intentional ignore: a runtime without region names leaves the home market unnamed.
204
+ return null;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * What a company from abroad's insiders report at home, which Form 4 data
210
+ * does not cover: "Canadian insider reports (SEDI) not covered." Where home
211
+ * is comes from the company's country (Gloom Cloud's issuer listings: RY in
212
+ * New York is Royal Bank of Canada), else its listing's venue abroad (RY:TSX).
213
+ * A foreign private issuer placed by neither gets its home market unnamed.
214
+ * Null for a US company, and for a US listing of a company that files as a
215
+ * domestic one, whose insiders file Form 4.
216
+ */
217
+ export function homeInsiderReportsNote({ country, exchange, foreignPrivateIssuer = false }: {
218
+ /** The company's country, ISO 3166 alpha-2, when known. */
219
+ country?: string | null;
220
+ /** The listing's venue. */
221
+ exchange?: string | null;
222
+ foreignPrivateIssuer?: boolean;
223
+ }): string | null {
224
+ const venueHome = venueHomeCountry(exchange) ?? "";
225
+ if (!venueHome && !foreignPrivateIssuer) return null;
226
+ // ZZ is the service's own word for a country it does not know.
227
+ const given = normalize(country ?? "");
228
+ const home = (given !== "ZZ" ? given : "") || venueHome;
229
+ if (home === "US") return null;
230
+ if (!home) return foreignPrivateIssuer ? "Home-market insider reports not covered." : null;
231
+ const named = HOME_INSIDER_REPORTS[home] ?? (EEA.has(home) ? "European insider reports (MAR managers' transactions)" : null);
232
+ if (named) return `${named} not covered.`;
233
+ const place = countryName(home);
234
+ return place ? `Insider reports filed in ${place} not covered.` : "Home-market insider reports not covered.";
235
+ }
236
+
166
237
  /**
167
238
  * The key an issuer read is cached and deduplicated under: the ticker as given
168
239
  * for a US listing or a symbol with no venue, SYMBOL:VENUE for a listing
@@ -14,6 +14,7 @@ const DEFAULT_REQUESTS_PER_MINUTE = 30;
14
14
  const DEFAULT_MAX_RETRIES = 2;
15
15
  const DEFAULT_TIMEOUT_MS = 10_000;
16
16
  const BACKOFF_BASE_MS = 1_000;
17
+ const DEFAULT_MAX_RETRY_WAIT_MS = 10_000;
17
18
 
18
19
  export interface ThrottledFetchOptions {
19
20
  /** Max requests per minute per host. Default: 30 */
@@ -24,6 +25,12 @@ export interface ThrottledFetchOptions {
24
25
  timeoutMs?: number;
25
26
  /** Initial retry backoff in ms. Default: 1000 */
26
27
  backoffBaseMs?: number;
28
+ /**
29
+ * Longest a retry waits, `Retry-After` included. A host that asks for more
30
+ * (a minute, say) gets no retry: the 429 or 5xx comes back as it is, so one
31
+ * rate-limited feed cannot hold its callers for that long. Default: 10000
32
+ */
33
+ maxRetryWaitMs?: number;
27
34
  /** Default headers applied to every request */
28
35
  defaultHeaders?: Record<string, string>;
29
36
  /** Share concurrent GET requests to the same URL. Default: true */
@@ -50,6 +57,7 @@ export function createThrottledFetch(
50
57
  const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
51
58
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
52
59
  const backoffBaseMs = options.backoffBaseMs ?? BACKOFF_BASE_MS;
60
+ const maxRetryWaitMs = options.maxRetryWaitMs ?? DEFAULT_MAX_RETRY_WAIT_MS;
53
61
  const defaultHeaders = options.defaultHeaders ?? {};
54
62
  const dedupeGetRequests = options.dedupeGetRequests ?? true;
55
63
  const fetchTransport =
@@ -190,10 +198,9 @@ export function createThrottledFetch(
190
198
  // Retry on 429 or 5xx
191
199
  if ((resp.status === 429 || resp.status >= 500) && retriesLeft > 0) {
192
200
  throwIfAborted(callerSignal);
193
- await wait(
194
- getBackoffMs(retriesLeft, resp.headers.get("retry-after")),
195
- callerSignal,
196
- );
201
+ const retryWaitMs = getBackoffMs(retriesLeft, resp.headers.get("retry-after"));
202
+ if (retryWaitMs > maxRetryWaitMs) return resp;
203
+ await wait(retryWaitMs, callerSignal);
197
204
  return executeRequest(url, init, retriesLeft - 1);
198
205
  }
199
206
 
@@ -0,0 +1,67 @@
1
+ import type { EarningsField, FinancialStatement } from "../types/financials";
2
+ import { areNearbyFinancialPeriodEnds } from "./financial-statements";
3
+ import { hasStatementGap } from "./statement-gaps";
4
+
5
+ const DAY_MS = 86_400_000;
6
+
7
+ /** A per-share amount the row reports: finite, not declared unavailable, and diluted EPS on a resolved share basis. */
8
+ function reportedEarnings(row: FinancialStatement, field: EarningsField): number | null {
9
+ if (hasStatementGap(row, field) || (field === "eps" && row.epsBasis?.status === "unresolved")) return null;
10
+ const value = row[field];
11
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
12
+ }
13
+
14
+ const consecutiveQuarters = (earlier: string, later: string) => {
15
+ const days = (Date.parse(later) - Date.parse(earlier)) / DAY_MS;
16
+ return days >= 60 && days <= 120;
17
+ };
18
+
19
+ /**
20
+ * The EPS of four consecutive quarters (oldest first) when one of them is a
21
+ * fiscal fourth quarter with no EPS of its own, as since 2021 a 10-K states
22
+ * the year but not its last quarter: the fiscal year's EPS, plus this fiscal
23
+ * year's quarters to date, less the same quarters a year earlier. The quarter
24
+ * itself is not given an EPS, since a year's EPS is not the sum of its
25
+ * quarters'. Null unless exactly one quarter lacks the line without declaring
26
+ * it unavailable, a fiscal year in the same currency ends with that quarter,
27
+ * and the year and its three earlier quarters report the line. Any input on
28
+ * an unresolved split basis, or two inputs on different share bases, leave it
29
+ * unknown. `inputs` are the rows the value reads, for its availability.
30
+ */
31
+ export function trailingEpsAcrossFiscalYearEnd(
32
+ window: readonly FinancialStatement[],
33
+ quarterlyStatements: readonly FinancialStatement[],
34
+ annualStatements: readonly FinancialStatement[],
35
+ field: EarningsField,
36
+ ): { value: number; inputs: FinancialStatement[] } | null {
37
+ if (window.length !== 4) return null;
38
+ const unreported = window.filter((row) => reportedEarnings(row, field) === null);
39
+ if (unreported.length !== 1) return null;
40
+ const fourth = unreported[0]!;
41
+ // A declared gap or an unverified share basis is a figure withheld, not one missing.
42
+ if (hasStatementGap(fourth, field) || (field === "eps" && fourth.epsBasis?.status === "unresolved")) return null;
43
+ const years = annualStatements.filter((row) => areNearbyFinancialPeriodEnds(row.date, fourth.date));
44
+ if (years.length !== 1 || years[0]!.currency !== fourth.currency) return null;
45
+ const year = years[0]!;
46
+ const yearValue = reportedEarnings(year, field);
47
+ if (yearValue === null) return null;
48
+
49
+ const sorted = [...quarterlyStatements].sort((a, b) => a.date.localeCompare(b.date));
50
+ const end = sorted.findIndex((row) => row.date === fourth.date);
51
+ const earlier = end < 3 ? [] : sorted.slice(end - 3, end);
52
+ if (earlier.length !== 3 || [...earlier, fourth].some((row, index, rows) => (
53
+ row.currency !== fourth.currency || (index > 0 && !consecutiveQuarters(rows[index - 1]!.date, row.date))
54
+ ))) return null;
55
+ // The window's quarters before the fourth are that year's last ones.
56
+ const position = window.indexOf(fourth);
57
+ if (window.slice(0, position).some((row, index) => row.date !== earlier[3 - position + index]!.date)) return null;
58
+
59
+ const after = window.slice(position + 1);
60
+ const before = earlier.slice(0, 3 - position);
61
+ const inputs = [year, ...before, ...after];
62
+ if ([...earlier, ...after].some((row) => reportedEarnings(row, field) === null)) return null;
63
+ const bases = new Set([...window, year, ...earlier].flatMap((row) => row.shareBasis ?? []));
64
+ if (bases.size > 1) return null;
65
+ const sum = (rows: readonly FinancialStatement[]) => rows.reduce((total, row) => total + reportedEarnings(row, field)!, 0);
66
+ return { value: yearValue + sum(after) - sum(before), inputs };
67
+ }