gloomberb 0.16.1 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (574) hide show
  1. package/README.md +2 -1
  2. package/package.json +2 -1
  3. package/src/api-client/beneficial-owners.ts +92 -0
  4. package/src/api-client/data.ts +26 -8
  5. package/src/api-client/errors.ts +12 -0
  6. package/src/api-client/geo.ts +363 -0
  7. package/src/api-client/index.ts +2 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/paths.ts +30 -1
  10. package/src/api-client/perps.ts +37 -0
  11. package/src/api-client/request.ts +5 -2
  12. package/src/api-client/types.ts +2 -0
  13. package/src/brokers/account-cache.ts +24 -0
  14. package/src/cli/commands/command-utils.ts +8 -0
  15. package/src/cli/commands/crypto-hints.ts +75 -0
  16. package/src/cli/commands/market.ts +414 -106
  17. package/src/cli/commands/overview.ts +77 -32
  18. package/src/cli/commands/ticker.ts +101 -43
  19. package/src/cli/desktop-pane-shot.ts +34 -4
  20. package/src/cli/freshness.ts +61 -0
  21. package/src/cli/help.ts +1 -0
  22. package/src/cli/helpers.ts +4 -8
  23. package/src/cli/index.ts +12 -2
  24. package/src/cli/listing-arg.ts +321 -0
  25. package/src/cli/pane-functions/access-gate.ts +42 -0
  26. package/src/cli/pane-functions/catalog.ts +28 -1
  27. package/src/cli/pane-functions/data.ts +3 -1
  28. package/src/cli/pane-functions/dom.ts +20 -2
  29. package/src/cli/pane-functions/freshness.ts +400 -0
  30. package/src/cli/pane-functions/function-help.ts +27 -20
  31. package/src/cli/pane-functions/headless.ts +39 -11
  32. package/src/cli/pane-functions/index.ts +15 -4
  33. package/src/cli/pane-functions/options.ts +3 -0
  34. package/src/cli/pane-functions/report.ts +4 -0
  35. package/src/cli/pane-functions/resolver.ts +35 -7
  36. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  37. package/src/cli/pane-functions/screenshot.ts +7 -3
  38. package/src/cli/result.ts +41 -27
  39. package/src/cli/ticker-resolution.ts +7 -5
  40. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  41. package/src/components/chart/composite/drawing-store.ts +59 -0
  42. package/src/components/chart/composite/legend.tsx +319 -0
  43. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  44. package/src/components/chart/composite/panel-gestures.ts +59 -0
  45. package/src/components/chart/composite/panel-labels.ts +107 -0
  46. package/src/components/chart/composite/panel-surface.tsx +876 -0
  47. package/src/components/chart/composite/tool-catalog.ts +98 -0
  48. package/src/components/chart/composite/toolbar.tsx +186 -0
  49. package/src/components/chart/composite/tools.ts +8 -0
  50. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  51. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  52. package/src/components/command-bar/list/model.ts +5 -0
  53. package/src/components/command-bar/list/view.tsx +16 -3
  54. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  55. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  56. package/src/components/command-bar/workflow/ops.ts +1 -1
  57. package/src/components/command-bar/workflow/tickers.ts +6 -0
  58. package/src/components/data-table/view.tsx +29 -10
  59. package/src/components/feedback-dialog.tsx +2 -1
  60. package/src/components/form-modal/field-row.tsx +2 -2
  61. package/src/components/layout/detached-pane-shell.tsx +6 -3
  62. package/src/components/layout/pane/content.tsx +23 -10
  63. package/src/components/layout/pane/error-boundary.tsx +103 -0
  64. package/src/components/layout/pane/footer/registration.tsx +68 -23
  65. package/src/components/layout/pane/header.tsx +11 -11
  66. package/src/components/layout/shell/menu.ts +2 -1
  67. package/src/components/layout/shell/pane/actions.ts +2 -1
  68. package/src/components/layout/shell/pane/layers.tsx +14 -3
  69. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  70. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  71. package/src/components/metric-treemap/layout.ts +2 -0
  72. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  73. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  74. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  75. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  76. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  77. package/src/components/ticker/list-table-view.tsx +13 -1
  78. package/src/components/ui/fields.tsx +8 -0
  79. package/src/components/ui/range-track.tsx +10 -3
  80. package/src/components/ui/status.tsx +8 -2
  81. package/src/components/ui/tabs.tsx +7 -4
  82. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  83. package/src/data/config/store/normalize.ts +17 -1
  84. package/src/i18n/es.ts +19 -1
  85. package/src/i18n/ja.ts +19 -1
  86. package/src/i18n/ko.ts +19 -1
  87. package/src/i18n/zh-cn.ts +19 -1
  88. package/src/i18n/zh-tw.ts +19 -1
  89. package/src/market-data/coordinator/chart.ts +1 -1
  90. package/src/market-data/coordinator/quotes.ts +23 -4
  91. package/src/market-data/fx-closes.ts +72 -0
  92. package/src/market-data/market/freshness.ts +78 -6
  93. package/src/market-data/market/session-close-note.ts +57 -0
  94. package/src/market-data/market/status.ts +151 -0
  95. package/src/market-data/published-apac-sessions.ts +66 -0
  96. package/src/market-data/quotes/contributions.ts +2 -0
  97. package/src/market-data/quotes/resolution.ts +2 -0
  98. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  99. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  100. package/src/plugins/builtin/alerts/cli.ts +10 -5
  101. package/src/plugins/builtin/alerts/index.tsx +1 -0
  102. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  103. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  104. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  105. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  106. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  107. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  108. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  109. package/src/plugins/builtin/attention/headless.ts +6 -4
  110. package/src/plugins/builtin/attention/index.tsx +1 -1
  111. package/src/plugins/builtin/attention/pane.tsx +1 -1
  112. package/src/plugins/builtin/awards/headless.ts +1 -0
  113. package/src/plugins/builtin/awards/pane.tsx +1 -1
  114. package/src/plugins/builtin/backtest/headless.ts +1 -0
  115. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  116. package/src/plugins/builtin/buildout/index.tsx +1 -0
  117. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  118. package/src/plugins/builtin/cds/headless.ts +2 -0
  119. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  120. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  121. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  122. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  123. package/src/plugins/builtin/chat/channels.ts +19 -3
  124. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  125. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  126. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  127. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  128. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  129. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  130. package/src/plugins/builtin/chat/index.tsx +1 -0
  131. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  132. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  133. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  134. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  135. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  136. package/src/plugins/builtin/chat/message/types.ts +14 -2
  137. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  138. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  139. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  140. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  141. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  142. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  143. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  144. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  145. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  146. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  147. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  148. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  149. package/src/plugins/builtin/correlation/compute.ts +59 -16
  150. package/src/plugins/builtin/correlation/geo.ts +34 -0
  151. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  152. package/src/plugins/builtin/correlation/headless.ts +42 -8
  153. package/src/plugins/builtin/correlation/history.ts +2 -0
  154. package/src/plugins/builtin/correlation/index.tsx +68 -14
  155. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  156. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  157. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  158. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  159. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  160. package/src/plugins/builtin/correlation/settings.ts +1 -1
  161. package/src/plugins/builtin/cot/headless.ts +6 -3
  162. package/src/plugins/builtin/cot/index.tsx +3 -3
  163. package/src/plugins/builtin/cot/model.ts +36 -3
  164. package/src/plugins/builtin/cpi/headless.ts +4 -0
  165. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  166. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  167. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  168. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  169. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  170. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  171. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  172. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  173. package/src/plugins/builtin/doe/headless.ts +7 -0
  174. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  175. package/src/plugins/builtin/earnings/headless.ts +5 -0
  176. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  177. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  178. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  179. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  180. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  181. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  182. package/src/plugins/builtin/econ/index.tsx +1 -0
  183. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  184. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  185. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  186. package/src/plugins/builtin/executives/index.tsx +1 -0
  187. package/src/plugins/builtin/exposure/headless.ts +3 -1
  188. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  189. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  190. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  191. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  192. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  193. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  194. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  195. package/src/plugins/builtin/gpu/headless.ts +7 -3
  196. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  197. package/src/plugins/builtin/gpu/model.ts +27 -1
  198. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  199. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  200. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  201. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  202. package/src/plugins/builtin/holders/headless.ts +139 -5
  203. package/src/plugins/builtin/holders/index.tsx +10 -3
  204. package/src/plugins/builtin/holders/pane.tsx +20 -3
  205. package/src/plugins/builtin/holders/report-header.ts +95 -0
  206. package/src/plugins/builtin/holders/table-model.ts +20 -5
  207. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  208. package/src/plugins/builtin/holders/types.ts +2 -1
  209. package/src/plugins/builtin/insider/headless.ts +2 -0
  210. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  211. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  212. package/src/plugins/builtin/jobs/headless.ts +1 -0
  213. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  214. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  215. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  216. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  217. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  218. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  219. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  220. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  221. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  222. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  223. package/src/plugins/builtin/members/client.ts +20 -3
  224. package/src/plugins/builtin/members/headless.ts +5 -3
  225. package/src/plugins/builtin/members/model.ts +10 -2
  226. package/src/plugins/builtin/members/pane.tsx +13 -6
  227. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  228. package/src/plugins/builtin/mna/headless.ts +1 -0
  229. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  230. package/src/plugins/builtin/news/headless.ts +2 -0
  231. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  232. package/src/plugins/builtin/notes/cli.ts +14 -6
  233. package/src/plugins/builtin/notes/index.tsx +1 -0
  234. package/src/plugins/builtin/openfda/index.tsx +1 -0
  235. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  236. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  237. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  238. package/src/plugins/builtin/pe-band/client.ts +48 -3
  239. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  240. package/src/plugins/builtin/pe-band/model.ts +92 -16
  241. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  242. package/src/plugins/builtin/perps/client.ts +50 -3
  243. package/src/plugins/builtin/perps/headless.ts +144 -16
  244. package/src/plugins/builtin/perps/index.tsx +6 -6
  245. package/src/plugins/builtin/perps/model.ts +189 -12
  246. package/src/plugins/builtin/perps/pane.tsx +248 -47
  247. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  248. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  249. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  250. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  251. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  252. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  253. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  254. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  255. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  256. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  257. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  258. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  259. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  260. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  261. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  262. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  263. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  264. package/src/plugins/builtin/power/headless.ts +3 -1
  265. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  266. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  267. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  268. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  269. package/src/plugins/builtin/research/events-headless.ts +2 -0
  270. package/src/plugins/builtin/research/headless.ts +2 -0
  271. package/src/plugins/builtin/research/index.tsx +1 -0
  272. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  273. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  274. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  275. package/src/plugins/builtin/research-search/index.tsx +1 -0
  276. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  277. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  278. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  279. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  280. package/src/plugins/builtin/scanner/headless.ts +14 -0
  281. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  282. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  283. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  284. package/src/plugins/builtin/sec/forms.ts +12 -0
  285. package/src/plugins/builtin/sec/headless.ts +2 -0
  286. package/src/plugins/builtin/sectors/client.ts +1 -0
  287. package/src/plugins/builtin/sectors/headless.ts +9 -1
  288. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  289. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  290. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  291. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  292. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  293. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  294. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  295. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  296. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  297. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  298. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  299. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  300. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  301. package/src/plugins/builtin/themes/headless.ts +3 -0
  302. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  303. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  304. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  305. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  306. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  308. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  309. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  310. package/src/plugins/builtin/volatility/headless.ts +6 -1
  311. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  312. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  313. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  314. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  315. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  316. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  317. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  318. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  319. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  320. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  321. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  322. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  323. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  324. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  325. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  326. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  327. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  328. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  329. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  330. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  331. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  332. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  333. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  334. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  335. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  336. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  337. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  338. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  339. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  340. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  341. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  342. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  343. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  344. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  345. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  346. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  347. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  348. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  349. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  350. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  351. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  352. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  353. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  354. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  355. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  356. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  357. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  358. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  359. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  360. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  361. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  362. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  363. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  364. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  365. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  366. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  367. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  368. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  369. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  524. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  525. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  526. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  527. package/src/plugins/loader.ts +13 -1
  528. package/src/plugins/registry/pane-settings.ts +1 -0
  529. package/src/renderers/dom/build-assets.ts +81 -3
  530. package/src/renderers/dom/data-table/row.tsx +2 -0
  531. package/src/renderers/dom/desktop/controls.tsx +7 -3
  532. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  533. package/src/renderers/dom/desktop/popover.tsx +73 -14
  534. package/src/renderers/dom/host/input.tsx +14 -3
  535. package/src/renderers/dom/styles.css +6 -7
  536. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  537. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
  538. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  539. package/src/renderers/opentui/host.tsx +5 -0
  540. package/src/sources/gloomberb-cloud/index.ts +25 -4
  541. package/src/sources/gloomberb-cloud/normalizers.ts +27 -3
  542. package/src/sources/provider-router/cached-routes.ts +4 -1
  543. package/src/sources/provider-router/financials.ts +8 -7
  544. package/src/sources/provider-router/history.ts +15 -10
  545. package/src/sources/sec-registrant.ts +75 -0
  546. package/src/state/app/context/index.tsx +24 -8
  547. package/src/state/hooks/quote-streaming.ts +41 -26
  548. package/src/sync/core-contributors.ts +4 -0
  549. package/src/telemetry/crash-reports.ts +4 -1
  550. package/src/test-support/cli-context.ts +10 -2
  551. package/src/theme/heat-colors.ts +266 -84
  552. package/src/time-series/resolve.ts +1 -1
  553. package/src/time-series/use-chart-resolution.ts +1 -1
  554. package/src/types/context-menu.ts +6 -0
  555. package/src/types/data-provider.ts +2 -0
  556. package/src/types/financials.ts +3 -0
  557. package/src/types/headless.ts +79 -2
  558. package/src/types/plugin.ts +20 -1
  559. package/src/types/ticker.ts +15 -0
  560. package/src/ui/host.tsx +14 -0
  561. package/src/updater/index.ts +9 -11
  562. package/src/utils/async-deadline.ts +21 -0
  563. package/src/utils/cli-output.ts +5 -1
  564. package/src/utils/crypto-pair.ts +41 -0
  565. package/src/utils/currency-units.ts +14 -0
  566. package/src/utils/depositary-receipt.ts +26 -0
  567. package/src/utils/exchanges.ts +72 -0
  568. package/src/utils/fire-and-forget.ts +24 -0
  569. package/src/utils/format.ts +13 -0
  570. package/src/utils/price-earnings.ts +70 -3
  571. package/src/utils/price-history.ts +58 -13
  572. package/src/utils/rpc-timeout-error.ts +27 -0
  573. package/src/utils/sec.ts +13 -1
  574. package/src/utils/utc-time.ts +79 -0
