gloomberb 0.16.4 → 0.16.5

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 (554) hide show
  1. package/README.md +14 -91
  2. package/package.json +4 -1
  3. package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
  4. package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
  5. package/src/account/billing.ts +57 -0
  6. package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
  7. package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
  8. package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
  9. package/src/account/session.ts +16 -0
  10. package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
  11. package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
  12. package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
  13. package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
  14. package/src/api-client/data.ts +24 -2
  15. package/src/api-client/earnings.ts +6 -0
  16. package/src/api-client/futures-curve.ts +56 -0
  17. package/src/api-client/index.ts +4 -1
  18. package/src/api-client/market-discovery.ts +10 -1
  19. package/src/api-client/paths.ts +7 -0
  20. package/src/api-client/research-activity.ts +15 -10
  21. package/src/api-client/types.ts +17 -1
  22. package/src/api-client/yield-curves.ts +8 -0
  23. package/src/app/global-shortcuts.ts +15 -1
  24. package/src/app/pane-runtime/ticker-open-runtime.ts +7 -3
  25. package/src/architecture/core-builtin-allowlist.json +4 -13
  26. package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
  27. package/src/capabilities/chart-series.ts +11 -1
  28. package/src/cli/commands/crypto-hints.ts +7 -0
  29. package/src/cli/commands/fred-series.ts +61 -0
  30. package/src/cli/commands/market.ts +407 -104
  31. package/src/cli/commands/news-hints.ts +62 -0
  32. package/src/cli/commands/news-search.ts +53 -0
  33. package/src/cli/commands/options-leaps.ts +5 -2
  34. package/src/cli/commands/overview.ts +93 -20
  35. package/src/cli/commands/remote.ts +2 -1
  36. package/src/cli/commands/search.ts +82 -9
  37. package/src/cli/commands/system.ts +55 -8
  38. package/src/cli/commands/ticker.ts +88 -11
  39. package/src/cli/context.ts +45 -14
  40. package/src/cli/desktop-pane-shot.ts +59 -7
  41. package/src/cli/earnings-calendar.ts +71 -0
  42. package/src/cli/freshness.ts +18 -7
  43. package/src/cli/glossary.ts +429 -0
  44. package/src/cli/history-rows.ts +48 -7
  45. package/src/cli/index-roots.ts +7 -0
  46. package/src/cli/index.ts +21 -7
  47. package/src/cli/known-gaps.ts +27 -0
  48. package/src/cli/listing-arg.ts +417 -56
  49. package/src/cli/not-a-ticker.ts +12 -5
  50. package/src/cli/pane-functions/access-gate.ts +5 -0
  51. package/src/cli/pane-functions/capabilities.ts +3 -2
  52. package/src/cli/pane-functions/catalog.ts +35 -5
  53. package/src/cli/pane-functions/data.ts +12 -8
  54. package/src/cli/pane-functions/discovery.ts +1 -0
  55. package/src/cli/pane-functions/dom.ts +20 -6
  56. package/src/cli/pane-functions/freshness.ts +363 -43
  57. package/src/cli/pane-functions/function-help.ts +12 -5
  58. package/src/cli/pane-functions/headless.ts +45 -17
  59. package/src/cli/pane-functions/index.ts +63 -10
  60. package/src/cli/pane-functions/market-session.ts +22 -8
  61. package/src/cli/pane-functions/options.ts +63 -22
  62. package/src/cli/pane-functions/report-notices.ts +25 -7
  63. package/src/cli/pane-functions/report.ts +15 -8
  64. package/src/cli/pane-functions/resolver.ts +49 -12
  65. package/src/cli/pane-functions/screenshot.ts +110 -23
  66. package/src/cli/report-tables.ts +3 -0
  67. package/src/cli/result.ts +4 -1
  68. package/src/cli/staged-tickers.ts +52 -0
  69. package/src/cli/symbol-args.ts +9 -0
  70. package/src/cli/symbol-list.ts +172 -0
  71. package/src/cli/ticker-resolution.ts +223 -30
  72. package/src/cli/types.ts +5 -0
  73. package/src/components/chart/composite/axis-overlays.tsx +6 -2
  74. package/src/components/chart/composite/composite-chart.tsx +40 -6
  75. package/src/components/chart/composite/legend.tsx +49 -2
  76. package/src/components/chart/composite/panel-surface.tsx +44 -17
  77. package/src/components/chart/composite/price-axis-labels.tsx +3 -1
  78. package/src/components/command-bar/panel/index.tsx +11 -3
  79. package/src/components/command-bar/panel/runtime.ts +3 -0
  80. package/src/components/command-bar/panel/types.ts +2 -0
  81. package/src/components/command-bar/routes/root/provider-search.ts +4 -3
  82. package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
  83. package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
  84. package/src/components/command-bar/workflow/tickers.ts +23 -4
  85. package/src/components/layout/detached-pane-shell.tsx +9 -0
  86. package/src/components/layout/floating-pane.tsx +3 -0
  87. package/src/components/layout/header.tsx +20 -5
  88. package/src/components/layout/pane/content.tsx +7 -13
  89. package/src/components/layout/pane/footer/index.tsx +6 -2
  90. package/src/components/layout/pane/footer/model.ts +35 -1
  91. package/src/components/layout/pane/header.tsx +24 -1
  92. package/src/components/layout/pane/index.tsx +12 -2
  93. package/src/components/layout/pane/sidebar.tsx +78 -10
  94. package/src/components/layout/shell/active-drag.ts +21 -5
  95. package/src/components/layout/shell/drag/index.ts +39 -9
  96. package/src/components/layout/shell/drag/live.ts +61 -51
  97. package/src/components/layout/shell/drag/runtime.ts +54 -12
  98. package/src/components/layout/shell/index.tsx +25 -7
  99. package/src/components/layout/shell/menu.ts +5 -0
  100. package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
  101. package/src/components/layout/shell/pane/layers.tsx +192 -45
  102. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
  103. package/src/components/markdown-text.tsx +62 -20
  104. package/src/components/metric-treemap/heat-surface.tsx +10 -1
  105. package/src/components/onboarding/account-step/form-panel.tsx +1 -1
  106. package/src/components/onboarding/account-step/index.tsx +1 -1
  107. package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
  108. package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
  109. package/src/components/onboarding/onboarding-keyboard.ts +1 -1
  110. package/src/components/onboarding/onboarding-wizard.tsx +2 -2
  111. package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
  112. package/src/components/onboarding/wizard-account.ts +2 -2
  113. package/src/components/pane-template-wizard.tsx +9 -1
  114. package/src/components/price-selector-dialog.tsx +11 -2
  115. package/src/components/sign-in-gate.tsx +3 -3
  116. package/src/components/ticker/list-table-view.tsx +24 -1
  117. package/src/components/ui/action-row.tsx +2 -1
  118. package/src/components/ui/button.tsx +7 -1
  119. package/src/components/ui/choice-dialog.tsx +6 -4
  120. package/src/components/ui/confirm-dialog.tsx +28 -18
  121. package/src/components/ui/data-table/index.tsx +17 -6
  122. package/src/components/ui/data-table/opentui/index.tsx +37 -6
  123. package/src/components/ui/data-table/types.ts +12 -0
  124. package/src/components/ui/dialog-actions.tsx +64 -0
  125. package/src/components/ui/display.tsx +17 -3
  126. package/src/components/ui/frame.tsx +35 -2
  127. package/src/components/ui/icon.tsx +2 -0
  128. package/src/components/ui/text-prompt-dialog.tsx +11 -9
  129. package/src/data/cached-query.ts +2 -0
  130. package/src/data/config/store/normalize.ts +7 -2
  131. package/src/data/ticker-repository.ts +5 -0
  132. package/src/data/ticker-store.ts +15 -0
  133. package/src/docs/content.ts +215 -0
  134. package/src/docs/load.ts +139 -0
  135. package/src/docs/requests.ts +39 -0
  136. package/src/docs/snapshot.chunk.ts +8 -0
  137. package/src/docs/snapshot.json +7416 -0
  138. package/src/docs/topics.ts +216 -0
  139. package/src/i18n/es.ts +61 -4
  140. package/src/i18n/ja.ts +61 -4
  141. package/src/i18n/ko.ts +61 -4
  142. package/src/i18n/zh-cn.ts +61 -4
  143. package/src/i18n/zh-tw.ts +61 -4
  144. package/src/layout/desks.ts +1 -1
  145. package/src/layout/pane-manager/dock-tree.ts +18 -0
  146. package/src/layout/pane-manager/index.ts +1 -0
  147. package/src/market-data/coordinator/chart.ts +10 -4
  148. package/src/market-data/coordinator/index.ts +6 -3
  149. package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
  150. package/src/market-data/futures-contract.ts +151 -0
  151. package/src/market-data/hooks.tsx +10 -2
  152. package/src/market-data/market/format.ts +39 -21
  153. package/src/market-data/market/freshness.ts +81 -18
  154. package/src/market-data/market/status.ts +19 -23
  155. package/src/market-data/published-apac-sessions.ts +23 -1
  156. package/src/market-data/quotes/contributions.ts +3 -0
  157. package/src/market-data/quotes/metadata.ts +7 -24
  158. package/src/market-data/quotes/resolution.ts +2 -0
  159. package/src/market-data/quotes/screener-live-quotes.ts +5 -4
  160. package/src/market-data/result-types.ts +2 -0
  161. package/src/plugins/builtin/account-management/model.ts +1 -56
  162. package/src/plugins/builtin/account-management/pane.tsx +6 -4
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
  164. package/src/plugins/builtin/alerts/cli.ts +313 -47
  165. package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
  166. package/src/plugins/builtin/alerts/events.ts +23 -2
  167. package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
  168. package/src/plugins/builtin/alerts/history.ts +2 -0
  169. package/src/plugins/builtin/alerts/index.tsx +41 -9
  170. package/src/plugins/builtin/alerts/pane.tsx +8 -0
  171. package/src/plugins/builtin/alerts/research-builder.ts +8 -3
  172. package/src/plugins/builtin/alerts/research-rules.ts +18 -2
  173. package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
  174. package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
  175. package/src/plugins/builtin/alerts/scheduled.ts +254 -0
  176. package/src/plugins/builtin/alerts/targets.ts +92 -0
  177. package/src/plugins/builtin/analytics/index.tsx +4 -2
  178. package/src/plugins/builtin/analytics/risk-headless.ts +30 -12
  179. package/src/plugins/builtin/analytics/risk-math.ts +91 -27
  180. package/src/plugins/builtin/analytics/risk-model.ts +35 -5
  181. package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
  182. package/src/plugins/builtin/attention/index.tsx +2 -0
  183. package/src/plugins/builtin/attention/pane.tsx +3 -1
  184. package/src/plugins/builtin/attention/series.ts +26 -0
  185. package/src/plugins/builtin/awards/index.tsx +2 -0
  186. package/src/plugins/builtin/awards/pane.tsx +3 -1
  187. package/src/plugins/builtin/awards/series.ts +29 -0
  188. package/src/plugins/builtin/bond-calculator/model.ts +2 -1
  189. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  190. package/src/plugins/builtin/cds/client.ts +70 -13
  191. package/src/plugins/builtin/cds/headless.ts +116 -19
  192. package/src/plugins/builtin/cds/index.tsx +3 -1
  193. package/src/plugins/builtin/cds/model.ts +193 -31
  194. package/src/plugins/builtin/cds/pane.tsx +56 -14
  195. package/src/plugins/builtin/cds/series.ts +19 -0
  196. package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
  197. package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
  198. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
  199. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
  200. package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
  201. package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
  202. package/src/plugins/builtin/chart-composer/headless.ts +8 -3
  203. package/src/plugins/builtin/chart-composer/index.tsx +9 -2
  204. package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
  205. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
  206. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
  207. package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
  208. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
  209. package/src/plugins/builtin/chat/controller/index.ts +2 -0
  210. package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
  211. package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
  212. package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
  213. package/src/plugins/builtin/cloud/askg/model.ts +27 -8
  214. package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
  215. package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
  216. package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
  217. package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
  218. package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
  219. package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
  220. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  221. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
  222. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
  223. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
  224. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
  225. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
  226. package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
  227. package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
  228. package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
  229. package/src/plugins/builtin/company-attention/headless.ts +19 -5
  230. package/src/plugins/builtin/company-attention/index.tsx +2 -0
  231. package/src/plugins/builtin/company-attention/model.ts +26 -7
  232. package/src/plugins/builtin/company-attention/pane.tsx +12 -9
  233. package/src/plugins/builtin/company-attention/series.ts +52 -0
  234. package/src/plugins/builtin/company-kpis/pane.tsx +4 -2
  235. package/src/plugins/builtin/composite-plugins.ts +19 -13
  236. package/src/plugins/builtin/cot/index.tsx +3 -1
  237. package/src/plugins/builtin/cot/model.ts +40 -11
  238. package/src/plugins/builtin/cot/series.ts +27 -0
  239. package/src/plugins/builtin/credit-boards/index.tsx +2 -0
  240. package/src/plugins/builtin/credit-boards/series.ts +66 -0
  241. package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
  242. package/src/plugins/builtin/credit-conditions/series.ts +26 -0
  243. package/src/plugins/builtin/credit-documents/headless.ts +31 -1
  244. package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
  245. package/src/plugins/builtin/crypto-board/headless.ts +2 -3
  246. package/src/plugins/builtin/crypto-board/index.tsx +2 -0
  247. package/src/plugins/builtin/crypto-board/model.ts +12 -0
  248. package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
  249. package/src/plugins/builtin/crypto-board/series.ts +48 -0
  250. package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
  251. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  252. package/src/plugins/builtin/debt-maturities/series.ts +45 -0
  253. package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
  254. package/src/plugins/builtin/dividend-yield/index.tsx +2 -0
  255. package/src/plugins/builtin/dividend-yield/series.ts +20 -0
  256. package/src/plugins/builtin/docs/blocks.tsx +181 -0
  257. package/src/plugins/builtin/docs/index.tsx +68 -0
  258. package/src/plugins/builtin/docs/pane.tsx +426 -0
  259. package/src/plugins/builtin/docs/targets.ts +72 -0
  260. package/src/plugins/builtin/docs/use-docs.ts +60 -0
  261. package/src/plugins/builtin/doe/index.tsx +2 -0
  262. package/src/plugins/builtin/doe/series.ts +62 -0
  263. package/src/plugins/builtin/earnings/client.ts +3 -2
  264. package/src/plugins/builtin/earnings/format.ts +1 -5
  265. package/src/plugins/builtin/earnings/headless.ts +58 -6
  266. package/src/plugins/builtin/earnings/history-model.ts +15 -1
  267. package/src/plugins/builtin/earnings/history-table.ts +13 -2
  268. package/src/plugins/builtin/earnings/history.tsx +7 -3
  269. package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
  270. package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
  271. package/src/plugins/builtin/econ/index.tsx +2 -0
  272. package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
  273. package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
  274. package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
  275. package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
  276. package/src/plugins/builtin/exposure/holdings.ts +1 -1
  277. package/src/plugins/builtin/exposure/pane.tsx +3 -1
  278. package/src/plugins/builtin/fear-greed/index.tsx +4 -0
  279. package/src/plugins/builtin/fear-greed/series.ts +52 -0
  280. package/src/plugins/builtin/futures/index.tsx +43 -3
  281. package/src/plugins/builtin/futures/model.ts +1 -1
  282. package/src/plugins/builtin/futures/returns.ts +1 -1
  283. package/src/plugins/builtin/futures/series.ts +13 -0
  284. package/src/plugins/builtin/futures/table.tsx +1 -1
  285. package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
  286. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  287. package/src/plugins/builtin/futures-curve/headless.ts +70 -9
  288. package/src/plugins/builtin/futures-curve/index.tsx +1 -1
  289. package/src/plugins/builtin/futures-curve/model.ts +66 -11
  290. package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
  291. package/src/plugins/builtin/fx-matrix/index.tsx +1 -1
  292. package/src/plugins/builtin/gpu/board.tsx +13 -10
  293. package/src/plugins/builtin/gpu/changes.tsx +4 -4
  294. package/src/plugins/builtin/gpu/client.ts +19 -1
  295. package/src/plugins/builtin/gpu/equities.tsx +14 -10
  296. package/src/plugins/builtin/gpu/headless.ts +56 -25
  297. package/src/plugins/builtin/gpu/history.tsx +3 -3
  298. package/src/plugins/builtin/gpu/index.tsx +2 -0
  299. package/src/plugins/builtin/gpu/model.ts +101 -16
  300. package/src/plugins/builtin/gpu/pane.tsx +28 -12
  301. package/src/plugins/builtin/gpu/series.ts +38 -0
  302. package/src/plugins/builtin/help/function-card.tsx +25 -6
  303. package/src/plugins/builtin/help/index.tsx +17 -8
  304. package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
  305. package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
  306. package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
  307. package/src/plugins/builtin/holders/client.ts +22 -3
  308. package/src/plugins/builtin/holders/format.ts +18 -3
  309. package/src/plugins/builtin/holders/headless.ts +36 -133
  310. package/src/plugins/builtin/holders/pane.tsx +11 -4
  311. package/src/plugins/builtin/holders/report-header.ts +40 -5
  312. package/src/plugins/builtin/holders/report-rows.ts +240 -0
  313. package/src/plugins/builtin/insider/client.ts +35 -20
  314. package/src/plugins/builtin/insider/headless.ts +41 -11
  315. package/src/plugins/builtin/insider/index.tsx +26 -9
  316. package/src/plugins/builtin/insider/insider-data.ts +10 -0
  317. package/src/plugins/builtin/insider/issuer.ts +49 -0
  318. package/src/plugins/builtin/insider/model.ts +3 -0
  319. package/src/plugins/builtin/iv-history/client.ts +14 -4
  320. package/src/plugins/builtin/iv-history/headless.ts +7 -4
  321. package/src/plugins/builtin/iv-history/index.tsx +2 -0
  322. package/src/plugins/builtin/iv-history/model.ts +111 -7
  323. package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
  324. package/src/plugins/builtin/iv-history/series.ts +29 -0
  325. package/src/plugins/builtin/jobs/headless.ts +28 -3
  326. package/src/plugins/builtin/jobs/index.tsx +3 -1
  327. package/src/plugins/builtin/jobs/series.ts +19 -0
  328. package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
  329. package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
  330. package/src/plugins/builtin/macro-day/client.ts +44 -3
  331. package/src/plugins/builtin/macro-day/headless.ts +13 -10
  332. package/src/plugins/builtin/macro-day/model.ts +56 -10
  333. package/src/plugins/builtin/macro-day/pane.tsx +30 -20
  334. package/src/plugins/builtin/market-heatmap/model.ts +19 -11
  335. package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
  336. package/src/plugins/builtin/market-movers/client.ts +7 -5
  337. package/src/plugins/builtin/market-movers/index.tsx +3 -2
  338. package/src/plugins/builtin/market-movers/model.ts +30 -0
  339. package/src/plugins/builtin/market-movers/screener.ts +31 -0
  340. package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
  341. package/src/plugins/builtin/market-valuation/index.tsx +3 -3
  342. package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
  343. package/src/plugins/builtin/members/headless.ts +7 -2
  344. package/src/plugins/builtin/members/model.ts +31 -3
  345. package/src/plugins/builtin/members/pane.tsx +3 -6
  346. package/src/plugins/builtin/mna/headless.ts +15 -3
  347. package/src/plugins/builtin/money-markets/index.tsx +2 -0
  348. package/src/plugins/builtin/money-markets/series.ts +41 -0
  349. package/src/plugins/builtin/notes/cli.ts +12 -7
  350. package/src/plugins/builtin/options/view.tsx +1 -1
  351. package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
  352. package/src/plugins/builtin/options-positioning/client.ts +7 -3
  353. package/src/plugins/builtin/options-positioning/headless.ts +1 -0
  354. package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
  355. package/src/plugins/builtin/options-scenario/client.ts +2 -1
  356. package/src/plugins/builtin/options-scenario/headless.ts +1 -0
  357. package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
  358. package/src/plugins/builtin/perps/client.ts +40 -6
  359. package/src/plugins/builtin/perps/equity-row.tsx +33 -11
  360. package/src/plugins/builtin/perps/freshness.ts +66 -0
  361. package/src/plugins/builtin/perps/headless.ts +45 -30
  362. package/src/plugins/builtin/perps/index.tsx +18 -1
  363. package/src/plugins/builtin/perps/model.ts +20 -5
  364. package/src/plugins/builtin/perps/pane.tsx +76 -20
  365. package/src/plugins/builtin/perps/series.ts +76 -0
  366. package/src/plugins/builtin/plugin-marketplace/pane.tsx +67 -61
  367. package/src/plugins/builtin/plugin-module.ts +3 -0
  368. package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
  369. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
  370. package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
  371. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +331 -65
  372. package/src/plugins/builtin/portfolio-list/cli/render.ts +200 -31
  373. package/src/plugins/builtin/portfolio-list/cli/shared.ts +24 -0
  374. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
  375. package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
  376. package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
  377. package/src/plugins/builtin/portfolio-list/headless.ts +34 -4
  378. package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
  379. package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
  380. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
  381. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
  382. package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
  383. package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
  384. package/src/plugins/builtin/power/client.ts +16 -0
  385. package/src/plugins/builtin/power/evidence.ts +6 -2
  386. package/src/plugins/builtin/power/headless.ts +71 -7
  387. package/src/plugins/builtin/power/index.tsx +4 -1
  388. package/src/plugins/builtin/power/model.ts +72 -1
  389. package/src/plugins/builtin/power/pane.tsx +32 -9
  390. package/src/plugins/builtin/rate-path/headless.ts +11 -3
  391. package/src/plugins/builtin/realized-vol/headless.ts +5 -0
  392. package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
  393. package/src/plugins/builtin/research/event-model.ts +68 -24
  394. package/src/plugins/builtin/research/events-headless.ts +2 -1
  395. package/src/plugins/builtin/research/headless.ts +2 -1
  396. package/src/plugins/builtin/seasonality/client.ts +15 -7
  397. package/src/plugins/builtin/seasonality/headless.ts +36 -12
  398. package/src/plugins/builtin/seasonality/index.tsx +2 -2
  399. package/src/plugins/builtin/seasonality/model.ts +119 -0
  400. package/src/plugins/builtin/seasonality/pane.tsx +108 -37
  401. package/src/plugins/builtin/series-inventory.ts +91 -0
  402. package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
  403. package/src/plugins/builtin/shared/command-tab.ts +30 -12
  404. package/src/plugins/builtin/shared/earnings-report.ts +55 -0
  405. package/src/plugins/builtin/shared/fred-points.ts +20 -0
  406. package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
  407. package/src/plugins/builtin/shared/open-description.ts +29 -0
  408. package/src/plugins/builtin/shared/report-freshness.ts +9 -0
  409. package/src/plugins/builtin/shared/series-warning.ts +5 -0
  410. package/src/plugins/builtin/short-interest/headless.ts +1 -0
  411. package/src/plugins/builtin/short-interest/index.tsx +2 -0
  412. package/src/plugins/builtin/short-interest/series.ts +36 -0
  413. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  414. package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
  415. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  416. package/src/plugins/builtin/short-volume/index.tsx +2 -0
  417. package/src/plugins/builtin/short-volume/series.ts +23 -0
  418. package/src/plugins/builtin/social-mentions/index.tsx +2 -0
  419. package/src/plugins/builtin/social-mentions/series.ts +23 -0
  420. package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
  421. package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
  422. package/src/plugins/builtin/thirteenf/api.ts +1 -0
  423. package/src/plugins/builtin/thirteenf/data.ts +6 -4
  424. package/src/plugins/builtin/thirteenf/headless.ts +217 -36
  425. package/src/plugins/builtin/thirteenf/model.ts +39 -1
  426. package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
  427. package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
  428. package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
  429. package/src/plugins/builtin/thirteenf/types.ts +2 -0
  430. package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
  431. package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
  432. package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
  433. package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
  434. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
  435. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
  436. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
  437. package/src/plugins/builtin/time-sales/headless.ts +1 -0
  438. package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
  439. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  440. package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
  441. package/src/plugins/builtin/vol-surface/headless.ts +1 -0
  442. package/src/plugins/builtin/volatility/index.tsx +2 -0
  443. package/src/plugins/builtin/volatility/series.ts +84 -0
  444. package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
  445. package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
  446. package/src/plugins/builtin/world-venue-map/index.ts +2 -1
  447. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
  448. package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
  449. package/src/plugins/builtin/yield-curve/chart.ts +2 -2
  450. package/src/plugins/builtin/yield-curve/curves.ts +21 -8
  451. package/src/plugins/builtin/yield-curve/headless.ts +71 -18
  452. package/src/plugins/builtin/yield-curve/index.tsx +3 -1
  453. package/src/plugins/builtin/yield-curve/series.ts +86 -0
  454. package/src/plugins/catalog-browser.ts +2 -1
  455. package/src/plugins/registry/index.ts +89 -4
  456. package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
  457. package/src/portfolio/position-value.ts +40 -0
  458. package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
  459. package/src/public/time-series.ts +17 -0
  460. package/src/react/auto-refresh.ts +13 -3
  461. package/src/remote/app-host.tsx +20 -4
  462. package/src/remote/confirm-change.tsx +85 -0
  463. package/src/remote/controller-utils.ts +8 -1
  464. package/src/remote/controller.ts +123 -6
  465. package/src/remote/pane-screenshot.ts +47 -0
  466. package/src/remote/schema.ts +62 -0
  467. package/src/remote/types.ts +30 -0
  468. package/src/remote/watchlist.ts +306 -0
  469. package/src/renderers/browser/main.tsx +3 -2
  470. package/src/renderers/cloudflare/worker.ts +1 -1
  471. package/src/renderers/dom/data-table/frozen-column.ts +3 -1
  472. package/src/renderers/dom/data-table/index.tsx +33 -6
  473. package/src/renderers/dom/data-table/row.tsx +27 -1
  474. package/src/renderers/dom/desktop/control-styles.ts +63 -1
  475. package/src/renderers/dom/desktop/controls.tsx +30 -23
  476. package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
  477. package/src/renderers/dom/desktop/icons.tsx +7 -0
  478. package/src/renderers/dom/dom-ui-host.tsx +3 -0
  479. package/src/renderers/dom/host/box.tsx +3 -0
  480. package/src/renderers/dom/host/chart-surface.tsx +6 -2
  481. package/src/renderers/dom/host/live-frame.ts +6 -2
  482. package/src/renderers/dom/host/overflow-fade.ts +14 -1
  483. package/src/renderers/dom/host/size-observer.ts +26 -0
  484. package/src/renderers/dom/host/surface-3d.tsx +7 -2
  485. package/src/renderers/dom/host/text.tsx +1 -1
  486. package/src/renderers/dom/styles.css +61 -0
  487. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +17 -2
  488. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +19 -11
  489. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
  490. package/src/renderers/electrobun/view/main.tsx +2 -0
  491. package/src/series/capability.ts +75 -0
  492. package/src/series/csv.ts +160 -0
  493. package/src/series/files.ts +107 -0
  494. package/src/series/registry.ts +198 -0
  495. package/src/series/resolve.ts +107 -0
  496. package/src/series/standalone.ts +48 -0
  497. package/src/series/types.ts +145 -0
  498. package/src/series/validate.ts +171 -0
  499. package/src/sources/gloomberb-cloud/index.ts +7 -7
  500. package/src/sources/gloomberb-cloud/normalizers.ts +1 -0
  501. package/src/sources/history-result.ts +1 -0
  502. package/src/sources/listing-symbols.ts +4 -1
  503. package/src/sources/provider-router/batches.ts +4 -1
  504. package/src/sources/provider-router/cache.ts +4 -2
  505. package/src/sources/provider-router/cached-routes.ts +1 -1
  506. package/src/sources/provider-router/financial-routes.ts +1 -0
  507. package/src/sources/provider-router/financials.ts +13 -0
  508. package/src/sources/provider-router/history.ts +43 -25
  509. package/src/sources/provider-router/search.ts +1 -0
  510. package/src/sync/core-contributors.ts +1 -0
  511. package/src/tickers/company-name.ts +75 -0
  512. package/src/tickers/instrument-kind.ts +25 -0
  513. package/src/tickers/open-target.ts +3 -2
  514. package/src/tickers/search/index.ts +45 -7
  515. package/src/tickers/search/ranking.ts +33 -17
  516. package/src/tickers/search/types.ts +7 -1
  517. package/src/tickers/us-listing.ts +34 -0
  518. package/src/time-series/hooks.ts +9 -0
  519. package/src/time-series/resolve.ts +95 -16
  520. package/src/time-series/types.ts +4 -2
  521. package/src/time-series/use-chart-resolution.ts +32 -0
  522. package/src/types/config.ts +6 -0
  523. package/src/types/data-provider.ts +5 -0
  524. package/src/types/financials.ts +52 -1
  525. package/src/types/headless.ts +61 -0
  526. package/src/types/plugin.ts +19 -0
  527. package/src/types/price-history.ts +7 -0
  528. package/src/types/ticker.ts +14 -0
  529. package/src/ui/host.tsx +18 -0
  530. package/src/ui/live-resize.ts +81 -0
  531. package/src/ui/live-stretch.ts +121 -0
  532. package/src/ui/measured-cells.ts +58 -0
  533. package/src/ui/pane-fill.ts +11 -0
  534. package/src/utils/basis-points.ts +17 -0
  535. package/src/utils/cli-output.ts +7 -1
  536. package/src/utils/concurrency.ts +25 -0
  537. package/src/utils/crypto-pair.ts +20 -0
  538. package/src/utils/currency-pair.ts +19 -0
  539. package/src/utils/decimal-comma.ts +19 -0
  540. package/src/utils/dom-screenshot-css.ts +265 -0
  541. package/src/utils/dom-screenshot-rasters.ts +276 -0
  542. package/src/utils/dom-screenshot.ts +437 -52
  543. package/src/utils/exchange-rate-snapshot.ts +6 -2
  544. package/src/utils/exchanges.ts +107 -1
  545. package/src/utils/format.ts +16 -3
  546. package/src/utils/fundamentals.ts +66 -0
  547. package/src/utils/futures-generic.ts +6 -12
  548. package/src/utils/fx-market-hours.ts +15 -5
  549. package/src/utils/price-history.ts +99 -42
  550. package/src/utils/sec-eps-basis.ts +24 -11
  551. package/src/utils/sec.ts +61 -7
  552. package/src/utils/utc-time.ts +51 -4
  553. package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
  554. /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  import type { PricePoint } from "../../../types/financials";
