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
@@ -1,3 +1,5 @@
1
+ import { ApiRequestError } from "../../../api-client/errors";
2
+ import { logFailure } from "../../../utils/log-failure";
1
3
  /**
2
4
  * Map series in G: numbers over time for things with a place (chokepoint
3
5
  * transits, port calls). `GEO:<id or alias>` resolves through the server's
@@ -54,9 +56,20 @@ export async function resolveGeoChartSeries(
54
56
  viewport: ChartViewportSpec,
55
57
  signal?: AbortSignal,
56
58
  ): Promise<ResolvedSeries> {
57
- const hit = await resolveGeoSeries(request, token, signal).catch(() => null);
59
+ let indexFailed = false;
60
+ const hit = await resolveGeoSeries(request, token, signal).catch((error: unknown) => {
61
+ if (signal?.aborted) throw error;
62
+ logFailure("world-venue-map.geo-series", "Resolve map series failed")(error);
63
+ indexFailed = true;
64
+ return null;
65
+ });
58
66
  const payload = await getGeoSeries(request, hit?.id ?? token, geoSeriesRange(viewport), signal).catch((error: unknown) => {
59
- throw hit ? error : new Error(`No map series named ${token}.`);
67
+ if (signal?.aborted) throw error;
68
+ if (hit) throw error;
69
+ if (!indexFailed && error instanceof ApiRequestError && error.status === 404) {
70
+ throw new Error(`No map series named ${token}.`);
71
+ }
72
+ throw new Error("Map series unavailable.", { cause: error });
60
73
  });
61
74
  const points: TimeSeriesPoint[] = (payload.points ?? []).flatMap(([day, value]) => {
62
75
  const date = new Date(`${day}T00:00:00Z`);
@@ -83,7 +96,11 @@ export async function resolveGeoChartSeries(
83
96
 
84
97
  export function createGeoChartSeriesCapability(request: GeoRequest = cloudGeoRequest): ChartSeriesCapability {
85
98
  const search = async (query: string, limit: number, signal?: AbortSignal) => {
86
- const { series } = await searchGeoSeries(request, query, signal).catch(() => ({ series: [] as GeoSeriesSearchHit[] }));
99
+ const { series } = await searchGeoSeries(request, query, signal).catch((error: unknown) => {
100
+ if (signal?.aborted) throw error;
101
+ logFailure("world-venue-map.geo-series", "Search map series failed")(error);
102
+ throw new Error("Map series search unavailable.", { cause: error });
103
+ });
87
104
  return series.slice(0, limit).map(catalogItem);
88
105
  };
89
106
  return chartSeriesProvider({
@@ -118,6 +135,25 @@ const CHOKEPOINT_NAMES: Readonly<Record<string, string>> = {
118
135
  BOSPHORUS: "Bosphorus",
119
136
  };
120
137
 
138
+ /**
139
+ * The key straits as a legend of all six can carry them: the full series
140
+ * names ("Strait of Hormuz transits") would be cut to a fragment each. The
141
+ * chart's title and unit already say these are transits.
142
+ */
143
+ const CHOKEPOINT_SHORT_NAMES: Readonly<Record<string, string>> = {
144
+ SUEZ: "Suez",
145
+ PANAMA: "Panama",
146
+ HORMUZ: "Hormuz",
147
+ BABELMANDEB: "Bab el-Mandeb",
148
+ MALACCA: "Malacca",
149
+ BOSPHORUS: "Bosphorus",
150
+ };
151
+
152
+ /** A key strait's short legend name; undefined for any other series, which keeps the name the index gives it. */
153
+ export function chokepointShortName(alias: string): string | undefined {
154
+ return CHOKEPOINT_SHORT_NAMES[alias.toUpperCase()];
155
+ }
156
+
121
157
  /**
122
158
  * Who publishes the chokepoints' daily transits, as the server's chokepoints
123
159
  * layer names them (`publisher`); the catalog lists the key straits offline.
@@ -161,16 +197,59 @@ export async function chokepointSeriesIds(request: GeoRequest, signal?: AbortSig
161
197
  });
162
198
  }
163
199
 
200
+ /** A series' chokepoint as the index names it: "Strait of Hormuz transits" is "Strait of Hormuz". */
201
+ function chokepointName(seriesName: string): string {
202
+ return seriesName.replace(/\s+transits$/i, "");
203
+ }
204
+
205
+ /** A chokepoint's name without what straits and canals share ("Strait of Hormuz" is Hormuz, "Suez Canal" Suez), as it is typed short. */
206
+ function chokepointCore(name: string): string {
207
+ return compact(chokepointName(name)
208
+ .replace(/^strait of\s+/i, "")
209
+ .replace(/\s+(strait|canal|channel|passage)$/i, ""));
210
+ }
211
+
212
+ function matchIndexedChokepoint(series: readonly GeoSeriesSearchHit[], wanted: string): string | null {
213
+ const chokepoints = series.filter((hit) => hit.layer === CHOKEPOINTS_LAYER && hit.alias);
214
+ // A main transit series has the plain alias (SUEZ), its splits a suffix (SUEZ.TANKER).
215
+ const exact = chokepoints.find((hit) => compact(hit.alias!) === wanted);
216
+ if (exact) return exact.alias!;
217
+ const main = chokepoints.filter((hit) => !hit.alias!.includes("."));
218
+ const named = main.find((hit) => compact(chokepointName(hit.name)) === wanted || chokepointCore(hit.name) === wanted);
219
+ if (named) return named.alias!;
220
+ // Part of one strait's name ("mandeb") finds it; part of several ("strait") names none.
221
+ return main.length === 1 ? main[0]!.alias! : null;
222
+ }
223
+
224
+ function matchKeyChokepoint(wanted: string): string | null {
225
+ return KEY_CHOKEPOINTS.find((alias) => alias === wanted || compact(CHOKEPOINT_NAMES[alias] ?? "") === wanted
226
+ || chokepointCore(CHOKEPOINT_NAMES[alias] ?? "") === wanted) ?? null;
227
+ }
228
+
164
229
  /**
165
230
  * The series a typed chokepoint names: "suez", "Bab el-Mandeb" and "Strait
166
- * of Hormuz" all find the strait's main transit series.
231
+ * of Hormuz" all find the strait's main transit series. Null for text that
232
+ * names no chokepoint (a ticker), so CHOKE can say which exist. Without the
233
+ * series index, the key straits are still known by name.
167
234
  */