@@ -14,6 +14,17 @@ import type { PricePoint } from "../../../types/financials";
14
14
  import { resolveDatedReturns, type DatedReturn } from "./metrics";
15
15
  import { qualifySharpeCadence } from "./sharpe-cadence";
16
16
  import { evidenceDay } from "./risk-evidence";
17
+ import { FX_LIVE_RATE_MAX_DEVIATION } from "../../../market-data/coordinator/fx-legs";
18
+ import {
19
+ convertClosesToUsd,
20
+ FX_DISAGREES,
21
+ FX_NO_PAIR,
22
+ FX_UNAVAILABLE,
23
+ riskConversion,
24
+ validateFxHistory,
25
+ type FxCloses,
26
+ type RiskConversion,
27
+ } from "./risk-fx";
17
28
 
18
29
  export interface RiskInstrument {
19
30
  symbol: string;
@@ -40,6 +51,11 @@ interface RiskMarketHistory {
40
51
  closeMark?: { price: number; date: string; currency: string } | null;
41
52
  /** The listing's own price when it does not quote in USD: it values a left-out holding, never a return. */
42
53
  listing?: { price: number; currency: string } | null;
54
+ /**
55
+ * Set when the listing quotes in another currency and its closes were
56
+ * restated in USD at same-date daily FX closes; quote and closeMark are then in USD.
57
+ */
58
+ converted?: { currency: string; fxAsOf: string } | null;
43
59
  error: string | null;
44
60
  }
45
61
  export interface RiskMarketSnapshot {
@@ -84,7 +100,7 @@ const message = (error: unknown) =>
84
100
  export const portfolioRiskCache = createPluginCache<RiskMarketSnapshot>({
85
101
  kind: "portfolio-risk",
86
102
  source: "gloom-cloud",
87
- schemaVersion: 3,
103
+ schemaVersion: 4,
88
104
  policy: { staleMs: 2 * 60_000, expireMs: 24 * 60 * 60_000 },
89
105
  });
90
106
  type RiskCloudClient = Pick<
@@ -96,10 +112,11 @@ function validateRiskQuote(
96
112
  quote: CloudQuotePayload | null,
97
113
  instrument: RiskInstrument,
98
114
  now = new Date(),
115
+ currency = "USD",
99
116
  ): asserts quote is CloudQuotePayload {
100
117
  // A stale quote still establishes listing identity; it is not used as a mark.
101
- if (!quote || quote.currency !== "USD")
102
- throw new Error("Current USD listing identity unavailable");
118
+ if (!quote || quote.currency !== currency)
119
+ throw new Error(currency === "USD" ? "Current USD listing identity unavailable" : "Current listing identity unavailable");
103
120
  if (normalizeSymbol(quote.symbol) !== normalizeSymbol(instrument.symbol))
104
121
  throw new Error("Quote symbol differs from the requested holding");
105
122
  const exchange = canonicalExchange(
@@ -122,16 +139,23 @@ function validateRiskQuote(
122
139
  throw new Error("Current quote price or timestamp unavailable");
123
140
  }
124
141
 
125
- /** Daily Cloud observations, currency and listing metadata must agree before risk math. */
142
+ /**
143
+ * Daily Cloud observations, currency and listing metadata must agree before
144
+ * risk math. With a conversion the listing quotes in that currency and its
145
+ * closes are restated in USD at same-date FX closes on the basket's calendar.
146
+ */
126
147
  export function validateRiskHistory(
127
148
  response: CloudMarketResponse<CloudPricePointPayload[]>,
128
149
  instrument: RiskInstrument,
129
150
  quote: CloudQuotePayload | null,
130
151
  now = new Date(),
152
+ conversion?: { listingCurrency: string; divisor: number; fx: FxCloses },
131
153
  ): {
132
154
  returns: DatedReturn[];
133
155
  asOf: string;
134
156
  closeMark: { price: number; date: string; currency: string } | null;
157
+ /** Latest completed close in the returns' currency, whatever marks the holding. */
158
+ latestClose: { price: number; date: string };
135
159
  } {
136
160
  if (
137
161
  response.status !== "success" ||
@@ -145,9 +169,10 @@ export function validateRiskHistory(
145
169
  response.providerMeta?.currency ??
146
170
  quote?.currency ??
147
171
  (US_LISTING_VENUES.has(canonicalExchange(instrument.exchange)) ? "USD" : null);
148
- if (quote) validateRiskQuote(quote, instrument, now);
149
- else if (historyCurrency !== "USD")
150
- throw new Error("Current USD listing identity unavailable");
172
+ const expectedCurrency = conversion?.listingCurrency ?? "USD";
173
+ if (quote) validateRiskQuote(quote, instrument, now, expectedCurrency);
174
+ else if (historyCurrency !== expectedCurrency)
175
+ throw new Error(conversion ? "Current listing identity unavailable" : "Current USD listing identity unavailable");
151
176
  const exchange = canonicalExchange(
152
177
  instrument.exchange || quote?.listingExchangeName || "",
153
178
  );
@@ -209,23 +234,37 @@ export function validateRiskHistory(
209
234
  ...point,
210
235
  date: new Date(point.date),
211
236
  }));
212
- const resolved = resolveDatedReturns(prices);
213
- if (resolved.integrity)
237
+ const local = resolveDatedReturns(prices);
238
+ if (local.integrity)
214
239
  throw new Error("Daily history has inconsistent OHLC observations");
215
- const cadence = qualifySharpeCadence(resolved.returns, [
216
- { symbol: instrument.symbol, exchange, history: prices },
240
+ // A converted series is on the NYSE calendar the basket and SPY share.
241
+ const series = conversion
242
+ ? convertClosesToUsd(
243
+ points.map((point) => ({ date: new Date(point.date).toISOString().slice(0, 10), close: point.close })),
244
+ conversion.fx,
245
+ conversion.divisor,
246
+ )
247
+ : prices;
248
+ const returns = conversion ? resolveDatedReturns(series).returns : local.returns;
249
+ const cadence = qualifySharpeCadence(returns, [
250
+ { symbol: instrument.symbol, exchange: conversion ? "NYSE" : exchange, history: series },
217
251
  ]);
218
252
  if (!cadence.supported)
219
253
  throw new Error(cadence.reason ?? "Daily session cadence unavailable");
220
- const lastPoint = points.at(-1)!;
254
+ const lastPoint = series.at(-1)!;
255
+ const latestClose = {
256
+ price: lastPoint.close,
257
+ date: lastPoint.date.toISOString().slice(0, 10),
258
+ };
221
259
  return {
222
- returns: resolved.returns,
223
- asOf: last,
260
+ returns,
261
+ asOf: latestClose.date,
224
262
  // A missing or stale quote is not a current mark; the latest dated close is.
225
263
  closeMark:
226
264
  quote && !quote.stale
227
265
  ? null
228
- : { price: lastPoint.close, date: last, currency: historyCurrency! },
266
+ : { ...latestClose, currency: conversion ? "USD" : historyCurrency! },
267
+ latestClose,
229
268
  };
230
269
  }
231
270
  function validateFred(
@@ -269,10 +308,13 @@ const largestFirst = (
269
308
 
270
309
  /**
271
310
  * Quotes every holding, then requests daily history only for the factor
272
- * proxies and the largest USD holdings that can enter the basket, up to
273
- * RISK_HISTORY_LIMIT. Holdings that quote in another currency, positions the
274
- * caller marks unsupported and holdings past the limit get no history request;
275
- * their entry says why. The signal stops the fetch: no request starts once it
311
+ * proxies and the largest holdings by current USD value that can enter the
312
+ * basket, up to RISK_HISTORY_LIMIT. A holding that quotes in another currency
313
+ * competes at its value at the current rate and, when requested, also loads
314
+ * its currency's daily FX history once per currency, so its closes convert to
315
+ * USD. Holdings with no FX pair, positions the caller marks unsupported and
316
+ * holdings past the limit get no history request; their entry says why. The
317
+ * signal stops the fetch: no request starts once it
276
318
  * aborts, the ones in flight are cancelled, and the call rejects instead of
277
319
  * returning a partial snapshot nobody is waiting for.
278
320
  */
@@ -314,6 +356,9 @@ export async function fetchPortfolioRiskMarket(
314
356
  ];
315
357
  const quotes = new Map<string, CloudQuotePayload>();
316
358
  const listings = new Map<string, { price: number; currency: string }>();
359
+ // Listings quoted in another currency that convert to USD, and why the others do not.
360
+ const conversions = new Map<string, RiskConversion>();
361
+ const foreignReasons = new Map<string, string>();
317
362
  const warnings: string[] = [];
318
363
  for (let start = 0; start < targets.length; start += 50) {
319
364
  signal?.throwIfAborted();
@@ -339,11 +384,24 @@ export async function fetchPortfolioRiskMarket(
339
384
  )
340
385
  continue;
341
386
  const id = riskInstrumentId(target);
342
- // A holding quoted in another currency never enters the USD basket;
343
- // its price only values what is left out.
387
+ // A holding quoted in another currency enters the USD basket only
388
+ // through its daily closes restated at same-date FX closes.
344
389
  if (!factors.has(id) && item.data.currency && item.data.currency !== "USD") {
345
390
  if (Number.isFinite(item.data.price) && item.data.price > 0)
346
391
  listings.set(id, { price: item.data.price, currency: item.data.currency });
392
+ if (entries.get(id)?.request.unsupported) continue;
393
+ const conversion = riskConversion(item.data.currency);
394
+ if (!conversion) {
395
+ foreignReasons.set(id, FX_NO_PAIR);
396
+ continue;
397
+ }
398
+ try {
399
+ validateRiskQuote(item.data, target, now, item.data.currency);
400
+ quotes.set(id, item.data);
401
+ conversions.set(id, conversion);
402
+ } catch (error) {
403
+ foreignReasons.set(id, message(error));
404
+ }
347
405
  continue;
348
406
  }
349
407
  try {
@@ -358,9 +416,55 @@ export async function fetchPortfolioRiskMarket(
358
416
  warnings.push(`Holding marks: ${message(error)}`);
359
417
  }
360
418
  }
419
+ // Current rates value a foreign holding: its rank, its mark, and its size when left out.
420
+ const fxCurrencies = [
421
+ ...new Set(
422
+ [...entries.values()].flatMap((row) => {
423
+ const currency = resolveCurrencyUnit(
424
+ listings.get(row.id)?.currency ?? (quotes.has(row.id) ? null : row.request.currency),
425
+ ).currency;
426
+ return currency && currency !== "USD" ? [currency] : [];
427
+ }),
428
+ ),
429
+ ].slice(0, MAX_FX_CURRENCIES);
430
+ const fx = await Promise.allSettled(
431
+ fxCurrencies.map(async (currency) => {
432
+ const response = await client.getCloudExchangeRate(currency);
433
+ const rate = response.status === "success" || response.status === "partial"
434
+ ? response.data?.rate
435
+ : undefined;
436
+ if (typeof rate !== "number" || !Number.isFinite(rate) || rate <= 0)
437
+ throw new Error(response.reasonCode ?? "Rate unavailable");
438
+ return [currency, rate] as const;
439
+ }),
440
+ );
441
+ signal?.throwIfAborted();
442
+ const fxRates: Record<string, number> = {};
443
+ for (const [index, result] of fx.entries()) {
444
+ if (result.status === "fulfilled") fxRates[result.value[0]] = result.value[1];
445
+ else {
446
+ if (isAccessDenied(result.reason)) throw result.reason;
447
+ warnings.push(`${fxCurrencies[index]} exchange rate: ${message(result.reason)}`);
448
+ }
449
+ }
450
+ /** The listing's quote in USD at the current rate; null when no rate converts it. */
451
+ const usdQuote = (id: string): CloudQuotePayload | null => {
452
+ const quote = quotes.get(id) ?? null,
453
+ conversion = conversions.get(id);
454
+ if (!quote || !conversion) return quote;
455
+ const rate = fxRates[conversion.currency];
456
+ return rate
457
+ ? { ...quote, currency: "USD", price: (quote.price * rate) / conversion.divisor }
458
+ : null;
459
+ };
361
460
  const candidates = quoted
362
- .filter((row) => !factors.has(row.id) && !row.request.unsupported && !listings.has(row.id))
363
- .map((row) => ({ ...row, rank: rankValue(row.request, quotes.get(row.id) ?? null) }))
461
+ .filter(
462
+ (row) =>
463
+ !factors.has(row.id) &&
464
+ !row.request.unsupported &&
465
+ (!listings.has(row.id) || conversions.has(row.id)),
466
+ )
467
+ .map((row) => ({ ...row, rank: rankValue(row.request, usdQuote(row.id)) }))
364
468
  .sort(largestFirst);
365
469
  const requestedIds = new Set([
366
470
  ...factors.keys(),
@@ -377,6 +481,15 @@ export async function fetchPortfolioRiskMarket(
377
481
  endDate = new Date(Date.parse(today) - 86_400_000)
378
482
  .toISOString()
379
483
  .slice(0, 10);
484
+ const historyParams = {
485
+ interval: "1day",
486
+ startDate,
487
+ // validateRiskHistory drops today's bar; ending on yesterday
488
+ // loses yesterday's close for most listings.
489
+ endDate: today,
490
+ outputsize: 1000,
491
+ rangeKey: "2Y",
492
+ } as const;
380
493
  const histories: RiskMarketHistory[] = instruments.map(([id, instrument]) => {
381
494
  const quote = quotes.get(id) ?? null,
382
495
  listing = listings.get(id) ?? null;
@@ -389,13 +502,43 @@ export async function fetchPortfolioRiskMarket(
389
502
  asOf: null,
390
503
  error: requestedIds.has(id)
391
504
  ? null
392
- : listing
393
- ? "Current USD listing identity unavailable"
505
+ : listing && !conversions.has(id)
506
+ ? (foreignReasons.get(id) ??
507
+ (entries.get(id)?.request.unsupported ? "Outside the long USD equity basket" : FX_UNAVAILABLE))
394
508
  : entries.get(id)?.request.unsupported
395
509
  ? "Outside the long USD equity basket"
396
510
  : BEYOND_SIZE_REASON,
397
511
  };
398
512
  });
513
+ // One daily FX history per currency, shared by every holding that converts with it.
514
+ const fxHistories = new Map<string, Promise<FxCloses>>();
515
+ const fxHistory = (conversion: RiskConversion) => {
516
+ let loading = fxHistories.get(conversion.currency);
517
+ if (!loading) {
518
+ loading = (async () => {
519
+ const { symbol, exchange } = conversion.leg.instrument;
520
+ let closes: FxCloses;
521
+ try {
522
+ closes = validateFxHistory(
523
+ await client.getCloudHistory(symbol, exchange ?? "", historyParams, signal ? { signal } : undefined),
524
+ conversion.leg,
525
+ now,
526
+ );
527
+ } catch (error) {
528
+ if (isAccessDenied(error) || signal?.aborted) throw error;
529
+ throw new Error(message(error).startsWith("Foreign holdings") ? message(error) : FX_UNAVAILABLE);
530
+ }
531
+ // A latest close far from the current rate is a wrong or inverted pair, not a move.
532
+ const rate = fxRates[conversion.currency];
533
+ const latest = closes.closes.get(closes.asOf)!;
534
+ if (rate && Math.abs(latest / rate - 1) > FX_LIVE_RATE_MAX_DEVIATION)
535
+ throw new Error(FX_DISAGREES);
536
+ return closes;
537
+ })();
538
+ fxHistories.set(conversion.currency, loading);
539
+ }
540
+ return loading;
541
+ };
399
542
  const pending = instruments.flatMap(([id], index) =>
400
543
  requestedIds.has(id) ? [index] : [],
401
544
  );
@@ -406,28 +549,40 @@ export async function fetchPortfolioRiskMarket(
406
549
  signal?.throwIfAborted();
407
550
  const index = pending[next++]!,
408
551
  entry = histories[index]!,
552
+ conversion = conversions.get(instruments[index]![0]),
409
553
  { instrument, quote } = entry;
410
554
  try {
555
+ const fxCloses = conversion ? await fxHistory(conversion) : null;
411
556
  const response = await client.getCloudHistory(
412
557
  instrument.symbol,
413
558
  instrument.exchange || quote?.listingExchangeName || "",
414
- {
415
- interval: "1day",
416
- startDate,
417
- // validateRiskHistory drops today's bar; ending on yesterday
418
- // loses yesterday's close for most listings.
419
- endDate: today,
420
- outputsize: 1000,
421
- rangeKey: "2Y",
422
- },
559
+ historyParams,
423
560
  signal ? { signal } : undefined,
424
561
  );
425
- const resolved = validateRiskHistory(
562
+ const { latestClose, ...resolved } = validateRiskHistory(
426
563
  response,
427
564
  instrument,
428
565
  quote,
429
566
  now,
567
+ conversion && fxCloses
568
+ ? { listingCurrency: conversion.listingCurrency, divisor: conversion.divisor, fx: fxCloses }
569
+ : undefined,
430
570
  );
571
+ if (conversion && fxCloses) {
572
+ // A current quote marks at the current rate; without one, or without
573
+ // a rate, the latest close at its own date's FX close does.
574
+ const current = quote && !quote.stale ? usdQuote(instruments[index]![0]) : null;
575
+ histories[index] = {
576
+ ...entry,
577
+ ...resolved,
578
+ quote: current,
579
+ currency: "USD",
580
+ closeMark: current ? null : { ...latestClose, currency: "USD" },
581
+ converted: { currency: conversion.listingCurrency, fxAsOf: fxCloses.asOf },
582
+ error: null,
583
+ };
584
+ continue;
585
+ }
431
586
  histories[index] = {
432
587
  ...entry,
433
588
  currency: quote?.currency ?? resolved.closeMark?.currency ?? null,
@@ -441,18 +596,7 @@ export async function fetchPortfolioRiskMarket(
441
596
  }
442
597
  }),
443
598
  );
444
- // Rates value left-out holdings that do not quote in USD; they never convert a return.
445
- const fxCurrencies = [
446
- ...new Set(
447
- [...entries.values()].flatMap((row) => {
448
- const currency = resolveCurrencyUnit(
449
- listings.get(row.id)?.currency ?? (quotes.has(row.id) ? null : row.request.currency),
450
- ).currency;
451
- return currency && currency !== "USD" ? [currency] : [];
452
- }),
453
- ),
454
- ].slice(0, MAX_FX_CURRENCIES);
455
- const [, fred, fx] = await Promise.all([
599
+ const [, fred] = await Promise.all([
456
600
  historyWork,
457
601
  Promise.allSettled(
458
602
  ["DGS10", "VIXCLS"].map(async (id) =>
@@ -467,17 +611,6 @@ export async function fetchPortfolioRiskMarket(
467
611
  ),
468
612
  ),
469
613
  ),
470
- Promise.allSettled(
471
- fxCurrencies.map(async (currency) => {
472
- const response = await client.getCloudExchangeRate(currency);
473
- const rate = response.status === "success" || response.status === "partial"
474
- ? response.data?.rate
475
- : undefined;
476
- if (typeof rate !== "number" || !Number.isFinite(rate) || rate <= 0)
477
- throw new Error(response.reasonCode ?? "Rate unavailable");
478
- return [currency, rate] as const;
479
- }),
480
- ),
481
614
  ]);
482
615
  signal?.throwIfAborted();
483
616
  for (const [index, result] of fred.entries()) {
@@ -488,14 +621,6 @@ export async function fetchPortfolioRiskMarket(
488
621
  );
489
622
  }
490
623
  }
491
- const fxRates: Record<string, number> = {};
492
- for (const [index, result] of fx.entries()) {
493
- if (result.status === "fulfilled") fxRates[result.value[0]] = result.value[1];
494
- else {
495
- if (isAccessDenied(result.reason)) throw result.reason;
496
- warnings.push(`${fxCurrencies[index]} exchange rate: ${message(result.reason)}`);
497
- }
498
- }
499
624
  return {
500
625
  histories,
501
626
  yields: fred[0]!.status === "fulfilled" ? fred[0]!.value : null,
@@ -0,0 +1,102 @@
1
+ import type { CloudMarketResponse, CloudPricePointPayload } from "../../../api-client/types";
2
+ import { fxLegForCurrency, type FxLeg } from "../../../market-data/coordinator/fx-legs";
3
+ import { dailyFxCloses, FxHistoryError, type FxCloses } from "../../../market-data/fx-closes";
4
+ import { getPublishedUsEquityCalendarDay } from "../../../market-data/published-us-sessions";
5
+ import type { PricePoint } from "../../../types/financials";
6
+ import { resolveCurrencyUnit } from "../../../utils/currency-units";
7
+
8
+ export type { FxCloses };
9
+
10
+ /** How a listing quoted in another currency is restated in USD. */
11
+ export interface RiskConversion {
12
+ /** The listing currency as quoted, sub-units included (GBp, ZAc). */
13
+ listingCurrency: string;
14
+ /** The currency the FX pair prices (GBP for GBp). */
15
+ currency: string;
16
+ /** Listing units per currency unit: 100 for pence. */
17
+ divisor: number;
18
+ leg: FxLeg;
19
+ }
20
+
21
+ export const FX_UNAVAILABLE = "Foreign holdings: daily FX closes unavailable";
22
+ export const FX_NO_PAIR = "Foreign holdings: no daily FX pair for this currency";
23
+ export const FX_STALE = "Foreign holdings: daily FX history is stale";
24
+ export const FX_GAP = "Foreign holdings: daily FX history has gaps on the holding's dates";
25
+ export const FX_DISAGREES = "Foreign holdings: daily FX closes disagree with the current rate";
26
+ export const LOCAL_GAP = "Foreign holdings: local daily history has a gap longer than a market holiday";
27
+
28
+ /**
29
+ * Longest run of calendar days a listing may go without a close while US
30
+ * sessions continue (Golden Week, Lunar New Year, national holidays). A longer
31
+ * silence is missing data, not a holiday, and is never carried across.
32
+ */
33
+ const MAX_LOCAL_CLOSED_DAYS = 10;
34
+ /** The calendar the basket, SPY and the factor proxies share. */
35
+ const BASKET_CALENDAR = "NYSE";
36
+ const DAY_MS = 86_400_000;
37
+
38
+ /** The pair that restates a listing currency in USD, or null when there is none. */
39
+ export function riskConversion(listingCurrency: string): RiskConversion | null {
40
+ const unit = resolveCurrencyUnit(listingCurrency);
41
+ if (!unit.currency || unit.currency === "USD") return null;
42
+ const leg = fxLegForCurrency(unit.currency);
43
+ return leg ? { listingCurrency, currency: unit.currency, divisor: unit.divisor, leg } : null;
44
+ }
45
+
46
+ /** Completed daily closes of the pair as USD per unit, today's still-trading bar left out. */
47
+ export function validateFxHistory(
48
+ response: CloudMarketResponse<CloudPricePointPayload[]>,
49
+ leg: FxLeg,
50
+ now = new Date(),
51
+ ): FxCloses {
52
+ try {
53
+ return dailyFxCloses(response, leg, now);
54
+ } catch (error) {
55
+ if (error instanceof FxHistoryError) throw new Error(error.problem === "stale" ? FX_STALE : FX_UNAVAILABLE);
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Restates a listing's completed local closes in USD on every NYSE session
62
+ * they span, each at the FX close of that same date, so the converted series
63
+ * shares the basket's calendar and its returns pair with SPY and the other
64
+ * holdings interval for interval.
65
+ *
66
+ * On a US session the listing did not trade (its own holiday), the holding is
67
+ * worth its last local close at that day's FX close: its USD value moves with
68
+ * the currency alone, as it would in the account. A US session with no FX
69
+ * close, or a local silence longer than any market holiday, rejects the whole
70
+ * series instead of bridging it. The series ends at the earlier of the last
71
+ * local close and the last FX close, so no close is carried past the data.
72
+ */
73
+ export function convertClosesToUsd(
74
+ local: readonly { date: string; close: number }[],
75
+ fx: FxCloses,
76
+ divisor: number,
77
+ ): PricePoint[] {
78
+ const points = [...local]
79
+ .map((point) => ({ date: point.date.slice(0, 10), close: point.close }))
80
+ .sort((left, right) => left.date.localeCompare(right.date));
81
+ if (points.length < 2) throw new Error("Daily history unavailable");
82
+ const start = points[0]!.date > fx.first ? points[0]!.date : fx.first;
83
+ const end = points.at(-1)!.date < fx.asOf ? points.at(-1)!.date : fx.asOf;
84
+ const converted: PricePoint[] = [];
85
+ let cursor = 0;
86
+ for (let time = Date.parse(`${start}T00:00:00Z`); time <= Date.parse(`${end}T00:00:00Z`); time += DAY_MS) {
87
+ const date = new Date(time).toISOString().slice(0, 10);
88
+ const day = getPublishedUsEquityCalendarDay(BASKET_CALENDAR, date);
89
+ if (day == null) throw new Error("Daily calendar unavailable");
90
+ if (day !== "session") continue;
91
+ while (cursor + 1 < points.length && points[cursor + 1]!.date <= date) cursor += 1;
92
+ const last = points[cursor]!;
93
+ if (last.date > date) continue;
94
+ if (time - Date.parse(`${last.date}T00:00:00Z`) > MAX_LOCAL_CLOSED_DAYS * DAY_MS) throw new Error(LOCAL_GAP);
95
+ const rate = fx.closes.get(date);
96
+ if (rate == null) throw new Error(FX_GAP);
97
+ const close = (last.close * rate) / divisor;
98
+ converted.push({ date: new Date(time), open: close, high: close, low: close, close });
99
+ }
100
+ if (converted.length < 2) throw new Error("Daily history unavailable");
101
+ return converted;
102
+ }
@@ -62,6 +62,7 @@ function coverageSummary(coverage: RiskCoverage, limit = Infinity) {
62
62
  coveredValue: coverage.coveredValue,
63
63
  holdings: coverage.holdings,
64
64
  covered: coverage.covered,
65
+ convertedFromLocalCurrency: coverage.converted,
65
66
  leftOut: coverage.leftOut
66
67
  .slice(0, limit)
67
68
  .map((row) => ({ symbol: row.symbol, reason: row.reason, share: row.share })),
@@ -69,12 +70,14 @@ function coverageSummary(coverage: RiskCoverage, limit = Infinity) {
69
70
  };
70
71
  }
71
72
 
72
- /** One line for the whole coverage in place of a notice per left-out holding. */
73
- function coverageError(coverage: RiskCoverage | undefined): string | null {
73
+ /** One line for the whole coverage in place of a note per left-out holding. */
74
+ function coverageLine(coverage: RiskCoverage | undefined): { error?: string; note?: string } {
74
75
  const shortfall = riskCoverageShortfall(coverage);
75
- if (shortfall) return `Basket estimates unavailable: ${shortfall.title} ${shortfall.message}`.trim();
76
- const text = riskCoverageText(coverage);
77
- return text ? `Basket ${text}; metadata.coverage lists each with its reason.` : null;
76
+ // No estimate at all is a failure; an estimate that leaves holdings out is a caveat.
77
+ if (shortfall) return { error: `Basket estimates unavailable: ${shortfall.title} ${shortfall.message}`.trim() };
78
+ return coverage?.leftOut.length
79
+ ? { note: `Basket ${riskCoverageText(coverage)}; metadata.coverage lists each with its reason.` }
80
+ : {};
78
81
  }
79
82
 
80
83
  /** One view, its strongest or first rows, and the portfolio's totals instead of the model. */
@@ -101,6 +104,7 @@ function compactRiskRows(model: PortfolioRiskModel, view: RiskView): { rows: Ris
101
104
 
102
105
  export const portfolioRiskHeadless: HeadlessPaneDefinition<"bundle"> = {
103
106
  shape: "bundle",
107
+ freshness: { source: "Local portfolio and Gloom Cloud", status: "not-a-feed", basis: "risk model on daily closes" },
104
108
  argument: {
105
109
  kind: "free-text",
106
110
  optional: true,
@@ -115,7 +119,7 @@ export const portfolioRiskHeadless: HeadlessPaneDefinition<"bundle"> = {
115
119
  "Explicit local evidence for account returns and attribution",
116
120
  ],
117
121
  limitations: [
118
- `Fixed-current-weight USD equity basket over the holdings that qualify, largest ${RISK_HISTORY_LIMIT} by value; metadata.coverage states the covered share of market value and each holding left out`,
122
+ `Fixed-current-weight USD equity basket over the holdings that qualify, largest ${RISK_HISTORY_LIMIT} by value; foreign listings convert to USD at daily FX closes; metadata.coverage states the covered share of market value and each holding left out`,
119
123
  "No account performance inferred",
120
124
  "ETF price-return factor proxies",
121
125
  "Historical percentiles need 20 rolling samples",
@@ -237,9 +241,12 @@ export const portfolioRiskHeadless: HeadlessPaneDefinition<"bundle"> = {
237
241
  : view === "factors"
238
242
  ? covered && model.factors.every((row) => row.value != null)
239
243
  : model.complete;
244
+ // A basket view with too little covered shows no estimate, and the report says why rather than leaving dashes.
245
+ const shortfall = BASKET_VIEWS.has(view as RiskView) ? coverageLine(model.coverage).error : undefined;
240
246
  return {
241
247
  complete,
242
- errors: [...riskCoverageNotices(model.coverage), ...model.warnings],
248
+ errors: [...(shortfall ? [shortfall] : []), ...model.warnings],
249
+ notes: [...riskCoverageNotices(model.coverage), ...model.notes],
243
250
  sections: RISK_VIEWS.map((view) => ({
244
251
  title: view,
245
252
  columns: RISK_COLUMNS,
@@ -253,17 +260,16 @@ export const portfolioRiskHeadless: HeadlessPaneDefinition<"bundle"> = {
253
260
  if (!model?.rows) return result;
254
261
  const view = isRiskView(args.options.view) ? args.options.view : "risk";
255
262
  const { rows, notices } = compactRiskRows(model, view);
256
- // Each left-out holding is in metadata.coverage; the errors keep one line for all of them.
263
+ // Each left-out holding is in metadata.coverage; the notes keep one line for all of them.
257
264
  const perHolding = new Set(riskCoverageNotices(model.coverage));
258
265
  const basketView = BASKET_VIEWS.has(view);
259
- const coverageLine = basketView ? coverageError(model.coverage) : null;
260
- const errors = [
261
- ...(coverageLine ? [coverageLine] : []),
262
- ...(result.errors ?? []).filter((error) => !perHolding.has(error)),
263
- ];
266
+ const coverage = basketView ? coverageLine(model.coverage) : {};
267
+ const errors = [...new Set([...(coverage.error ? [coverage.error] : []), ...(result.errors ?? [])])];
268
+ const notes = [...(coverage.note ? [coverage.note] : []), ...(result.notes ?? []).filter((note) => !perHolding.has(note))];
264
269
  return {
265
270
  complete: result.complete,
266
271
  ...(errors.length ? { errors } : {}),
272
+ ...(notes.length ? { notes } : {}),
267
273
  sections: [{ title: view, columns: RISK_COLUMNS, rows: rows.map(displayRow) }],
268
274
  metadata: {
269
275
  portfolio: {