2
2
  import { rollingRealizedVolatility } from "../shared/volatility";
3
- import type { IvHistoryPayload, IvMethod, IvPoint, IvScreenRow, IvStats } from "./client";
3
+ import type { IvCoverageStatus, IvHistoryPayload, IvMethod, IvPoint, IvScreenRow, IvStats } from "./client";
4
4
 
5
5
  export const HV_WINDOWS = [20, 30] as const;
6
6
  export type HvWindow = (typeof HV_WINDOWS)[number];
@@ -26,6 +26,44 @@ export interface IvStatRow {
26
26
  }
27
27
  /** Rank and percentile need this many prior sessions, matching the server. */
28
28
  const MIN_RANK_SAMPLES = 120;
29
+ /**
30
+ * Minutes a symbol's first backfill usually takes from the request that
31
+ * queued it: nine in ten have history by then (docs/research-data.md).
32
+ */
33
+ const FIRST_BACKFILL_MINUTES = 10;
34
+ const FIRST_BACKFILL_TEXT = `a first backfill usually takes under ${FIRST_BACKFILL_MINUTES} minutes`;
35
+
36
+ /** "06:41 UTC" today, with the date on another day: "2026-10-09 06:41 UTC". */
37
+ function utcClock(time: number, now: number): string {
38
+ const iso = new Date(time).toISOString();
39
+ return iso.slice(0, 10) === new Date(now).toISOString().slice(0, 10)
40
+ ? `${iso.slice(11, 16)} UTC` : `${iso.slice(0, 16).replace("T", " ")} UTC`;
41
+ }
42
+
43
+ /** When a symbol was queued, or null when the answer does not say (older servers) or says a time still to come. */
44
+ function queuedTime(queuedAt: string | null | undefined, now: number): number | null {
45
+ const time = queuedAt ? Date.parse(queuedAt) : Number.NaN;
46
+ return Number.isFinite(time) && time <= now ? time : null;
47
+ }
48
+
49
+ /**
50
+ * When to look again for history queued at `from`: "a first backfill usually
51
+ * takes under 10 minutes, try again after 06:41 UTC". One queued for longer
52
+ * than that is running late, and says so rather than give a time it cannot
53
+ * know. A view that checks again by itself (`retry: false`) needs no time to
54
+ * come back at.
55
+ */
56
+ function firstBackfillText(from: number, now: number, retry: boolean): string {
57
+ const retryAt = from + FIRST_BACKFILL_MINUTES * 60_000;
58
+ if (retryAt <= now) return `longer than the usual ${FIRST_BACKFILL_MINUTES} minutes${retry ? ", try again shortly" : ""}`;
59
+ return retry ? `${FIRST_BACKFILL_TEXT}, try again after ${utcClock(retryAt, now)}` : FIRST_BACKFILL_TEXT;
60
+ }
61
+
62
+ /** "queued for backfill since 06:31 UTC; a first backfill usually takes under 10 minutes, try again after 06:41 UTC" */
63
+ function queuedText(queuedAt: string | null | undefined, now: number): string {
64
+ const queued = queuedTime(queuedAt, now);
65
+ return `queued for backfill${queued == null ? "" : ` since ${utcClock(queued, now)}`}; ${firstBackfillText(queued ?? now, now, true)}`;
66
+ }
29
67
  export interface IvHistoryModel {
30
68
  symbol: string;
31
69
  status: IvHistoryPayload["status"];
@@ -93,7 +131,7 @@ function byMethod(series: readonly IvPoint[], method: IvMethod, field: "iv30" |
93
131
  */
94
132
  export function projectIvHistory(
95
133
  payload: IvHistoryPayload, prices: readonly PricePoint[],
96
- options: { lookback?: IvLookback; hvWindow?: HvWindow } = {},
134
+ options: { lookback?: IvLookback; hvWindow?: HvWindow; now?: number } = {},
97
135
  ): IvHistoryModel {
98
136
  const lookback = options.lookback ?? "1Y";
99
137
  const window = options.hvWindow ?? 20;
@@ -139,9 +177,12 @@ export function projectIvHistory(
139
177
  seriesStat("spread", `IV 30d - HV ${window}`, "points", spreadAll, "trade-close"),
140
178
  seriesStat("term", "IV 30d / 90d", "ratio", termAll, "trade-close"));
141
179
 
142
- if (payload.status === "queued") warnings.push(`${payload.symbol} implied volatility history is queued; the first backfill takes a few minutes.`);
143
- else if (payload.status === "backfilling") warnings.push(`${payload.symbol} history is still backfilling (through ${payload.coverage?.backfilledThrough ?? "--"}).`);
144
- else if (payload.status === "unavailable" && !payload.warnings.length) warnings.push(`No listed options history found for ${payload.symbol}.`);
180
+ // Worded as VCA words each symbol (screenCoverage).
181
+ if (payload.status === "queued") warnings.push(`${payload.symbol} ${queuedText(payload.coverage?.addedAt, options.now ?? Date.now())}.`);
182
+ else if (payload.status === "backfilling") {
183
+ const through = payload.coverage?.backfilledThrough;
184
+ warnings.push(`${payload.symbol} backfilling${through ? ` through ${through}` : ""}.`);
185
+ } else if (payload.status === "unavailable" && !payload.warnings.length) warnings.push(`No options history for ${payload.symbol}.`);
145
186
  if (!prices.length) warnings.push("Realized volatility unavailable: daily price history is missing.");
146
187
 
147
188
  const tradeIv365 = byMethod(payload.series, "trade-close", "iv365");
@@ -163,7 +204,14 @@ export function projectIvHistory(
163
204
  export type RichCheap = "rich" | "cheap" | "fair" | null;
164
205
  export interface RichCheapRow {
165
206
  symbol: string;
166
- status: IvScreenRow["status"];
207
+ /** Where the symbol's history stands, as HIVG reads it; an older server says only ready or queued. */
208
+ status: IvCoverageStatus;
209
+ /** When the symbol was queued for backfill, when the server says. */
210
+ queuedAt: string | null;
211
+ /** The last session its backfill has reached. */
212
+ backfilledThrough: string | null;
213
+ /** Why an unavailable symbol is not covered yet (demand coverage is full): not final, unlike one with no options history. */
214
+ warning: string | null;
167
215
  iv30: number | null;
168
216
  date: string | null;
169
217
  method: IvMethod | null;
@@ -228,7 +276,9 @@ export function projectRichCheap(rows: readonly IvScreenRow[], hv: ReadonlyMap<s
228
276
  const realized = hv.get(row.symbol) ?? null;
229
277
  const percentile = row.iv30?.percentile ?? null;
230
278
  return {
231
- symbol: row.symbol, status: row.status, iv30, date: latest?.date ?? null, method: latest?.method ?? null,
279
+ symbol: row.symbol, status: row.coverageStatus ?? row.status, queuedAt: row.coverage?.addedAt ?? null,
280
+ backfilledThrough: row.coverage?.backfilledThrough ?? null, warning: row.warnings?.[0] ?? null,
281
+ iv30, date: latest?.date ?? null, method: latest?.method ?? null,
232
282
  rank: row.iv30?.rank ?? null, percentile, rankDate: row.iv30?.date ?? null,
233
283
  termSlope: iv30 != null && latest?.iv90 != null ? iv30 - latest.iv90 : null,
234
284
  iv1y: latest?.iv365 ?? null,
@@ -240,6 +290,60 @@ export function projectRichCheap(rows: readonly IvScreenRow[], hv: ReadonlyMap<s
240
290
  });
241
291
  }
242
292
 
293
+ interface ScreenCoverage {
294
+ /** One line per state, each symbol dated: queued since when, backfilled through which session. */
295
+ notes: string[];
296
+ /** Symbols whose history is still to come or not covered yet: the screen is incomplete while there are any. */
297
+ pending: string[];
298
+ /** Those of them with no reading to show yet. */
299
+ missing: string[];
300
+ }
301
+
302
+ /**
303
+ * What VCA says about symbols without a full history, in the report's notes
304
+ * and the pane's notices. A queued symbol says since when and, with `retry`,
305
+ * when to look again: ten minutes after the newest of them was queued. One
306
+ * backfilling says how far it has got. One that finished with no options
307
+ * history is final and leaves the screen complete; one demand coverage could
308
+ * not take is not final. An older server says only ready or queued, without
309
+ * dates, so its queued symbols are counted from now as before.
310
+ */
311
+ export function screenCoverage(rows: readonly RichCheapRow[], options: { now?: number; retry?: boolean } = {}): ScreenCoverage {
312
+ const now = options.now ?? Date.now();
313
+ const queued = rows.filter((row) => row.status === "queued");
314
+ const backfilling = rows.filter((row) => row.status === "backfilling");
315
+ const final = rows.filter((row) => row.status === "unavailable" && !row.warning);
316
+ const uncovered = rows.filter((row) => row.status === "unavailable" && row.warning);
317
+ const notes: string[] = [];
318
+ if (queued.length) {
319
+ const times = queued.map((row) => queuedTime(row.queuedAt, now));
320
+ const listed = queued.map((row, index) => times[index] == null ? row.symbol : `${row.symbol} since ${utcClock(times[index]!, now)}`);
321
+ const newest = Math.max(...times.map((time) => time ?? now));
322
+ notes.push(`Queued for backfill: ${listed.join(", ")}; ${firstBackfillText(newest, now, options.retry ?? true)}.`);
323
+ }
324
+ if (backfilling.length) {
325
+ notes.push(`Backfilling: ${backfilling.map((row) => row.backfilledThrough ? `${row.symbol} through ${row.backfilledThrough}` : row.symbol).join(", ")}.`);
326
+ }
327
+ if (final.length) notes.push(`No options history: ${final.map((row) => row.symbol).join(", ")}.`);
328
+ const reasons = new Map<string, string[]>();
329
+ for (const row of uncovered) reasons.set(row.warning!, [...(reasons.get(row.warning!) ?? []), row.symbol]);
330
+ for (const [reason, symbols] of reasons) notes.push(`Not queued: ${symbols.join(", ")}. ${reason}`);
331
+ const pending = [...queued, ...backfilling, ...uncovered];
332
+ return { notes, pending: pending.map((row) => row.symbol),
333
+ missing: pending.filter((row) => row.iv30 == null && row.date == null).map((row) => row.symbol) };
334
+ }
335
+
336
+ /**
337
+ * The VCA pane's notices: the report's wording without a time to come back
338
+ * at, since the pane checks again by itself, and a count of ready symbols with
339
+ * no IV30, which is how an older server reports one that found no options.
340
+ */
341
+ export function screenPaneNotices(rows: readonly RichCheapRow[], now = Date.now()): string[] {
342
+ const empty = rows.filter((row) => row.status === "ready" && row.iv30 == null).length;
343
+ return [...screenCoverage(rows, { now, retry: false }).notes,
344
+ ...(empty ? [`${empty} symbol${empty === 1 ? " has" : "s have"} no stored implied volatility yet.`] : [])];
345
+ }
346
+
243
347
  export const VCA_PRESETS = {
244
348
  etfs: ["SPY", "QQQ", "IWM", "DIA", "EEM", "EFA", "FXI", "EWZ", "GLD", "SLV", "GDX", "USO", "TLT", "HYG",
245
349
  "XLE", "XLF", "XLK", "XLV", "XLY", "XLP", "XLI", "XLU", "SMH", "XBI", "KRE"],
@@ -10,7 +10,7 @@ import { useAutoRefresh } from "../../../react/auto-refresh";
10
10
  import { compareSortValues, nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
11
11
  import { loadIvScreen, loadRealizedVolatilities } from "./client";
12
12
  import { formatPoints, formatRank, formatVol, readingLabel, sharedDates, shortDate, VCA_DEFINITIONS, verdictLabel } from "./format";
13
- import { projectRichCheap, type RichCheapRow, richCheapDates, type VcaPreset } from "./model";
13
+ import { projectRichCheap, type RichCheapRow, richCheapDates, screenPaneNotices, type VcaPreset } from "./model";
14
14
  import { vcaUniverse } from "./universe";
15
15
 
16
16
  type SortId = keyof RichCheapRow;
@@ -78,12 +78,8 @@ export function IvScreenPane({ width, height, focused }: PaneProps) {
78
78
  }
79
79
  }), [rows, dates]);
80
80
  const [selected, setSelected] = useState<string | null>(null);
81
- const queued = rows.filter((row) => row.status === "queued").length;
82
- const uncovered = rows.filter((row) => row.status !== "queued" && row.iv30 == null).length;
83
- const notices = [universe.error, resource.error,
84
- ...(queued ? [`${queued} symbol${queued === 1 ? "" : "s"} queued for implied volatility history; they fill in after backfill.`] : []),
85
- ...(uncovered ? [`${uncovered} symbol${uncovered === 1 ? " has" : "s have"} no stored implied volatility yet.`] : [])]
86
- .filter((value): value is string => !!value);
81
+ const coverageNotices = useMemo(() => screenPaneNotices(rows), [rows]);
82
+ const notices = [universe.error, resource.error, ...coverageNotices].filter((value): value is string => !!value);
87
83
  usePaneNoticeFooter({ registrationId: "iv-screen-notices", notices, focused });
88
84
  const openHistory = (row: RichCheapRow) => createPaneFromTemplate("iv-history-pane", { symbol: row.symbol });
89
85
  // The full wording gives way to the short form in a pane too narrow to show the rank date.
@@ -102,8 +98,8 @@ export function IvScreenPane({ width, height, focused }: PaneProps) {
102
98
  switch (id) {
103
99
  case "symbol": return { text: row.symbol, color: colors.textBright };
104
100
  case "iv30": return { text: formatVol(row.iv30), color: colors.warning };
105
- case "date": return { text: row.date ? readingLabel(row.date, row.method, true) : row.status === "queued" ? "queued" : "--",
106
- color: colors.textDim };
101
+ case "date": return { text: row.date ? readingLabel(row.date, row.method, true)
102
+ : row.status === "queued" || row.status === "backfilling" ? row.status : "--", color: colors.textDim };
107
103
  case "rankDate": return { text: row.rankDate ? `${shortDate(row.rankDate)} close` : "--", color: colors.textDim };
108
104
  case "rank": return { text: formatRank(row.rank) };
109
105
  case "percentile": return { text: formatRank(row.percentile) };
@@ -0,0 +1,29 @@
1
+ import type { SeriesDefinition } from "../../../series/types";
2
+ import { loadIvHistory, type IvPoint } from "./client";
3
+
4
+ const TENORS: ReadonlyArray<readonly [key: "iv30" | "iv90" | "iv365", label: string]> = [
5
+ ["iv30", "30-day"],
6
+ ["iv90", "90-day"],
7
+ ["iv365", "1-year"],
8
+ ];
9
+
10
+ /** A ticker's at-the-money implied volatility by tenor, one close per session. */
11
+ export const IV_SERIES: readonly SeriesDefinition[] = TENORS.map(([key, label]): SeriesDefinition => ({
12
+ id: `implied-vol:${key}`,
13
+ name: `${label} implied volatility`,
14
+ unit: "%",
15
+ frequency: "daily",
16
+ group: "Volatility",
17
+ tags: ["IV", "implied volatility", "options", key],
18
+ input: "ticker",
19
+ load: async ({ ticker, signal }) => {
20
+ const payload = await loadIvHistory(ticker!, { ...(signal ? { signal } : {}) });
21
+ return {
22
+ points: payload.series.map((point: IvPoint) => ({
23
+ date: point.sessionDate,
24
+ value: point[key] == null ? null : point[key]! * 100,
25
+ })),
26
+ ...(payload.warnings.length ? { warning: payload.warnings.join("; ") } : {}),
27
+ };
28
+ },
29
+ }));
@@ -1,7 +1,9 @@
1
1
  import type {
2
2
  HeadlessBundleResult,
3
+ HeadlessPaneContext,
3
4
  HeadlessPaneDefinition,
4
5
  } from "../../../types/plugin";
6
+ import { ApiRequestError } from "../../../api-client/errors";
5
7
  import { formatCompact, formatNumber } from "../../../utils/format";
6
8
  import { fetchJobs, fetchJobsMovers, type JobsCloudClient, type JobsCompanyState } from "./client";
7
9
  import {
@@ -13,8 +15,21 @@ import {
13
15
  functionLabel,
14
16
  } from "./model";
15
17
 
16
- function companyBundle(symbol: string, state: JobsCompanyState, roleLimit: number): HeadlessBundleResult {
17
- if (state.kind === "denied") throw new Error(state.message);
18
+ /**
19
+ * What a refused account needs, and the free preview to try instead: HIRE
20
+ * reads the same careers systems, and is named only when it answers for this
21
+ * company.
22
+ */
23
+ async function jobsDeniedMessage(symbol: string | undefined, status: 401 | 402 | 403, client: Pick<HeadlessPaneContext["apiClient"], "getCloudHiring">): Promise<string> {
24
+ const need = status === 401 ? "sign-in" : status === 403 ? "a verified email" : "Gloom Pro";
25
+ const preview = await client.getCloudHiring(symbol, { limit: 1 })
26
+ .then((payload) => !!payload && (symbol ? "symbol" in payload : "companies" in payload), () => false);
27
+ return `JOBS needs ${need}${preview ? `; HIRE${symbol ? ` ${symbol}` : ""} is free to preview` : ""}.`;
28
+ }
29
+
30
+ const DENIED = new Set([401, 402, 403]);
31
+
32
+ function companyBundle(symbol: string, state: Exclude<JobsCompanyState, { kind: "denied" }>, roleLimit: number): HeadlessBundleResult {
18
33
  if (state.kind === "pending") {
19
34
  return {
20
35
  sections: [{ title: "Hiring", entries: [{ label: "Status", value: "pending", formatted: state.message }] }],
@@ -169,15 +184,25 @@ export const jobsHeadless: HeadlessPaneDefinition<"bundle"> = {
169
184
  maximum: 200,
170
185
  },
171
186
  ],
187
+ // No aliases: the template's keywords stay the catalog's search terms.
188
+ discovery: {
189
+ dataRequirements: ["Gloom Cloud Pro account"],
190
+ limitations: ["Pro only; HIRE <ticker> previews a company's hiring for free"],
191
+ },
172
192
  describe: (args) => (args.symbols[0] ? `Hiring | ${args.symbols[0]}` : "Hiring | coverage"),
173
193
  async load(args, ctx): Promise<HeadlessBundleResult> {
174
194
  const limit = Number(args.options.roles);
175
195
  const symbol = args.symbols[0];
176
196
  if (symbol) {
177
197
  const state = await fetchJobs(symbol, { force: true, client: ctx.apiClient as JobsCloudClient });
198
+ if (state.kind === "denied") throw new Error(await jobsDeniedMessage(symbol, state.status, ctx.apiClient));
178
199
  return companyBundle(symbol, state, limit);
179
200
  }
180
- const payload = await fetchJobsMovers(ctx.apiClient as JobsCloudClient);
201
+ const payload = await fetchJobsMovers(ctx.apiClient as JobsCloudClient).catch(async (error: unknown) => {
202
+ const status = error instanceof ApiRequestError ? error.status ?? 0 : 0;
203
+ if (DENIED.has(status)) throw new Error(await jobsDeniedMessage(undefined, status as 401 | 402 | 403, ctx.apiClient));
204
+ throw error;
205
+ });
181
206
  const rows = buildMoverRows(payload.movers).slice(0, Math.max(limit, 15)).map((row) => ({
182
207
  ticker: row.ticker,
183
208
  company: row.company,
@@ -1,12 +1,13 @@
1
1
  import { publicTickerBindingSymbol } from "../../../tickers/selection";
2
2
  import type { PluginModule } from "../plugin-module";
3
+ import { JOBS_SERIES } from "./series";
3
4
  import { resetJobsCache } from "./client";
4
5
  import { jobsHeadless } from "./headless";
5
6
  import { JOBS_PANE_ID, JobsPane, JobsResearchTab } from "./pane";
6
7
 
7
8
 
8
9
  const description =
9
- "Hiring read from the company's own careers system: open roles over time, by function and location, new roles, pay ranges. Alone, every covered company ranked.";
10
+ "Pro hiring read from the company's own careers system: open roles over time, by function and location, new roles, pay ranges. Alone, every covered company ranked.";
10
11
 
11
12
  function explicitSymbol(options?: { arg?: string; symbol?: string | null }): string | null {
12
13
  const symbol = (options?.symbol ?? options?.arg ?? "").trim().toUpperCase();
@@ -14,6 +15,7 @@ function explicitSymbol(options?: { arg?: string; symbol?: string | null }): str
14
15
  }
15
16
 
16
17
  export const jobsModule: PluginModule = {
18
+ series: JOBS_SERIES,
17
19
  setup(ctx) {
18
20
  ctx.registerTickerResearchTab({
19
21
  id: "jobs",
@@ -0,0 +1,19 @@
1
+ import type { SeriesDefinition } from "../../../series/types";
2
+ import { fetchJobs } from "./client";
3
+
4
+ /** A company's open job postings each day, as the jobs pane charts them. */
5
+ export const JOBS_SERIES: readonly SeriesDefinition[] = [{
6
+ id: "jobs:open-roles",
7
+ name: "Open job postings",
8
+ unit: "jobs",
9
+ frequency: "daily",
10
+ group: "Alternative data",
11
+ tags: ["hiring", "jobs", "postings", "headcount"],
12
+ input: "ticker",
13
+ load: async ({ ticker }) => {
14
+ const state = await fetchJobs(ticker!);
15
+ if (state.kind === "pending") throw new Error(state.message || `Postings for ${ticker} are still being collected.`);
16
+ if (state.kind !== "ready") throw new Error(state.message || `No postings for ${ticker}.`);
17
+ return state.summary.series.map((point) => ({ date: point.day, value: point.open }));
18
+ },
19
+ }];
@@ -85,7 +85,8 @@ import {
85
85
  buildKellyHistoryItems,
86
86
  buildKellyResultItems,
87
87
  } from "./sections";
88
- import { getPortfolioPositionValue, resolveActivePortfolioId, resolveKellyBankroll } from "./portfolio";
88
+ import { resolveActivePortfolioId, resolveKellyBankroll } from "./portfolio";
89
+ import { getPortfolioPositionValue } from "../../../portfolio/position-value";
89
90
  import { useKellyCommonAssumptions } from "./state";
90
91
 
91
92
  export function KellySizerPane({ focused, width, height }: PaneProps) {
@@ -1,46 +1,7 @@
1
1
  import type { AppConfig } from "../../../types/config";
2
- import type { TickerFinancials } from "../../../types/financials";
3
2
  import type { TickerRecord } from "../../../types/ticker";
4
- import { convertCurrency } from "../../../utils/format";
5
3
  import { allocationTotal, type AllocationHolding } from "../portfolio-list/allocation";
6
4
  import { getCollectionTypeFromConfig } from "../portfolio-list/pane/data";
7
- import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue } from "../portfolio-list/position-metrics";
8
-
9
- export function getPortfolioPositionValue({
10
- ticker,
11
- financials,
12
- portfolioId,
13
- baseCurrency,
14
- exchangeRates,
15
- }: {
16
- ticker: TickerRecord | null;
17
- financials: TickerFinancials | null;
18
- portfolioId: string | null;
19
- baseCurrency: string;
20
- exchangeRates: Map<string, number>;
21
- }): number {
22
- if (!ticker) return 0;
23
- const scopedTicker: TickerRecord = portfolioId
24
- ? {
25
- ...ticker,
26
- metadata: {
27
- ...ticker.metadata,
28
- positions: ticker.metadata.positions.filter((position) => position.portfolio === portfolioId),
29
- },
30
- }
31
- : ticker;
32
- const quote = financials?.quote;
33
-
34
- const quoteCurrency = quote?.currency || ticker.metadata.currency || baseCurrency;
35
- const metrics = getPortfolioPositionMetrics(scopedTicker, undefined, quoteCurrency, {
36
- currency: baseCurrency, convert: (value, currency) => convertCurrency(value, currency, baseCurrency, exchangeRates),
37
- }, quote);
38
- const activeQuote = getPortfolioQuoteDisplay(metrics, quote);
39
-
40
- return resolvePortfolioMarketValue(metrics, activeQuote
41
- ? convertCurrency(activeQuote.price, quoteCurrency, baseCurrency, exchangeRates) : null)?.gross
42
- ?? (metrics.positionCount > 0 ? Number.NaN : 0);
43
- }
44
5
 
45
6
  /**
46
7
  * What the portfolio is worth to bet with: its equity. A broker account's is
@@ -7,7 +7,9 @@ import type { DataProvider } from "../../../types/data-provider";
7
7
  import type { PricePoint } from "../../../types/financials";
8
8
  import { abortable, abortError } from "../../../utils/async-deadline";
9
9
  import { errorMessage } from "../../../utils/errors";
10
- import { resolveExchangeTimeZone } from "../../../utils/exchanges";
10
+ import { parsePublicTickerKey, resolveExchangeTimeZone } from "../../../utils/exchanges";
11
+ import { loadEarningsHistory } from "../earnings/client";
12
+ import type { MacroDayEarningsReport } from "./model";
11
13
  import { bundledMacroReleases, mergeMacroReleases, type MacroReleaseList } from "./releases";
12
14
 
13
15
  export const macroReleaseCache = createPluginCache<CloudMacroReleaseDaysPayload>({
@@ -43,11 +45,12 @@ const US_LISTINGS_ONLY = "Macro-day moves are for US listings.";
43
45
 
44
46
  /**
45
47
  * A listing whose venue sits outside New York trades US releases on a different clock.
46
- * A bare symbol such as 7203.T carries no exchange, so its venue suffix names it.
48
+ * A bare symbol such as 7203.T carries no exchange, so its venue suffix names it,
49
+ * and a listing key such as SAP:XETR names it before the ticker record has loaded.
47
50
  */
48
51
  function isNonUsVenue(instrument: InstrumentRef): boolean {
49
52
  if (tickerHasListingSuffix(instrument.symbol)) return true;
50
- const zone = resolveExchangeTimeZone(instrument.exchange);
53
+ const zone = resolveExchangeTimeZone(instrument.exchange || parsePublicTickerKey(instrument.symbol).exchange);
51
54
  return !!zone && zone !== "America/New_York";
52
55
  }
53
56
 
@@ -72,3 +75,41 @@ export async function loadMacroDayHistory(
72
75
  return { history: [], stale: false, error: errorMessage(error), fetchedAt: now };
73
76
  }
74
77
  }
78
+
79
+ export interface MacroDayEarnings {
80
+ reports: MacroDayEarningsReport[];
81
+ stale: boolean;
82
+ /** Why the reports are missing; the study then runs without the earnings marker. */
83
+ error: string | null;
84
+ }
85
+
86
+ /** The earnings history's own depth: the oldest report that can matter is five years back, about twenty quarters. */
87
+ const EARNINGS_REPORT_LIMIT = 24;
88
+
89
+ /**
90
+ * The ticker's own earnings reports, from the history `ERN` shows. Never
91
+ * rejects but on cancel: a listing outside the US or without reports has none,
92
+ * and a failed read leaves the study as it was, with the reason in `error`.
93
+ */
94
+ export async function loadMacroDayEarnings(
95
+ request: { instrument: InstrumentRef; signal?: AbortSignal; forceRefresh?: boolean },
96
+ load: typeof loadEarningsHistory = loadEarningsHistory,
97
+ ): Promise<MacroDayEarnings> {
98
+ if (request.signal?.aborted) throw abortError(CANCELLED);
99
+ if (isNonUsVenue(request.instrument)) return { reports: [], stale: false, error: null };
100
+ try {
101
+ // Earnings are keyed by the bare US symbol; a listing key ("CRWV:XNAS") drops its exchange.
102
+ const symbol = parsePublicTickerKey(request.instrument.symbol.trim()).symbol.toUpperCase();
103
+ const resource = await abortable(load(symbol, request.forceRefresh, EARNINGS_REPORT_LIMIT), request.signal, CANCELLED);
104
+ return { reports: resource.payload.reports.map((report) => ({ date: report.date, timing: report.timing, reactionSession: report.realized?.afterSession ?? null })),
105
+ stale: resource.stale, error: null };
106
+ } catch (error) {
107
+ if (request.signal?.aborted || (error instanceof Error && error.name === "AbortError")) throw abortError(CANCELLED);
108
+ return { reports: [], stale: false, error: errorMessage(error) };
109
+ }
110
+ }
111
+
112
+ /** What the pane footer and the report notes say when the earnings marker could not be applied. */
113
+ export function macroDayEarningsNotice(earnings: MacroDayEarnings): string | null {
114
+ return earnings.error ? `Earnings dates unavailable (${earnings.error}); release days on the company's own report are not marked.` : null;
115
+ }
@@ -1,7 +1,7 @@
1
1
  import type { HeadlessPaneDefinition } from "../../../types/plugin";
2
2
  import { resolveHeadlessInstrument } from "../shared/headless-market-data";
3
- import { loadMacroDayHistory, loadMacroReleases } from "./client";
4
- import { projectMacroDays, type MacroDayStats } from "./model";
3
+ import { loadMacroDayEarnings, loadMacroDayHistory, loadMacroReleases, macroDayEarningsNotice } from "./client";
4
+ import { macroDayEventLabel, projectMacroDays, type MacroDayStats } from "./model";
5
5
  import { MACRO_EVENT_KINDS, MACRO_EVENT_LABELS, MACRO_RELEASE_SOURCES, staleMacroReleasesNotice } from "./releases";
6
6
 
7
7
  // A mean that rounds to zero reads 0.00%, never -0.00%.
@@ -11,12 +11,14 @@ const share = (value: unknown) => typeof value === "number" ? `${Math.round(valu
11
11
 
12
12
  export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
13
13
  shape: "bundle", argument: { kind: "ticker", description: "Ticker" },
14
+ usListingsOnly: "Macro release day data",
14
15
  // Dated by the release list (releasesThrough) as much as by the closes, so no session schedule applies.
15
16
  freshness: { status: "not-a-feed", basis: "release-day study on daily closes" },
16
17
  describe: (args) => `MDAY ${args.symbols[0] ?? ""}`,
17
18
  discovery: { screenshotReadiness: "live-dom", limitations: [
18
19
  "US listings; close-to-close on the release day.",
19
20
  "Published CPI, jobs and FOMC days; sessions after the list's last covered day (releasesThrough) are left out.",
21
+ "A release day that is also the reaction session of the company's own earnings report is marked earnings and left out of the release averages and the normal day.",
20
22
  ] },
21
23
  options: [
22
24
  { key: "release", type: "enum", values: [{ value: "all" }, ...MACRO_EVENT_KINDS.map((value) => ({ value }))], defaultValue: "all",
@@ -25,11 +27,12 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
25
27
  ],
26
28
  async load(args, ctx) {
27
29
  const instrument = await resolveHeadlessInstrument(ctx, args.symbols[0]!);
28
- const [history, releases] = await Promise.all([loadMacroDayHistory({ instrument, signal: ctx.signal }, ctx.marketData), loadMacroReleases()]);
30
+ const [history, releases, earnings] = await Promise.all([loadMacroDayHistory({ instrument, signal: ctx.signal }, ctx.marketData), loadMacroReleases(),
31
+ loadMacroDayEarnings({ instrument, signal: ctx.signal })]);
29
32
  ctx.signal.throwIfAborted();
30
33
  const model = projectMacroDays(history.history, { symbol: instrument.symbol, lookbackYears: Number(args.options.lookbackYears) || 5,
31
- releases: releases.releases, coveredThrough: releases.coveredThrough });
32
- const releasesNotice = staleMacroReleasesNotice(releases);
34
+ releases: releases.releases, coveredThrough: releases.coveredThrough, earnings: earnings.reports });
35
+ const notes = [staleMacroReleasesNotice(releases), macroDayEarningsNotice(earnings)].filter((note): note is string => !!note);
33
36
  const row = (label: string, stats: MacroDayStats) => ({ label, ...stats });
34
37
  const release = String(args.options.release ?? "all");
35
38
  const events = release === "all" ? model.events : model.events.filter((event) => event.kind === release);
@@ -39,18 +42,18 @@ export const macroDayHeadless: HeadlessPaneDefinition<"bundle"> = {
39
42
  { key: "meanAbs", header: "Avg |move|", format: percent }, { key: "multiple", header: "vs normal", format: multiple },
40
43
  { key: "mean", header: "Avg move", format: percent }, { key: "hitRate", header: "Up", format: share }],
41
44
  rows: [...MACRO_EVENT_KINDS.map((kind) => row(MACRO_EVENT_LABELS[kind], model.byKind[kind])),
42
- row("Any release", model.allEvents), row("Normal day", model.normal)] },
45
+ row("Any release", model.allEvents), ...(model.earnings.count ? [row("Earnings", model.earnings)] : []), row("Normal day", model.normal)] },
43
46
  { title: "Release days", columns: [{ key: "date", header: "Date" }, { key: "event", header: "Release" },
44
47
  { key: "session", header: "Session" }, { key: "move", header: "Move", format: percent }, { key: "multiple", header: "vs normal", format: multiple }],
45
- rows: events.map((event) => ({ ...event, event: MACRO_EVENT_LABELS[event.kind] })) },
48
+ rows: events.map((event) => ({ ...event, event: macroDayEventLabel(event) })) },
46
49
  ],
47
- complete: !history.stale && !history.error && !releasesNotice && model.events.length > 0,
50
+ complete: !history.stale && !earnings.stale && !history.error && !notes.length && model.events.length > 0,
48
51
  unavailableSymbols: model.events.length ? [] : [instrument.symbol],
49
52
  errors: history.error ? [history.error] : [],
50
- ...(releasesNotice ? { notes: [releasesNotice] } : {}),
53
+ ...(notes.length ? { notes } : {}),
51
54
  metadata: { unit: "decimal return, close to close on the release day", start: model.start, asOf: model.asOf,
52
55
  releasesThrough: releases.coveredThrough, releaseSources: Object.values(MACRO_RELEASE_SOURCES),
53
- stale: history.stale, fetchedAt: history.fetchedAt, methodology: "docs/research-data.md#macro-day-reaction" },
56
+ stale: history.stale || earnings.stale, fetchedAt: history.fetchedAt, methodology: "docs/research-data.md#macro-day-reaction" },
54
57
  };
55
58
  },
56
59
  };