gloomberb 0.16.1 → 0.16.3

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 (643) 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 +69 -0
  14. package/src/cli/command-options.ts +74 -0
  15. package/src/cli/commands/command-utils.ts +37 -0
  16. package/src/cli/commands/crypto-hints.ts +75 -0
  17. package/src/cli/commands/market.ts +560 -136
  18. package/src/cli/commands/overview.ts +136 -48
  19. package/src/cli/commands/ticker.ts +220 -67
  20. package/src/cli/desktop-pane-shot.ts +37 -4
  21. package/src/cli/freshness.ts +61 -0
  22. package/src/cli/help.ts +27 -6
  23. package/src/cli/helpers.ts +4 -8
  24. package/src/cli/history-rows.ts +187 -15
  25. package/src/cli/index.ts +23 -8
  26. package/src/cli/listing-arg.ts +321 -0
  27. package/src/cli/options.ts +5 -1
  28. package/src/cli/pane-functions/access-gate.ts +42 -0
  29. package/src/cli/pane-functions/capabilities.ts +5 -0
  30. package/src/cli/pane-functions/catalog.ts +28 -1
  31. package/src/cli/pane-functions/data.ts +3 -1
  32. package/src/cli/pane-functions/dom.ts +87 -16
  33. package/src/cli/pane-functions/freshness.ts +400 -0
  34. package/src/cli/pane-functions/function-help.ts +31 -22
  35. package/src/cli/pane-functions/headless.ts +201 -41
  36. package/src/cli/pane-functions/index.ts +24 -6
  37. package/src/cli/pane-functions/options.ts +3 -0
  38. package/src/cli/pane-functions/report.ts +12 -1
  39. package/src/cli/pane-functions/resolver.ts +63 -9
  40. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  41. package/src/cli/pane-functions/screenshot.ts +11 -3
  42. package/src/cli/registry.ts +6 -3
  43. package/src/cli/report-tables.ts +460 -0
  44. package/src/cli/result.ts +53 -29
  45. package/src/cli/ticker-resolution.ts +7 -5
  46. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  47. package/src/components/chart/composite/drawing-store.ts +59 -0
  48. package/src/components/chart/composite/legend.tsx +319 -0
  49. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  50. package/src/components/chart/composite/panel-gestures.ts +59 -0
  51. package/src/components/chart/composite/panel-labels.ts +107 -0
  52. package/src/components/chart/composite/panel-surface.tsx +876 -0
  53. package/src/components/chart/composite/tool-catalog.ts +98 -0
  54. package/src/components/chart/composite/toolbar.tsx +186 -0
  55. package/src/components/chart/composite/tools.ts +8 -0
  56. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  57. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  58. package/src/components/command-bar/list/model.ts +5 -0
  59. package/src/components/command-bar/list/view.tsx +16 -3
  60. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  61. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  62. package/src/components/command-bar/workflow/ops.ts +1 -1
  63. package/src/components/command-bar/workflow/tickers.ts +6 -0
  64. package/src/components/data-table/export.ts +6 -51
  65. package/src/components/data-table/view.tsx +32 -11
  66. package/src/components/feedback-dialog.tsx +2 -1
  67. package/src/components/form-modal/field-row.tsx +2 -2
  68. package/src/components/layout/detached-pane-shell.tsx +6 -3
  69. package/src/components/layout/pane/content.tsx +23 -10
  70. package/src/components/layout/pane/error-boundary.tsx +103 -0
  71. package/src/components/layout/pane/footer/registration.tsx +68 -23
  72. package/src/components/layout/pane/header.tsx +11 -11
  73. package/src/components/layout/shell/active-drag.ts +67 -35
  74. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  75. package/src/components/layout/shell/drag/index.ts +12 -7
  76. package/src/components/layout/shell/drag/live.ts +122 -0
  77. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  78. package/src/components/layout/shell/drag/runtime.ts +62 -50
  79. package/src/components/layout/shell/drag/slide.ts +10 -0
  80. package/src/components/layout/shell/index.tsx +37 -51
  81. package/src/components/layout/shell/menu.ts +2 -1
  82. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  83. package/src/components/layout/shell/native/surfaces.ts +30 -14
  84. package/src/components/layout/shell/pane/actions.ts +2 -1
  85. package/src/components/layout/shell/pane/layers.tsx +419 -168
  86. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  87. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  88. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  89. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  90. package/src/components/metric-treemap/layout.ts +2 -0
  91. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  93. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  94. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  95. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  96. package/src/components/ticker/list-table-view.tsx +13 -1
  97. package/src/components/ui/fields.tsx +8 -0
  98. package/src/components/ui/range-track.tsx +10 -3
  99. package/src/components/ui/status.tsx +8 -2
  100. package/src/components/ui/tabs.tsx +7 -4
  101. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  102. package/src/data/config/store/normalize.ts +17 -1
  103. package/src/data/json-persistence.ts +17 -3
  104. package/src/data/json-storage.ts +2 -0
  105. package/src/data/memory-resource-store.ts +46 -0
  106. package/src/i18n/es.ts +19 -1
  107. package/src/i18n/ja.ts +19 -1
  108. package/src/i18n/ko.ts +19 -1
  109. package/src/i18n/zh-cn.ts +19 -1
  110. package/src/i18n/zh-tw.ts +19 -1
  111. package/src/market-data/coordinator/chart.ts +1 -1
  112. package/src/market-data/coordinator/quotes.ts +23 -4
  113. package/src/market-data/fx-closes.ts +72 -0
  114. package/src/market-data/market/freshness.ts +123 -6
  115. package/src/market-data/market/session-close-note.ts +57 -0
  116. package/src/market-data/market/status.ts +151 -0
  117. package/src/market-data/published-apac-sessions.ts +66 -0
  118. package/src/market-data/quotes/contributions.ts +2 -0
  119. package/src/market-data/quotes/freshness.ts +5 -0
  120. package/src/market-data/quotes/resolution.ts +2 -0
  121. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  122. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  123. package/src/plugins/builtin/alerts/cli.ts +10 -5
  124. package/src/plugins/builtin/alerts/index.tsx +1 -0
  125. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  126. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  127. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  128. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  129. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  130. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  131. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  132. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  133. package/src/plugins/builtin/attention/headless.ts +6 -4
  134. package/src/plugins/builtin/attention/index.tsx +1 -1
  135. package/src/plugins/builtin/attention/pane.tsx +2 -2
  136. package/src/plugins/builtin/awards/headless.ts +1 -0
  137. package/src/plugins/builtin/awards/pane.tsx +1 -1
  138. package/src/plugins/builtin/backtest/headless.ts +1 -0
  139. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  140. package/src/plugins/builtin/buildout/index.tsx +1 -0
  141. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  142. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  143. package/src/plugins/builtin/cds/headless.ts +2 -0
  144. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  145. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  146. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  147. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  148. package/src/plugins/builtin/chat/channels.ts +19 -3
  149. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  150. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  151. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  152. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  153. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  154. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  155. package/src/plugins/builtin/chat/index.tsx +1 -0
  156. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  157. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  158. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  159. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  160. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  161. package/src/plugins/builtin/chat/message/types.ts +14 -2
  162. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  163. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  164. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  165. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  166. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  167. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  168. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  169. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  170. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  171. package/src/plugins/builtin/company-attention/pane.tsx +2 -2
  172. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  173. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  174. package/src/plugins/builtin/correlation/compute.ts +59 -16
  175. package/src/plugins/builtin/correlation/geo.ts +34 -0
  176. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  177. package/src/plugins/builtin/correlation/headless.ts +42 -8
  178. package/src/plugins/builtin/correlation/history.ts +2 -0
  179. package/src/plugins/builtin/correlation/index.tsx +68 -14
  180. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  181. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  182. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  183. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  184. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  185. package/src/plugins/builtin/correlation/settings.ts +1 -1
  186. package/src/plugins/builtin/cot/headless.ts +6 -3
  187. package/src/plugins/builtin/cot/index.tsx +3 -3
  188. package/src/plugins/builtin/cot/model.ts +36 -3
  189. package/src/plugins/builtin/cpi/headless.ts +4 -0
  190. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  191. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  192. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  193. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  194. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  195. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  196. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  197. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  198. package/src/plugins/builtin/doe/headless.ts +7 -0
  199. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  200. package/src/plugins/builtin/earnings/headless.ts +5 -0
  201. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  202. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  203. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  204. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  205. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  206. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  207. package/src/plugins/builtin/econ/index.tsx +1 -0
  208. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  209. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  210. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  211. package/src/plugins/builtin/executives/index.tsx +1 -0
  212. package/src/plugins/builtin/exposure/headless.ts +3 -1
  213. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  214. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  215. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  216. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  217. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  218. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  219. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  220. package/src/plugins/builtin/gpu/headless.ts +7 -3
  221. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  222. package/src/plugins/builtin/gpu/model.ts +27 -1
  223. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  224. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  225. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  226. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  227. package/src/plugins/builtin/holders/client.ts +3 -6
  228. package/src/plugins/builtin/holders/format.ts +31 -1
  229. package/src/plugins/builtin/holders/headless.ts +161 -6
  230. package/src/plugins/builtin/holders/index.tsx +10 -3
  231. package/src/plugins/builtin/holders/pane.tsx +42 -16
  232. package/src/plugins/builtin/holders/report-header.ts +136 -0
  233. package/src/plugins/builtin/holders/table-model.ts +20 -5
  234. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  235. package/src/plugins/builtin/holders/types.ts +2 -1
  236. package/src/plugins/builtin/insider/headless.ts +2 -0
  237. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  238. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  239. package/src/plugins/builtin/jobs/headless.ts +1 -0
  240. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  241. package/src/plugins/builtin/kelly-sizer/pane.tsx +12 -6
  242. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  243. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  244. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  245. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  246. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  247. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  248. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  249. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  250. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  251. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  252. package/src/plugins/builtin/members/client.ts +20 -3
  253. package/src/plugins/builtin/members/headless.ts +5 -3
  254. package/src/plugins/builtin/members/model.ts +10 -2
  255. package/src/plugins/builtin/members/pane.tsx +23 -6
  256. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  257. package/src/plugins/builtin/mna/headless.ts +1 -0
  258. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  259. package/src/plugins/builtin/news/headless.ts +2 -0
  260. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  261. package/src/plugins/builtin/notes/cli.ts +14 -6
  262. package/src/plugins/builtin/notes/index.tsx +1 -0
  263. package/src/plugins/builtin/openfda/index.tsx +1 -0
  264. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  265. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  266. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  267. package/src/plugins/builtin/pe-band/client.ts +48 -3
  268. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  269. package/src/plugins/builtin/pe-band/model.ts +92 -16
  270. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  271. package/src/plugins/builtin/perps/client.ts +50 -3
  272. package/src/plugins/builtin/perps/headless.ts +144 -16
  273. package/src/plugins/builtin/perps/index.tsx +6 -6
  274. package/src/plugins/builtin/perps/model.ts +189 -12
  275. package/src/plugins/builtin/perps/pane.tsx +249 -48
  276. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  277. package/src/plugins/builtin/portfolio-list/allocation.ts +248 -0
  278. package/src/plugins/builtin/portfolio-list/cached-account.ts +42 -0
  279. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  280. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  281. package/src/plugins/builtin/portfolio-list/cli/render.ts +232 -39
  282. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  283. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  284. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  285. package/src/plugins/builtin/portfolio-list/headless.ts +100 -16
  286. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  287. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  288. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  289. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  290. package/src/plugins/builtin/portfolio-list/pane/index.tsx +53 -16
  291. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  292. package/src/plugins/builtin/portfolio-list/summary/index.tsx +46 -5
  293. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  294. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  295. package/src/plugins/builtin/power/headless.ts +3 -1
  296. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  297. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  298. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  299. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  300. package/src/plugins/builtin/research/events-headless.ts +2 -0
  301. package/src/plugins/builtin/research/headless.ts +2 -0
  302. package/src/plugins/builtin/research/index.tsx +1 -0
  303. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  304. package/src/plugins/builtin/research/relative-valuation-model.ts +18 -6
  305. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  306. package/src/plugins/builtin/research-search/index.tsx +1 -0
  307. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  308. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  309. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  310. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  311. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  312. package/src/plugins/builtin/scanner/headless.ts +14 -0
  313. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  314. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  315. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  316. package/src/plugins/builtin/sec/forms.ts +12 -0
  317. package/src/plugins/builtin/sec/headless.ts +2 -0
  318. package/src/plugins/builtin/sectors/client.ts +1 -0
  319. package/src/plugins/builtin/sectors/headless.ts +9 -1
  320. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  321. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  322. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  323. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  324. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  325. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  326. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  327. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  328. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  329. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  330. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  331. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  332. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  333. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  334. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  335. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  336. package/src/plugins/builtin/themes/headless.ts +3 -0
  337. package/src/plugins/builtin/themes/pane.tsx +3 -0
  338. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  339. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  340. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  341. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  342. package/src/plugins/builtin/ticker-detail/headless.ts +27 -5
  343. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  344. package/src/plugins/builtin/ticker-detail/overview/model.ts +17 -5
  345. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +27 -26
  346. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  347. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  348. package/src/plugins/builtin/volatility/headless.ts +6 -1
  349. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  350. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +159 -0
  351. package/src/plugins/builtin/world-venue-map/basemap.ts +232 -0
  352. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  353. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  354. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  355. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  356. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  357. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  358. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  359. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  360. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +448 -0
  361. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  362. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  363. package/src/plugins/builtin/world-venue-map/index.ts +69 -6
  364. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +565 -0
  365. package/src/plugins/builtin/world-venue-map/layers.ts +406 -0
  366. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  367. package/src/plugins/builtin/world-venue-map/map.tsx +557 -85
  368. package/src/plugins/builtin/world-venue-map/model.ts +23 -4
  369. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +202 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/pane.tsx +35 -44
  564. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  565. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  566. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  567. package/src/plugins/loader.ts +13 -1
  568. package/src/plugins/registry/pane-settings.ts +1 -0
  569. package/src/renderers/browser/app-services.ts +3 -1
  570. package/src/renderers/dom/build-assets.ts +81 -3
  571. package/src/renderers/dom/data-table/dom.ts +31 -0
  572. package/src/renderers/dom/data-table/index.tsx +5 -0
  573. package/src/renderers/dom/data-table/row.tsx +2 -0
  574. package/src/renderers/dom/desktop/controls.tsx +7 -3
  575. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  576. package/src/renderers/dom/desktop/popover.tsx +73 -14
  577. package/src/renderers/dom/host/box.tsx +113 -17
  578. package/src/renderers/dom/host/drag-shield.ts +24 -0
  579. package/src/renderers/dom/host/input.tsx +14 -3
  580. package/src/renderers/dom/host/live-frame.ts +54 -0
  581. package/src/renderers/dom/host/mouse.ts +8 -1
  582. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  583. package/src/renderers/dom/host/tabs.tsx +3 -2
  584. package/src/renderers/dom/styles.css +16 -9
  585. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  586. package/src/renderers/electrobun/bun/desktop/backend.ts +2 -0
  587. package/src/renderers/electrobun/bun/desktop/initialization.ts +13 -0
  588. package/src/renderers/electrobun/shared/protocol.ts +10 -1
  589. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  590. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -0
  591. package/src/renderers/electrobun/view/host/native.ts +0 -10
  592. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  593. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  594. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  595. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  596. package/src/renderers/opentui/host.tsx +5 -0
  597. package/src/renderers/opentui/ui-host.tsx +7 -1
  598. package/src/sources/gloomberb-cloud/index.ts +25 -4
  599. package/src/sources/gloomberb-cloud/normalizers.ts +43 -23
  600. package/src/sources/provider-router/batches.ts +3 -3
  601. package/src/sources/provider-router/cached-routes.ts +4 -1
  602. package/src/sources/provider-router/financial-routes.ts +2 -2
  603. package/src/sources/provider-router/financials.ts +76 -12
  604. package/src/sources/provider-router/history.ts +15 -10
  605. package/src/sources/provider-router/primary.ts +2 -2
  606. package/src/sources/sec-registrant.ts +75 -0
  607. package/src/state/app/context/index.tsx +24 -8
  608. package/src/state/hooks/quote-streaming.ts +41 -26
  609. package/src/state/pane-scroll-registry.tsx +67 -35
  610. package/src/sync/core-contributors.ts +4 -0
  611. package/src/telemetry/crash-reports.ts +4 -1
  612. package/src/test-support/cli-context.ts +10 -2
  613. package/src/test-support/margin-account.ts +108 -0
  614. package/src/theme/heat-colors.ts +266 -84
  615. package/src/time-series/resolution.ts +15 -0
  616. package/src/time-series/resolve.ts +1 -1
  617. package/src/time-series/use-chart-resolution.ts +1 -1
  618. package/src/types/context-menu.ts +6 -0
  619. package/src/types/data-provider.ts +2 -0
  620. package/src/types/financials.ts +35 -3
  621. package/src/types/headless.ts +79 -2
  622. package/src/types/plugin.ts +20 -1
  623. package/src/types/ticker.ts +15 -0
  624. package/src/ui/host.tsx +26 -0
  625. package/src/ui/index.tsx +8 -3
  626. package/src/ui/scroll-box-axis.ts +26 -0
  627. package/src/updater/index.ts +9 -11
  628. package/src/utils/async-deadline.ts +21 -0
  629. package/src/utils/cli-output.ts +5 -1
  630. package/src/utils/crypto-pair.ts +41 -0
  631. package/src/utils/currency-units.ts +19 -0
  632. package/src/utils/depositary-receipt.ts +70 -0
  633. package/src/utils/display-number.ts +63 -0
  634. package/src/utils/exchanges.ts +72 -0
  635. package/src/utils/fire-and-forget.ts +24 -0
  636. package/src/utils/format.ts +13 -0
  637. package/src/utils/fundamentals.ts +17 -1
  638. package/src/utils/price-earnings.ts +70 -3
  639. package/src/utils/price-history-integrity.ts +19 -6
  640. package/src/utils/price-history.ts +58 -13
  641. package/src/utils/rpc-timeout-error.ts +27 -0
  642. package/src/utils/sec.ts +13 -1
  643. package/src/utils/utc-time.ts +79 -0
