gloomberb 0.18.3 → 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 (886) 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/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. 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
  }
@@ -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
  };
@@ -267,8 +267,17 @@ function HelpPane({ focused, width, height }: PaneProps) {
267
267
  entry("place", ["Enter", "Arrows"], "With a tool picked, Enter starts it at the cursor, the arrows move its end, and Enter finishes it."),
268
268
  entry("drawings", ["[", "]"], "With a drawing tool picked, pick a drawing."),
269
269
  entry("colour", ["c", "Backspace"], "Cycle the drawing colour, or delete the picked drawing (the last one while a drawing tool is picked)."),
270
- entry("zoom-pointer", ["Ctrl+Scroll"], "Zoom around the pointer."),
271
- entry("zoom-keys", ["+/-", "0"], "Zoom the focused chart in or out, or reset it."),
270
+ entry("zoom-pointer", shortcutDisplayMode === "terminal" ? ["Ctrl+Scroll"] : ["Ctrl+Scroll", "Pinch"], "Zoom time around the pointer."),
271
+ entry("time-axis", ["Drag"], "On the time axis, zoom time: left zooms in, right zooms out. Double-click fits it again."),
272
+ entry("value-axis", ["Drag", "Scroll"], "On a value axis, stretch or squeeze that panel's values. Dragging the plot then moves them too."),
273
+ entry("value-fit", ["Double-click", "Auto"], "Fit a hand-scaled panel's values again, from its axis or the Auto button at its foot."),
274
+ entry("zoom-keys", ["+/-", "0"], "Zoom the focused chart in or out, or reset it, values included."),
275
+ shortcutDisplayMode === "terminal"
276
+ ? entry("panel-height", ["Shift+Up/Down"], "Make the top panel taller or shorter; Reset Panel Heights in the pane menu puts back the split.")
277
+ : entry("panel-height", ["Drag", "Shift+Up/Down"], "Drag the line between two panels to resize them, double-click it for the default split; Shift+Up and Shift+Down resize the top panel."),
278
+ entry("panel-max", ["Double-click"], "Double-click a panel to give it the whole chart, and again to show every panel."),
279
+ entry("tools-hide", ["Shift+T"], "Hide the drawing tools to a single button, or show them again."),
280
+ entry("legend-fold", ["Shift+L"], "Fold the legend to its first series and a count of the rest, or open it again."),
272
281
  ],
273
282
  },
274
283
  {
@@ -334,7 +343,7 @@ function HelpPane({ focused, width, height }: PaneProps) {
334
343
  )}
335
344
 
336
345
  <Box flexDirection="column" gap={1}>
337
- <SectionHeading title="How To Use Gloomberb" />
346
+ <SectionHeading title="How To Use Gloom" />
338
347
  <Box flexDirection="column">
339
348
  <Text fg={colors.textDim} wrapText>{t("Click anything to use it; the keys shown beside actions are shortcuts.")}</Text>
340
349
  <Text fg={colors.textDim} wrapText>
@@ -413,6 +422,7 @@ export const helpModule: PluginModule = {
413
422
  name: "Help",
414
423
  icon: "?",
415
424
  component: HelpPane,
425
+ reportFreshness: { status: "not-a-feed", basis: "documentation" },
416
426
  defaultPosition: "right",
417
427
  defaultMode: "floating",
418
428
  defaultFloatingSize: { width: 88, height: 32 },
@@ -1,7 +1,7 @@
1
1
  import type { BeneficialOwnerFiling, BeneficialOwnerStatus } from "../../../api-client/beneficial-owners";
2
2
  import type { DataTableColumn } from "../../../components";
3
3
  import type { HolderRecord } from "../../../types/financials";
4
- import { clipToDisplayWidth, displayWidth, formatCompact } from "../../../utils/format";
4
+ import { clipToDisplayWidth, displayWidth, formatCompact, MISSING_VALUE } from "../../../utils/format";
5
5
  import { compareSortValues, type SortDirection } from "../../../utils/sort-values";
6
6
  import { matchFilerToHolder, type Holder13FMatch } from "./thirteenf-match";
7
7
 
@@ -98,11 +98,8 @@ export function thirteenFAction(holder: HolderRecord | undefined): ThirteenFActi
98
98
  if (changeShares > 0 && prior === 0) return { label: "NEW", value: Number.POSITIVE_INFINITY };
99
99
  if (prior > 0) return { label: signedPercent(changeShares / prior), value: changeShares / prior };
100
100
  }
101
- if (changePercent != null && Number.isFinite(changePercent)) {
102
- // The Table tab reads a provider's change the same way: a fraction up to 1, percent points above.
103
- const fraction = Math.abs(changePercent) <= 1 ? changePercent : changePercent / 100;
104
- return { label: signedPercent(fraction), value: fraction };
105
- }
101
+ // A holder record's change is a fraction of the prior position, as the Table tab reads it.
102
+ if (changePercent != null && Number.isFinite(changePercent)) return { label: signedPercent(changePercent), value: changePercent };
106
103
  return null;
