gloomberb 0.16.0 → 0.16.2

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 (691) hide show
  1. package/README.md +2 -1
  2. package/package.json +2 -1
  3. package/src/api-client/beneficial-owners.ts +92 -0
  4. package/src/api-client/data.ts +44 -11
  5. package/src/api-client/errors.ts +12 -0
  6. package/src/api-client/geo.ts +363 -0
  7. package/src/api-client/index.ts +5 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/members.ts +26 -0
  10. package/src/api-client/paths.ts +30 -1
  11. package/src/api-client/perps.ts +41 -0
  12. package/src/api-client/request.ts +5 -2
  13. package/src/api-client/types.ts +9 -1
  14. package/src/app/global-shortcuts.ts +14 -6
  15. package/src/app/keybindings/actions.ts +1 -1
  16. package/src/app/runtime/update.ts +38 -2
  17. package/src/app.tsx +10 -6
  18. package/src/brokers/account-cache.ts +24 -0
  19. package/src/cli/commands/command-utils.ts +8 -0
  20. package/src/cli/commands/crypto-hints.ts +75 -0
  21. package/src/cli/commands/market.ts +414 -106
  22. package/src/cli/commands/overview.ts +77 -32
  23. package/src/cli/commands/ticker.ts +101 -43
  24. package/src/cli/desktop-pane-shot.ts +34 -4
  25. package/src/cli/freshness.ts +61 -0
  26. package/src/cli/help.ts +1 -0
  27. package/src/cli/helpers.ts +4 -8
  28. package/src/cli/index.ts +12 -2
  29. package/src/cli/listing-arg.ts +321 -0
  30. package/src/cli/pane-functions/access-gate.ts +42 -0
  31. package/src/cli/pane-functions/catalog.ts +28 -1
  32. package/src/cli/pane-functions/data.ts +3 -1
  33. package/src/cli/pane-functions/dom.ts +20 -2
  34. package/src/cli/pane-functions/freshness.ts +400 -0
  35. package/src/cli/pane-functions/function-help.ts +36 -22
  36. package/src/cli/pane-functions/headless.ts +39 -11
  37. package/src/cli/pane-functions/index.ts +15 -4
  38. package/src/cli/pane-functions/options.ts +3 -0
  39. package/src/cli/pane-functions/report.ts +4 -0
  40. package/src/cli/pane-functions/resolver.ts +35 -7
  41. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  42. package/src/cli/pane-functions/screenshot.ts +7 -3
  43. package/src/cli/result.ts +41 -27
  44. package/src/cli/ticker-resolution.ts +7 -5
  45. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  46. package/src/components/chart/composite/drawing-store.ts +59 -0
  47. package/src/components/chart/composite/legend.tsx +319 -0
  48. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  49. package/src/components/chart/composite/panel-gestures.ts +59 -0
  50. package/src/components/chart/composite/panel-labels.ts +107 -0
  51. package/src/components/chart/composite/panel-surface.tsx +876 -0
  52. package/src/components/chart/composite/tool-catalog.ts +98 -0
  53. package/src/components/chart/composite/toolbar.tsx +186 -0
  54. package/src/components/chart/composite/tools.ts +8 -0
  55. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  56. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  57. package/src/components/command-bar/commands/collection.ts +5 -1
  58. package/src/components/command-bar/list/model.ts +5 -0
  59. package/src/components/command-bar/list/view.tsx +16 -3
  60. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  61. package/src/components/command-bar/routes/root/command-items.ts +9 -2
  62. package/src/components/command-bar/routes/root/plugin-install.ts +2 -2
  63. package/src/components/command-bar/routes/root/provider-search.ts +19 -5
  64. package/src/components/command-bar/routes/root/runtime.ts +14 -5
  65. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  66. package/src/components/command-bar/routes/ticker-search/results.ts +31 -5
  67. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  68. package/src/components/command-bar/routing/actions.ts +2 -0
  69. package/src/components/command-bar/selection-runtime.ts +38 -0
  70. package/src/components/command-bar/surface/index.tsx +7 -2
  71. package/src/components/command-bar/workflow/ops.ts +1 -1
  72. package/src/components/command-bar/workflow/tickers.ts +6 -0
  73. package/src/components/data-table/view.tsx +29 -10
  74. package/src/components/feedback-dialog.tsx +2 -1
  75. package/src/components/form-modal/field-row.tsx +2 -2
  76. package/src/components/index.ts +2 -2
  77. package/src/components/layout/detached-pane-shell.tsx +6 -3
  78. package/src/components/layout/floating-pane.tsx +6 -0
  79. package/src/components/layout/header.tsx +24 -7
  80. package/src/components/layout/missing-pane.tsx +2 -2
  81. package/src/components/layout/pane/content.tsx +23 -10
  82. package/src/components/layout/pane/error-boundary.tsx +103 -0
  83. package/src/components/layout/pane/footer/registration.tsx +68 -23
  84. package/src/components/layout/pane/header.tsx +61 -28
  85. package/src/components/layout/pane/index.tsx +6 -0
  86. package/src/components/layout/pane/title.ts +31 -7
  87. package/src/components/layout/shell/drag/index.ts +6 -5
  88. package/src/components/layout/shell/drag/runtime.ts +3 -0
  89. package/src/components/layout/shell/index.tsx +6 -0
  90. package/src/components/layout/shell/menu.ts +2 -1
  91. package/src/components/layout/shell/pane/actions.ts +2 -1
  92. package/src/components/layout/shell/pane/layers.tsx +29 -10
  93. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -3
  94. package/src/components/layout/shell/ticker-link-menu.ts +23 -9
  95. package/src/components/metric-treemap/heat-surface.tsx +745 -0
  96. package/src/components/metric-treemap/heat-tiles.ts +228 -0
  97. package/src/components/metric-treemap/index.tsx +2 -0
  98. package/src/components/metric-treemap/layout.ts +8 -0
  99. package/src/components/metric-treemap/scene.ts +432 -0
  100. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  101. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  102. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  103. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  104. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  105. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -0
  106. package/src/components/ticker/list-table-view.tsx +13 -1
  107. package/src/components/toggle-list.tsx +38 -12
  108. package/src/components/ui/country-flag-art.tsx +449 -0
  109. package/src/components/ui/country-flag.tsx +41 -0
  110. package/src/components/ui/data-table/types.ts +13 -0
  111. package/src/components/ui/fields.tsx +8 -0
  112. package/src/components/ui/icon.tsx +2 -0
  113. package/src/components/ui/index.ts +4 -0
  114. package/src/components/ui/multi-select/dialog.tsx +108 -22
  115. package/src/components/ui/range-track.tsx +53 -0
  116. package/src/components/ui/status.tsx +8 -2
  117. package/src/components/ui/tabs.tsx +11 -4
  118. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  119. package/src/core/state/app/types.ts +10 -1
  120. package/src/data/config/store/normalize.ts +17 -1
  121. package/src/i18n/es.ts +24 -4
  122. package/src/i18n/ja.ts +24 -4
  123. package/src/i18n/ko.ts +24 -4
  124. package/src/i18n/zh-cn.ts +24 -4
  125. package/src/i18n/zh-tw.ts +24 -4
  126. package/src/layout/ticker-navigation.ts +28 -2
  127. package/src/market-data/coordinator/chart.ts +1 -1
  128. package/src/market-data/coordinator/quotes.ts +23 -4
  129. package/src/market-data/fx-closes.ts +72 -0
  130. package/src/market-data/market/freshness.ts +78 -6
  131. package/src/market-data/market/session-close-note.ts +57 -0
  132. package/src/market-data/market/status.ts +151 -0
  133. package/src/market-data/published-apac-sessions.ts +66 -0
  134. package/src/market-data/quotes/contributions.ts +8 -0
  135. package/src/market-data/quotes/freshness.ts +36 -0
  136. package/src/market-data/quotes/resolution.ts +24 -8
  137. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  138. package/src/market-data/quotes/time.ts +16 -0
  139. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  140. package/src/plugins/builtin/alerts/cli.ts +10 -5
  141. package/src/plugins/builtin/alerts/index.tsx +1 -0
  142. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  143. package/src/plugins/builtin/analytics/risk-client.ts +330 -79
  144. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  145. package/src/plugins/builtin/analytics/risk-headless.ts +61 -49
  146. package/src/plugins/builtin/analytics/risk-math.ts +33 -13
  147. package/src/plugins/builtin/analytics/risk-model.ts +282 -64
  148. package/src/plugins/builtin/analytics/risk-pane.tsx +62 -30
  149. package/src/plugins/builtin/analytics/risk-test-data.ts +206 -0
  150. package/src/plugins/builtin/attention/headless.ts +6 -4
  151. package/src/plugins/builtin/attention/index.tsx +1 -1
  152. package/src/plugins/builtin/attention/pane.tsx +1 -1
  153. package/src/plugins/builtin/awards/headless.ts +1 -0
  154. package/src/plugins/builtin/awards/pane.tsx +1 -1
  155. package/src/plugins/builtin/backtest/headless.ts +1 -0
  156. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  157. package/src/plugins/builtin/buildout/cells.tsx +15 -12
  158. package/src/plugins/builtin/buildout/detail/company.tsx +3 -3
  159. package/src/plugins/builtin/buildout/detail/index.tsx +0 -1
  160. package/src/plugins/builtin/buildout/detail/intel.tsx +6 -6
  161. package/src/plugins/builtin/buildout/detail/site.tsx +4 -7
  162. package/src/plugins/builtin/buildout/detail/ui.tsx +0 -19
  163. package/src/plugins/builtin/buildout/index.tsx +1 -0
  164. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  165. package/src/plugins/builtin/catalysts/index.tsx +2 -2
  166. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  167. package/src/plugins/builtin/cds/headless.ts +2 -0
  168. package/src/plugins/builtin/cds/index.tsx +1 -0
  169. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  170. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  171. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  172. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  173. package/src/plugins/builtin/chat/channels.ts +19 -3
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  175. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  176. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  177. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  178. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  179. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  180. package/src/plugins/builtin/chat/index.tsx +1 -0
  181. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  182. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  183. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  184. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  185. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  186. package/src/plugins/builtin/chat/message/types.ts +14 -2
  187. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  188. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  189. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  190. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  191. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  192. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  193. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  194. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  195. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  196. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  197. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  198. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  199. package/src/plugins/builtin/correlation/compute.ts +59 -16
  200. package/src/plugins/builtin/correlation/geo.ts +34 -0
  201. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  202. package/src/plugins/builtin/correlation/headless.ts +42 -8
  203. package/src/plugins/builtin/correlation/history.ts +2 -0
  204. package/src/plugins/builtin/correlation/index.tsx +68 -14
  205. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  206. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  207. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  208. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  209. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  210. package/src/plugins/builtin/correlation/settings.ts +1 -1
  211. package/src/plugins/builtin/cot/headless.ts +6 -3
  212. package/src/plugins/builtin/cot/index.tsx +3 -3
  213. package/src/plugins/builtin/cot/model.ts +36 -3
  214. package/src/plugins/builtin/cpi/headless.ts +4 -0
  215. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  216. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  217. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  218. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  219. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  220. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  221. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  222. package/src/plugins/builtin/debt-maturities/wall-bar.tsx +14 -41
  223. package/src/plugins/builtin/dividend-yield/client.ts +4 -2
  224. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  225. package/src/plugins/builtin/doe/headless.ts +7 -0
  226. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  227. package/src/plugins/builtin/earnings/client.ts +2 -2
  228. package/src/plugins/builtin/earnings/headless.ts +5 -0
  229. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  230. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  231. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  232. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  234. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  235. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  236. package/src/plugins/builtin/econ/index.tsx +1 -0
  237. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  238. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  239. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  240. package/src/plugins/builtin/executives/index.tsx +1 -0
  241. package/src/plugins/builtin/exposure/headless.ts +3 -1
  242. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  243. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  244. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  245. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  246. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  247. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  248. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  249. package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
  250. package/src/plugins/builtin/fx-matrix/index.tsx +71 -33
  251. package/src/plugins/builtin/fx-matrix/live-legs.ts +17 -0
  252. package/src/plugins/builtin/fx-matrix/pairs.ts +68 -14
  253. package/src/plugins/builtin/fx-matrix/settings.ts +62 -0
  254. package/src/plugins/builtin/gpu/headless.ts +7 -3
  255. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  256. package/src/plugins/builtin/gpu/model.ts +27 -1
  257. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  258. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  259. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  260. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  261. package/src/plugins/builtin/holders/headless.ts +139 -5
  262. package/src/plugins/builtin/holders/index.tsx +10 -3
  263. package/src/plugins/builtin/holders/pane.tsx +20 -3
  264. package/src/plugins/builtin/holders/report-header.ts +95 -0
  265. package/src/plugins/builtin/holders/table-model.ts +20 -5
  266. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  267. package/src/plugins/builtin/holders/types.ts +2 -1
  268. package/src/plugins/builtin/insider/headless.ts +2 -0
  269. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  270. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  271. package/src/plugins/builtin/jobs/headless.ts +1 -0
  272. package/src/plugins/builtin/jobs/index.tsx +1 -0
  273. package/src/plugins/builtin/jobs/share-bars.tsx +5 -28
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  275. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  276. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +30 -2
  278. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  279. package/src/plugins/builtin/market-heatmap/index.tsx +245 -81
  280. package/src/plugins/builtin/market-heatmap/live.ts +88 -0
  281. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  282. package/src/plugins/builtin/market-heatmap/portfolio.ts +50 -11
  283. package/src/plugins/builtin/market-movers/client.ts +3 -2
  284. package/src/plugins/builtin/market-movers/headless.ts +10 -1
  285. package/src/plugins/builtin/market-movers/screener.ts +18 -5
  286. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  287. package/src/plugins/builtin/members/client.ts +53 -0
  288. package/src/plugins/builtin/members/headless.ts +35 -0
  289. package/src/plugins/builtin/members/index.tsx +20 -0
  290. package/src/plugins/builtin/members/model.ts +68 -0
  291. package/src/plugins/builtin/members/pane.tsx +175 -0
  292. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  293. package/src/plugins/builtin/mna/headless.ts +1 -0
  294. package/src/plugins/builtin/mna/index.tsx +2 -1
  295. package/src/plugins/builtin/mna/pane.tsx +5 -2
  296. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  297. package/src/plugins/builtin/news/headless.ts +2 -0
  298. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  299. package/src/plugins/builtin/notes/cli.ts +14 -6
  300. package/src/plugins/builtin/notes/index.tsx +1 -0
  301. package/src/plugins/builtin/openfda/index.tsx +1 -0
  302. package/src/plugins/builtin/options/index.tsx +2 -1
  303. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  304. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  305. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  306. package/src/plugins/builtin/pe-band/client.ts +74 -2
  307. package/src/plugins/builtin/pe-band/headless.ts +26 -8
  308. package/src/plugins/builtin/pe-band/model.ts +102 -22
  309. package/src/plugins/builtin/pe-band/pane.tsx +34 -16
  310. package/src/plugins/builtin/pe-band/report-dates.ts +78 -0
  311. package/src/plugins/builtin/perps/client.ts +66 -5
  312. package/src/plugins/builtin/perps/equity-identity.ts +16 -0
  313. package/src/plugins/builtin/perps/equity-row.tsx +6 -4
  314. package/src/plugins/builtin/perps/headless.ts +144 -16
  315. package/src/plugins/builtin/perps/index.tsx +6 -6
  316. package/src/plugins/builtin/perps/model.ts +189 -12
  317. package/src/plugins/builtin/perps/pane.tsx +248 -47
  318. package/src/plugins/builtin/plugin-marketplace/activation.ts +19 -0
  319. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -2
  320. package/src/plugins/builtin/plugin-marketplace/store.ts +2 -0
  321. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  322. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  323. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  324. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  325. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  326. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  327. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  328. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  329. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  330. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  331. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  332. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  333. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  334. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  335. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +43 -2
  336. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +48 -5
  337. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  338. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  339. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  340. package/src/plugins/builtin/power/headless.ts +3 -1
  341. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  342. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  343. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  344. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  345. package/src/plugins/builtin/research/events-headless.ts +2 -0
  346. package/src/plugins/builtin/research/headless.ts +2 -0
  347. package/src/plugins/builtin/research/index.tsx +1 -0
  348. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  349. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  350. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  351. package/src/plugins/builtin/research-search/index.tsx +1 -0
  352. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  353. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  354. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  355. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  356. package/src/plugins/builtin/scanner/headless.ts +14 -0
  357. package/src/plugins/builtin/scanner/hilo-bars.tsx +12 -70
  358. package/src/plugins/builtin/scanner/hilo-model.ts +0 -18
  359. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  360. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  361. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  362. package/src/plugins/builtin/sec/forms.ts +12 -0
  363. package/src/plugins/builtin/sec/headless.ts +2 -0
  364. package/src/plugins/builtin/sectors/client.ts +1 -0
  365. package/src/plugins/builtin/sectors/headless.ts +9 -1
  366. package/src/plugins/builtin/sectors/move-bar.tsx +10 -32
  367. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  368. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  369. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  370. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  371. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  372. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  373. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  374. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  375. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  376. package/src/plugins/builtin/social-mentions/index.tsx +1 -1
  377. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  378. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  379. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  380. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  381. package/src/plugins/builtin/themes/headless.ts +3 -0
  382. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  383. package/src/plugins/builtin/thirteenf/index.tsx +2 -1
  384. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  385. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  386. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  387. package/src/plugins/builtin/ticker-detail/overview/components.tsx +5 -60
  388. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  389. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +1 -1
  390. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  391. package/src/plugins/builtin/ticker-detail/pane.tsx +1 -1
  392. package/src/plugins/builtin/ticker-detail/quote-footer.ts +7 -1
  393. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -4
  394. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -2
  395. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  396. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  397. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  398. package/src/plugins/builtin/volatility/headless.ts +6 -1
  399. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  400. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  401. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  402. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  403. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  404. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  405. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  406. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  407. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  408. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  409. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  410. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  411. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  412. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  413. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  414. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  415. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  416. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  417. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  418. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  564. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  565. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  566. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  567. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  568. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  569. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  570. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  571. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  572. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  573. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  574. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  575. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  576. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  577. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  578. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  579. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  580. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  581. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  582. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  583. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  584. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  585. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  586. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  587. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  588. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  589. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  590. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  591. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  592. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  593. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  594. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  595. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  596. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  597. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  598. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  599. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  600. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  601. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  602. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  603. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  604. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  605. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  606. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  607. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  608. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  609. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  610. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  611. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  612. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  613. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  614. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  615. package/src/plugins/catalog-browser.ts +2 -0
  616. package/src/plugins/catalog-manifest.ts +1 -0
  617. package/src/plugins/catalog-ui.ts +3 -1
  618. package/src/plugins/catalog.ts +1 -1
  619. package/src/plugins/host-link.ts +33 -1
  620. package/src/plugins/installer.ts +48 -12
  621. package/src/plugins/loader.ts +30 -3
  622. package/src/plugins/manager-node.ts +15 -5
  623. package/src/plugins/registry/index.ts +1 -1
  624. package/src/plugins/registry/pane-settings.ts +26 -7
  625. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  626. package/src/renderers/dom/build-assets.ts +81 -3
  627. package/src/renderers/dom/data-table/row.tsx +52 -16
  628. package/src/renderers/dom/desktop/controls.tsx +7 -3
  629. package/src/renderers/dom/desktop/icons.tsx +1 -0
  630. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  631. package/src/renderers/dom/desktop/popover.tsx +73 -14
  632. package/src/renderers/dom/host/box.tsx +69 -26
  633. package/src/renderers/dom/host/input.tsx +14 -3
  634. package/src/renderers/dom/host/tabs.tsx +37 -1
  635. package/src/renderers/dom/styles.css +41 -7
  636. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +11 -6
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  638. package/src/renderers/electrobun/bun/desktop/update.ts +181 -87
  639. package/src/renderers/electrobun/bun/index.ts +73 -6
  640. package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
  641. package/src/renderers/electrobun/shared/protocol.ts +3 -0
  642. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  643. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -1
  644. package/src/renderers/electrobun/view/main.tsx +7 -1
  645. package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
  646. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  647. package/src/renderers/electrobun/view/update-host.ts +45 -21
  648. package/src/renderers/opentui/host.tsx +5 -0
  649. package/src/sources/gloomberb-cloud/index.ts +25 -4
  650. package/src/sources/gloomberb-cloud/normalizers.ts +36 -5
  651. package/src/sources/provider-router/cached-routes.ts +4 -1
  652. package/src/sources/provider-router/financials.ts +11 -8
  653. package/src/sources/provider-router/history.ts +15 -10
  654. package/src/sources/sec-registrant.ts +75 -0
  655. package/src/state/app/context/index.tsx +24 -8
  656. package/src/state/hooks/quote-streaming.ts +41 -26
  657. package/src/sync/core-contributors.ts +4 -0
  658. package/src/telemetry/benign-window-error.ts +7 -0
  659. package/src/telemetry/crash-reports-dom.ts +2 -0
  660. package/src/telemetry/crash-reports.ts +4 -1
  661. package/src/test-support/cli-context.ts +10 -2
  662. package/src/test-support/daily-nav.ts +25 -0
  663. package/src/theme/heat-colors.ts +357 -2
  664. package/src/tickers/instrument-kind.ts +34 -7
  665. package/src/tickers/search/asset-classes.ts +24 -9
  666. package/src/tickers/search/index.ts +44 -11
  667. package/src/tickers/search/ranking.ts +23 -3
  668. package/src/time-series/resolve.ts +1 -1
  669. package/src/time-series/use-chart-resolution.ts +1 -1
  670. package/src/types/context-menu.ts +6 -0
  671. package/src/types/data-provider.ts +2 -0
  672. package/src/types/financials.ts +5 -0
  673. package/src/types/headless.ts +79 -2
  674. package/src/types/plugin.ts +38 -6
  675. package/src/types/ticker.ts +15 -0
  676. package/src/ui/context-menu.tsx +1 -0
  677. package/src/ui/host.tsx +16 -0
  678. package/src/updater/index.ts +38 -13
  679. package/src/utils/async-deadline.ts +21 -0
  680. package/src/utils/cli-output.ts +5 -1
  681. package/src/utils/crypto-pair.ts +41 -0
  682. package/src/utils/currency-units.ts +14 -0
  683. package/src/utils/depositary-receipt.ts +26 -0
  684. package/src/utils/exchanges.ts +72 -0
  685. package/src/utils/fire-and-forget.ts +24 -0
  686. package/src/utils/format.ts +13 -0
  687. package/src/utils/price-earnings.ts +70 -3
  688. package/src/utils/price-history.ts +58 -13
  689. package/src/utils/rpc-timeout-error.ts +27 -0
  690. package/src/utils/sec.ts +13 -1
  691. package/src/utils/utc-time.ts +79 -0