@@ -1,7 +1,8 @@
1
1
  import { withConfigData } from "../../../cli/scoped-context";
2
2
  import { CLI_COMMAND_GROUPS } from "../../../cli/help";
3
3
  import { dryRunNote } from "../../../cli/helpers";
4
- import { requireArg } from "../../../cli/commands/command-utils";
4
+ import { requireArg, takeOption } from "../../../cli/commands/command-utils";
5
+ import { EXCHANGE_OPTION, requireListingArg } from "../../../cli/listing-arg";
5
6
  import { saveConfig } from "../../../data/config/store";
6
7
  import type { CliCommandDef } from "../../../types/plugin";
7
8
  import { cliStyles } from "../../../utils/cli-output";
@@ -18,10 +19,14 @@ export const alertsCliCommand: CliCommandDef = {
18
19
  help: {
19
20
  group: CLI_COMMAND_GROUPS.portfolios,
20
21
  usage: ["alerts list", "alerts add <symbol> <above|below|crosses> <price>", "alerts delete <id>", "alerts rearm <id>"],
21
- examples: ["alerts", "alerts add AAPL above 250", "alerts add BTC-USD below 60000"],
22
+ options: [EXCHANGE_OPTION],
23
+ examples: ["alerts", "alerts add AAPL above 250", "alerts add SAN:EPA below 70", "alerts add BTC-USD below 60000"],
22
24
  },
23
- execute: async (args, ctx) => {
25
+ execute: async (rawArgs, ctx) => {
26
+ const args = [...rawArgs];
27
+ const exchange = takeOption(args, "--exchange");
24
28
  const action = args[0] ?? "list";
29
+ const listing = action === "add" && args[1] ? await requireListingArg(args[1], exchange, ctx) : null;
25
30
  await withConfigData(ctx, async (config) => {
26
31
  const raw = config.config.pluginConfig[ALERTS_PLUGIN_ID]?.[ALERTS_KEY];
27
32
  const alerts = deserializeAlerts(typeof raw === "string" ? raw : "[]");
@@ -56,12 +61,12 @@ export const alertsCliCommand: CliCommandDef = {
56
61
  return;
57
62
  }
58
63
  if (action === "add") {
59
- const symbol = requireArg(args[1]?.toUpperCase(), "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx);
64
+ const symbol = requireArg(listing?.key, "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx);
60
65
  const condition = requireArg(args[2], "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx) as AlertCondition;
61
66
  if (!["above", "below", "crosses"].includes(condition)) ctx.fail("Condition must be above, below, or crosses.");
62
67
  const price = Number(requireArg(args[3], "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx));
63
68
  if (!Number.isFinite(price)) ctx.fail("Alert price must be a finite number.");
64
- const alert = createAlert(symbol, condition, price);
69
+ const alert = createAlert(symbol, condition, price, listing?.exchange);
65
70
  await saveAlerts([...alerts, alert]);
66
71
  ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert } }, {
67
72
  text: (data) => `Added alert ${data.alert.id}: ${symbol} ${condition} ${price}.${dryRunNote(data.dryRun)}`,
@@ -282,6 +282,7 @@ export const alertsPlugin: GloomPlugin = {
282
282
 
283
283
  ctx.registerPane({
284
284
  id: "alerts",
285
+ reportFreshness: { source: "Your alerts", status: "not-a-feed", basis: "your alert rules" },
285
286
  name: "Alerts",
286
287
  icon: "A",
287
288
  component: AlertsPane,
@@ -33,13 +33,18 @@ export function syntheticPositionUnsupportedReason(ticker: TickerRecord, quoteCu
33
33
  if (positions.some((position) => position.side === "short" || position.shares < 0)) {
34
34
  return "Short positions: signed exposure history required";
35
35
  }
36
+ const category = (ticker.metadata.assetCategory ?? "").toUpperCase();
37
+ // Crypto trades every day at another close, so its daily returns do not pair with the equity factor proxies.
38
+ if (category.startsWith("CRYPTO") || /-[A-Z]{3,4}$/.test(ticker.metadata.ticker)) {
39
+ return "Crypto is not covered by the equity basket";
40
+ }
36
41
  // "Common Stock" is the category provider search hands a ticker added from
37
42
  // the command bar, so a plain equity must not read as unsupported.
38
43
  const equityCategories = new Set(["", "STK", "STOCK", "COMMON STOCK", "EQUITY", "ETF", "ETN", "FUND", "ADR", "REIT"]);
39
- if (!equityCategories.has((ticker.metadata.assetCategory ?? "").toUpperCase())
44
+ if (!equityCategories.has(category)
40
45
  || positions.some((position) => position.multiplier != null && position.multiplier !== 1)
41
- || /=[A-Z]+$|-[A-Z]{3,4}$|\d{6}[CP]\d{8}$/.test(ticker.metadata.ticker)) {
42
- return "Unsupported asset: equity basket estimate only";
46
+ || /=[A-Z]+$|\d{6}[CP]\d{8}$/.test(ticker.metadata.ticker)) {
47
+ return "Asset type is not covered by the equity basket";
43
48
  }
44
49
  if (quoteCurrency !== "USD") return "Foreign holdings: historical FX returns required";
45
50
  return null;
@@ -12,6 +12,7 @@ import { instrumentFromTicker, type ChartRequest, type TickerInstrumentOptions }
12
12
  import { buildChartKey } from "../../../market-data/selectors";
13
13
  import {
14
14
  resolvePortfolioAccountMetrics,
15
+ resolvePortfolioLeverage,
15
16
  resolvePortfolioMarketValue,
16
17
  resolvePortfolioNetLiquidation,
17
18
  } from "../portfolio-list/account-metrics";
@@ -69,8 +70,8 @@ function formatAccountFreshness(account: ResolvedPortfolioAccountState["account"
69
70
  }
70
71
 
71
72
  function formatMarginLeverage(netLiquidation: number | undefined, totalMarketValue: number): string | null {
72
- if (!isFiniteNumber(netLiquidation) || netLiquidation <= 0 || !isFiniteNumber(totalMarketValue) || totalMarketValue < 0) return null;
73
- return `${(totalMarketValue / netLiquidation).toFixed(1)}x`;
73
+ const leverage = resolvePortfolioLeverage(totalMarketValue, netLiquidation);
74
+ return leverage == null ? null : `${leverage.toFixed(1)}x`;
74
75
  }
75
76
 
76
77
  export function buildPortfolioChartTargets(
@@ -205,6 +206,7 @@ export function buildPortfolioBetaResult(portfolio: PortfolioReturnSeriesResult,
205
206
 
206
207
  export function buildAnalyticsSummaryRows({
207
208
  accountState,
209
+ activePortfolio,
208
210
  brokerPerformance,
209
211
  portfolioStats,
210
212
  convertAccountValue = (value) => value,
@@ -232,6 +234,8 @@ export function buildAnalyticsSummaryRows({
232
234
  const totalMarketValue = resolvePortfolioMarketValue(portfolioStats, account, convertAccountValue, basis);
233
235
  const netLiquidation = resolvePortfolioNetLiquidation(portfolioStats, account, convertAccountValue, basis);
234
236
  const hasMarketValue = portfolioStats.hasPositions || isFiniteNumber(account?.grossPositionValue);
237
+ // As in the portfolio header: a broker account is worth its equity, and its positions alone are its gross.
238
+ const broker = !!account || !!activePortfolio?.brokerInstanceId;
235
239
 
236
240
  if (portfolioStats.unavailableConversions?.length || (account && !Number.isFinite(convertAccountValue(1)))) {
237
241
  rows.push({
@@ -241,19 +245,19 @@ export function buildAnalyticsSummaryRows({
241
245
  });
242
246
  }
243
247
 
244
- if (netLiquidation != null) {
248
+ if (netLiquidation != null || (broker && hasMarketValue)) {
245
249
  rows.push({
246
250
  id: "net-liquidation",
247
251
  label: "Net Liq",
248
- value: money(netLiquidation),
249
- color: colors.text,
252
+ value: money(netLiquidation ?? Number.NaN),
253
+ color: netLiquidation != null ? colors.text : colors.textDim,
250
254
  });
251
255
  }
252
256
 
253
257
  if (hasMarketValue) {
254
258
  rows.push({
255
259
  id: "total-value",
256
- label: "Val",
260
+ label: broker || portfolioStats.hasShorts ? "Gross" : "Val",
257
261
  value: money(totalMarketValue),
258
262
  color: colors.text,
259
263
  });
@@ -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
+ }