168
- export async function chokepointSeriesFor(request: GeoRequest, typed: string, signal?: AbortSignal): Promise<string> {
235
+ export async function chokepointSeriesFor(request: GeoRequest, typed: string, signal?: AbortSignal): Promise<string | null> {
169
236
  const wanted = compact(typed);
170
- const { series } = await searchGeoSeries(request, typed, signal).catch(() => ({ series: [] as GeoSeriesSearchHit[] }));
171
- const chokepoints = series.filter((hit) => hit.layer === CHOKEPOINTS_LAYER);
172
- const exact = chokepoints.find((hit) => hit.alias && compact(hit.alias) === wanted);
173
- // A main transit series has the plain alias (SUEZ), its splits a suffix (SUEZ.TANKER).
174
- const named = chokepoints.find((hit) => hit.alias && !hit.alias.includes("."));
175
- return exact?.alias ?? named?.alias ?? wanted;
237
+ if (!wanted) return null;
238
+ let series: GeoSeriesSearchHit[];
239
+ try {
240
+ ({ series } = await searchGeoSeries(request, typed, signal));
241
+ } catch (error) {
242
+ if (signal?.aborted) throw error;
243
+ logFailure("world-venue-map.geo-series", "Find chokepoint series failed")(error);
244
+ const known = matchKeyChokepoint(wanted);
245
+ if (known) return known;
246
+ throw new Error("Chokepoint search unavailable. Try again.", { cause: error });
247
+ }
248
+ return matchIndexedChokepoint(series, wanted) ?? matchKeyChokepoint(wanted);
249
+ }
250
+
251
+ /** What CHOKE tells text that names no chokepoint: the key straits, as it takes them typed. */
252
+ export function unknownChokepointMessage(typed: string): string {
253
+ const names = KEY_CHOKEPOINTS.map((alias) => (CHOKEPOINT_SHORT_NAMES[alias] ?? alias).toUpperCase());
254
+ return `No chokepoint named ${typed.trim()}. Try CHOKE ${names.slice(0, -1).join(", ")} or ${names.at(-1)}.`;
176
255
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `gloomberb fn MAP`: the venue list, or with a layer (`MAP ships`,
2
+ * `gloom fn MAP`: the venue list, or with a layer (`MAP ships`,
3
3
  * `--layer vessels`) that layer's entity table with plain values and the
4
4
  * linked tickers, for JSON and CSV.
5
5
  */
@@ -1,19 +1,15 @@
1
1
  import { peekGeoCatalog } from "../../../api-client/geo";
2
- import { SERIES_COLORS } from "../../../theme/series-colors";
3
2
  import { CHART_COMPOSER_PANE_ID } from "../../../types/config";
4
3
  import type { PaneTemplateDef } from "../../../types/plugin";
5
4
  import { chartHeadless } from "../chart-composer/headless";
6
- import { buildCustomChartPreset } from "../chart-composer/presets";
7
- import { formatGeoSeriesExpression } from "../chart-composer/series-expression";
8
5
  import type { PluginModule } from "../plugin-module";
6
+ import { createChokepointInstance } from "./chokepoint-chart";
9
7
  import { cloudGeoRequest } from "./client";
10
- import { chokepointSeriesFor, chokepointSeriesIds, CHOKEPOINT_SERIES, createGeoChartSeriesCapability, KEY_CHOKEPOINTS } from "./geo-series";
8
+ import { CHOKEPOINT_SERIES, createGeoChartSeriesCapability } from "./geo-series";
11
9
  import { mapHeadless } from "./headless";
12
10
  import { buildMapSettingsDef, groupTitle, MAP_PRESET_OPTIONS, parseMapPreset } from "./layers";
13
11
  import { WORLD_VENUE_MAP_PANE_ID, WorldVenueMapPane } from "./pane";
14
12
 
15
- const CHOKE_SERIES_LIMIT = 6;
16
-
17
13
  const chartComposerHeadless = chartHeadless("chart-composer-pane");
18
14
 
19
15
  const chokepointTemplate: PaneTemplateDef = {
@@ -37,26 +33,7 @@ const chokepointTemplate: PaneTemplateDef = {
37
33
  keywords: ["chokepoint", "chokepoints", "transits", "suez", "panama", "hormuz", "malacca", "shipping", "strait", "canal"],
38
34
  shortcut: { prefix: "CHOKE", argPlaceholder: "chokepoint", argKind: "text", argOptional: true },
39
35
  canCreate: () => true,
40
- createInstance: async (_context, options) => {
41
- const typed = options?.arg?.trim();
42
- const named = typed ? await chokepointSeriesFor(cloudGeoRequest, typed) : null;
43
- const ids = named
44
- ? [named]
45
- // Without the series index, CHOKE still asks for the key straits by name.
46
- : await chokepointSeriesIds(cloudGeoRequest).then((found) => (found.length ? found : KEY_CHOKEPOINTS)).catch(() => KEY_CHOKEPOINTS);
47
- const base = buildCustomChartPreset(ids.slice(0, CHOKE_SERIES_LIMIT).map(formatGeoSeriesExpression).join(", "));
48
- // Daily transits read best over a year, each strait in its own colour.
49
- const spec = {
50
- ...base,
51
- viewport: { ...base.viewport, range: "1Y" as const },
52
- series: base.series.map((series, index) => ({ ...series, color: series.color ?? SERIES_COLORS[index % SERIES_COLORS.length]! })),
53
- };
54
- return {
55
- title: named ? `G ${named} transits` : "G Chokepoint transits",
56
- placement: "floating",
57
- settings: { chartSpec: spec },
58
- };
59
- },
36
+ createInstance: (_context, options) => createChokepointInstance(cloudGeoRequest, options?.arg),
60
37
  };
61
38
 
62
39
  export const worldVenueMapModule: PluginModule = {
@@ -163,7 +163,7 @@ const LAYER_COVERAGE: Readonly<Record<string, string>> = {
163
163
 
164
164
  /**
165
165
  * Why a layer can have nothing for a search or a view outside its region:
166
- * "Terminals cover the United States only (as of 31 Aug 2023)." Null for a
166
+ * "Terminals cover the United States only (as of 1 Oct 2026)." Null for a
167
167
  * worldwide layer.
168
168
  */
169
169
  export function layerCoverageNote(layer: Pick<GeoLayerInfo, "id" | "name" | "asOf" | "cadence">): string | null {
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../../../utils/log-failure";
1
2
  import { createElement, useCallback, useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent } from "react";
2
3
  import type { CloudWorldVenuePayload } from "../../../api-client";
3
4
  import { Box, ChartSurface, Text, useNativeRenderer, useUiCapabilities, useUiHost } from "../../../ui";
@@ -399,7 +400,7 @@ function useCountryLabels(): readonly CountryLabel[] | null {
399
400
  let current = true;
400
401
  loadCountryLabels().then((loaded) => {
401
402
  if (current) setLabels(loaded);
402
- }, () => {});
403
+ }, logFailure("world-venue-map.map", "Load country labels failed"));
403
404
  return () => {
404
405
  current = false;
405
406
  };
@@ -21,6 +21,7 @@ import { useAsyncResource, usePaneSettingValue, usePluginPaneState } from "../..
21
21
  import { colors } from "../../../theme/colors";
22
22
  import type { PaneProps } from "../../../types/plugin";
23
23
  import { Box, TextAttributes, useUiCapabilities } from "../../../ui";
24
+ import { withCountryFlag } from "../../../components/ui/country-flag";
24
25
  import { loadWorldVenues } from "./client";
25
26
  import { WORLD_VENUE_MAP_PANE_ID } from "./ids";
26
27
  import { LayeredMapView } from "./layered-pane";
@@ -160,11 +161,12 @@ function VenueMapView({ focused, width, height }: PaneProps) {
160
161
  case "mic":
161
162
  return { text: venue.mic, color: colors.textBright, attributes: TextAttributes.BOLD };
162
163
  case "name":
163
- return { text: venue.title, color: colors.textMuted };
164
+ // The desktop and the web lead each venue with its country's flag.
165
+ return withCountryFlag({ text: venue.title, color: colors.textMuted }, venue.countryCode, nativePaneChrome === true);
164
166
  case "time":
165
167
  return { text: formatVenueLocalTime(venue.timezone, now), color: colors.textDim };
166
168
  }
167
- }, [now]);
169
+ }, [nativePaneChrome, now]);
168
170
 
169
171
  const sidebarHeader = (
170
172
  <QueryBar
@@ -1,6 +1,7 @@
1
1
  import type { CloudWorldVenuePayload } from "../../../api-client";
2
2
  import { colors } from "../../../theme/colors";
3
- import { Box, Text, TextAttributes } from "../../../ui";
3
+ import { CountryFlagLead } from "../../../components/ui/country-flag";
4
+ import { Box, Text, TextAttributes, useUiCapabilities } from "../../../ui";
4
5
  import { formatVenueCountdown, formatVenueLocalTime, venueRemainingSeconds } from "./model";
5
6
 
6
7
  export function SelectedVenueHeader({
@@ -14,19 +15,29 @@ export function SelectedVenueHeader({
14
15
  now: number;
15
16
  width: number;
16
17
  }) {
18
+ const desktop = useUiCapabilities().nativePaneChrome === true;
17
19
  if (!venue) return <Box height={2} />;
18
20
  const remaining = formatVenueCountdown(venueRemainingSeconds(venue, checkedAt, now));
19
21
  const state = venue.isOpen ? "OPEN" : "CLOSED";
20
22
  const transition = remaining ? `${venue.isOpen ? "closes" : "opens"} ${remaining}` : "";
23
+ const place = (
24
+ <Text fg={colors.textMuted}>
25
+ {`${venue.city}, ${venue.country} · ${formatVenueLocalTime(venue.timezone, now)}${transition ? ` · ${transition}` : ""}`}
26
+ </Text>
27
+ );
21
28
  return (
22
29
  <Box flexDirection="column" height={2} width={width} paddingX={1}>
23
30
  <Box flexDirection="row" justifyContent="space-between" width="100%">
24
31
  <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{venue.mic}</Text>
25
32
  <Text fg={venue.isOpen ? colors.positive : colors.textDim}>{state}</Text>
26
33
  </Box>
27
- <Text fg={colors.textMuted}>
28
- {`${venue.city}, ${venue.country} · ${formatVenueLocalTime(venue.timezone, now)}${transition ? ` · ${transition}` : ""}`}
29
- </Text>
34
+ {desktop ? (
35
+ // The desktop and the web lead the venue's place with its country's flag.
36
+ <Box flexDirection="row" alignItems="center">
37
+ <CountryFlagLead value={venue.countryCode} />
38
+ {place}
39
+ </Box>
40
+ ) : place}
30
41
  </Box>
31
42
  );
32
43
  }
@@ -0,0 +1,25 @@
1
+ import { apiClient } from "../../../api-client";
2
+ import type { SwapCurvePayload, SwapTenor } from "../../../api-client/swap-curve";
3
+ import { createPluginCache } from "../../../data/plugin-cache";
4
+ import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
5
+ import { validateSwapCurve } from "./swaps-model";
6
+
7
+ export const swapCurveCache = createPluginCache<SwapCurvePayload>({
8
+ kind: "sofr-swap-curve", source: "gloom-cloud", schemaVersion: 1,
9
+ policy: { staleMs: 60 * 60_000, expireMs: 7 * 24 * 60 * 60_000 },
10
+ });
11
+
12
+ export async function fetchSwapCurve(tenor: SwapTenor,
13
+ client: Pick<typeof apiClient, "getCloudSwapCurve"> = apiClient): Promise<SwapCurvePayload> {
14
+ try {
15
+ const payload = validateSwapCurve(await client.getCloudSwapCurve(tenor, 365));
16
+ if (payload.history.tenor !== tenor) throw new Error("The server returned history for a different swap tenor");
17
+ return payload;
18
+ } catch (error) {
19
+ throw unavailableOnServer(error, "SOFR swap rates are not available yet.");
20
+ }
21
+ }
22
+
23
+ export const getCachedSwapCurve = (tenor: SwapTenor) => cachedCloudResource(swapCurveCache, tenor, validateSwapCurve);
24
+ export const loadSwapCurve = (tenor: SwapTenor, force = false) =>
25
+ loadCloudResource(swapCurveCache, tenor, () => fetchSwapCurve(tenor), { force, validate: validateSwapCurve });
@@ -5,7 +5,7 @@ import type { MoveHorizon } from "../shared/move-rank";
5
5
  import type { CurveBasis } from "./forward";
6
6
  import { yieldSpreads, type YieldLookbackCurves } from "./chart";
7
7
  import { completeYieldCurve, loadHistoricalYieldCurve, yieldCurveDate } from "./history";
8
- import { DATE_OPTION_FORMATS, readCalendarDate } from "../../../utils/calendar-date";
8
+ import { DATE_OPTION_FORMATS, dateOptionHint, readCalendarDate } from "../../../utils/calendar-date";
9
9
  import { isYieldObservationDate, loadYieldCurve, type YieldPoint } from "./treasury-data";
10
10
 
11
11
  export type CurveId = CloudCurveId;
@@ -178,7 +178,7 @@ export async function loadComparePoints(curve: CurveId, date: string, client: Cu
178
178
  }
179
179
 
180
180
  const RELATIVE = /^(\d{1,2})([DWMY])$/i;
181
- const COMPARE_HELP = `Compare with a date as ${DATE_OPTION_FORMATS}, or a span such as 1D, 1W, 3M or 1Y.`;
181
+ const COMPARE_FORMS = `Compare with a date as ${DATE_OPTION_FORMATS}, or a span such as 1D, 1W, 3M or 1Y`;
182
182
 
183
183
  /** A span back from the session (1D, 1W, 3M, 1Y): what the compare field takes besides a date. */
184
184
  export function isCompareSpan(value: string): boolean {
@@ -191,12 +191,15 @@ export function isCompareSpan(value: string): boolean {
191
191
  * session (1D, 1W, 3M, 1Y). Empty turns comparing off. Anything else gets the
192
192
  * help for both forms, not the as-of field's date-only message.
193
193
  */
194
- export function parseCompareInput(value: string, now = new Date()): string {
194
+ export function parseCompareInput(value: string, now = new Date(), option = ""): string {
195
195
  const text = value.trim();
196
196
  if (!text) return "";
197
197
  if (isCompareSpan(text)) return text.toUpperCase();
198
198
  const date = readCalendarDate(text);
199
- if (!date) throw new Error(COMPARE_HELP);
199
+ if (!date) {
200
+ const hint = dateOptionHint(text, option);
201
+ throw new Error(hint ? `${COMPARE_FORMS}: ${hint}.` : `${COMPARE_FORMS}.`);
202
+ }
200
203
  return yieldCurveDate(date, now);
201
204
  }
202
205
 
@@ -12,6 +12,9 @@ import { loadYieldCurveLookbacks, yieldCurveDate } from "./history";
12
12
  import { loadWorldRows, type WorldRow } from "./world";
13
13
  import { formatBasisPoints, toBasisPoints } from "../../../utils/basis-points";
14
14
  import { DATE_OPTION_EXAMPLES } from "../../../utils/calendar-date";
15
+ import { SWAP_TENORS, type SwapCurvePayload, type SwapTenor } from "../../../api-client/swap-curve";
16
+ import { fetchSwapCurve } from "./client";
17
+ import { formatSwapBp, selectedSwapTenor } from "./swaps-model";
15
18
 
16
19
  const percent = (value: unknown) => value == null ? "-" : `${Number(value).toFixed(2)}%`;
17
20
  /** A move already in basis points: `+3bp`, `-1bp`, `0bp`. */
@@ -65,6 +68,14 @@ const WORLD_COLUMNS = [
65
68
  { key: "tenYearChangeBasisPoints", header: "10Y 1D bp", align: "right" as const, format: basisPoints },
66
69
  { key: "asOf", header: "As of" },
67
70
  ];
71
+ const SWAP_COLUMNS = [
72
+ { key: "tenor", header: "Tenor" },
73
+ { key: "rate", header: "Rate %", align: "right" as const, format: percent },
74
+ { key: "changeBp", header: "1D bp", align: "right" as const, format: (value: unknown) => value == null ? "-" : formatSwapBp(Number(value)) },
75
+ { key: "spreadBp", header: "Spread bp", align: "right" as const, format: (value: unknown) => value == null ? "-" : formatSwapBp(Number(value)) },
76
+ { key: "tradeCount", header: "Trades", align: "right" as const,
77
+ format: (value: unknown, row: Record<string, unknown>) => `${value}${row.quality === "thin" ? " Thin" : ""}` },
78
+ ];
68
79
 
69
80
  export interface YieldCurveHeadlessDependencies {
70
81
  load(curve: CurveId, requestedDate: string, context: HeadlessPaneContext): Promise<CurveData>;
@@ -72,6 +83,7 @@ export interface YieldCurveHeadlessDependencies {
72
83
  /** The 1D, 1W and 1M curves of a session, for a curve loaded without them (the FRED fallback). */
73
84
  lookbacks?(session: string, context: HeadlessPaneContext): Promise<YieldLookbackCurves>;
74
85
  world?(context: HeadlessPaneContext): Promise<WorldRow[]>;
86
+ swaps?(tenor: SwapTenor, context: HeadlessPaneContext): Promise<SwapCurvePayload>;
75
87
  }
76
88
 
77
89
  const defaultDependencies: Required<YieldCurveHeadlessDependencies> = {
@@ -82,6 +94,7 @@ const defaultDependencies: Required<YieldCurveHeadlessDependencies> = {
82
94
  .map((lookback) => [lookback.id, lookback.points]),
83
95
  ),
84
96
  world: (context) => loadWorldRows(context.apiClient),
97
+ swaps: (tenor, context) => fetchSwapCurve(tenor, context.apiClient),
85
98
  };
86
99
 
87
100
  const bp = (value: number | null) => value == null ? null : Math.round(value * 100);
@@ -116,6 +129,7 @@ export function createYieldCurveHeadless(
116
129
  const loadCompare = dependencies.compare ?? defaultDependencies.compare;
117
130
  const loadLookbacks = dependencies.lookbacks ?? defaultDependencies.lookbacks;
118
131
  const loadWorld = dependencies.world ?? defaultDependencies.world;
132
+ const loadSwaps = dependencies.swaps ?? defaultDependencies.swaps;
119
133
  return {
120
134
  shape: "rows",
121
135
  argument: { kind: "free-text", optional: true, placeholder: "[curve] [date]",
@@ -129,14 +143,21 @@ export function createYieldCurveHeadless(
129
143
  pluginState: { pluginId: "macro", key: "yield-curve:view" } },
130
144
  { key: "forward", type: "boolean", description: "Overlay the one-year-forward curve on the chart.",
131
145
  pluginState: { pluginId: "macro", key: "yield-curve:forward" } },
132
- { key: "tab", type: "enum", values: [{ value: "curve" }, { value: "world" }],
133
- description: "curve (default), or world: each market's 2Y, 10Y, 30Y and 2s10s on its own latest session.",
146
+ { key: "tab", type: "enum", values: [{ value: "curve" }, { value: "world" }, { value: "swaps" }],
147
+ description: "curve (default), world (government yields), or swaps (USD SOFR rates and Treasury swap spreads). Spreads use native quotes: annual ACT/360 OIS versus semiannual bond-equivalent Treasury yields.",
134
148
  pluginState: { pluginId: "macro", key: "yield-curve:tab" } },
149
+ { key: "tenor", type: "enum", values: SWAP_TENORS.map((value) => ({ value })),
150
+ description: "Swaps history tenor (default 10Y); the curve table always includes every tenor.",
151
+ pluginState: { pluginId: "macro", key: "yield-curve:swap-tenor" } },
152
+ { key: "swap-view", type: "enum", values: [{ value: "curve" }, { value: "history" }],
153
+ description: "Swaps chart: current curve with 1W/1M comparisons, or the selected tenor's history.",
154
+ pluginState: { pluginId: "macro", key: "yield-curve:swap-view" } },
135
155
  ],
136
156
  columns: COLUMNS,
137
157
  // The title names the curve, as the pane's Curve select does.
138
158
  describe: (args) => {
139
159
  if (args.options.tab === "world") return "Yield Curves | World";
160
+ if (args.options.tab === "swaps") return "Yield Curves | USD SOFR Swaps";
140
161
  try {
141
162
  const curve = curveIdOf(args.options.curve) ?? parseCurveArgument(args.argument).curve ?? "us";
142
163
  return `Yield Curves | ${curveOption(curve).label}`;
@@ -145,6 +166,19 @@ export function createYieldCurveHeadless(
145
166
  }
146
167
  },
147
168
  async load(args, context) {
169
+ if (args.options.tab === "swaps") {
170
+ const data = await loadSwaps(selectedSwapTenor(args.options.tenor), context);
171
+ return {
172
+ freshness: { source: "DTCC public swap reports", status: "not-a-feed", basis: "daily trade estimates", cadence: "daily",
173
+ tradingDayMarket: "US" },
174
+ columns: SWAP_COLUMNS,
175
+ rows: (data.asOf ? data.points : []).map((point) => ({ ...point, asOf: data.asOf })),
176
+ metadata: { currency: data.currency, benchmark: data.benchmark, units: "Percent", changeUnit: "Basis points",
177
+ asOf: data.asOf, fetchedAt: data.fetchedAt, stale: data.stale, previousDate: data.previousDate,
178
+ treasuryDate: data.treasuryDate, comparisons: data.comparisons, history: data.history },
179
+ errors: !data.asOf ? ["No swap rates yet."] : data.stale ? ["The swap curve is stale."] : [],
180
+ };
181
+ }
148
182
  if (args.options.tab === "world") {
149
183
  const rows = await loadWorld(context);
150
184
  return {
@@ -161,8 +195,8 @@ export function createYieldCurveHeadless(
161
195
  throw new Error(`Unknown curve ${String(args.options.curve)}; use ${CURVE_OPTIONS.map((option) => option.aliases[0]).join(", ")}.`);
162
196
  }
163
197
  const curve = optionCurve ?? argument.curve ?? "us";
164
- const requestedDate = args.options.date != null ? yieldCurveDate(args.options.date) : argument.date;
165
- const compareInput = args.options.compare == null ? "" : parseCompareInput(String(args.options.compare));
198
+ const requestedDate = args.options.date != null ? yieldCurveDate(args.options.date, new Date(), "--date") : argument.date;
199
+ const compareInput = args.options.compare == null ? "" : parseCompareInput(String(args.options.compare), new Date(), "--compare");
166
200
  const data = await dependencies.load(curve, requestedDate, context);
167
201
  const points = data.points;
168
202
  const session = curveAsOf(points);
@@ -1,6 +1,6 @@
1
1
  import { apiClient, type CloudFredSeriesPayload } from "../../../api-client";
2
2
  import { isYieldObservationDate, TREASURY_MATURITIES, type YieldPoint } from "./treasury-data";
3
- import { DATE_OPTION_FORMATS, readCalendarDate } from "../../../utils/calendar-date";
3
+ import { dateOptionError, dateOptionFormatsOr, readCalendarDate } from "../../../utils/calendar-date";
4
4
 
5
5
  const DAY_MS = 86_400_000;
6
6
  export type TreasurySeriesLoader = (seriesId: string, options: {
@@ -10,12 +10,13 @@ export type TreasurySeriesLoader = (seriesId: string, options: {
10
10
  sortOrder: "desc";
11
11
  }) => Promise<CloudFredSeriesPayload>;
12
12
 
13
- export function yieldCurveDate(value: unknown, now = new Date()): string {
13
+ /** An as-of date, or "latest" (empty) for the newest session; `option` is how a command line types it (`--date`), empty for the pane's field. */
14
+ export function yieldCurveDate(value: unknown, now = new Date(), option = ""): string {
14
15
  if (value == null) return "";
15
16
  const text = typeof value === "string" ? value.trim() : "";
16
17
  if (typeof value === "string" && (text === "" || text.toLowerCase() === "latest")) return "";
17
18
  const date = readCalendarDate(text);
18
- if (!date) throw new Error(`Use an as-of date as ${DATE_OPTION_FORMATS}, or latest.`);
19
+ if (!date) throw new Error(dateOptionError(text, option, { now, subject: "an as-of date", formats: dateOptionFormatsOr("latest") }));
19
20
  if (date > now.toISOString().slice(0, 10)) throw new Error("A Treasury curve cannot use a future date.");
20
21
  return date;
21
22
  }
@@ -35,9 +35,11 @@ import { forwardCurve, type CurveBasis } from "./forward";
35
35
  import { loadYieldCurveLookbacks, yieldCurveDate } from "./history";
36
36
  import { curveAsOf, isYieldObservationDate, type YieldPoint, yieldCurveErrors } from "./treasury-data";
37
37
  import { loadWorldRows, type WorldRow } from "./world";
38
+ import { SwapsTab } from "./swaps";
39
+ import { swapCurveCache } from "./client";
38
40
 
39
- type GcTab = "curve" | "world";
40
- const TABS = [{ label: "Curve", value: "curve" }, { label: "World", value: "world" }];
41
+ type GcTab = "curve" | "world" | "swaps";
42
+ const TABS = [{ label: "Curve", value: "curve" }, { label: "World", value: "world" }, { label: "Swaps", value: "swaps" }];
41
43
  const EMPTY_POINTS: YieldPoint[] = [];
42
44
  /** Axis word for the loaded curve's basis. */
43
45
  const BASIS_LABEL: Record<CurveBasis, string> = {
@@ -129,13 +131,15 @@ export function YieldCurvePane({ focused, width, height }: PaneProps) {
129
131
  return (
130
132
  <Box flexDirection="column" width={width} height={height}>
131
133
  {strip}
132
- {(["curve", "world"] as const).map((id) => mounted.has(id) ? (
134
+ {(["curve", "world", "swaps"] as const).map((id) => mounted.has(id) ? (
133
135
  <Box key={id} visible={tab === id} flexDirection="column" width={width} height={bodyHeight} flexGrow={1} flexBasis={0}
134
136
  overflow="hidden">
135
137
  <PaneFooterScope active={tab === id}>
136
138
  {id === "curve"
137
139
  ? <CurveTab curve={curve} onCurveChange={setCurve} focused={focused && tab === "curve"} width={width} height={bodyHeight} />
138
- : <WorldTab onOpen={openCurve} focused={focused && tab === "world"} width={width} height={bodyHeight} />}
140
+ : id === "world"
141
+ ? <WorldTab onOpen={openCurve} focused={focused && tab === "world"} width={width} height={bodyHeight} />
142
+ : <SwapsTab focused={focused && tab === "swaps"} width={width} height={bodyHeight} />}
139
143
  </PaneFooterScope>
140
144
  </Box>
141
145
  ) : null)}
@@ -461,6 +465,8 @@ function WorldTab({ onOpen, focused, width, height }: {
461
465
  }
462
466
 
463
467
  export const yieldCurveModule: PluginModule = {
468
+ setup(ctx) { swapCurveCache.attach(ctx.persistence); },
469
+ dispose() { swapCurveCache.reset(); },
464
470
  series: [...TREASURY_SERIES, ...GOVERNMENT_BOND_SERIES],
465
471
  panes: [{
466
472
  id: "yield-curve",
@@ -475,9 +481,9 @@ export const yieldCurveModule: PluginModule = {
475
481
  id: "yield-curve-pane",
476
482
  paneId: "yield-curve",
477
483
  label: "Yield Curve",
478
- description: "Government bond curves: US Treasury, TIPS real, breakevens, euro AAA, Bund, Gilt, JGB and Canada, with a world grid.",
484
+ description: "Government bond curves, a world grid, and USD SOFR swap rates, swap spreads and tenor history.",
479
485
  keywords: ["yield", "curve", "treasury", "bonds", "rates", "gc", "interest", "tips", "breakeven", "bund", "gilt", "jgb",
480
- "sovereign", "world", "forward"],
486
+ "sovereign", "world", "forward", "swaps", "sofr", "ois", "swap spread"],
481
487
  shortcut: { prefix: "GC", argPlaceholder: "[curve] [date]", argKind: "text", argOptional: true },
482
488
  headless: yieldCurveHeadless,
483
489
  createInstance: (_context, options) => {
@@ -0,0 +1,93 @@
1
+ import { SWAP_TENORS, type SwapCurvePayload, type SwapPoint, type SwapTenor } from "../../../api-client/swap-curve";
2
+ import type { CurveSeries } from "../../../components/chart/curve";
3
+ import { staticSeries } from "../../../components/chart/static/series";
4
+
5
+ const finite = (value: unknown) => value === null || typeof value === "number" && Number.isFinite(value);
6
+ const count = (value: number) => Number.isInteger(value) && value >= 0;
7
+ const date = (value: unknown) => value === null || typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)
8
+ && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
9
+ const quality = (value: unknown) => value === "ok" || value === "thin" || value === "missing";
10
+
11
+ /** Reject incompatible observations before they replace the last usable cache. */
12
+ export function validateSwapCurve(payload: SwapCurvePayload): SwapCurvePayload {
13
+ const invalid = () => { throw new Error("The server returned invalid SOFR swap observations"); };
14
+ if (!payload || payload.currency !== "USD" || payload.benchmark !== "SOFR" || typeof payload.stale !== "boolean"
15
+ || !date(payload.asOf) || !date(payload.previousDate) || !date(payload.treasuryDate)
16
+ || !payload.comparisons?.week || !payload.comparisons.month || !payload.history
17
+ || !SWAP_TENORS.includes(payload.history.tenor) || !Array.isArray(payload.history.points)) return invalid();
18
+ for (const snapshot of [{ date: payload.asOf, points: payload.points }, payload.comparisons.week, payload.comparisons.month]) {
19
+ if (!date(snapshot.date) || !Array.isArray(snapshot.points)
20
+ || new Set(snapshot.points.map((point) => point.tenor)).size !== snapshot.points.length) return invalid();
21
+ for (const point of snapshot.points) {
22
+ if (!SWAP_TENORS.includes(point.tenor) || point.years !== Number.parseInt(point.tenor)
23
+ || !finite(point.rate) || !finite(point.changeBp) || !finite(point.spreadBp) || !finite(point.treasuryRate)
24
+ || !count(point.tradeCount) || !count(point.candidateCount) || !count(point.excludedCount)
25
+ || !Number.isFinite(point.notionalUsd) || point.notionalUsd < 0 || !quality(point.quality)
26
+ || point.rate != null && (snapshot.date == null || point.tradeCount < 3 || point.quality === "missing" || point.quality === "ok" && point.tradeCount < 5)
27
+ || point.rate == null && (point.changeBp != null || point.spreadBp != null)
28
+ || point.tenor === "15Y" && point.spreadBp != null) return invalid();
29
+ }
30
+ }
31
+ if (payload.points.length !== SWAP_TENORS.length) return invalid();
32
+ let previous = "";
33
+ for (const point of payload.history.points) {
34
+ if (point.date == null || !date(point.date) || point.date <= previous || !finite(point.rate) || !finite(point.spreadBp)
35
+ || !count(point.tradeCount) || !quality(point.quality)
36
+ || point.rate != null && (point.tradeCount < 3 || point.quality === "missing" || point.quality === "ok" && point.tradeCount < 5)
37
+ || point.rate == null && point.spreadBp != null
38
+ || payload.history.tenor === "15Y" && point.spreadBp != null) return invalid();
39
+ previous = point.date;
40
+ }
41
+ return payload;
42
+ }
43
+
44
+ export function selectedSwapTenor(value: unknown): SwapTenor {
45
+ return SWAP_TENORS.includes(value as SwapTenor) ? value as SwapTenor : "10Y";
46
+ }
47
+
48
+ const curvePoints = (points: readonly SwapPoint[]) => points.map((point) => ({
49
+ id: point.tenor, label: point.tenor, x: point.years, value: point.rate,
50
+ }));
51
+
52
+ /** Missing tenors remain null nodes, which break the line instead of bridging the gap. */
53
+ export function swapCurveSeries(payload: SwapCurvePayload,
54
+ colors: { current: string; thin: string; ghosts: Readonly<Record<string, string>> }): CurveSeries[] {
55
+ const series: CurveSeries[] = [
56
+ { id: "sofr", label: "SOFR", role: "primary", color: colors.current, points: curvePoints(payload.points) },
57
+ ...(["week", "month"] as const).flatMap((key): CurveSeries[] => {
58
+ const snapshot = payload.comparisons[key];
59
+ const id = key === "week" ? "1W" : "1M";
60
+ return snapshot.date == null ? [] : [{ id, label: id, role: "ghost", asOf: snapshot.date,
61
+ color: colors.ghosts[id], points: curvePoints(snapshot.points) }];
62
+ }),
63
+ ];
64
+ // The shared marker layer identifies thin observations on every dated curve.
65
+ // Keep their values in the main series so selection/readouts still show them.
66
+ const snapshots = [payload.points, ...(["week", "month"] as const)
67
+ .filter((key) => payload.comparisons[key].date != null).map((key) => payload.comparisons[key].points)];
68
+ return [...series, ...series.flatMap((entry, index): CurveSeries[] => {
69
+ const points = curvePoints(snapshots[index]!.filter((point) => point.quality === "thin" && point.rate != null));
70
+ return points.length ? [{ id: `${entry.id}-thin`, label: "Thin", role: "marker", color: colors.thin, points }] : [];
71
+ })];
72
+ }
73
+
74
+ /** Preserve missing sessions and their null values on the calendar axis. */
75
+ export function swapHistorySeries(history: SwapCurvePayload["history"], color: string, thinColor: string) {
76
+ const observation = (point: SwapCurvePayload["history"]["points"][number], thin: boolean) => ({
77
+ date: new Date(point.date), observedAt: new Date(point.date),
78
+ value: (point.quality === "thin") === thin ? point.rate : null,
79
+ });
80
+ return [
81
+ staticSeries(history.points.map((point) => observation(point, false)),
82
+ { id: `sofr-${history.tenor}`, label: `${history.tenor} SOFR`, color, calendarSpaced: true }),
83
+ ...(history.points.some((point) => point.quality === "thin" && point.rate != null)
84
+ ? [staticSeries(history.points.map((point) => observation(point, true)),
85
+ { id: `sofr-${history.tenor}-thin`, label: "Thin", color: thinColor, style: "points", calendarSpaced: true })] : []),
86
+ ];
87
+ }
88
+
89
+ /** Already in basis points: never multiply a swap change or spread by 100 again. */
90
+ export function formatSwapBp(value: number): string {
91
+ const rounded = Math.round(value * 10) / 10;
92
+ return `${rounded > 0 ? "+" : ""}${rounded.toFixed(1)}bp`;
93
+ }