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
@@ -2,14 +2,16 @@ import { join } from "path";
2
2
  import { VERSION } from "../../version";
3
3
  import { saveConfig } from "../../data/config/store";
4
4
  import { builtinPluginGroupMembers } from "../../plugins/ownership";
5
- import type { TelemetryConfig } from "../../types/config";
5
+ import type { AppConfig, TelemetryConfig } from "../../types/config";
6
+ import { canonicalTimeZone } from "../../utils/utc-time";
6
7
  import type { CliCommandDef } from "../../types/plugin";
7
- import { withCliServices, withConfigData } from "../context";
8
+ import { resolveCliDataDir, withCliServices, withConfigData } from "../context";
8
9
  import { CLI_COMMAND_GROUPS } from "../help";
9
10
  import { dryRunNote, formatBytes, formatStatusCell } from "../helpers";
10
11
  import { cliStyles, cliTerminalWidth, renderSection, renderTable, wrapText } from "../../utils/cli-output";
11
12
  import { getThemeIds } from "../../theme/themes";
12
13
  import { requireArg } from "./command-utils";
14
+ import { fail } from "../errors";
13
15
  import { describeThemeId, requireThemeId, themeListNote, themeListRows, themeName } from "./themes";
14
16
  import {
15
17
  applyKeybindingCliSet,
@@ -27,6 +29,10 @@ const TELEMETRY_CONFIG_KEYS = {
27
29
  type TelemetryConfigKey = keyof typeof TELEMETRY_CONFIG_KEYS;
28
30
  /** The one-time GitHub star line in the terminal's status bar. */
29
31
  const STAR_PROMPT_CONFIG_KEY = "starPrompt.enabled";
32
+ /** The zone CLI text shows local times in, beside UTC. */
33
+ const TIMEZONE_CONFIG_KEY = "timezone";
34
+ /** What `config set timezone` takes to go back to UTC alone. */
35
+ const NO_TIMEZONE = "none";
30
36
  const EDITABLE_CONFIG_KEYS = [
31
37
  "baseCurrency",
32
38
  "refreshIntervalMinutes",
@@ -34,6 +40,7 @@ const EDITABLE_CONFIG_KEYS = [
34
40
  "valueFlashingEnabled",
35
41
  ...Object.keys(TELEMETRY_CONFIG_KEYS),
36
42
  STAR_PROMPT_CONFIG_KEY,
43
+ TIMEZONE_CONFIG_KEY,
37
44
  ];
38
45
 
39
46
  function isTelemetryConfigKey(key: string): key is TelemetryConfigKey {
@@ -44,6 +51,19 @@ function isBooleanConfigKey(key: string): boolean {
44
51
  return key === "valueFlashingEnabled" || key === STAR_PROMPT_CONFIG_KEY || isTelemetryConfigKey(key);
45
52
  }
46
53
 
54
+ /** The canonical zone name `config set timezone` stores, or null to clear it; fails on a name that is not a zone. */
55
+ function parseTimezoneSetting(value: string): string | null {
56
+ if (value.trim().toLowerCase() === NO_TIMEZONE) return null;
57
+ return canonicalTimeZone(value) ?? fail(
58
+ `"${value.trim()}" is not a time zone name.`,
59
+ `Use an IANA zone name, Region/City, such as Asia/Tokyo, or ${NO_TIMEZONE} for UTC alone.`,
60
+ );
61
+ }
62
+
63
+ function describeTimezone(value: unknown): string {
64
+ return typeof value === "string" && value ? value : "not set, times print in UTC alone";
65
+ }
66
+
47
67
  function describeConfigValue(value: unknown): string {
48
68
  if (value == null) return "nothing";
49
69
  if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "nothing";
@@ -51,6 +71,11 @@ function describeConfigValue(value: unknown): string {
51
71
  return String(value);
52
72
  }
53
73
 
74
+ function withTimezone(config: AppConfig, timezone: string | null): AppConfig {
75
+ const { timezone: _previous, ...rest } = config;
76
+ return timezone ? { ...rest, timezone } : rest;
77
+ }
78
+
54
79
  function summarizeKeybindings(value: unknown): string {
55
80
  const described = value as { actions?: Record<string, unknown>; commands?: Record<string, unknown>; issues?: unknown[] };
56
81
  const parts = [
@@ -122,9 +147,11 @@ export function createSystemCliCommands(): CliCommandDef[] {
122
147
  description: "Print the version, runtime, and data folder",
123
148
  help: { group: CLI_COMMAND_GROUPS.app, usage: ["version"] },
124
149
  execute: async (_args, ctx) => {
150
+ // Where the data lives, or will once a command or the app first runs:
151
+ // printing the version never creates the folder.
125
152
  let dataDir: string | null = null;
126
153
  try {
127
- dataDir = await withConfigData(ctx, ({ dataDir: configuredDataDir }) => configuredDataDir);
154
+ dataDir = await resolveCliDataDir();
128
155
  } catch {
129
156
  dataDir = null;
130
157
  }
@@ -148,7 +175,11 @@ export function createSystemCliCommands(): CliCommandDef[] {
148
175
  const checks: Array<{ check: string; status: string; detail: string }> = [];
149
176
  try {
150
177
  await withCliServices(ctx, async (services) => {
151
- checks.push({ check: "config", status: "ok", detail: services.dataDir });
178
+ checks.push({
179
+ check: "config",
180
+ status: "ok",
181
+ detail: services.createdDataDir ? `${services.dataDir} (created now)` : services.dataDir,
182
+ });
152
183
  checks.push({ check: "database", status: "ok", detail: join(services.dataDir, ".gloomberb-cache.db") });
153
184
  checks.push({ check: "plugins", status: "ok", detail: String(services.services.pluginRegistry.allPlugins.size) });
154
185
  checks.push({ check: "capabilities", status: "ok", detail: String(services.services.pluginRegistry.capabilities.manifests().length) });
@@ -187,6 +218,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
187
218
  "config set telemetry.crashReports false",
188
219
  "config set telemetry.usage false",
189
220
  "config set starPrompt.enabled false",
221
+ "config set timezone <Region/City>|none",
190
222
  "config get keybindings",
191
223
  "config set keybindings.actions.<action> <keys>|null|default",
192
224
  "config set keybindings.commands.<keys> <command>|null",
@@ -194,6 +226,11 @@ export function createSystemCliCommands(): CliCommandDef[] {
194
226
  sections: [{
195
227
  title: "Editable keys",
196
228
  lines: [`${EDITABLE_CONFIG_KEYS.join(", ")}, and keybindings.*`],
229
+ }, {
230
+ title: "Time zone",
231
+ lines: [
232
+ "Times print in UTC. timezone takes an IANA zone name such as Asia/Tokyo or America/New_York, and CLI text and function reports then show your local time beside every UTC time. Trading days keep their market's date. none goes back to UTC alone. JSON and CSV keep the raw values.",
233
+ ],
197
234
  }, {
198
235
  title: "Themes",
199
236
  lines: [
@@ -205,6 +242,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
205
242
  "config themes",
206
243
  "config set theme colorblind",
207
244
  "config set baseCurrency EUR",
245
+ "config set timezone Asia/Tokyo",
208
246
  "config get keybindings",
209
247
  "config set keybindings.actions.ticker-search \"Ctrl+T\"",
210
248
  "config set keybindings.commands.Alt+1 \"DES AAPL\"",
@@ -248,6 +286,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
248
286
  "telemetry.usage": context.config.telemetry?.usage !== false,
249
287
  "telemetry.attention": context.config.telemetry?.attention === true,
250
288
  [STAR_PROMPT_CONFIG_KEY]: context.config.starPrompt?.enabled !== false,
289
+ [TIMEZONE_CONFIG_KEY]: context.config.timezone ?? null,
251
290
  };
252
291
 
253
292
  if (action === "list") {
@@ -257,6 +296,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
257
296
  header: key,
258
297
  ...(key === KEYBINDINGS_CONFIG_KEY ? { format: summarizeKeybindings } : {}),
259
298
  ...(key === "theme" ? { format: (value: unknown) => describeThemeId(String(value)) } : {}),
299
+ ...(key === TIMEZONE_CONFIG_KEY ? { format: describeTimezone } : {}),
260
300
  })),
261
301
  });
262
302
  return;
@@ -274,7 +314,8 @@ export function createSystemCliCommands(): CliCommandDef[] {
274
314
  return;
275
315
  }
276
316
  ctx.printResult({ data: { key, value: safeConfig[key] } }, {
277
- text: (data) => key === KEYBINDINGS_CONFIG_KEY ? renderKeybindings(data.value) : describeConfigValue(data.value),
317
+ text: (data) => key === KEYBINDINGS_CONFIG_KEY ? renderKeybindings(data.value)
318
+ : key === TIMEZONE_CONFIG_KEY ? describeTimezone(data.value) : describeConfigValue(data.value),
278
319
  });
279
320
  return;
280
321
  }
@@ -309,7 +350,9 @@ export function createSystemCliCommands(): CliCommandDef[] {
309
350
  // A display name may come unquoted: config set theme White Phosphor.
310
351
  const parsedValue = key === "theme"
311
352
  ? requireThemeId(args.slice(2).join(" "))
312
- : key === "refreshIntervalMinutes"
353
+ : key === TIMEZONE_CONFIG_KEY
354
+ ? parseTimezoneSetting(value)
355
+ : key === "refreshIntervalMinutes"
313
356
  ? Number(value)
314
357
  : isBooleanConfigKey(key)
315
358
  ? value === "true"
@@ -318,7 +361,9 @@ export function createSystemCliCommands(): CliCommandDef[] {
318
361
  ? { ...context.config, telemetry: { ...context.config.telemetry, [TELEMETRY_CONFIG_KEYS[key]]: parsedValue as boolean } }
319
362
  : key === STAR_PROMPT_CONFIG_KEY
320
363
  ? { ...context.config, starPrompt: { ...context.config.starPrompt, enabled: parsedValue as boolean } }
321
- : { ...context.config, [key]: parsedValue };
364
+ : key === TIMEZONE_CONFIG_KEY
365
+ ? withTimezone(context.config, parsedValue as string | null)
366
+ : { ...context.config, [key]: parsedValue };
322
367
  if (!ctx.cliOptions.dryRun) await saveConfig(nextConfig);
323
368
  ctx.printResult({
324
369
  data: {
@@ -329,7 +374,9 @@ export function createSystemCliCommands(): CliCommandDef[] {
329
374
  ...(key === "theme" ? { name: themeName(String(parsedValue)) } : {}),
330
375
  },
331
376
  }, {
332
- text: (data) => `Set ${key} to ${key === "theme" ? describeThemeId(String(data.value)) : describeConfigValue(data.value)}.${dryRunNote(data.dryRun)}`,
377
+ text: (data) => key === TIMEZONE_CONFIG_KEY && data.value == null
378
+ ? `Cleared timezone: times print in UTC alone.${dryRunNote(data.dryRun)}`
379
+ : `Set ${key} to ${key === "theme" ? describeThemeId(String(data.value)) : describeConfigValue(data.value)}.${dryRunNote(data.dryRun)}`,
333
380
  });
334
381
  return;
335
382
  }
@@ -1,6 +1,6 @@
1
1
  import { formatShortDate, parseDisplayDate } from "../../utils/datetime-format";
2
2
  import { formatReportedMoney } from "../../utils/reported-money";
3
- import { fundamentalsCurrency, reportedEnterpriseValue } from "../../utils/fundamentals";
3
+ import { convertedPriceEarningsNote, epsConversionNote, fundamentalsCurrency, reportedEnterpriseValue, ttmConversionNote } from "../../utils/fundamentals";
4
4
  import { latestFinancialPeriod } from "../../utils/latest-financial-period";
5
5
  import { exportedFundamentals, formatPriceEarnings, priceEarningsOnEarnings } from "../../utils/price-earnings";
6
6
  import { describeFundamentalMarketCap, selectMarketCapitalization } from "../../utils/market-capitalization";
@@ -29,14 +29,17 @@ import {
29
29
  getRegularSessionDisplay,
30
30
  marketStateLabel,
31
31
  } from "../../market-data/market/status";
32
+ import type { FuturesContractTermsPayload } from "../../api-client/futures-curve";
33
+ import { futuresContractLines, futuresSettleText, isFuturesInstrument, loadFuturesContractTerms } from "../../market-data/futures-contract";
32
34
  import type { AppConfig } from "../../types/config";
33
35
  import type { FinancialStatement, TickerFinancials } from "../../types/financials";
34
36
  import { computeTickerPriceReturns } from "../../market-data/ticker-price-returns";
37
+ import { instrumentTypeLabel, isCryptoVenuePerpetual } from "../../tickers/instrument-kind";
35
38
  import type { SecFilingItem } from "../../types/data-provider";
36
39
  import type { NewsArticle } from "../../news/types";
37
40
  import type { TickerRecord } from "../../types/ticker";
38
41
  import type { CliCommandContext } from "../../types/plugin";
39
- import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue, resolvePortfolioPositionPnl } from "../../plugins/builtin/portfolio-list/position-metrics";
42
+ import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue, resolvePortfolioPositionPnl } from "../../portfolio/position-metrics";
40
43
  import { createBaseConverter } from "../base-converter";
41
44
  import { initMarketData, withMarketData } from "../context";
42
45
  import { fail } from "../errors";
@@ -53,7 +56,9 @@ import {
53
56
  import { NotesFiles } from "../../plugins/builtin/notes/files";
54
57
  import { isKnownNonUsListing } from "../../utils/sec";
55
58
  import { canonicalExchange, exchangeLabel, isKnownExchangeCode } from "../../utils/exchanges";
56
- import { failIfNotTraded, ListingArgError, listingIdentity, resolveCliListing, type CliListing } from "../listing-arg";
59
+ import {
60
+ failIfNotTraded, ListingArgError, listingChoiceNotes, listingIdentity, loadBareVenues, resolveCliListing, type CliListing,
61
+ } from "../listing-arg";
57
62
  import { isNotATickerMessage } from "../not-a-ticker";
58
63
  import { sharesOutstandingInReceipts } from "../../utils/depositary-receipt";
59
64
  import { cliFreshnessFooter } from "../result";
@@ -74,6 +79,25 @@ interface TickerCommandDependencies {
74
79
  initMarketData?: () => Promise<MarketContext>;
75
80
  fail?: (message: string, details?: string) => never;
76
81
  printResult?: CliCommandContext["printResult"];
82
+ /** A futures symbol's contract terms and settlement; a failure leaves them out of the report. */
83
+ futuresContract?: (symbol: string) => Promise<FuturesContractTermsPayload | null>;
84
+ }
85
+
86
+ /**
87
+ * A futures symbol's contract terms and latest confirmed settlement. Any other
88
+ * symbol is not looked up, and a failed request leaves both out of the report.
89
+ */
90
+ export async function cliFuturesContract(
91
+ symbol: string,
92
+ instrumentType: string | undefined,
93
+ load: (symbol: string) => Promise<FuturesContractTermsPayload | null> = loadFuturesContractTerms,
94
+ ): Promise<FuturesContractTermsPayload | null> {
95
+ return isFuturesInstrument(symbol, instrumentType) ? load(symbol).catch(() => null) : null;
96
+ }
97
+
98
+ /** The Contract block as `ticker` and `quote` print it; a contract past first notice is in the warning colour. */
99
+ export function futuresContractMetrics(terms: FuturesContractTermsPayload, symbol: string): Array<[string, string]> {
100
+ return futuresContractLines(terms, symbol).map((line) => [line.label, line.warning ? cliStyles.warning(line.value) : line.value]);
77
101
  }
78
102
 
79
103
  function appendMetricSection(lines: string[], title: string, metrics: Array<[string, string]>) {
@@ -316,7 +340,7 @@ function fundamentalsMetrics(
316
340
  { label: "Forward P/E", ...multipleFigure(priceEarningsOnEarnings(fundamentals?.forwardPE, fundamentals?.forwardEps)) },
317
341
  { label: "PEG", ...multipleFigure(fundamentals?.pegRatio) },
318
342
  // The flows and EPS are one trailing-twelve-month block, the same twelve months for each line.
319
- { label: "EPS (TTM)", ...reportedMoney(fundamentals?.eps, reportingCurrency, true) },
343
+ { label: "EPS (TTM)", ...reportedMoney(fundamentals?.eps, fundamentals?.epsCurrency?.trim() || reportingCurrency, true) },
320
344
  {
321
345
  label: `Dividend Yield${fundamentals?.dividendYieldBasis ? ` (${fundamentals.dividendYieldBasis})` : ""}`,
322
346
  ...percentFigure(fundamentals?.dividendYield, formatFractionPercentCell),
@@ -345,6 +369,26 @@ function metricLines(metrics: readonly FundamentalsMetric[]): Array<[string, str
345
369
  return metrics.map((metric) => [metric.label, metric.text]);
346
370
  }
347
371
 
372
+ const TRAILING_MONEY_METRICS = new Set(["EPS (TTM)", "Revenue (TTM)", "Net Income (TTM)", "Operating Cash Flow (TTM)", "Free Cash Flow (TTM)"]);
373
+
374
+ /**
375
+ * The unit notes under shown fundamentals: what a converted TTM block was converted from, an EPS in another
376
+ * currency than the amounts beside it, and what a P/E on such an EPS divides.
377
+ */
378
+ function trailingConversionNotes(
379
+ fundamentals: TickerFinancials["fundamentals"],
380
+ metrics: readonly FundamentalsMetric[],
381
+ quoteCurrency: string | undefined,
382
+ ): string[] {
383
+ const shown = (label: string) => metrics.some((metric) => metric.label === label && metric.text !== "—");
384
+ const conversion = [...TRAILING_MONEY_METRICS].some(shown) ? ttmConversionNote(fundamentals) : undefined;
385
+ const eps = shown("EPS (TTM)") ? epsConversionNote(fundamentals) : undefined;
386
+ const priceEarnings = metrics.some((metric) => metric.label === "P/E (TTM)" && typeof metric.value === "number")
387
+ ? convertedPriceEarningsNote(fundamentals, quoteCurrency)
388
+ : undefined;
389
+ return [conversion, eps, priceEarnings].filter((note): note is string => !!note);
390
+ }
391
+
348
392
  const VALUATION_METRICS = new Set(["Market Cap", "Enterprise Value", "P/E (TTM)", "Forward P/E", "PEG", "EPS (TTM)"]);
349
393
 
350
394
  type FundamentalsReportData = TickerFinancials & { symbol: string; exchange?: string };
@@ -381,8 +425,10 @@ export function renderFundamentalsReport(
381
425
  if (profileParts.length > 0) lines.push(cliStyles.muted(profileParts.join(METADATA_SEPARATOR)));
382
426
 
383
427
  const before = lines.length;
384
- appendMetricSection(lines, view === "valuation" ? "Valuation" : "Fundamentals", metricLines(fundamentalsReportMetrics(financials, view)));
428
+ const metrics = fundamentalsReportMetrics(financials, view);
429
+ appendMetricSection(lines, view === "valuation" ? "Valuation" : "Fundamentals", metricLines(metrics));
385
430
  if (lines.length === before) lines.push("", cliStyles.muted(`No ${view} reported for ${financials.symbol}.`));
431
+ for (const note of trailingConversionNotes(financials.fundamentals, metrics, quote?.currency)) lines.push(cliStyles.muted(note));
386
432
  if (view === "fundamentals") appendTextSection(lines, "Description", profile?.description);
387
433
  return lines.join("\n");
388
434
  }
@@ -418,7 +464,9 @@ export function fundamentalsReportTables(
418
464
  tables,
419
465
  footer: reportFooterLines({
420
466
  freshness,
421
- notes: metrics.length === 0 ? [`No ${view} reported for ${financials.symbol}.`] : [],
467
+ notes: metrics.length === 0
468
+ ? [`No ${view} reported for ${financials.symbol}.`]
469
+ : trailingConversionNotes(financials.fundamentals, metrics, financials.quote?.currency),
422
470
  }),
423
471
  };
424
472
  }
@@ -444,12 +492,16 @@ export async function buildTickerReport({
444
492
  toBase,
445
493
  notes,
446
494
  quoteNote,
495
+ listingNote,
447
496
  recentNews = [],
448
497
  recentSecFilings = [],
498
+ futuresContract = null,
449
499
  }: {
450
500
  symbol: string;
451
501
  /** The exchange the command named, as a canonical code. */
452
502
  listingExchange?: string;
503
+ /** A futures symbol's contract terms and latest confirmed settlement. */
504
+ futuresContract?: FuturesContractTermsPayload | null;
453
505
  tickerFile: TickerRecord | null;
454
506
  financials: TickerFinancials;
455
507
  config: AppConfig;
@@ -457,6 +509,8 @@ export async function buildTickerReport({
457
509
  notes?: string;
458
510
  /** Why there is no quote, when a source said; "Quote unavailable." otherwise. */
459
511
  quoteNote?: string;
512
+ /** "CAR = Avis Budget Group, NASDAQ (also XETRA, ASX)", for a bare symbol several companies list. */
513
+ listingNote?: string;
460
514
  recentNews?: NewsArticle[];
461
515
  recentSecFilings?: SecFilingItem[];
462
516
  }): Promise<string> {
@@ -483,10 +537,11 @@ export async function buildTickerReport({
483
537
  if (summaryParts.length > 0) {
484
538
  lines.push(cliStyles.muted(summaryParts.join(METADATA_SEPARATOR)));
485
539
  }
540
+ if (listingNote) lines.push(cliStyles.muted(listingNote));
486
541
 
487
- const instrumentType = quote?.instrumentType?.trim()
542
+ const instrumentType = instrumentTypeLabel(quote?.instrumentType?.trim()
488
543
  || financials.quoteMetadata?.instrumentType?.trim()
489
- || tickerFile?.metadata.assetCategory;
544
+ || tickerFile?.metadata.assetCategory, name, { detail: true });
490
545
  const metadataParts = [
491
546
  instrumentType ? `Type ${instrumentType}` : undefined,
492
547
  (tickerFile?.metadata.sector || profile?.sector) ? `Sector ${tickerFile?.metadata.sector || profile?.sector}` : undefined,
@@ -537,8 +592,12 @@ export async function buildTickerReport({
537
592
  extended.change,
538
593
  )
539
594
  : "—";
595
+ // A futures price is its latest trade; the exchange settlement is its own row, dated by its session.
540
596
  appendMetricSection(lines, "Quote", [
541
597
  ["Last", colorBySign(formatMarketPriceWithCurrency(session.price, quote.currency, quoteOptions), session.change)],
598
+ ...futuresContract
599
+ ? [["Settle", futuresSettleText(futuresContract, quote)] as [string, string]]
600
+ : [],
542
601
  ["Change", colorBySign(`${formatMarketChangeWithCurrency(session.change, quote.currency, quoteOptions, session.price)} (${formatPercentRaw(session.changePercent)})`, session.change)],
543
602
  ["Open", quote.open != null ? formatMarketPriceWithCurrency(quote.open, quote.currency, quoteOptions) : "—"],
544
603
  ["Day Range", quote.low != null || quote.high != null
@@ -548,17 +607,23 @@ export async function buildTickerReport({
548
607
  ? formatPriceRange(quote.low52w, quote.high52w, quote.currency, quoteOptions)
549
608
  : "—"],
550
609
  ["Bid / Ask", formatBidAsk(quote.bid, quote.ask, quote.bidSize, quote.askSize, quote.currency, quoteOptions)],
551
- ["Volume", quote.volume != null ? formatNumber(quote.volume, 0) : "—"],
610
+ // A perpetual's price comes with no volume; its 0 means not reported, not no trades.
611
+ ["Volume", quote.volume != null && !(quote.volume === 0 && isCryptoVenuePerpetual(quote.instrumentType, quote.name))
612
+ ? formatNumber(quote.volume, 0) : "—"],
552
613
  ["Updated", formatTimestamp(quote.lastUpdated)],
553
614
  ]);
554
615
 
616
+ if (futuresContract) appendMetricSection(lines, "Contract", futuresContractMetrics(futuresContract, quote.symbol ?? symbol));
617
+
555
618
  appendMetricSection(lines, "Extended Hours", [
556
619
  [EXTENDED_SESSION_LABELS.PRE, extendedRow("PRE")],
557
620
  [EXTENDED_SESSION_LABELS.POST, extendedRow("POST")],
558
621
  ]);
559
622
  }
560
623
 
561
- appendMetricSection(lines, "Fundamentals", metricLines(fundamentalsMetrics(quote, fundamentals, profile, { text: marketCapText }, priceReturns, fundamentalsCurrency(financials), { text: enterpriseValueText })));
624
+ const metrics = fundamentalsMetrics(quote, fundamentals, profile, { text: marketCapText }, priceReturns, fundamentalsCurrency(financials), { text: enterpriseValueText });
625
+ appendMetricSection(lines, "Fundamentals", metricLines(metrics));
626
+ for (const note of trailingConversionNotes(fundamentals, metrics, quote?.currency)) lines.push(cliStyles.muted(note));
562
627
 
563
628
  if (capitalization?.provenance.kind === "fundamentals") {
564
629
  lines.push(cliStyles.muted(`Market cap: ${describeFundamentalMarketCap(capitalization.provenance)}.`));
@@ -623,9 +688,11 @@ function buildTickerStructuredData({
623
688
  notes,
624
689
  recentNews,
625
690
  recentSecFilings,
691
+ futuresContract,
626
692
  }: {
627
693
  symbol: string;
628
694
  listing: CliListing;
695
+ futuresContract: FuturesContractTermsPayload | null;
629
696
  tickerFile: TickerRecord | null;
630
697
  financials: TickerFinancials;
631
698
  config: AppConfig;
@@ -657,6 +724,8 @@ function buildTickerStructuredData({
657
724
  lastUpdated: quote.lastUpdated ? new Date(quote.lastUpdated).toISOString() : "",
658
725
  } : null,
659
726
  quoteMetadata: financials.quoteMetadata,
727
+ // Only for futures: the contract the symbol prices, its terms and its latest confirmed settlement.
728
+ ...futuresContract ? { futuresContract } : {},
660
729
  ticker: tickerFile ? {
661
730
  ticker: tickerFile.metadata.ticker,
662
731
  name: tickerFile.metadata.name ?? "",
@@ -725,6 +794,8 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
725
794
  const tickerFile = listing.saved;
726
795
  const { symbol: requestSymbol, exchange } = listing.request;
727
796
  const toBase = createBaseConverter(dataProvider, config.baseCurrency);
797
+ // A bare symbol's venues load beside its research, to name the company it was read as when several list it.
798
+ const venues = loadBareVenues(listing, { store, dataProvider });
728
799
 
729
800
  let financials: TickerFinancials | null = null;
730
801
  try {
@@ -778,6 +849,7 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
778
849
  : Promise.resolve([]),
779
850
  ]);
780
851
 
852
+ const futuresContract = await cliFuturesContract(quote?.symbol ?? requestSymbol, quote?.instrumentType, dependencies.futuresContract);
781
853
  const notes = notesResult.status === "fulfilled" ? notesResult.value : "";
782
854
  const recentNews = newsResult.status === "fulfilled" ? newsResult.value : [];
783
855
  const recentSecFilings = secFilingsResult.status === "fulfilled" ? secFilingsResult.value : [];
@@ -785,10 +857,12 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
785
857
  // The quote is the feed in this report; without one, the fundamentals date it.
786
858
  const freshness = quote ? quotesFreshness([quote]) : fundamentalsFreshness(resolvedFinancials);
787
859
  const quoteNote = quote ? undefined : await quoteUnavailableNote(dataProvider, requestSymbol, exchange);
860
+ const [listingNote] = await listingChoiceNotes(listing, { store, dataProvider }, quote, venues);
788
861
  if (dependencies.printResult) {
862
+ const warnings = [listingNote, quoteNote].filter((note): note is string => !!note);
789
863
  dependencies.printResult({
790
864
  freshness,
791
- warnings: quote ? undefined : [quoteNote!],
865
+ warnings: warnings.length > 0 ? warnings : undefined,
792
866
  data: buildTickerStructuredData({
793
867
  symbol: normalized,
794
868
  listing,
@@ -798,6 +872,7 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
798
872
  notes,
799
873
  recentNews,
800
874
  recentSecFilings,
875
+ futuresContract,
801
876
  }),
802
877
  });
803
878
  return;
@@ -812,8 +887,10 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
812
887
  toBase,
813
888
  notes,
814
889
  quoteNote,
890
+ listingNote,
815
891
  recentNews,
816
892
  recentSecFilings,
893
+ futuresContract,
817
894
  }));
818
895
  if (freshness) console.log(`\n${cliFreshnessFooter(freshness)}`);
819
896
  });
@@ -1,6 +1,7 @@
1
1
  import { join } from "path";
2
- import { existsSync } from "fs";
3
- import { getDataDir, loadConfig } from "../data/config/store";
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { getDataDir, initDataDir, loadConfig } from "../data/config/store";
4
+ import { getGloomberbDirs } from "../data/config/home";
4
5
  import { AppPersistence } from "../data/app-persistence";
5
6
  import { TickerRepository } from "../data/ticker-repository";
6
7
  import { AssetDataRouter } from "../sources/provider-router";
@@ -9,6 +10,7 @@ import type { PluginCapability } from "../capabilities";
9
10
  import type { AppConfig } from "../types/config";
10
11
  import type { GloomPlugin } from "../types/plugin";
11
12
  import { getLoadablePlugins } from "../plugins/catalog";
13
+ import { standaloneSeriesInvoker } from "../series/standalone";
12
14
  import type { PluginRegistry } from "../plugins/registry";
13
15
  import type { LoadedExternalPlugin } from "../plugins/loader";
14
16
  import { fail } from "./errors";
@@ -59,13 +61,45 @@ export async function loadCliConfigIfAvailable(): Promise<AppConfig | null> {
59
61
  return loadConfig(dataDir);
60
62
  }
61
63
 
62
- export async function initConfigData(): Promise<ConfigContext> {
63
- const dataDir = await getDataDir();
64
- if (!dataDir || !existsSync(dataDir)) {
65
- fail("No data directory configured.", "Run gloomberb once to initialize your local data.");
64
+ function isUnreadableConfig(configFile: string): boolean {
65
+ try {
66
+ JSON.parse(readFileSync(configFile, "utf-8"));
67
+ return false;
68
+ } catch (error) {
69
+ return (error as NodeJS.ErrnoException).code !== "ENOENT";
66
70
  }
71
+ }
72
+
73
+ /** The data folder this profile uses, whether or not anything has created it yet. Never writes. */
74
+ export async function resolveCliDataDir(): Promise<string> {
75
+ return await getDataDir() ?? getGloomberbDirs().data;
76
+ }
67
77
 
68
- const config = await loadConfig(dataDir);
78
+ /**
79
+ * The data folder and config a command works with. The first command on a new
80
+ * profile creates them with the same `initDataDir` a first launch of the
81
+ * terminal or desktop app runs, so the CLI works before the app was ever
82
+ * opened. That config leaves onboarding unfinished, so the app's first launch
83
+ * still walks through it.
84
+ *
85
+ * A config.json that is there but does not parse would be replaced with
86
+ * defaults by that, so the command fails instead and leaves it for the user.
87
+ */
88
+ async function openCliDataDir(): Promise<{ dataDir: string; config: AppConfig; created: boolean }> {
89
+ const recorded = await getDataDir();
90
+ if (recorded && existsSync(recorded)) {
91
+ return { dataDir: recorded, config: await loadConfig(recorded), created: false };
92
+ }
93
+ const { configFile, data } = getGloomberbDirs();
94
+ if (!recorded && isUnreadableConfig(configFile)) {
95
+ fail(`Could not read ${configFile}.`, "Fix the file or move it aside; the next command creates a fresh one.");
96
+ }
97
+ const dataDir = recorded ?? data;
98
+ return { dataDir, config: await initDataDir(dataDir), created: true };
99
+ }
100
+
101
+ export async function initConfigData(): Promise<ConfigContext> {
102
+ const { dataDir, config } = await openCliDataDir();
69
103
  const persistence = new AppPersistence(join(dataDir, ".gloomberb-cache.db"));
70
104
  const store = new TickerRepository(persistence.tickers);
71
105
  return { config, persistence, store, dataDir };
@@ -83,16 +117,12 @@ export async function initMarketData(options: CliContextOptions = {}): Promise<M
83
117
  dataProvider.attachRegistry(registryAdapter as PluginRegistry);
84
118
  dataProvider.setConfigAccessor(() => context.config);
85
119
  dataProvider.setBackgroundRevalidation(false);
86
- return { ...context, dataProvider };
120
+ const seriesCapabilities = standaloneSeriesInvoker(plugins, context.dataDir, context.config.disabledPlugins);
121
+ return { ...context, dataProvider, seriesCapabilities };
87
122
  }
88
123
 
89
124
  export async function initCliServices(options: CliServicesOptions = {}) {
90
- const dataDir = await getDataDir();
91
- if (!dataDir || !existsSync(dataDir)) {
92
- fail("No data directory configured.", "Run gloomberb once to initialize your local data.");
93
- }
94
-
95
- const config = await loadConfig(dataDir);
125
+ const { dataDir, config, created } = await openCliDataDir();
96
126
  const services = createAppServices({
97
127
  config,
98
128
  plugins: getLoadablePlugins(options.externalPlugins ?? []),
@@ -104,6 +134,7 @@ export async function initCliServices(options: CliServicesOptions = {}) {
104
134
  return {
105
135
  config,
106
136
  dataDir,
137
+ createdDataDir: created,
107
138
  services,
108
139
  dataProvider: services.providerRouter,
109
140
  persistence: services.persistence,