@@ -5,13 +5,28 @@ import { compositeAxisTicks } from "../../../components/chart/composite/format";
5
5
  import type { CompositeAxisDomain } from "../../../components/chart/composite/types";
6
6
  import { FUTURES_CONTRACTS, tickDecimals } from "../futures/contracts";
7
7
  import { formatPercentileRank } from "../../../utils/format";
8
+ import { cryptoPairCoin, isFuturesSymbol } from "../shared/crypto-pair";
9
+ import { parseReportTime } from "../../../utils/utc-time";
8
10
  import { compareSortValues, type SortDirection } from "../../../utils/sort-values";
9
11
 
12
+ /**
13
+ * CME crypto futures have a curve but no FUT board row, so their name and tick
14
+ * live here rather than in the catalogue the board lists. Outright ticks:
15
+ * BTC $5, ETH $0.50, SOL $0.05, XRP $0.0005 (each $25 a contract).
16
+ */
17
+ const CRYPTO_CURVE_ROOTS: readonly { code: string; name: string; tick: number }[] = [
18
+ { code: "BTC", name: "Bitcoin", tick: 5 }, { code: "ETH", name: "Ether", tick: 0.5 },
19
+ { code: "SOL", name: "Solana", tick: 0.05 }, { code: "XRP", name: "XRP", tick: 0.0005 },
20
+ ];
21
+ const rootLabel = (code: string, name: string) => name === code ? code : `${code} ${name}`;
22
+
10
23
  export const CURVE_ROOTS = [
11
- ...FUTURES_CONTRACTS.filter((row) => row.curve !== false).map((row) => ({ value: row.code, label: `${row.code} ${row.name}` })),
24
+ ...FUTURES_CONTRACTS.filter((row) => row.curve !== false).map((row) => ({ value: row.code, label: rootLabel(row.code, row.name) })),
12
25
  { value: "VX", label: "VX VIX Futures" },
26
+ ...CRYPTO_CURVE_ROOTS.map((row) => ({ value: row.code, label: rootLabel(row.code, row.name) })),
13
27
  ];
