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
@@ -5,7 +5,7 @@ import { curveStrip, curveSurfaceMinRows } from "../../../components/chart/curve
5
5
  import { isAccessDenied } from "../../../api-client/errors";
6
6
  import type { FuturesContract } from "../../../api-client/futures-curve";
7
7
  import { futuresTickText } from "../../../market-data/futures-contract";
8
- import { useAsyncResource, usePaneSettingValue, usePluginPaneState, useShortcut } from "../../../public/react";
8
+ import { useAssetData, useAsyncResource, usePaneSettingValue, usePluginPaneState, useShortcut } from "../../../public/react";
9
9
  import { usePaneInstance, usePaneTitle } from "../../../state/app/context";
10
10
  import { useThemeColors } from "../../../theme/theme-context";
11
11
  import type { PaneProps } from "../../../types/plugin";
@@ -16,8 +16,8 @@ import { useAutoRefresh } from "../../../react/auto-refresh";
16
16
  import { futuresSessionRefreshInterval } from "../shared/futures-session";
17
17
  import { useResearchCloudSession } from "../shared/research-cloud-session";
18
18
  import { useQuoteBoard } from "../shared/use-quote-board";
19
- import { getCachedFuturesCurve, loadFuturesCurve, loadFuturesCurveAsOf } from "./client";
20
- import { basisSpotSymbol, curveAsOfDate, curveAxisPrice, curveBasisPercent, curveBasisRows, curveChangeText, curveContractChanges, curveContractCode, curvePrice, curveRank, curveSpot, curveSpotLabel, curveSpotSource, curveSpreadLabel, curveTimestamp, curveTitle, curveUnitLabel, DEFAULT_CURVE_HORIZON, futuresCurveSeries, newestQuote, normalizeCurveRoot, paneCurveRoot, sortCurveContracts, thinContractCount, thinContractsNotice, unsupportedCurveRootMessage, type CurveContractChanges, type CurveSpot } from "./model";
19
+ import { getCachedFuturesCurve, loadCurveSpotBars, loadFuturesCurve, loadFuturesCurveAsOf } from "./client";
20
+ import { BASIS_CONVENTION, basisSpotSymbol, blankBasisNote, curveAsOfDate, curveAxisPrice, curveBasisPercent, curveBasisRows, curveChangeText, curveContractChanges, curveContractCode, curvePrice, curveRank, curveSpot, curveSpotLabel, curveSpotSource, curveSpreadBasis, curveSpreadLabel, curveTimestamp, curveTitle, curveUnitLabel, DEFAULT_CURVE_HORIZON, futuresCurveSeries, newestQuote, noTradeChangeNote, normalizeCurveRoot, paneCurveRoot, shortDatedBasis, shortDatedBasisNote, sortCurveContracts, thinSpreadNotice, unsupportedCurveRootMessage, type CurveContractChanges, type CurveSpot } from "./model";
21
21
 
22
22
  const TABS = [{ value: "curve", label: "Curve" }, { value: "contracts", label: "Contracts" }];
23
23
  // One column order for every root. SETTLE is the exchange settlement for the
@@ -61,6 +61,8 @@ const BASIS_CURVE_COLUMNS: DataTableColumn[] = [
61
61
  ...CURVE_COLUMNS.slice(4),
62
62
  ];
63
63
  const BASIS_DROP_ORDER = ["percentile", "volume", "change1m", "oi", "settle", "change1w", "vsSpot", "expiry"];
64
+ // Inside a week of expiry Vs Spot is the figure to read, so a curve with such a contract keeps it.
65
+ const SHORT_DATED_DROP_ORDER = BASIS_DROP_ORDER.filter((id) => id !== "vsSpot");
64
66
  const NO_SYMBOLS: string[] = [];
65
67
  /** The spot's age is judged against the clock, so a quote that stops moving still goes stale on screen. */
66
68
  const SPOT_CLOCK_MS = 30_000;
@@ -71,8 +73,12 @@ const contractKey = (row: FuturesContract) => row.symbol;
71
73
  const contractDate = (row: FuturesContract) => new Date(row.expiration);
72
74
  const sentenceCase = (text: string) => text.charAt(0).toUpperCase() + text.slice(1);
73
75
 
74
- /** The footer's spot, naming the quote's own listing and feed; a narrow footer gives those up before the price and its time. */
76
+ /**
77
+ * The footer's latest spot, naming what the quote is and its feed; a narrow footer gives those up before the price
78
+ * and its time. Each basis reads the spot in its own trade's minute, so a quote gone stale only says so.
79
+ */
75
80
  function spotSegment(spot: CurveSpot, root: string, clock: number): PaneFooterSegment {
81
+ if (spot.status !== "ok") return { id: "spot", parts: [{ text: spot.reason ?? `no ${spot.symbol} quote`, tone: "warning" }] };
76
82
  const label = curveSpotLabel(spot, root, clock);
77
83
  const source = curveSpotSource(spot);
78
84
  return { id: "spot", parts: [{ text: source ? `${label} (${source})` : label, tone: "muted" }], ...source ? { variants: [[{ text: label, tone: "muted" }]] } : {} };
@@ -128,6 +134,12 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
128
134
  return () => clearInterval(timer);
129
135
  }, [basisActive]);
130
136
  const spotState = spotSymbol ? spotQuotes.get(spotSymbol) : undefined;
137
+ // Each trade is read against the spot's one-minute bar of its own minute, fetched again whenever the curve is.
138
+ const assetData = useAssetData();
139
+ const barsLoader = useMemo(() => basisActive && spotSymbol && assetData
140
+ ? (force: boolean) => loadCurveSpotBars(spotSymbol, assetData, force) : null,
141
+ [assetData, basisActive, spotSymbol, resource.updatedAt]);
142
+ const spotBars = useAsyncResource(barsLoader, { keepPreviousData: true });
131
143
  // Until the first answer there is nothing to say: no reason, only blank cells.
132
144
  const spot = useMemo(() => !basisActive || !spotSymbol || !spotState || spotState.loading && !spotState.quote ? null
133
145
  : curveSpot(spotSymbol, spotState.quote, clock), [basisActive, clock, spotState, spotSymbol]);