107
104
  }
108
105
 
@@ -166,9 +163,9 @@ export function stakeToday(
166
163
  return { percent, sharesOutstanding };
167
164
  }
168
165
 
169
- /** `about 49.4% on 35.26M sh today`. */
170
- export function formatStakeToday(today: StakeToday): string {
171
- return `about ${formatClassPercent(today.percent)} on ${formatCompact(today.sharesOutstanding)} sh today`;
166
+ /** `about 49.4% on 35.26M sh today`, or `as of 2026-09-03` for a count dated by its filing. */
167
+ export function formatStakeToday(today: StakeToday, sharesOutstandingAsOf?: string | null): string {
168
+ return `about ${formatClassPercent(today.percent)} on ${formatCompact(today.sharesOutstanding)} sh ${sharesOutstandingAsOf ? `as of ${sharesOutstandingAsOf}` : "today"}`;
172
169
  }
173
170
 
174
171
  function beneficialFormLabel(filing: Pick<BeneficialOwnerFiling, "kind" | "amendment">): string {
@@ -308,7 +305,7 @@ export function formatPercentOfClass(
308
305
  ): string {
309
306
  const value = row.filing.percentOfClass;
310
307
  const mark = marker ? stakeMarker(row.stake) : "";
311
- if (value == null) return mark || "-";
308
+ if (value == null) return mark || MISSING_VALUE;
312
309
  return mark ? `${formatClassPercent(value)} ${mark}` : formatClassPercent(value);
313
310
  }
314
311
 
@@ -320,7 +317,7 @@ export function formatPointChange(value: number | null): string {
320
317
  }
321
318
 
322
319
  export function formatOwnerShares(value: number | null): string {
323
- return value == null ? "-" : formatCompact(value);
320
+ return value == null ? MISSING_VALUE : formatCompact(value);
324
321
  }
325
322
 
326
323
  /**
@@ -14,7 +14,7 @@ import {
14
14
  type BeneficialSortPreference,
15
15
  } from "./beneficial-model";
16
16
  import { buildRows } from "./table-model";
17
- import { formatCompact } from "../../../utils/format";
17
+ import { formatCompact, MISSING_VALUE } from "../../../utils/format";
18
18
 
19
19
  /** What `holders --form` lists: the 13F table, or the 13D/13G beneficial owners. */
20
20
  export type HolderForm = "13f" | "13d" | "13g" | "all";
@@ -53,7 +53,7 @@ export const BENEFICIAL_REPORT_COLUMNS = [
53
53
  align: "right" as const,
54
54
  format: (value: unknown, row: Record<string, unknown>) => {
55
55
  const marker = stakeMarker(row.status === "below-threshold" || row.status === "exited" ? row.status : null);
56
- if (typeof value !== "number") return marker || "-";
56
+ if (typeof value !== "number") return marker || MISSING_VALUE;
57
57
  return marker ? `${formatClassPercent(value)} ${marker}` : formatClassPercent(value);
58
58
  },
59
59
  },
@@ -76,9 +76,9 @@ export const BENEFICIAL_REPORT_COLUMNS = [
76
76
  align: "right" as const,
77
77
  format: (value: unknown) => formatOwnerShares(typeof value === "number" ? value : null),
78
78
  },
79
- { key: "eventDate", header: "Event", format: (value: unknown) => typeof value === "string" ? value : "-" },
79
+ { key: "eventDate", header: "Event", format: (value: unknown) => typeof value === "string" ? value : MISSING_VALUE },
80
80
  { key: "filingDate", header: "Filed" },
81
- { key: "thirteenF", header: "13F", align: "right" as const, format: (value: unknown) => typeof value === "string" ? value : "-" },
81
+ { key: "thirteenF", header: "13F", align: "right" as const, format: (value: unknown) => typeof value === "string" ? value : MISSING_VALUE },
82
82
  ];
83
83
 
84
84
  /** The 13D/13G columns these rows fill: `% Today` only when a report's base is no longer today's count. */
@@ -94,10 +94,11 @@ export function beneficialReportColumns(rows: readonly BeneficialReportRow[]): t
94
94
  export function beneficialReportLegend(
95
95
  rows: readonly BeneficialReportRow[],
96
96
  sharesOutstanding: number | null | undefined,
97
+ sharesOutstandingAsOf?: string | null,
97
98
  ): string | null {
98
99
  if (rows.length === 0) return null;
99
100
  const today = sharesOutstanding != null && rows.some((row) => row.percentToday != null)
100
- ? `; % Today is the same shares over ${formatCompact(sharesOutstanding)} shares outstanding today`
101
+ ? `; % Today is the same shares over ${formatCompact(sharesOutstanding)} shares outstanding ${sharesOutstandingAsOf ? `as of ${sharesOutstandingAsOf}` : "today"}`
101
102
  : "";
102
103
  const others = rows.some((row) => row.otherPersons > 0) ? " +N after a name: other reporting persons on the same report." : "";
103
104
  return `% Class is as filed${today}.${others}`;