14
28
 
29
+ /** A root typed as an argument, a stored pane setting or a request: every listed root, crypto included. */
15
30
  export function normalizeCurveRoot(value: unknown): string | null {
16
31
  if (typeof value !== "string") return null;
17
32
  const raw = value.trim().toUpperCase().replace(/=F$/, "");
@@ -19,6 +34,26 @@ export function normalizeCurveRoot(value: unknown): string | null {
19
34
  return CURVE_ROOTS.some((row) => row.value === root) ? root : null;
20
35
  }
21
36
 
37
+ /** What a root nothing lists is told, with roots that work. */
38
+ export function unsupportedCurveRootMessage(value: unknown): string {
39
+ const examples = ["ES", "CL", "GC", "ZN"].filter((root) => CURVE_ROOTS.some((row) => row.value === root));
40
+ return `Unsupported futures root: ${String(value)}. Try ${examples.join(", ")}.`;
41
+ }
42
+
43
+ /**
44
+ * The root for the ticker under the cursor, when CTM is opened without an
45
+ * argument. The crypto roots are also US ticker symbols (Grayscale's mini
46
+ * trusts are BTC and ETH), so a bare one is the equity's, never the future's:
47
+ * only a futures symbol (BTC=F) or a coin pair (BTC-USD) names the crypto curve.
48
+ */
49
+ export function curveRootForTicker(ticker: unknown): string | null {
50
+ if (typeof ticker !== "string") return null;
51
+ const coin = cryptoPairCoin(ticker);
52
+ if (coin) return CRYPTO_CURVE_ROOTS.some((row) => row.code === coin) ? coin : null;
53
+ const root = normalizeCurveRoot(ticker);
54
+ return root && CRYPTO_CURVE_ROOTS.some((row) => row.code === root) && !isFuturesSymbol(ticker) ? null : root;
55
+ }
56
+
22
57
  /**
23
58
  * Treasury futures trade in fractions of a 32nd (ZT to 1/256, ZF 1/128, ZN
24
59
  * 1/64, ZB and UB 1/32), so the catalogue leaves their tick unset. A root
@@ -32,7 +67,7 @@ function curvePriceDecimals(root: string): number {
32
67
  const rateTick = RATE_TICKS[root];
33
68
  if (rateTick != null) return tickDecimals(rateTick);
34
69
  if (root === "VX") return 4;
35
- const tick = FUTURES_CONTRACTS.find((row) => row.code === root)?.tick;
70
+ const tick = FUTURES_CONTRACTS.find((row) => row.code === root)?.tick ?? CRYPTO_CURVE_ROOTS.find((row) => row.code === root)?.tick;
36
71
  return tick == null ? 5 : Math.max(2, tickDecimals(tick));
37
72
  }
38
73
 
@@ -155,17 +190,166 @@ const CHANGE_COLUMNS: Readonly<Record<string, CurveLookback>> = { change1w: "1W"
155
190
 
156
191
  type CurveSortKey = "symbol" | "expiration" | "price" | "change" | "openInterest" | "volume" | "percentile" | "asOf";
157
192
 
193
+ const BASIS_COLUMNS: Readonly<Record<string, "vsSpotPct" | "annualisedBasisPct">> = { vsSpot: "vsSpotPct", annBasis: "annualisedBasisPct" };
194
+
158
195
  export function sortCurveContracts(rows: readonly FuturesContract[], id: string, direction: SortDirection,
159
- changes?: CurveContractChanges): FuturesContract[] {
196
+ changes?: CurveContractChanges, basis?: CurveBasisRows): FuturesContract[] {
160
197
  const keys: Record<string, CurveSortKey> = { symbol: "symbol", expiry: "expiration", price: "price", change: "change", oi: "openInterest", volume: "volume", percentile: "percentile", asOf: "asOf" };
161
198
  const key = keys[id] ?? "expiration";
162
199
  const lookback = CHANGE_COLUMNS[id];
163
- const value = (row: FuturesContract) => lookback ? changes?.get(row.symbol)?.[lookback] ?? null : row[key] ?? null;
200
+ const basisKey = BASIS_COLUMNS[id];
201
+ const value = (row: FuturesContract) => lookback ? changes?.get(row.symbol)?.[lookback] ?? null
202
+ : basisKey ? basis?.get(row.symbol)?.[basisKey] ?? null : row[key] ?? null;
164
203
  return [...rows].sort((a, b) => compareSortValues(value(a), value(b), direction));
165
204
  }
166
205
 
167
206
  const DAY_MS = 86_400_000;
168
207
 
208
+ /**
209
+ * The quote each crypto root's basis is measured against: the terminal's USD
210
+ * pair, the same quote every other pane shows, not the CME reference rate, so
211
+ * a small gap to settlement is expected. A root missing here has no basis
212
+ * columns; add its spot symbol to give it some.
213
+ */
214
+ const BASIS_SPOT_SYMBOLS: ReadonlyMap<string, string> = new Map([
215
+ ["BTC", "BTC-USD"], ["ETH", "ETH-USD"], ["SOL", "SOL-USD"], ["XRP", "XRP-USD"],
216
+ ]);
217
+
218
+ export function basisSpotSymbol(root: string): string | null {
219
+ return BASIS_SPOT_SYMBOLS.get(root) ?? null;
220
+ }
221
+
222
+ /** Crypto trades around the clock, so a spot quote older than this no longer prices a live future. */
223
+ const SPOT_STALE_MS = 10 * 60_000;
224
+ /** A free account's quote is held back on purpose; that delay is not staleness, so it is allowed on top. */
225
+ const DELAYED_SPOT_MS = 15 * 60_000;
226
+
227
+ /** The spot a curve's basis uses, or why there is none. `asOf` is the quote's own time, never the fetch's. */
228
+ export interface CurveSpot {
229
+ symbol: string;
230
+ price: number | null;
231
+ /** UTC ISO instant of the quote. */
232
+ asOf: string | null;
233
+ status: "ok" | "stale" | "missing";
234
+ reason: string | null;
235
+ }
236
+
237
+ export interface SpotQuote {
238
+ price: number;
239
+ lastUpdated: number;
240
+ stale?: boolean;
241
+ dataSource?: string;
242
+ }
243
+
244
+ const positive = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value) && value > 0;
245
+
246
+ function quoteAge(ms: number): string {
247
+ const minutes = Math.floor(ms / 60_000);
248
+ if (minutes < 60) return `${minutes}m`;
249
+ const hours = Math.floor(minutes / 60);
250
+ return hours < 48 ? `${hours}h` : `${Math.floor(hours / 24)}d`;
251
+ }
252
+
253
+ /** Judges one spot quote at `now`: usable, older than the rule allows, or absent. */
254
+ export function curveSpot(symbol: string, quote: SpotQuote | null | undefined, now: number): CurveSpot {
255
+ if (!quote || !positive(quote.price)) return { symbol, price: null, asOf: null, status: "missing", reason: `no ${symbol} quote` };
256
+ const time = quote.lastUpdated;
257
+ if (!Number.isFinite(time) || time <= 0) {
258
+ return { symbol, price: quote.price, asOf: null, status: "stale", reason: `${symbol} quote has no time` };
259
+ }
260
+ const asOf = new Date(time).toISOString().replace(".000Z", "Z");
261
+ const age = now - time;
262
+ const limit = SPOT_STALE_MS + (quote.dataSource === "delayed" ? DELAYED_SPOT_MS : 0);
263
+ if (age > limit) return { symbol, price: quote.price, asOf, status: "stale", reason: `${symbol} quote is ${quoteAge(age)} old` };
264
+ if (quote.stale === true) return { symbol, price: quote.price, asOf, status: "stale", reason: `${symbol} quote is stale` };
265
+ return { symbol, price: quote.price, asOf, status: "ok", reason: null };
266
+ }
267
+
268
+ /** How far the contract trades over (positive) or under spot, in percent. */
269
+ export function vsSpotPct(futures: number | null | undefined, spot: number | null | undefined): number | null {
270
+ return positive(futures) && positive(spot) ? (futures / spot - 1) * 100 : null;
271
+ }
272
+
273
+ /** Calendar days from the UTC date of the spot's timestamp to the contract's expiration date. */
274
+ export function daysToExpiry(spotTime: number, expiration: string): number | null {
275
+ const expiry = Date.parse(`${expiration}T00:00:00Z`);
276
+ if (!Number.isFinite(spotTime) || !Number.isFinite(expiry)) return null;
277
+ const spotDay = Date.parse(`${new Date(spotTime).toISOString().slice(0, 10)}T00:00:00Z`);
278
+ return Math.round((expiry - spotDay) / DAY_MS);
279
+ }
280
+
281
+ /** The premium over spot as a yearly rate, on a 365-day year. Blank on or past expiry, where it has no meaning. */
282
+ export function annualisedBasisPct(futures: number | null | undefined, spot: number | null | undefined, days: number | null): number | null {
283
+ if (!positive(futures) || !positive(spot) || days == null || !Number.isFinite(days) || days <= 0) return null;
284
+ return (futures / spot - 1) * 365 / days * 100;
285
+ }
286
+
287
+ /** A contract that printed longer than this before the spot quote is too old to read against it. */
288
+ const CONTRACT_PRINT_MAX_AGE_MS = 60 * 60_000;
289
+
290
+ export interface ContractBasis {
291
+ vsSpotPct: number | null;
292
+ annualisedBasisPct: number | null;
293
+ /** Priced and not flagged stale, but its last print is over an hour before spot: a thin month. */
294
+ thin: boolean;
295
+ }
296
+
297
+ const NO_BASIS: ContractBasis = { vsSpotPct: null, annualisedBasisPct: null, thin: false };
298
+
299
+ /**
300
+ * One contract's basis. Blank without a usable spot, and for a contract whose
301
+ * own price is missing or stale: an old future against a live spot misleads.
302
+ * So does a print more than an hour older than the spot (`thin`): the server's
303
+ * stale flag allows two sessions, which a quote that moves all day outruns. A
304
+ * date-only `asOf` is UTC midnight of that date, one that cannot be read
305
+ * counts as old, and a print newer than the spot is never old (the spot of a
306
+ * delayed feed trails the futures).
307
+ */
308
+ export function contractBasis(row: Pick<FuturesContract, "price" | "stale" | "expiration" | "asOf">, spot: CurveSpot): ContractBasis {
309
+ if (spot.status !== "ok" || spot.asOf == null || row.stale) return NO_BASIS;
310
+ const vs = vsSpotPct(row.price, spot.price);
311
+ if (vs == null) return NO_BASIS;
312
+ const spotTime = Date.parse(spot.asOf);
313
+ const printed = parseReportTime(row.asOf)?.time ?? null;
314
+ if (printed == null || spotTime - printed > CONTRACT_PRINT_MAX_AGE_MS) return { ...NO_BASIS, thin: true };
315
+ return { vsSpotPct: vs, annualisedBasisPct: annualisedBasisPct(row.price, spot.price, daysToExpiry(spotTime, row.expiration)), thin: false };
316
+ }
317
+
318
+ /** A basis in percent, signed; one that rounds to zero stays unsigned. */
319
+ export function curveBasisPercent(value: number | null, decimals: number): string {
320
+ if (value == null) return "--";
321
+ const text = Math.abs(value).toFixed(decimals);
322
+ return /[1-9]/.test(text) ? `${value > 0 ? "+" : "-"}${text}%` : `${text}%`;
323
+ }
324
+
325
+ /** The footer's spot: `spot BTC-USD 67,250.10 · 07:25 UTC`, with the date when the quote is not from today. */
326
+ export function curveSpotLabel(spot: CurveSpot, root: string, now: number): string {
327
+ const decimals = curvePriceDecimals(root);
328
+ const price = spot.price == null ? "--" : spot.price.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
329
+ const stamp = curveTimestamp(spot.asOf);
330
+ const today = new Date(now).toISOString().slice(0, 10);
331
+ return `spot ${spot.symbol} ${price} · ${spot.asOf?.startsWith(today) ? stamp.slice(11) : stamp} UTC`;
332
+ }
333
+
334
+ /** The basis columns' sort values, which live beside the contract rather than on it. */
335
+ export type CurveBasisRows = ReadonlyMap<string, ContractBasis>;
336
+
337
+ export function curveBasisRows(rows: readonly FuturesContract[], spot: CurveSpot | null): CurveBasisRows {
338
+ return new Map(rows.map((row) => [row.symbol, spot ? contractBasis(row, spot) : NO_BASIS]));
339
+ }
340
+
341
+ /** The contracts a good spot left blank for their old print. */
342
+ export function thinContractCount(basis: Iterable<ContractBasis>): number {
343
+ let count = 0;
344
+ for (const row of basis) if (row.thin) count += 1;
345
+ return count;
346
+ }
347
+
348
+ /** The footer warning that thin contracts have no basis: the pane's `!` notice, which keeps the footer row for the quote times. */
349
+ export function thinContractsNotice(count: number): string {
350
+ return `Basis blank on ${count} thin contract${count === 1 ? "" : "s"} (last print over 1h before spot)`;
351
+ }
352
+
169
353
  /** A past date for the as-of view: empty (or "latest") for the live curve, else YYYY-MM-DD no later than today. */