@@ -138,8 +150,9 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
138
150
  requestedDate ? Date.parse(`${requestedDate}T00:00:00Z`) : Date.now(), requestedDate ? data.asOf ?? requestedDate : undefined) : [],
139
151
  [data, colors, horizon, requestedDate]);
140
152
  const changes = useMemo<CurveContractChanges>(() => data ? curveContractChanges(data) : new Map(), [data]);
141
- const basis = useMemo(() => curveBasisRows(basisActive ? data?.contracts ?? [] : [], spot), [basisActive, data, spot]);
142
- const thinCount = useMemo(() => thinContractCount(basis.values()), [basis]);
153
+ // Until the bars first answer there is nothing to say: no reason, only blank cells.
154
+ const bars = basisActive ? spotBars.data : null;
155
+ const basis = useMemo(() => curveBasisRows(basisActive ? data?.contracts ?? [] : [], bars), [basisActive, bars, data]);
143
156
  const rows = useMemo(() => sortCurveContracts(data?.contracts ?? [], sort.columnId, sort.direction, changes, basis), [data, sort, changes, basis]);
144
157
  // The curve tab lists the contracts the chart plots; Contracts keeps every one.
145
158
  const curveRows = useMemo(() => {
@@ -151,13 +164,20 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
151
164
  useChartTableSelection({ rows: curveRows, getId: contractKey, getDate: contractDate, selectedId, onSelect: setSelected,
152
165
  focused, enabled: curveTab });
153
166
  // The highlighted row carries the selected contract's price and rank.
154
- const slopeDate = data?.slope.asOf && curveTimestamp(data.slope.asOf) !== curveTimestamp(newest) ? curveTimestamp(data.slope.asOf) : null;
167
+ // The spread says which prices it subtracts (settlements or two last trades made together); a server that does not say is dated.
168
+ const spreadBasis = data ? curveSpreadBasis(data.slope, newest) : null;
169
+ const slopeDate = !spreadBasis && data?.slope.asOf && curveTimestamp(data.slope.asOf) !== curveTimestamp(newest) ? curveTimestamp(data.slope.asOf) : null;
170
+ // A thin spread makes no curve call: it and the roll yield read from it take the warning tone and give no rank.
171
+ const thin = data?.slope.state === "thin";
172
+ const spreadRank = data && !thin ? curveRank(data.slope.percentile, data.slope.samples) : null;
173
+ const thinSpread = data ? thinSpreadNotice(data) : null;
155
174
  const statItems: StatItem[] = data ? [
156
175
  { id: "roll", label: "Ann. roll yield", value: signedPercent(data.slope.annualizedRollYield),
157
- detail: curveRank(data.slope.rollPercentile, data.slope.samples) },
176
+ ...thin ? { tone: "warning" as const } : { detail: curveRank(data.slope.rollPercentile, data.slope.samples) } },
158
177
  // Named by the contracts it subtracts: the front is the first month the market trades, not always the first row.
159
178
  { id: "spread", label: curveSpreadLabel(data.slope), value: data.slope.value == null ? "--" : curvePrice(data.slope.value, root),
160
- detail: [data.slope.state, curveRank(data.slope.percentile, data.slope.samples), slopeDate].filter(Boolean).join(" · ") },
179
+ ...thin ? { tone: "warning" as const } : {},
180
+ detail: [data.slope.state, spreadBasis, spreadRank, slopeDate].filter(Boolean).join(" · ") },
161
181
  ...data.spec ? [{ id: "contract", label: "Contract", value: data.spec.size, detail: `tick ${futuresTickText(data.spec)}` }] : [],
162
182
  ] : [];
163
183
  const { strip: tabStrip, rows: tabRows } = usePaneTabs({ tabs: TABS, activeValue: tab, onSelect: setTab, focused, dense: true, queryBarWidth: width });
@@ -168,6 +188,7 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
168
188
  const settleLabel = data?.settlementDate ? `SETTLE ${data.settlementDate.slice(5)}` : "SETTLE";
169
189
  // Cash-settled roots (ES, CL's Brent, VX) have no first notice day to show.
170
190
  const noNotice = !data?.contracts.some((row) => row.firstNotice);
191
+ const shortDated = curveTab && curveRows.some((row) => shortDatedBasis(basis.get(row.symbol)));
171
192
  const columns = useMemo(() => {
172
193
  // Labelled before fitting: a dated header is wider than the column's numbers.
173
194
  const shown = (list: DataTableColumn[]) => list.filter((entry) => !(settlesOnly && entry.id === "price") && !(noNotice && entry.id === "notice"))
@@ -175,9 +196,9 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
175
196
  // A curve that only settles keeps its settlement: it is the price.
176
197
  const order = (list: string[]) => settlesOnly ? list.filter((id) => id !== "settle") : list;
177
198
  return !curveTab ? fitChartTableColumns(shown(COLUMNS), width, order(CONTRACTS_DROP_ORDER))
178
- : basisActive ? fitChartTableColumns(shown(BASIS_CURVE_COLUMNS), width, order(BASIS_DROP_ORDER))
199
+ : basisActive ? fitChartTableColumns(shown(BASIS_CURVE_COLUMNS), width, order(shortDated ? SHORT_DATED_DROP_ORDER : BASIS_DROP_ORDER))
179
200
  : fitChartTableColumns(shown(CURVE_COLUMNS), width, order(CURVE_DROP_ORDER));
180
- }, [basisActive, curveTab, noNotice, settleLabel, settlesOnly, width]);
201
+ }, [basisActive, curveTab, noNotice, settleLabel, settlesOnly, shortDated, width]);
181
202
  const formatValue = useCallback((value: number) => curvePrice(value, root), [root]);
182
203
  const formatChange = useCallback((value: number) => curveChangeText(value, root), [root]);
183
204
  const unit = data ? curveUnitLabel(data) : null;
@@ -212,11 +233,16 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
212
233
  };
213
234
  // A past date with no archived curve says why in the body, not behind the warning.
214
235
  const emptyPast = !!requestedDate && !!data && !data.contracts.length;
215
- // Thin contracts join the data warnings: the info row has no room for them beside the quote times.
236
+ // The basis notes the report prints under its table: the convention, why a cell is blank, and which rates spot noise swamps.
237
+ const basisNotes = basisActive && bars ? [BASIS_CONVENTION, blankBasisNote(tableRows, basis), shortDatedBasisNote(tableRows, basis)]
238
+ .filter((note): note is string => !!note) : [];
216
239
  // A long in these can be assigned delivery: the hedge a trader rolls before first notice.
217
240
  const delivering = data?.contracts.filter((row) => row.inDelivery).map((row) => curveContractCode(row.symbol)) ?? [];
241
+ // A blank Change on Contracts says why, as a blank basis does: no trade since the settlement it is measured from.
242
+ const untraded = data && !requestedDate && columns.some((entry) => entry.id === "change")
243
+ ? noTradeChangeNote(tableRows, data, { count: true }) : null;
218
244
  usePaneNoticeFooter({ registrationId: "futures-curve:notices", focused,
219
- notices: emptyPast ? [] : [...data?.gaps ?? [], ...thinCount ? [thinContractsNotice(thinCount)] : [],
245
+ notices: emptyPast ? [] : [...data?.gaps ?? [], ...thinSpread ? [thinSpread] : [], ...basisNotes, ...untraded ? [untraded] : [],
220
246
  ...delivering.length ? [`${delivering.join(", ")} ${delivering.length === 1 ? "is" : "are"} past first notice.`] : []] });
221
247
  const delay = Math.max(0, ...(data?.contracts.map((row) => row.delayMinutes ?? 0) ?? []));
222
248
  usePaneStatusFooter({ registrationId: "futures-curve", loading: resource.loading, error: resource.error,
@@ -227,8 +253,7 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
227
253
  info: data ? [
228
254
  ...splitDelayedStatus({ id: "source", parts: [{ text: `${requestedDate ? "daily archive" : data.source === "cboe" ? "settlement" : delay > 0 ? `${delay}m delayed` : "dated quotes"} · ${unit ?? "units unavailable"} · ${curveTimestamp(newest)}${newest?.includes("T") ? " UTC" : ""}`, tone: "muted" }] }),
229
255
  ...(staleCount ? [{ id: "stale", parts: [{ text: `${staleCount} of ${data.contracts.length} stale`, tone: "warning" as const }] }] : []),
230
- ...(spot ? [spot.status === "ok" ? spotSegment(spot, root, clock)
231
- : { id: "spot", parts: [{ text: `Basis blank: ${spot.reason}`, tone: "warning" as const }] }] : []),
256
+ ...(spot ? [spotSegment(spot, root, clock)] : []),
232
257
  ] : [],
233
258
  });
234
259
  const tableChromeRows = chartTableChromeRows(columns, width);
@@ -250,7 +275,7 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
250
275
  if (column.id === "price") {
251
276
  const lastTrade = requestedDate && row.settlement != null ? null : row.price;
252
277
  // Beside its settlement the last trade is the secondary price; without one it is the row's price.
253
- return { text: curvePrice(lastTrade, root), color: curveTab && (row.stale || basis.get(row.symbol)?.thin) ? colors.warning
278
+ return { text: curvePrice(lastTrade, root), color: curveTab && row.stale ? colors.warning
254
279
  : row.settlement != null ? colors.textMuted : undefined };
255
280
  }
256
281
  if (column.id === "change") {
@@ -263,7 +288,11 @@ function FuturesCurveView({ width, height, focused, root, ownsTitle }: PaneProps
263
288
  }
264
289
  if (column.id === "vsSpot" || column.id === "annBasis") {
265
290
  const cell = basis.get(row.symbol);
266
- return { text: column.id === "vsSpot" ? curveBasisPercent(cell?.vsSpotPct ?? null, 2) : curveBasisPercent(cell?.annualisedBasisPct ?? null, 1), color: colors.textMuted };
291
+ const text = column.id === "vsSpot" ? curveBasisPercent(cell?.vsSpotPct ?? null, 2) : curveBasisPercent(cell?.annualisedBasisPct ?? null, 1);
292
+ // The figure to read is in the text colour, the other greyed: the annualised rate, until a week before
293
+ // expiry, where spot noise swings it by whole points and Vs Spot leads.
294
+ const lead = column.id === "vsSpot" ? shortDatedBasis(cell) : !shortDatedBasis(cell);
295
+ return { text, color: lead && text !== "--" ? undefined : colors.textMuted };
267
296
  }
268
297
  if (column.id === "percentile") return { text: row.samples < 2 || row.percentile == null ? "--" : row.percentile.toFixed(0) };
269
298
  if (column.id === "oi") return { text: integer(row.openInterest) };
@@ -1,9 +1,11 @@
1
+ import { logFailure } from "../../../utils/log-failure";
1
2
  import { MarketDataCoordinator } from "../../../market-data/coordinator";
2
3
  import type { QueryEntry } from "../../../market-data/result-types";
3
4
  import { resolveEntryData } from "../../../market-data/selectors";
4
5
  import type { DataProvider } from "../../../types/data-provider";
5
6
  import type { Quote } from "../../../types/financials";
6
- import { fxLegReferenceRate, fxLegs, liveFxLegEntry, type FxLeg } from "./live-legs";
7
+ import { quoteChangeBasis, type QuoteChangeBasis } from "../../../market-data/market/change-basis";
8
+ import { fxLegMoveReference, fxLegs, liveFxLegEntry, type FxLeg } from "./live-legs";
7
9
 
8
10
  /** One USD leg as the board reads it: the rate it draws and where that rate came from. */
9
11
  export interface FxLegReading {
@@ -13,10 +15,13 @@ export interface FxLegReading {
13
15
  /** The pair quote, only when the rate is its own: it then says what the rate is (live or delayed, its session). */
14
16
  quote: Quote | null;
15
17
  /**
16
- * USD per unit at the pair's previous close, only when the rate is the pair
17
- * quote's: a loaded rate has no previous close to read against.
18
+ * USD per unit at the pair quote's previous daily close, for the rate drawn
19
+ * whether it is the quote's or a loaded rate of the same FX trading day
20
+ * (fxLegMoveReference); null when the quote is stale or has none.
18
21
  */
19
22
  reference: number | null;
23
+ /** The close `reference` is, as `quote` names it ("vs Wed 21 Oct close"). */
24
+ changeBasis?: QuoteChangeBasis | null;
20
25
  /** Why the leg has no rate, when it has none. */
21
26
  error: string | null;
22
27
  }
@@ -25,9 +30,9 @@ export interface FxLegReading {
25
30
  * The board's USD legs, read once the way the pane reads them, through the
26
31
  * same market data coordinator over the same service: each leg's loaded rate
27
32
  * (`loadFxRate`, dated by the shared FX cache), replaced by its pair quote when
28
- * that quote is current and at least as new. Only a pair quote brings the
29
- * previous close a cross's move is read against. A leg with neither has no
30
- * rate, never parity.
33
+ * that quote is current and at least as new. The pair quote brings the
34
+ * previous close a cross's move is read against, for either rate. A leg with
35
+ * neither has no rate, never parity.
31
36
  */
32
37
  export async function loadFxBoard(
33
38
  currencies: readonly string[],
@@ -42,18 +47,20 @@ export async function loadFxBoard(
42
47
  Promise.all(legs.map((leg) => coordinator.loadFxRate(leg.currency, { forceRefresh }))),
43
48
  // A pair quote only refines a loaded rate, so a failed batch leaves the loaded rates.
44
49
  coordinator.loadQuotesBatch(legs.map((leg) => ({ symbol: leg.symbol, exchange: "" })), { forceRefresh })
45
- .catch((): QueryEntry<Quote>[] => []),
50
+ .catch(logFailure("fx-matrix.client", "Load FX pair quotes failed", [] as QueryEntry<Quote>[])),
46
51
  ]);
47
52
  return new Map(legs.map((leg, index) => {
48
53
  const rate = rates[index]!;
49
54
  const quote = quotes[index];
50
55
  const live = liveFxLegEntry(leg, quote, rate);
51
56
  const entry = live ?? (resolveEntryData(rate) != null ? rate : null);
57
+ const close = fxLegMoveReference(leg, quote, entry);
52
58
  return [leg.currency, {
53
59
  leg,
54
60
  entry,
55
61
  quote: live ? resolveEntryData(quote) : null,
56
- reference: live ? fxLegReferenceRate(leg, quote) : null,
62
+ reference: close?.rate ?? null,
63
+ changeBasis: close ? quoteChangeBasis(close.quote) : null,
57
64
  error: entry ? null : rate.error?.message ?? "No rate came back.",
58
65
  }] as const;
59
66
  }));
@@ -1,4 +1,5 @@
1
- import type { HeadlessPaneDefinition, HeadlessRowsResult } from "../../../types/plugin";
1
+ import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessRowsResult } from "../../../types/plugin";
2
+ import { combinedChangeBasisLabel } from "../../../market-data/market/change-basis";
2
3
  import { resolveEntryData } from "../../../market-data/selectors";
3
4
  import { formatPercentRaw } from "../../../utils/format";
4
5
  import { formatUtcTime } from "../../../utils/utc-time";
@@ -10,6 +11,26 @@ import { FX_MATRIX_REPORT_OPTIONS, fxMatrixReportNotices } from "./settings";
10
11
 
11
12
  const iso = (time: number | null | undefined) => time != null && Number.isFinite(time) ? new Date(time).toISOString() : null;
12
13
 
14
+ const MOVE_COLUMN: HeadlessPaneColumn = { key: "movePercent", header: "Move", align: "right", format: (value) => typeof value === "number" ? formatPercentRaw(value) : "—" };
15
+ const CROSS_COLUMNS: HeadlessPaneColumn[] = [
16
+ { key: "pair", header: "Pair" },
17
+ { key: "rate", header: "Rate", align: "right", format: (value) => typeof value === "number" ? formatRate(value) : "—" },
18
+ MOVE_COLUMN,
19
+ { key: "asOf", header: "As of", shrink: false, format: (value) => typeof value === "string" ? formatUtcTime(value) : "—" },
20
+ { key: "stale", header: "Status", format: (value, row) => (
21
+ row.rate == null ? "unavailable" : value ? "stale" : row.asOf == null ? "time unknown" : "current"
22
+ ) },
23
+ ];
24
+
25
+ /**
26
+ * The columns with the move's header naming its close, as `quote` names a
27
+ * pair's (`Move vs Wed 21 Oct close`), in the text report; exports keep
28
+ * `Move`, so a column read by its name stays where it was.
29
+ */
30
+ function crossColumns(basis: string | null): HeadlessPaneColumn[] {
31
+ return basis ? CROSS_COLUMNS.map((column) => column === MOVE_COLUMN ? { ...column, textHeader: `Move ${basis}` } : column) : CROSS_COLUMNS;
32
+ }
33
+
13
34
  /** USD per unit, USD itself as 1; null for a leg with no usable rate. */
14
35
  function usdPerUnit(readings: ReadonlyMap<string, FxLegReading>, currency: string): number | null {
15
36
  if (currency === "USD") return 1;
@@ -22,9 +43,10 @@ function usdPerUnit(readings: ReadonlyMap<string, FxLegReading>, currency: strin
22
43
  * reads: one unit of `base` buys `rate` of `quote`, through the USD legs. Each
23
44
  * leg is dated as the pane dates it for `fn` and `shot` (`fxRateObservations`),
24
45
  * a cross as `fx <PAIR>` dates one (`fxPairObservation`): as old as its older
25
- * leg, stale when either leg is. It moves only when both legs carry a previous
26
- * close, the pane's tint rule. The report itself is dated by the legs, so its
27
- * closing line is the one the pane's capture ends with.
46
+ * leg, stale when either leg is. It moves from both legs' previous daily
47
+ * closes, the pane's tint rule, and the Move header names that close. The
48
+ * report itself is dated by the legs, so its closing line is the one the
49
+ * pane's capture ends with.
28
50
  */
29
51
  export function projectFxCrosses(
30
52
  currencies: readonly FxCurrency[],
@@ -69,12 +91,15 @@ export function projectFxCrosses(
69
91
  };
70
92
  }));
71
93
 
94
+ // The closes the moves are read from: one day for a board read at once, "vs mixed closes" otherwise.
95
+ const moveBasis = combinedChangeBasisLabel(currencies.map((currency) => readings.get(currency)?.changeBasis));
72
96
  const unavailable = currencies.filter((currency) => !rates.has(currency));
73
97
  const errors = unavailable.flatMap((currency) => {
74
98
  const error = readings.get(currency)?.error;
75
99
  return error ? [`${currency}: ${error}`] : [];
76
100
  });
77
101
  return {
102
+ columns: crossColumns(moveBasis),
78
103
  rows,
79
104
  ...(rows.length === 0 ? { empty: "One currency has no crosses. Add another, such as --currencies USD,EUR." } : {}),
80
105
  complete: unavailable.length === 0,
@@ -86,7 +111,8 @@ export function projectFxCrosses(
86
111
  currencies,
87
112
  unit: "quote currency per one unit of the base currency",
88
113
  method: "the base currency's USD rate divided by the quote currency's; the two legs' observation times can differ",
89
- movePercent: "the cross against both legs' previous closes; empty where a leg has no current pair quote",
114
+ movePercent: "the cross against both legs' previous daily closes, from each pair's quote; empty where a leg has no current pair quote",
115
+ ...(moveBasis ? { changeBasis: moveBasis } : {}),
90
116
  legs: currencies.filter((currency) => currency !== "USD").map((currency) => {
91
117
  const reading = readings.get(currency);
92
118
  const observation = observed.get(currency);
@@ -119,15 +145,7 @@ export const fxMatrixHeadless: HeadlessPaneDefinition<"rows"> = {
119
145
  screenshotReadiness: "live-dom",
120
146
  },
121
147
  options: [...FX_MATRIX_REPORT_OPTIONS],
122
- columns: [
123
- { key: "pair", header: "Pair" },
124
- { key: "rate", header: "Rate", align: "right", format: (value) => typeof value === "number" ? formatRate(value) : "—" },
125
- { key: "movePercent", header: "Move", align: "right", format: (value) => typeof value === "number" ? formatPercentRaw(value) : "—" },
126
- { key: "asOf", header: "As of", shrink: false, format: (value) => typeof value === "string" ? formatUtcTime(value) : "—" },
127
- { key: "stale", header: "Status", format: (value, row) => (
128
- row.rate == null ? "unavailable" : value ? "stale" : row.asOf == null ? "time unknown" : "current"
129
- ) },
130
- ],
148
+ columns: CROSS_COLUMNS,
131
149
  // Ask Gloom gets every cross of all 45 currencies inside its result limit:
132
150
  // the pair names both codes, and each leg's feed and delay are in metadata.
133
151
  compact: (result) => ({
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../../../utils/log-failure";
1
2
  import { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
2
3
  import {
3
4
  DataTableView,
@@ -24,7 +25,7 @@ import { fxRateObservations } from "../shared/fx-rate-freshness";
24
25
  import { useLiveQuoteEntries } from "../../../state/hooks/quote-streaming";
25
26
  import { useAutoRefresh, useUpdatedAgo } from "../../../react/auto-refresh";
26
27
  import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
27
- import { fxLegQuoteKey, fxLegReferenceRate, fxLegTargets, fxLegs, fxLegsBehind, liveFxLegEntry } from "./live-legs";
28
+ import { fxLegMoveReference, fxLegQuoteKey, fxLegReferenceRate, fxLegTargets, fxLegs, fxLegsBehind, liveFxLegEntry } from "./live-legs";
28
29
  import { CURRENCY_FLAG_REGIONS, FX_CURRENCIES, formatRate, resolveCurrencies, type FxCurrency } from "./pairs";
29
30
  import { crossMovePercent, directionTint, nextTintLevel } from "./direction";
30
31
  import { fxMatrixSettings } from "./settings";
@@ -91,20 +92,21 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
91
92
  }
92
93
  return references;
93
94
  }, [legEntries, legs, rates]);
94
- // A cell's direction is its cross against the pair's previous close. Only a
95
- // leg whose rate is the live quote's and whose quote carries a previous close
96
- // counts, USD (the base) as 1: the first rate this pane drew is no daily
97
- // reference, and a snapshot rate or a stale quote has no previous close to
98
- // read against.
95
+ // A cell's direction is its cross against the pairs' previous daily closes,
96
+ // USD (the base) as 1: each leg's pair quote's close, whether the rate drawn
97
+ // is that quote's or a snapshot rate of the same FX trading day
98
+ // (fxLegMoveReference). The first rate this pane drew is no daily reference,
99
+ // and a stale quote has no close to read against.
99
100
  const directionReferences = useMemo(() => {
101
+ const coordinator = getSharedMarketDataCoordinator();
100
102
  const references = new Map<string, number>([["USD", 1]]);
101
103
  for (const leg of legs) {
102
- if (!liveEntries.has(leg.currency)) continue;
103
- const reference = fxLegReferenceRate(leg, legEntries.get(fxLegQuoteKey(leg)));
104
- if (reference != null) references.set(leg.currency, reference);
104
+ const drawn = liveEntries.get(leg.currency) ?? coordinator?.getFxEntry(leg.currency);
105
+ const reference = fxLegMoveReference(leg, legEntries.get(fxLegQuoteKey(leg)), drawn);
106
+ if (reference) references.set(leg.currency, reference.rate);
105
107
  }
106
108
  return references;
107
- }, [legEntries, legs, liveEntries]);
109
+ }, [legEntries, legs, liveEntries, snapshotRates]);
108
110
  // The step each cell showed last, which a tick near a boundary keeps.
109
111
  const tintLevels = useRef(new Map<string, number>());
110
112
  // Export the provenance of this render's rates, even if a provider response
@@ -156,7 +158,7 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
156
158
  if (!coordinator) return;
157
159
  for (const currency of codes) {
158
160
  if (currency === "USD") continue;
159
- void coordinator.loadFxRate(currency, { forceRefresh: true }).catch(() => {});
161
+ void coordinator.loadFxRate(currency, { forceRefresh: true }).catch(logFailure("fx-matrix", "Refresh FX rate failed"));
160
162
  }
161
163
  }, []);
162
164
  const refresh = useCallback(() => reload(currencies), [currencies, reload]);
@@ -3,6 +3,7 @@ import { buildQuoteKey, resolveEntryData } from "../../../market-data/selectors"
3
3
  import type { QuoteSubscriptionTarget } from "../../../types/data-provider";
4
4
  import type { Quote } from "../../../types/financials";
5
5
  import { isStreamCarryingQuote } from "../shared/use-quote-board";
6
+ import { fxTradingDay } from "../../../utils/fx-market-hours";
6
7
 
7
8
  /** Currencies the market quotes as USD per unit; the rest quote as units per USD. */
8
9
  const USD_QUOTED = new Set(["EUR", "GBP", "AUD", "NZD"]);
@@ -92,3 +93,24 @@ export function fxLegReferenceRate(leg: FxLeg, entry: QueryEntry<Quote> | undefi
92
93
  if (close == null || !Number.isFinite(close) || close <= 0) return null;
93
94
  return leg.inverted ? 1 / close : close;
94
95
  }
96
+
97
+ /**
98
+ * What a leg's move is read against: its pair quote's previous daily close
99
+ * (USD per unit), the close `quote` measures the pair from, whichever rate
100
+ * the board draws. A loaded rate a minute newer than the quote moves from the
101
+ * same close, so it reads against it too, as long as it is of the quote's FX
102
+ * trading day. Null when the quote is stale or carries no previous close, or
103
+ * when the drawn rate is of a later trading day (or undated) than the quote.
104
+ */
105
+ export function fxLegMoveReference(
106
+ leg: FxLeg,
107
+ entry: QueryEntry<Quote> | undefined,
108
+ drawn: Pick<QueryEntry<number>, "asOf"> | null | undefined,
109
+ ): { rate: number; quote: Quote } | null {
110
+ const quote = resolveEntryData(entry);
111
+ const rate = fxLegReferenceRate(leg, entry);
112
+ if (!quote || quote.stale === true || rate == null || !Number.isFinite(quote.lastUpdated)) return null;
113
+ const drawnAt = drawn?.asOf;
114
+ if (drawnAt == null || !Number.isFinite(drawnAt) || fxTradingDay(drawnAt) !== fxTradingDay(quote.lastUpdated)) return null;
115
+ return { rate, quote };
116
+ }
@@ -16,6 +16,7 @@ function currencyName(code: string): string | null {
16
16
  const name = currencyNames.of(code);
17
17
  return name && name !== code ? name : null;
18
18
  } catch {
19
+ // Intentional ignore: runtimes without currency display names keep the currency code as the picker label.
19
20
  currencyNames = null;
20
21
  return null;
21
22
  }
@@ -98,5 +99,5 @@ export const FX_MATRIX_REPORT_OPTIONS: readonly PaneReportOptionDef[] = [{
98
99
  /** A report of the default board says it is only the majors, and how to choose others. */
99
100
  export function fxMatrixReportNotices(settings: Readonly<Record<string, unknown>>): string[] {
100
101
  if (settings.currencies != null) return [];
101
- return [`Showing the ${MAJOR_CURRENCIES.length} majors. Choose others with --currencies USD,ZAR,NGN; gloomberb catalog FXC lists all ${FX_CURRENCIES.length}.`];
102
+ return [`Showing the ${MAJOR_CURRENCIES.length} majors. Choose others with --currencies USD,ZAR,NGN; gloom catalog FXC lists all ${FX_CURRENCIES.length}.`];
102
103
  }
@@ -79,9 +79,11 @@ function boardColumns(width: number, options: { windows: GpuChangeKey[]; trend:
79
79
  ];
80
80
  }
81
81
 
82
- export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, select, emptyTitle, width, height, focused }: {
82
+ export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, select, emptyTitle, width, height, focused, pending = false }: {
83
83
  rows: GpuBoardRow[]; models: readonly string[]; asOf: string | null; model: string; setModel: (value: string) => void; selectedId: string; select: (row: GpuBoardRow, open?: boolean) => void;
84
84
  emptyTitle: string; width: number; height: number; focused: boolean;
85
+ /** The picked GPU's free preview is still loading. */
86
+ pending?: boolean;
85
87
  }) {
86
88
  const colors = useThemeColors();
87
89
  const sections = useMemo(() => gpuBoardSections(rows, model), [rows, model]);
@@ -153,6 +155,7 @@ export function GpuBoard({ rows, models, asOf, model, setModel, selectedId, sele
153
155
 
154
156
  const query = <GpuModelQuery models={models} model={model} setModel={setModel} width={width} />;
155
157
  return <DataTableView<BoardRow> columns={columns} items={items} rootWidth={width} rootHeight={height} focused={focused}
158
+ status={pending && !items.length ? { loading: true, subject: "GPU rental prices" } : undefined}
156
159
  rootBefore={ladder.length ? <ChartTableHeader width={width} height={height} tableRows={items.length} tableColumns={columns} query={query}
157
160
  chart={{ maxRows: ladder.length + 1, minRows: Math.min(3, ladder.length + 1), strip: null,
158
161
  render: (size) => <GpuPriceLadder rows={ladder} width={size.width} height={size.height}
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useMemo } from "react";
2
2
  import type { GpuEvent } from "../../../api-client/gpu";
3
3
  import {
4
- Badge, buildSectionedRows, DataTableView, EMPTY_TABLE_CELL, isSectionedItemRow, PaneStatusBody, renderSectionedRowHeader,
4
+ Badge, buildSectionedRows, DataTableView, EMPTY_TABLE_CELL, isSectionedItemRow, renderSectionedRowHeader,
5
5
  usePaneNoticeFooter, usePaneStatusFooter, type DataTableCell, type DataTableColumn, type SectionedRow,
6
6
  } from "../../../components";
7
7
  import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
@@ -82,13 +82,13 @@ export function GpuChanges({ models, model, setModel, reloadBoard, width, height
82
82
  }
83
83
  };
84
84
 
85
- return <PaneStatusBody loading={resource.loading && !resource.data} error={!resource.data ? resource.error : null} subject="GPU price changes">
86
- <DataTableView<ChangeRow> columns={columns} items={items} rootWidth={width} rootHeight={height} focused={focused}
87
- rootBefore={<GpuModelQuery models={models} model={model} setModel={setModel} width={width} />}
88
- selection={{ kind: "id", selectedId: selected && items.some((item) => item.key === selected) ? selected : firstId, getId: rowKey,
89
- onChange: (id) => setSelected(String(id)) }}
90
- isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
91
- getItemKey={rowKey} onActivate={(row) => setSelected(row.key)} sortColumnId={null} sortDirection="desc"
92
- selectedTextOverridesCellColor renderCell={renderCell} emptyStateTitle={resource.data?.payload.access?.locked ? "Full change history is available with Pro." : "No price or availability changes recorded yet."} />
93
- </PaneStatusBody>;
85
+ // A GPU picked from the filter loads under it; the filter stays.
86
+ return <DataTableView<ChangeRow> columns={columns} items={items} rootWidth={width} rootHeight={height} focused={focused}
87
+ rootBefore={<GpuModelQuery models={models} model={model} setModel={setModel} width={width} />}
88
+ status={resource.data ? undefined : { loading: resource.loading, error: resource.error, subject: "GPU price changes" }}
89
+ selection={{ kind: "id", selectedId: selected && items.some((item) => item.key === selected) ? selected : firstId, getId: rowKey,
90
+ onChange: (id) => setSelected(String(id)) }}
91
+ isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
92
+ getItemKey={rowKey} onActivate={(row) => setSelected(row.key)} sortColumnId={null} sortDirection="desc"
93
+ selectedTextOverridesCellColor renderCell={renderCell} emptyStateTitle={resource.data?.payload.access?.locked ? "Full change history is available with Pro." : "No price or availability changes recorded yet."} />;
94
94
  }
@@ -39,9 +39,11 @@ function equityColumns(width: number): DataTableColumn[] {
39
39
  }
40
40
 
41
41
  /** Related shares by what ties them to GPU rents, each beside the list price that ties it. */
42
- export function GpuEquities({ board, models, locked, model, setModel, reloadBoard, width, height, focused }: {
42
+ export function GpuEquities({ board, models, locked, model, setModel, reloadBoard, width, height, focused, pending = false }: {
43
43
  board: GpuBoardRow[]; models: readonly string[]; locked: boolean; model: string; setModel: (value: string) => void; reloadBoard: () => void;
44
44
  width: number; height: number; focused: boolean;
45
+ /** The picked GPU's free preview is still loading. */
46
+ pending?: boolean;
45
47
  }) {
46
48
  const colors = useThemeColors();
47
49
  const provider = useAssetData();
@@ -110,6 +112,7 @@ export function GpuEquities({ board, models, locked, model, setModel, reloadBoar
110
112
  }, [colors, desktop]);
111
113
  return <DataTableView<Row> columns={equityColumns(width)} items={items} rootWidth={width} rootHeight={height} focused={focused}
112
114
  rootBefore={<GpuModelQuery models={models} model={model || "H100"} setModel={setModel} width={width} allowAll={false} />}
115
+ status={pending ? { loading: true, subject: "GPU rental prices" } : undefined}
113
116
  selection={{ kind: "id", selectedId: selected, getId: rowKey, onChange: (id) => setSelected(String(id)) }} getItemKey={rowKey}
114
117
  isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
115
118
  getRowVersion={(row) => isSectionedItemRow(row) ? [quotes.get(row.item.symbol)?.quote?.lastUpdated, quotes.get(row.item.symbol)?.loading,
@@ -7,6 +7,7 @@ import {
7
7
  gpuLabel, gpuNoMatch, gpuPrice, gpuProvenanceLabel, gpuRows, gpuSource, gpuTab,
8
8
  } from "./model";
9
9
  import { quoteFreshnessFields } from "../shared/report-freshness";
10
+ import { countOf, freePreviewNote } from "../shared/free-preview";
10
11
  import { getRegularSessionDisplay } from "../../../market-data/market/status";
11
12
 
12
13
  const observationRow = (row: GpuObservation) => ({ gpu: gpuLabel(row), source: gpuSource(row), basis: gpuBasisLabel(row.basis),
@@ -73,8 +74,12 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
73
74
  const previews = await fetchGpuModelPreviews(gpuPreviewModels(board, tab === "equities" ? gpuEquityModels(selected) : [selected]), ctx.apiClient);
74
75
  const rows = [...board.rows, ...previews];
75
76
  const previewed = [...new Set(previews.map((row) => row.gpuModel))];
76
- const notes = previewed.length ? [`${previewed.join(" and ")} ${previewed.length > 1 ? "are" : "is"} not on the free board: ${previewed.length > 1 ? "their" : "its"} latest free rows stand in. Pro adds every provider and the 1D, 7D and 30D moves.`]
77
- : board.access?.locked && !model && tab === "board" ? ["The free board shows three rows; name a GPU (GPU H100) for its latest free rows, or upgrade to Pro for every GPU."] : [];
77
+ // What the free view leaves to Pro is the report's Pro line, never a note or an error.
78
+ const locks = previewed.length
79
+ ? [freePreviewNote({ shows: `the latest rows of ${previewed.join(" and ")}, which the free board leaves out`, adds: "every provider and the 1D, 7D and 30D moves" })]
80
+ : board.access?.locked && !model && tab === "board"
81
+ ? [freePreviewNote({ shows: `${countOf(board.rows.length, "board row")}, or the latest rows of a GPU you name (GPU H100)`, adds: "every GPU and provider, with the 1D, 7D and 30D moves" })]
82
+ : [];
78
83
  if (tab === "equities") {
79
84
  const related = gpuEquityRows(rows, selected, !!board.access?.locked);
80
85
  const histories = await loadGpuEquityHistory(ctx.marketData);
@@ -97,12 +102,12 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
97
102
  { key: "gpu", header: "GPU" }, { key: "gpuSource", header: "GPU list series", format: (value: unknown, row: HeadlessPaneRow) => typeof value === "string" ? value : String(row.gpuGap ?? "-") },
98
103
  { key: "gpuPrice", header: "$/GPU-hr", align: "right" as const, format: (value: unknown, row: HeadlessPaneRow) => locked(value, row, price) },
99
104
  { key: "gpuChange7d", header: "GPU 7D", align: "right" as const, format: (value: unknown, row: HeadlessPaneRow) => locked(value, row, percent) },
100
- ], rows: equities }], complete: !board.access?.locked, metadata, notes, errors: [...board.gaps, ...histories.flatMap((entry) => entry.error ? [entry.error] : [])],
105
+ ], rows: equities }], complete: !board.access?.locked, metadata, errors: [...locks, ...board.gaps, ...histories.flatMap((entry) => entry.error ? [entry.error] : [])],
101
106
  unavailableSymbols: equities.filter((row) => row.price == null || row.change5d == null).map((row) => row.symbol) };
102
107
  }
103
108
  const boardRows = gpuRows(rows, model).map((row) => ({ ...observationRow(row), change1d: row.change1d, change7d: row.change7d,
104
109
  change30d: row.change30d, stale: row.stale, sample: row.stats ?? null, ...(gpuChangesLocked(row) ? { changesLocked: true } : {}) }));
105
110
  return { sections: [{ title: "GPU rental prices", columns, rows: boardRows, empty: `No ${model ? `${model} ` : ""}rental prices have been observed yet.` }],
106
- freshness, complete: !board.access?.locked, metadata, notes, errors: board.gaps };
111
+ freshness, complete: !board.access?.locked, metadata, errors: [...locks, ...board.gaps] };
107
112
  },
108
113
  };
@@ -92,9 +92,11 @@ function HistoryDetail({ row, peers, width, height, refreshVersion }: {
92
92
  </PaneStatusBody>;
93
93
  }
94
94
 
95
- export function GpuHistory({ rows, models: boardModels, model, setModel, selectedId, onSelect, reloadBoard, width, height, focused }: {
95
+ export function GpuHistory({ rows, models: boardModels, model, setModel, selectedId, onSelect, reloadBoard, width, height, focused, pending = false }: {
96
96
  rows: GpuBoardRow[]; models: readonly string[]; model: string; setModel: (value: string) => void; selectedId: string; onSelect: (id: string) => void;
97
97
  reloadBoard: () => void; width: number; height: number; focused: boolean;
98
+ /** The picked GPU's free preview is still loading. */
99
+ pending?: boolean;
98
100
  }) {
99
101
  const colors = useThemeColors();
100
102
  const [basis, setBasis] = usePluginPaneState<string>("historyBasis", "all");
@@ -133,7 +135,7 @@ export function GpuHistory({ rows, models: boardModels, model, setModel, selecte
133
135
  searchPlaceholder="Provider or variant" columns={listColumns} rows={listRows} getRowId={listRowId}
134
136
  filters={[{ id: "gpu", label: "GPU", value: activeModel, options: models.map((value) => ({ value, label: value })), onChange: setModel }]}
135
137
  isNavigable={isSectionedItemRow} renderSectionHeader={renderSectionedRowHeader}
136
- renderCell={renderCell} emptyStateTitle="No series match."
138
+ renderCell={renderCell} emptyStateTitle="No series match." status={pending && !listRows.length ? { loading: true, subject: "GPU rental prices" } : undefined}
137
139
  renderDetail={(size) => list.selected ? <HistoryDetail key={list.selected.id} row={list.selected} peers={modelRows} refreshVersion={refreshVersion} {...size} />
138
140
  : <PaneStatusBody empty emptyTitle={`No ${gpuBasisLabel(basis === "all" ? "list" : basis as GpuBasis)} series yet.`} />} />;
139
141
  }
@@ -61,13 +61,15 @@ function GpuView({ width, height, focused }: PaneProps) {
61
61
  usePaneFooter("gpu:actions", () => ({ info: data?.access?.preview ? [{ id: "preview", parts: [{ text: "Free preview", tone: "muted" }] }] : [],
62
62
  hints: [...(data?.access?.locked ? [{ id: "upgrade", key: CLOUD_PLAN_KEY, label: "upgrade", title: "Unlock full GPU history with Pro", onPress: upgrade }] : []), { id: "buildout", key: "t", label: "BO", title: "Open TheBuildout",
63
63
  onPress: () => createPaneFromTemplate("buildout-pane") }] }), [createPaneFromTemplate, data?.access, upgrade]);
64
- const { strip, rows: tabRows } = usePaneTabs(data ? { tabs: [...GPU_TABS], activeValue: tab, onSelect: setTab, focused, dense: true } : null);
65
- const body = { width, height: Math.max(3, height - tabRows), focused };
64
+ const { strip, rows: tabRows } = usePaneTabs(!signIn ? { tabs: [...GPU_TABS], activeValue: tab, onSelect: setTab, focused, dense: true } : null);
65
+ // Another GPU's free preview waits in the table under the GPU filter, so picking a model never takes the filter away.
66
+ const pending = !!previewKey && preview.loading && !preview.data;
67
+ const body = { width, height: Math.max(3, height - tabRows), focused, pending };
66
68
  const reloadBoard = useCallback(() => { void resource.reload(); void preview.reload(); }, [resource.reload, preview.reload]);
67
69
  if (signIn) return <SignInWall placement="gpu" width={width} height={height} action="browse GPU rental prices" needsVerification={session.needsVerification} />;
68
70
  return <Box width={width} height={height} flexDirection="column">
69
71
  {strip}
70
- <PaneStatusBody loading={(resource.loading && !data) || (!!previewKey && preview.loading && !preview.data)} error={!data && !notAvailable ? resource.error : null}
72
+ <PaneStatusBody loading={resource.loading && !data} error={!data && !notAvailable ? resource.error : null}
71
73
  empty={notAvailable} emptyTitle={GPU_NOT_AVAILABLE} subject="GPU rental prices">
72
74
  {data ? tab === "board" ? <GpuBoard rows={rows} models={models} asOf={data.asOf} model={activeModel} setModel={setModel} selectedId={selectedId} emptyTitle={emptyTitle}
73
75
  select={(row, open) => { setSelectedId(row.id); if (open) setTab("history"); }} {...body} />