170
354
  export function curveAsOfDate(value: unknown, now = new Date()): string {
171
355
  const date = typeof value === "string" ? value.trim() : "";
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { chartTableChromeRows, ChartTableHeader, CurveSurface, curveGhostColors, DataTableView, Notice, PaneStatusBody, QueryBar, useChartTableSelection, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, type DataTableColumn, type StatItem } from "../../../components";
3
+ import { fitChartTableColumns } from "../../../components/chart-table";
3
4
  import { curveStrip, curveSurfaceMinRows } from "../../../components/chart/curve";
4
5
  import { isAccessDenied } from "../../../api-client/errors";
5
6
  import type { FuturesContract } from "../../../api-client/futures-curve";
@@ -13,8 +14,9 @@ import { nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
13
14
  import { useAutoRefresh } from "../../../react/auto-refresh";
14
15
  import { futuresSessionRefreshInterval } from "../shared/futures-session";
15
16
  import { useResearchCloudSession } from "../shared/research-cloud-session";
17
+ import { useQuoteBoard } from "../shared/use-quote-board";
16
18
  import { getCachedFuturesCurve, loadFuturesCurve, loadFuturesCurveAsOf } from "./client";
17
- import { curveAsOfDate, curveAxisPrice, curveChangeText, curveContractChanges, curvePrice, curveRank, curveTimestamp, DEFAULT_CURVE_HORIZON, futuresCurveSeries, newestQuote, normalizeCurveRoot, sortCurveContracts, type CurveContractChanges } from "./model";
19
+ import { basisSpotSymbol, curveAsOfDate, curveAxisPrice, curveBasisPercent, curveBasisRows, curveChangeText, curveContractChanges, curvePrice, curveRank, curveSpot, curveSpotLabel, curveTimestamp, DEFAULT_CURVE_HORIZON, futuresCurveSeries, newestQuote, normalizeCurveRoot, sortCurveContracts, thinContractCount, thinContractsNotice, unsupportedCurveRootMessage, type CurveContractChanges } from "./model";
18
20
 
19
21
  const TABS = [{ value: "curve", label: "Curve" }, { value: "contracts", label: "Contracts" }];
20
22
  const COLUMNS: DataTableColumn[] = [
@@ -38,6 +40,19 @@ const CURVE_COLUMNS: DataTableColumn[] = [
38
40
  { id: "change1m", label: "VS 1M", width: 10, align: "right" },
39
41
  ...COLUMNS.slice(4, -1),
40
42
  ];
43
+ // A crypto curve also reads against spot. Its widths are the tightest each column
44
+ // holds (BTCV26.CME, 128000.00), so the two new columns fit beside the rest, and
45
+ // a narrow pane drops the least useful ones before a number would be clipped.
46
+ const BASIS_CURVE_COLUMNS: DataTableColumn[] = [
47
+ { ...CURVE_COLUMNS[0]!, width: 10 }, CURVE_COLUMNS[1]!, { ...CURVE_COLUMNS[2]!, width: 10 },
48
+ { id: "vsSpot", label: "VS SPOT", width: 9, align: "right" },
49
+ { id: "annBasis", label: "ANN BASIS", width: 11, align: "right" },
50
+ ...CURVE_COLUMNS.slice(3),
51
+ ];
52
+ const BASIS_DROP_ORDER = ["percentile", "volume", "change1m", "oi"];
53
+ const NO_SYMBOLS: string[] = [];
54
+ /** The spot's age is judged against the clock, so a quote that stops moving still goes stale on screen. */
55
+ const SPOT_CLOCK_MS = 30_000;
41
56
  const signedPercent = (value: number | null) => value == null ? "--" : formatPercentRaw(value);
42
57
  const integer = (value: number | null) => value == null ? "--" : value.toLocaleString("en-US");
43
58
  const contractKey = (row: FuturesContract) => row.symbol;
@@ -50,7 +65,7 @@ export function FuturesCurvePane(props: PaneProps) {
50
65
  const requested = pane?.settings?.root ?? pane?.params?.root ?? "ES";
51
66
  const root = normalizeCurveRoot(requested);
52
67
  return root ? <FuturesCurveView key={root} {...props} root={root} />
53
- : <PaneStatusBody error={`Unsupported futures root: ${String(requested)}`} subject="futures curve" />;
68
+ : <PaneStatusBody error={unsupportedCurveRootMessage(requested)} subject="futures curve" />;
54
69
  }
55
70
 
56
71
  function FuturesCurveView({ width, height, focused, root }: PaneProps & { root: string }) {
@@ -79,6 +94,23 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
79
94
  // A pending date change must never relabel the previous curve as the new one.
80
95
  const data = resource.data?.date === requestedDate ? resource.data.payload : null;
81
96
  usePaneTitle(`CTM ${root}`);
97
+ const curveTab = tab === "curve";
98
+ // The Curve tab of a crypto root reads against spot; a past date has no spot history.
99
+ const spotSymbol = basisSpotSymbol(root);
100
+ const basisActive = curveTab && !requestedDate && spotSymbol != null;
101
+ const spotSymbols = useMemo(() => basisActive ? [spotSymbol] : NO_SYMBOLS, [basisActive, spotSymbol]);
102
+ const { quotes: spotQuotes } = useQuoteBoard(spotSymbols);
103
+ const [clock, setClock] = useState(() => Date.now());
104
+ useEffect(() => {
105
+ if (!basisActive) return;
106
+ setClock(Date.now());
107
+ const timer = setInterval(() => setClock(Date.now()), SPOT_CLOCK_MS);
108
+ return () => clearInterval(timer);
109
+ }, [basisActive]);
110
+ const spotState = spotSymbol ? spotQuotes.get(spotSymbol) : undefined;
111
+ // Until the first answer there is nothing to say: no reason, only blank cells.
112
+ const spot = useMemo(() => !basisActive || !spotSymbol || !spotState || spotState.loading && !spotState.quote ? null
113
+ : curveSpot(spotSymbol, spotState.quote, clock), [basisActive, clock, spotState, spotSymbol]);
82
114
  const staleCount = data?.contracts.filter((row) => row.stale).length ?? 0;
83
115
  const newest = data ? newestQuote(data.contracts) : null;
84
116
  // A past curve is named by the session it holds: a weekend or holiday date shows the session before it.
@@ -86,8 +118,9 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
86
118
  requestedDate ? Date.parse(`${requestedDate}T00:00:00Z`) : Date.now(), requestedDate ? data.asOf ?? requestedDate : undefined) : [],
87
119
  [data, colors, horizon, requestedDate]);
88
120
  const changes = useMemo<CurveContractChanges>(() => data ? curveContractChanges(data) : new Map(), [data]);
89
- const rows = useMemo(() => sortCurveContracts(data?.contracts ?? [], sort.columnId, sort.direction, changes), [data, sort, changes]);
90
- const curveTab = tab === "curve";
121
+ const basis = useMemo(() => curveBasisRows(basisActive ? data?.contracts ?? [] : [], spot), [basisActive, data, spot]);
122
+ const thinCount = useMemo(() => thinContractCount(basis.values()), [basis]);
123
+ const rows = useMemo(() => sortCurveContracts(data?.contracts ?? [], sort.columnId, sort.direction, changes, basis), [data, sort, changes, basis]);
91
124
  // The curve tab lists the contracts the chart plots; Contracts keeps every one.
92
125
  const curveRows = useMemo(() => {
93
126
  const charted = new Set(curves[0]?.points.map((point) => point.id));
@@ -108,7 +141,8 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
108
141
  const { strip: tabStrip, rows: tabRows } = usePaneTabs({ tabs: TABS, activeValue: tab, onSelect: setTab, focused, dense: true });
109
142
  // The query bar takes one row below the tabs, and a refused date one more.
110
143
  const bodyHeight = Math.max(1, height - tabRows - 1 - (dateError ? 1 : 0));
111
- const columns = curveTab ? CURVE_COLUMNS : COLUMNS;
144
+ const columns = useMemo(() => !curveTab ? COLUMNS : basisActive ? fitChartTableColumns(BASIS_CURVE_COLUMNS, width, BASIS_DROP_ORDER) : CURVE_COLUMNS,
145
+ [basisActive, curveTab, width]);
112
146
  const formatValue = useCallback((value: number) => curvePrice(value, root), [root]);
113
147
  const formatChange = useCallback((value: number) => curveChangeText(value, root), [root]);
114
148
  const caption = `${sentenceCase(data?.quoteUnit ?? data?.currency ?? "price")} by contract month`;
@@ -142,7 +176,9 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
142
176
  };
143
177
  // A past date with no archived curve says why in the body, not behind the warning.
144
178
  const emptyPast = !!requestedDate && !!data && !data.contracts.length;
145
- usePaneNoticeFooter({ registrationId: "futures-curve:notices", focused, notices: emptyPast ? [] : data?.gaps ?? [] });
179
+ // Thin contracts join the data warnings: the info row has no room for them beside the quote times.
180
+ usePaneNoticeFooter({ registrationId: "futures-curve:notices", focused,
181
+ notices: emptyPast ? [] : [...data?.gaps ?? [], ...thinCount ? [thinContractsNotice(thinCount)] : []] });
146
182
  const delay = Math.max(0, ...(data?.contracts.map((row) => row.delayMinutes ?? 0) ?? []));
147
183
  usePaneStatusFooter({ registrationId: "futures-curve", loading: resource.loading, error: resource.error,
148
184
  hints: [
@@ -152,6 +188,8 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
152
188
  info: data ? [
153
189
  { id: "source", parts: [{ text: `${requestedDate ? "daily archive" : data.source === "cboe" ? "settlement" : delay > 0 ? `${delay}m delayed` : "dated quotes"} · ${data.quoteUnit ?? data.currency ?? "units unavailable"} · ${curveTimestamp(newest)}${newest?.includes("T") ? " UTC" : ""}`, tone: "muted" }] },
154
190
  ...(staleCount ? [{ id: "stale", parts: [{ text: `${staleCount} of ${data.contracts.length} stale`, tone: "warning" as const }] }] : []),
191
+ ...(spot ? [{ id: "spot", parts: [spot.status === "ok" ? { text: curveSpotLabel(spot, root, clock), tone: "muted" as const }
192
+ : { text: `Basis blank: ${spot.reason}`, tone: "warning" as const }] }] : []),
155
193
  ] : [],
156
194
  });
157
195
  const tableChromeRows = chartTableChromeRows(columns, width);
@@ -166,8 +204,8 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
166
204
  const renderCell = useCallback((row: FuturesContract, column: DataTableColumn) => {
167
205
  if (column.id === "symbol") return { text: row.symbol };
168
206
  if (column.id === "expiry") return { text: row.expiration, color: colors.textMuted };
169
- // Without the AS OF column, a stale quote shows on its price.
170
- if (column.id === "price") return { text: curvePrice(row.price, root), color: curveTab && row.stale ? colors.warning : undefined };
207
+ // Without the AS OF column, a stale quote shows on its price, and so does a print too old for the spot.
208
+ if (column.id === "price") return { text: curvePrice(row.price, root), color: curveTab && (row.stale || basis.get(row.symbol)?.thin) ? colors.warning : undefined };
171
209
  if (column.id === "change") {
172
210
  const text = curveChangeText(row.change ?? null, root);
173
211
  return { text, color: text.startsWith("+") ? colors.positive : text.startsWith("-") && /[1-9]/.test(text) ? colors.negative : colors.textMuted };
@@ -176,11 +214,15 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
176
214
  const change = changes.get(row.symbol)?.[column.id === "change1w" ? "1W" : "1M"] ?? null;
177
215
  return { text: curveChangeText(change, root), color: colors.textMuted };
178
216
  }
217
+ if (column.id === "vsSpot" || column.id === "annBasis") {
218
+ const cell = basis.get(row.symbol);
219
+ return { text: column.id === "vsSpot" ? curveBasisPercent(cell?.vsSpotPct ?? null, 2) : curveBasisPercent(cell?.annualisedBasisPct ?? null, 1), color: colors.textMuted };
220
+ }
179
221
  if (column.id === "percentile") return { text: row.samples < 2 || row.percentile == null ? "--" : row.percentile.toFixed(0) };
180
222
  if (column.id === "oi") return { text: integer(row.openInterest) };
181
223
  if (column.id === "volume") return { text: integer(row.volume) };
182
224
  return { text: curveTimestamp(row.asOf), color: row.stale ? colors.warning : colors.textMuted };
183
- }, [changes, colors, curveTab, root]);
225
+ }, [basis, changes, colors, curveTab, root]);
184
226
  return <Box width={width} height={height} flexDirection="column">
185
227
  {tabStrip}
186
228
  <QueryBar width={width} filters={[{
@@ -0,0 +1,81 @@
1
+ import { colors } from "../../../theme/colors";
2
+ import { blendHex, contrastRatio } from "../../../theme/color-utils";
3
+
4
+ /** A cross that moved at least this many percent from its previous close earns the step. */
5
+ export const TINT_THRESHOLDS = [0.05, 0.15, 0.3, 0.6, 1] as const;
6
+ /**
7
+ * A step holds until the move falls this far below the threshold that earned
8
+ * it: a fifth of the threshold, and never less than 0.02% (about 2 pips on
9
+ * EUR/USD, the size a quote jitters between ticks).
10
+ */
11
+ const RELEASE_BAND_SHARE = 0.2;
12
+ const RELEASE_BAND_MIN = 0.02;
13
+ /** How far each step leans toward the theme's up or down colour. */
14
+ const TINT_STRENGTH = [0.14, 0.2, 0.27, 0.35, 0.44] as const;
15
+ const MIN_TEXT_CONTRAST = 4.5;
16
+
17
+ /**
18
+ * The cross's move since the previous close, in percent: the row currency
19
+ * against the column currency, so a positive move is the row currency gaining.
20
+ * Each rate and reference is USD per unit.
21
+ */
22
+ export function crossMovePercent(
23
+ rowRate: number,
24
+ rowReference: number,
25
+ columnRate: number,
26
+ columnReference: number,
27
+ ): number | null {
28
+ const move = (rowRate / rowReference / (columnRate / columnReference) - 1) * 100;
29
+ return Number.isFinite(move) ? move : null;
30
+ }
31
+
32
+ /**
33
+ * The signed tint step (0 for none, 1 to 5 up, -1 to -5 down) of a move.
34
+ * `held` is the step the cell showed last: it stays while the move is still
35
+ * within the release band of its threshold, so a rate ticking around a
36
+ * boundary does not flicker between two shades. A move of the other sign
37
+ * starts afresh.
38
+ */
39
+ export function tintLevel(movePercent: number, held = 0): number {
40
+ const sign = Math.sign(movePercent);
41
+ if (sign === 0) return 0;
42
+ const size = Math.abs(movePercent);
43
+ const earned = TINT_THRESHOLDS.filter((threshold) => size >= threshold).length;
44
+ const kept = sign === Math.sign(held) ? Math.abs(held) : 0;
45
+ const heldThreshold = kept > 0 ? TINT_THRESHOLDS[kept - 1]! : 0;
46
+ const holds = kept > earned && size >= heldThreshold - Math.max(heldThreshold * RELEASE_BAND_SHARE, RELEASE_BAND_MIN);
47
+ const level = holds ? kept : earned;
48
+ return level === 0 ? 0 : sign * level;
49
+ }
50
+
51
+ /** The step a cell shows now, remembering it under `key` for the next tick; null forgets it. */
52
+ export function nextTintLevel(held: Map<string, number>, key: string, movePercent: number | null): number {
53
+ const level = movePercent === null ? 0 : tintLevel(movePercent, held.get(key) ?? 0);
54
+ if (level === 0) held.delete(key);
55
+ else held.set(key, level);
56
+ return level;
57
+ }
58
+
59
+ const tints = new Map<string, string | undefined>();
60
+
61
+ /**
62
+ * A cell background for a tint step: the theme's own up or down colour
63
+ * blended into `background`. The strength comes down from the step's until
64
+ * `text` reads as well on it as on the plain background (4.5 to 1 where the
65
+ * theme allows), so the numbers stay the primary element in every theme.
66
+ */
67
+ export function directionTint(level: number, surface: { background: string; text: string }): string | undefined {
68
+ if (level === 0) return undefined;
69
+ const hue = level > 0 ? colors.positive : colors.negative;
70
+ const key = `${level}|${hue}|${surface.background}|${surface.text}`;
71
+ if (tints.has(key)) return tints.get(key);
72
+ const floor = Math.min(MIN_TEXT_CONTRAST, contrastRatio(surface.text, surface.background) * 0.9);
73
+ let tint: string | undefined;
74
+ for (let strength = TINT_STRENGTH[Math.abs(level) - 1]!; strength > 0; strength = Math.round((strength - 0.02) * 100) / 100) {
75
+ const candidate = blendHex(surface.background, hue, strength);
76
+ if (contrastRatio(surface.text, candidate) >= floor) { tint = candidate; break; }
77
+ }
78
+ if (tints.size > 512) tints.clear();
79
+ tints.set(key, tint);
80
+ return tint;
81
+ }