gloomberb 0.18.0 → 0.18.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 (365) hide show
  1. package/package.json +1 -1
  2. package/src/api-client/commodity-balances.ts +6 -0
  3. package/src/api-client/company-kpis.ts +9 -0
  4. package/src/api-client/crypto-markets.ts +9 -0
  5. package/src/api-client/data.ts +6 -5
  6. package/src/api-client/equity-screener.ts +2 -0
  7. package/src/api-client/market-movers.ts +7 -0
  8. package/src/api-client/types.ts +14 -0
  9. package/src/app/pane-runtime/plugin-bindings.ts +3 -1
  10. package/src/app/pane-runtime/ticker-open-runtime.ts +17 -5
  11. package/src/app/runtime/plugin-bindings.ts +6 -2
  12. package/src/brokers/logos.ts +66 -0
  13. package/src/brokers/profile-form.ts +3 -0
  14. package/src/brokers/remote-broker-adapter.ts +13 -3
  15. package/src/capabilities/index.ts +1 -1
  16. package/src/capabilities/service-capabilities.ts +7 -1
  17. package/src/cli/base-converter.ts +60 -18
  18. package/src/cli/commands/fred-series.ts +15 -5
  19. package/src/cli/commands/market.ts +290 -159
  20. package/src/cli/commands/news-search.ts +35 -8
  21. package/src/cli/commands/overview.ts +31 -6
  22. package/src/cli/commands/system.ts +35 -1
  23. package/src/cli/commands/ticker.ts +105 -52
  24. package/src/cli/currency-hints.ts +27 -0
  25. package/src/cli/fx-pair.ts +41 -4
  26. package/src/cli/glossary.ts +57 -4
  27. package/src/cli/helpers.ts +36 -3
  28. package/src/cli/index.ts +4 -1
  29. package/src/cli/known-gaps.ts +37 -1
  30. package/src/cli/listing-arg.ts +37 -2
  31. package/src/cli/pane-functions/catalog.ts +3 -1
  32. package/src/cli/pane-functions/discovery.ts +15 -1
  33. package/src/cli/pane-functions/dom.ts +9 -4
  34. package/src/cli/pane-functions/freshness.ts +4 -4
  35. package/src/cli/pane-functions/function-help.ts +31 -7
  36. package/src/cli/pane-functions/headless.ts +6 -5
  37. package/src/cli/pane-functions/index.ts +3 -2
  38. package/src/cli/pane-functions/lock-note.ts +7 -4
  39. package/src/cli/pane-functions/resolver.ts +2 -1
  40. package/src/cli/quote-answer.ts +179 -0
  41. package/src/cli/series-search.ts +2 -1
  42. package/src/cli/symbol-list.ts +51 -16
  43. package/src/cli/types.ts +4 -0
  44. package/src/components/chart/composite/composite-chart.tsx +22 -7
  45. package/src/components/chart/composite/format.ts +13 -1
  46. package/src/components/chart/composite/level-labels.ts +30 -0
  47. package/src/components/chart/composite/levels.ts +17 -6
  48. package/src/components/chart/composite/panel-surface.tsx +64 -16
  49. package/src/components/chart/composite/price-axis-labels.tsx +2 -1
  50. package/src/components/chart/composite/types.ts +4 -0
  51. package/src/components/command-bar/routes/ticker-search/actions.ts +6 -0
  52. package/src/components/command-bar/workflow/collection-actions.ts +4 -0
  53. package/src/components/index.ts +2 -0
  54. package/src/components/layout/pane/content.tsx +16 -14
  55. package/src/components/layout/pane/footer/index.tsx +22 -3
  56. package/src/components/layout/pane/footer/model.ts +99 -4
  57. package/src/components/layout/pane/header-tabs.tsx +15 -1
  58. package/src/components/onboarding/onboarding-frame.tsx +9 -11
  59. package/src/components/onboarding/use-onboarding-broker-form.ts +5 -2
  60. package/src/components/onboarding/wizard-model.ts +10 -3
  61. package/src/components/onboarding/wizard-positions.ts +8 -0
  62. package/src/components/ui/button.tsx +11 -4
  63. package/src/components/ui/choice-dialog.tsx +8 -5
  64. package/src/components/ui/display.tsx +11 -6
  65. package/src/components/ui/fields.tsx +100 -20
  66. package/src/components/ui/icon.tsx +6 -1
  67. package/src/components/ui/list-row-lines.tsx +87 -0
  68. package/src/components/ui/list-view.tsx +14 -0
  69. package/src/components/ui/logo-mark.tsx +64 -0
  70. package/src/components/ui/number-step.ts +54 -0
  71. package/src/components/ui/select-button.tsx +5 -4
  72. package/src/components/ui/select-field.tsx +3 -3
  73. package/src/components/ui/status.tsx +17 -3
  74. package/src/components/ui/tabs.tsx +9 -1
  75. package/src/components/ui/toggle.tsx +17 -3
  76. package/src/core/state/app/instrument.ts +8 -3
  77. package/src/data/config/store/normalize.ts +4 -1
  78. package/src/data/plugin-cache.ts +32 -6
  79. package/src/docs/snapshot.json +57 -22
  80. package/src/i18n/es.ts +9 -3
  81. package/src/i18n/ja.ts +9 -3
  82. package/src/i18n/ko.ts +9 -3
  83. package/src/i18n/zh-cn.ts +10 -4
  84. package/src/i18n/zh-tw.ts +10 -4
  85. package/src/market-data/commodity-names.ts +80 -0
  86. package/src/market-data/coordinator/auxiliary.ts +3 -1
  87. package/src/market-data/coordinator/chart.ts +3 -1
  88. package/src/market-data/futures-board.ts +38 -1
  89. package/src/market-data/futures-contract.ts +91 -0
  90. package/src/market-data/fx-closes.ts +78 -1
  91. package/src/market-data/market/change-basis.ts +219 -0
  92. package/src/market-data/market/format.ts +28 -12
  93. package/src/market-data/market/freshness.ts +38 -0
  94. package/src/market-data/market/status.ts +55 -9
  95. package/src/market-data/market/year-to-date.ts +46 -0
  96. package/src/market-data/quotes/contributions.ts +12 -0
  97. package/src/market-data/quotes/delisting.ts +34 -0
  98. package/src/market-data/quotes/live-valuation.ts +23 -0
  99. package/src/market-data/quotes/resolution.ts +8 -2
  100. package/src/market-data/tax-equivalent.ts +10 -2
  101. package/src/notifications/app-notifier.ts +2 -2
  102. package/src/notifications/notification-log.ts +8 -6
  103. package/src/plugins/builtin/alerts/cli.ts +26 -18
  104. package/src/plugins/builtin/alerts/headless.ts +1 -1
  105. package/src/plugins/builtin/alerts/index.tsx +13 -4
  106. package/src/plugins/builtin/analytics/index.tsx +1 -1
  107. package/src/plugins/builtin/analytics/risk-client.ts +44 -11
  108. package/src/plugins/builtin/analytics/risk-fx.ts +61 -0
  109. package/src/plugins/builtin/analytics/risk-headless.ts +10 -1
  110. package/src/plugins/builtin/analytics/risk-model.ts +79 -16
  111. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  112. package/src/plugins/builtin/analytics/risk-test-data.ts +3 -0
  113. package/src/plugins/builtin/attention/trending.tsx +8 -6
  114. package/src/plugins/builtin/backtest/client.ts +5 -2
  115. package/src/plugins/builtin/backtest/window.ts +16 -3
  116. package/src/plugins/builtin/broker-manager/add-flow.tsx +40 -8
  117. package/src/plugins/builtin/broker-manager/detail.tsx +26 -1
  118. package/src/plugins/builtin/broker-manager/index.tsx +20 -9
  119. package/src/plugins/builtin/broker-manager/model.ts +5 -0
  120. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -1
  121. package/src/plugins/builtin/broker-manager/table.ts +5 -1
  122. package/src/plugins/builtin/broker-trading/account-picker.tsx +91 -0
  123. package/src/plugins/builtin/broker-trading/chart-levels.ts +54 -0
  124. package/src/plugins/builtin/broker-trading/command.ts +103 -0
  125. package/src/plugins/builtin/broker-trading/controller.ts +688 -0
  126. package/src/plugins/builtin/broker-trading/dock.tsx +182 -0
  127. package/src/plugins/builtin/broker-trading/headless.ts +76 -0
  128. package/src/plugins/builtin/broker-trading/index.tsx +77 -0
  129. package/src/plugins/builtin/broker-trading/order-feed.ts +383 -0
  130. package/src/plugins/builtin/broker-trading/orders-data.ts +133 -0
  131. package/src/plugins/builtin/broker-trading/orders.tsx +437 -0
  132. package/src/plugins/builtin/broker-trading/overview-orders.tsx +121 -0
  133. package/src/plugins/builtin/broker-trading/pane-model.ts +207 -0
  134. package/src/plugins/builtin/broker-trading/pane.tsx +505 -0
  135. package/src/plugins/builtin/broker-trading/strategy.ts +153 -0
  136. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +117 -0
  137. package/src/plugins/builtin/broker-trading/ticket.tsx +573 -0
  138. package/src/plugins/builtin/buildout/data-runtime.ts +1 -1
  139. package/src/plugins/builtin/buildout/format.ts +1 -0
  140. package/src/plugins/builtin/buildout/model/index.ts +16 -7
  141. package/src/plugins/builtin/buildout/pane/index.tsx +3 -2
  142. package/src/plugins/builtin/buildout/pane/status.tsx +3 -3
  143. package/src/plugins/builtin/chart-composer/cli-options.ts +19 -0
  144. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  145. package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -3
  146. package/src/plugins/builtin/chart-composer/headless.ts +2 -1
  147. package/src/plugins/builtin/chart-composer/option-range.ts +39 -0
  148. package/src/plugins/builtin/chart-composer/pane.tsx +31 -11
  149. package/src/plugins/builtin/chart-composer/presets.ts +2 -2
  150. package/src/plugins/builtin/chart-composer/series-catalog.ts +4 -2
  151. package/src/plugins/builtin/chart-composer/shortcuts.ts +3 -3
  152. package/src/plugins/builtin/chart-composer/viewport-labels.ts +5 -2
  153. package/src/plugins/builtin/chat/index.tsx +1 -1
  154. package/src/plugins/builtin/chat/status-widget.tsx +1 -1
  155. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +6 -1
  156. package/src/plugins/builtin/chat/unread-inbox.ts +1 -1
  157. package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -3
  158. package/src/plugins/builtin/cloud/team/pane.tsx +30 -9
  159. package/src/plugins/builtin/commodity-balances/client.ts +6 -4
  160. package/src/plugins/builtin/commodity-balances/headless.ts +41 -7
  161. package/src/plugins/builtin/commodity-balances/index.tsx +19 -4
  162. package/src/plugins/builtin/commodity-balances/model.ts +22 -0
  163. package/src/plugins/builtin/commodity-balances/pane.tsx +36 -5
  164. package/src/plugins/builtin/company-kpis/client.ts +3 -2
  165. package/src/plugins/builtin/company-kpis/headless.ts +5 -7
  166. package/src/plugins/builtin/company-kpis/model.ts +85 -2
  167. package/src/plugins/builtin/company-kpis/pane.tsx +26 -12
  168. package/src/plugins/builtin/composite-plugins.ts +3 -2
  169. package/src/plugins/builtin/cot/headless.ts +1 -1
  170. package/src/plugins/builtin/cot/index.tsx +1 -1
  171. package/src/plugins/builtin/cot/model.ts +35 -12
  172. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  173. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +2 -1
  174. package/src/plugins/builtin/credit-conditions/index.tsx +2 -1
  175. package/src/plugins/builtin/crypto-board/client.ts +33 -2
  176. package/src/plugins/builtin/crypto-board/headless.ts +5 -2
  177. package/src/plugins/builtin/crypto-board/model.ts +28 -8
  178. package/src/plugins/builtin/custom-view/loader.ts +3 -2
  179. package/src/plugins/builtin/custom-view/pane.tsx +5 -4
  180. package/src/plugins/builtin/custom-view/view-spec.ts +1 -0
  181. package/src/plugins/builtin/dividend-yield/headless.ts +30 -8
  182. package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
  183. package/src/plugins/builtin/earnings-calls/headless.ts +4 -4
  184. package/src/plugins/builtin/earnings-calls/lock-note.ts +12 -3
  185. package/src/plugins/builtin/econ/calendar-model.ts +38 -2
  186. package/src/plugins/builtin/econ/fred-catalog.ts +6 -2
  187. package/src/plugins/builtin/econ/fred-series-map.ts +12 -1
  188. package/src/plugins/builtin/econ/index.tsx +31 -4
  189. package/src/plugins/builtin/econ/print-watch.ts +279 -0
  190. package/src/plugins/builtin/equity-screener/headless.ts +20 -5
  191. package/src/plugins/builtin/equity-screener/model.ts +40 -2
  192. package/src/plugins/builtin/equity-screener/pane.tsx +24 -21
  193. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -2
  194. package/src/plugins/builtin/estimate-revisions/lock-note.ts +29 -7
  195. package/src/plugins/builtin/executives/data.ts +24 -8
  196. package/src/plugins/builtin/executives/index.tsx +2 -18
  197. package/src/plugins/builtin/executives/pane.tsx +34 -10
  198. package/src/plugins/builtin/exposure/client.ts +4 -1
  199. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  200. package/src/plugins/builtin/exposure/evidence.ts +4 -1
  201. package/src/plugins/builtin/exposure/holdings.ts +3 -1
  202. package/src/plugins/builtin/exposure/pane.tsx +37 -16
  203. package/src/plugins/builtin/filing-events/index.tsx +9 -3
  204. package/src/plugins/builtin/futures/table.tsx +6 -3
  205. package/src/plugins/builtin/futures-curve/pane.tsx +2 -2
  206. package/src/plugins/builtin/insider/base-values.ts +72 -0
  207. package/src/plugins/builtin/insider/headless.ts +29 -4
  208. package/src/plugins/builtin/jobs/headless.ts +2 -2
  209. package/src/plugins/builtin/jobs/lock-note.ts +14 -3
  210. package/src/plugins/builtin/market-halts/model.ts +31 -6
  211. package/src/plugins/builtin/market-halts/pane.tsx +3 -0
  212. package/src/plugins/builtin/market-movers/client.ts +2 -2
  213. package/src/plugins/builtin/market-movers/index.tsx +0 -3
  214. package/src/plugins/builtin/market-movers/session-body.tsx +27 -11
  215. package/src/plugins/builtin/market-movers/session.ts +25 -10
  216. package/src/plugins/builtin/market-valuation/pane.tsx +2 -1
  217. package/src/plugins/builtin/members/pane.tsx +8 -3
  218. package/src/plugins/builtin/news/wire/feed-config.ts +33 -13
  219. package/src/plugins/builtin/news/wire/index.tsx +7 -2
  220. package/src/plugins/builtin/news/wire/letters/client.ts +1 -0
  221. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +6 -3
  222. package/src/plugins/builtin/news/wire/news/detail-view.tsx +2 -1
  223. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +11 -3
  224. package/src/plugins/builtin/news/wire/news/table.tsx +2 -1
  225. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -2
  226. package/src/plugins/builtin/news/wire/rss/feed-xml.ts +6 -1
  227. package/src/plugins/builtin/news/wire/rss/managed-feed.ts +1 -0
  228. package/src/plugins/builtin/news/wire/rss/source.ts +13 -4
  229. package/src/plugins/builtin/notes/cli.ts +33 -8
  230. package/src/plugins/builtin/notes/export.ts +49 -3
  231. package/src/plugins/builtin/notes/files.ts +14 -3
  232. package/src/plugins/builtin/notes/index.tsx +3 -3
  233. package/src/plugins/builtin/notes/quick-notes-pane.tsx +45 -10
  234. package/src/plugins/builtin/notes/store.ts +13 -3
  235. package/src/plugins/builtin/options/footer.ts +25 -20
  236. package/src/plugins/builtin/options/live-quotes.ts +33 -18
  237. package/src/plugins/builtin/options/view.tsx +77 -22
  238. package/src/plugins/builtin/options-calculator/pane.tsx +4 -2
  239. package/src/plugins/builtin/options-scenario/pane.tsx +41 -6
  240. package/src/plugins/builtin/portfolio-list/account-metrics.ts +3 -0
  241. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +8 -7
  242. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +166 -15
  243. package/src/plugins/builtin/portfolio-list/cli/render.ts +69 -15
  244. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +2 -2
  245. package/src/plugins/builtin/portfolio-list/headless.ts +3 -1
  246. package/src/plugins/builtin/portfolio-list/pane/data.ts +23 -1
  247. package/src/plugins/builtin/portfolio-list/pane/index.tsx +44 -17
  248. package/src/plugins/builtin/portfolio-list/settings.ts +20 -2
  249. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -1
  250. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +42 -7
  251. package/src/plugins/builtin/portfolio-list/summary/totals.ts +24 -3
  252. package/src/plugins/builtin/primary-dealers/client.ts +137 -62
  253. package/src/plugins/builtin/primary-dealers/headless.ts +30 -5
  254. package/src/plugins/builtin/primary-dealers/index.tsx +3 -2
  255. package/src/plugins/builtin/primary-dealers/model.ts +179 -129
  256. package/src/plugins/builtin/primary-dealers/pane.tsx +135 -15
  257. package/src/plugins/builtin/research/analyst-model.ts +5 -1
  258. package/src/plugins/builtin/risk-factors/index.tsx +12 -3
  259. package/src/plugins/builtin/scanner/feed.ts +6 -1
  260. package/src/plugins/builtin/scanner/headless.ts +1 -1
  261. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  262. package/src/plugins/builtin/series-inventory.ts +1 -0
  263. package/src/plugins/builtin/shared/broker-reconnect.ts +105 -0
  264. package/src/plugins/builtin/shared/cloud-denied.ts +7 -6
  265. package/src/plugins/builtin/shared/cloud-upgrade.ts +10 -11
  266. package/src/plugins/builtin/shared/free-routes.ts +24 -0
  267. package/src/plugins/builtin/shared/headless-options.ts +11 -0
  268. package/src/plugins/builtin/shared/pro-feature-wall.tsx +9 -4
  269. package/src/plugins/builtin/shared/pro-lock-note.ts +89 -7
  270. package/src/plugins/builtin/shared/research-trading.tsx +62 -0
  271. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  272. package/src/plugins/builtin/social-mentions/lock-note.ts +1 -1
  273. package/src/plugins/builtin/themes/headless.ts +8 -2
  274. package/src/plugins/builtin/themes/model.ts +43 -10
  275. package/src/plugins/builtin/themes/pane.tsx +8 -6
  276. package/src/plugins/builtin/thirteenf/api.ts +4 -1
  277. package/src/plugins/builtin/thirteenf/data.ts +5 -1
  278. package/src/plugins/builtin/thirteenf/headless.ts +3 -1
  279. package/src/plugins/builtin/thirteenf/pane.tsx +8 -3
  280. package/src/plugins/builtin/ticker-detail/overview/model.ts +44 -10
  281. package/src/plugins/builtin/ticker-detail/overview/option-contract.ts +64 -0
  282. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +82 -21
  283. package/src/plugins/builtin/ticker-detail/pane.tsx +21 -14
  284. package/src/plugins/builtin/ticker-detail/quote-footer.ts +5 -1
  285. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +37 -8
  286. package/src/plugins/builtin/time-sales/pane.tsx +2 -2
  287. package/src/plugins/builtin/trace-bonds/client.ts +14 -3
  288. package/src/plugins/builtin/trace-bonds/headless.ts +1 -1
  289. package/src/plugins/builtin/vol-surface/pane.tsx +2 -2
  290. package/src/plugins/builtin-editorial.ts +1 -1
  291. package/src/plugins/compat.ts +3 -0
  292. package/src/plugins/external-runtime.ts +5 -0
  293. package/src/plugins/loader.ts +6 -0
  294. package/src/plugins/plugin-icon.ts +61 -0
  295. package/src/plugins/registry/host-actions.ts +2 -1
  296. package/src/plugins/registry/index.ts +9 -1
  297. package/src/plugins/runtime/context.tsx +5 -0
  298. package/src/plugins/runtime/index.tsx +3 -0
  299. package/src/portfolio/column-values.ts +13 -4
  300. package/src/portfolio/cost-fx.ts +61 -0
  301. package/src/portfolio/mutations.ts +16 -2
  302. package/src/portfolio/position-metrics.ts +45 -4
  303. package/src/portfolio/position-quantity.ts +43 -0
  304. package/src/public/__snapshots__/public-api.test.ts.snap +5 -0
  305. package/src/public/broker.ts +8 -0
  306. package/src/public/market-data.ts +5 -0
  307. package/src/renderers/dom/desktop/control-styles.ts +7 -1
  308. package/src/renderers/dom/desktop/controls.tsx +39 -7
  309. package/src/renderers/dom/desktop/icons.tsx +14 -0
  310. package/src/renderers/dom/desktop/list-view.tsx +51 -19
  311. package/src/renderers/dom/desktop/menu.tsx +10 -1
  312. package/src/renderers/dom/desktop/select-field.tsx +8 -6
  313. package/src/renderers/dom/host/style.ts +6 -1
  314. package/src/renderers/dom/host/tabs.tsx +6 -0
  315. package/src/renderers/dom/styles.css +12 -5
  316. package/src/renderers/electrobun/bun/core-capabilities.ts +12 -0
  317. package/src/renderers/electrobun/bun/external-plugins.ts +3 -0
  318. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  319. package/src/renderers/electrobun/view/broker-remote-client.ts +16 -0
  320. package/src/renderers/electrobun/view/external-plugins.ts +1 -0
  321. package/src/series/validate.ts +2 -2
  322. package/src/sources/gloomberb-cloud/fred-series.ts +27 -2
  323. package/src/sources/gloomberb-cloud/index.ts +50 -17
  324. package/src/sources/gloomberb-cloud/normalizers.ts +4 -0
  325. package/src/sources/history-result.ts +3 -0
  326. package/src/sources/provider-errors.ts +3 -1
  327. package/src/sources/provider-router/history.ts +49 -6
  328. package/src/sources/provider-router/index.ts +2 -1
  329. package/src/state/app/activity.ts +13 -0
  330. package/src/sync/controller.ts +4 -0
  331. package/src/sync/core-contributors.ts +63 -11
  332. package/src/sync/local-only.ts +121 -0
  333. package/src/sync/react.ts +6 -0
  334. package/src/sync/types.ts +3 -0
  335. package/src/team/collections.ts +2 -0
  336. package/src/tickers/listing-fate.ts +27 -8
  337. package/src/tickers/metadata.ts +10 -0
  338. package/src/tickers/search/index.ts +24 -3
  339. package/src/time-series/price-comparison.ts +35 -0
  340. package/src/time-series/resolve.ts +52 -11
  341. package/src/time-series/spec.ts +1 -1
  342. package/src/time-series/types.ts +5 -0
  343. package/src/types/broker.ts +56 -1
  344. package/src/types/config.ts +6 -0
  345. package/src/types/data-provider.ts +5 -1
  346. package/src/types/financials.ts +23 -0
  347. package/src/types/headless.ts +12 -2
  348. package/src/types/instrument.ts +4 -0
  349. package/src/types/plugin.ts +24 -6
  350. package/src/types/price-history.ts +6 -1
  351. package/src/types/ticker.ts +9 -0
  352. package/src/types/trading.ts +67 -0
  353. package/src/ui/host.tsx +4 -0
  354. package/src/utils/base-currency.ts +108 -0
  355. package/src/utils/currency-names.ts +128 -0
  356. package/src/utils/currency-pair.ts +2 -1
  357. package/src/utils/currency-units.ts +41 -1
  358. package/src/utils/dom-screenshot.ts +22 -9
  359. package/src/utils/exchange-notation.generated.ts +360 -0
  360. package/src/utils/exchanges.ts +15 -322
  361. package/src/utils/format.ts +31 -1
  362. package/src/utils/instrument-identity.ts +38 -1
  363. package/src/utils/options.ts +46 -1
  364. package/src/utils/price-history.ts +4 -0
  365. package/src/utils/reported-money.ts +8 -4
@@ -3,6 +3,7 @@ import type { Portfolio, TickerRecord } from "../../../../types/ticker";
3
3
  import { convertCurrency, formatCompactAmount } from "../../../../utils/format";
4
4
  import { getCurrencySymbol } from "../../../../market-data/market/format";
5
5
  import { getActiveQuoteDisplay } from "../../../../market-data/market/status";
6
+ import { combinedChangeBasisLabel, quoteChangeBasis, type QuoteChangeBasis } from "../../../../market-data/market/change-basis";
6
7
  import { isQuoteStaleForCurrentSession } from "../../../../market-data/quotes/freshness";
7
8
  import { headlineQuoteDisplay } from "../../../../portfolio/column-values";
8
9
  import { isManualPortfolio } from "../../../../portfolio/mutations";
@@ -45,6 +46,13 @@ export interface PortfolioSummaryTotals {
45
46
  unrealizedPnlBasis?: PortfolioPositionPnl["basis"];
46
47
  dailyPnl: number;
47
48
  dailyPnlPct: number;
49
+ /**
50
+ * What Day (or a watchlist's Avg Day) is measured from, as each holding's
51
+ * change line says it: "vs Fri 16 Oct close" when they agree, "vs mixed
52
+ * closes" when they do not (an HKEX listing beside a US one). Absent
53
+ * without a move.
54
+ */
55
+ dayChangeBasis?: string;
48
56
  totalCostBasis: number;
49
57
  hasPositions: boolean;
50
58
  unrealizedPnl: number;
@@ -131,6 +139,7 @@ export function calculatePortfolioSummaryTotals(
131
139
  let hasPositions = false;
132
140
  let watchlistChangeSum = 0;
133
141
  let watchlistCount = 0;
142
+ const dayBases: Array<QuoteChangeBasis | null> = [];
134
143
  const unavailableConversions = new Set<string>();
135
144
  let livePriced = true;
136
145
  const pricedLots: PricedPortfolioLot[] = [];
@@ -150,11 +159,12 @@ export function calculatePortfolioSummaryTotals(
150
159
  const quoteCurrency = quote?.currency || ticker.metadata.currency || "USD";
151
160
 
152
161
  if (!isPortfolio) {
153
- // The CHG% column's value, so Avg Day is the mean of that column.
154
- const changePercent = headlineQuoteDisplay(activeQuote, quote)?.changePercent;
162
+ // The CHG% column's value, so Avg Day is the mean of that column; a listing that no longer trades has none.
163
+ const changePercent = quote?.delisting ? undefined : headlineQuoteDisplay(activeQuote, quote)?.changePercent;
155
164
  if (changePercent != null) {
156
165
  watchlistChangeSum += changePercent;
157
166
  watchlistCount++;
167
+ dayBases.push(quoteChangeBasis(quote));
158
168
  }
159
169
  continue;
160
170
  }
@@ -220,9 +230,18 @@ export function calculatePortfolioSummaryTotals(
220
230
  totalMktValue += marketValue.gross;
221
231
  netMktValue += marketValue.net;
222
232
  if (activeQuote && Number.isFinite(grossPriceUnits)) {
223
- const previousClose = activeQuote.change != null ? activeQuote.price - activeQuote.change : Number.NaN;
233
+ // A listing that no longer trades does not move: its last session's change is not today's.
234
+ const previousClose = quote?.delisting ? activeQuote.price
235
+ : activeQuote.change != null ? activeQuote.price - activeQuote.change : Number.NaN;
224
236
  totalPrevValue += toBaseQuote(grossPriceUnits * previousClose);
225
237
  signedDailyPnl += toBaseQuote(totalPriceUnits * (activeQuote.price - previousClose));
238
+ // The move valued: the latest print's from the daily reference, the regular close's from the session before it.
239
+ if (!quote?.delisting) {
240
+ dayBases.push(quoteChangeBasis(quote, {
241
+ session: valuationBasis === "regular-close" ? "regular" : "active",
242
+ terms: futuresTerms?.get(ticker.metadata.ticker),
243
+ }));
244
+ }
226
245
  } else {
227
246
  // A broker mark is not evidence of an unchanged day.
228
247
  signedDailyPnl = Number.NaN;
@@ -242,6 +261,7 @@ export function calculatePortfolioSummaryTotals(
242
261
  const unrealizedPnl = signedUnrealizedPnl;
243
262
  const unrealizedPnlPct = portfolioPnlPercent(unrealizedPnl, totalCostBasis) ?? Number.NaN;
244
263
  const avgWatchlistChange = watchlistCount > 0 ? watchlistChangeSum / watchlistCount : 0;
264
+ const dayChangeBasis = combinedChangeBasisLabel(dayBases, now);
245
265
 
246
266
  return {
247
267
  totalMktValue,
@@ -258,6 +278,7 @@ export function calculatePortfolioSummaryTotals(
258
278
  : pnlBases.size > 1 ? "mixed" : [...pnlBases][0],
259
279
  dailyPnl,
260
280
  dailyPnlPct,
281
+ ...(dayChangeBasis ? { dayChangeBasis } : {}),
261
282
  totalCostBasis,
262
283
  hasPositions,
263
284
  unrealizedPnl,
@@ -2,26 +2,17 @@ import { httpFetch } from "../../../utils/http-transport";
2
2
  import { isoDateOrNull, numericOrNull, recordOrNull, textOrNull } from "../../../utils/guards";
3
3
  import {
4
4
  boardAsOf,
5
- selectSeries,
6
- toDealerRow,
7
- type CatalogEntry,
5
+ buildDealerRows,
6
+ newestSeriesBreak,
7
+ sumSeries,
8
8
  type DealerRow,
9
- type DealerTab,
9
+ type SeriesPoint,
10
10
  } from "./model";
11
11
 
12
- const LIST_URL = "https://markets.newyorkfed.org/api/pd/list/timeseries.json";
12
+ const API = "https://markets.newyorkfed.org/api/pd";
13
13
  const FETCH_TIMEOUT_MS = 15_000;
14
-
15
- interface SeriesPoint {
16
- asOf: string;
17
- value: number;
18
- }
19
-
20
- interface ParsedSeries {
21
- keyid: string;
22
- latest: SeriesPoint | null;
23
- previous: SeriesPoint | null;
24
- }
14
+ /** The release is weekly; a series read is kept this long so moving through the board never asks twice. */
15
+ const HISTORY_TTL_MS = 30 * 60_000;
25
16
 
26
17
  export interface DealerBoard {
27
18
  seriesBreak: string;
@@ -30,52 +21,57 @@ export interface DealerBoard {
30
21
  asOf: string | null;
31
22
  }
32
23
 
33
- function parseCatalog(payload: unknown): CatalogEntry[] {
24
+ /** A row's weekly history: the sum of the series behind it, oldest first. */
25
+ export interface DealerHistory {
26
+ rowId: string;
27
+ points: SeriesPoint[];
28
+ }
29
+
30
+ function timeseriesRows(payload: unknown): unknown[] {
34
31
  const root = recordOrNull(payload);
35
32
  const pd = recordOrNull(root?.pd);
36
- const rows = Array.isArray(pd?.timeseries) ? pd.timeseries : Array.isArray(root?.timeseries) ? root.timeseries : [];
37
- const entries: CatalogEntry[] = [];
38
- for (const raw of rows) {
39
- const row = recordOrNull(raw);
40
- if (!row) continue;
41
- const seriesBreak = textOrNull(row.seriesbreak) ?? textOrNull(row.seriesBreak);
42
- const keyid = textOrNull(row.keyid) ?? textOrNull(row.keyId);
43
- if (!seriesBreak || !keyid) continue;
44
- entries.push({ seriesBreak, keyid, description: textOrNull(row.description) ?? "" });
45
- }
46
- return entries;
33
+ return Array.isArray(pd?.timeseries) ? pd.timeseries : Array.isArray(root?.timeseries) ? root.timeseries : [];
47
34
  }
48
35
 
49
- /** Latest two observations. Points may arrive oldest-last or newest-last. */
50
- function parseSeriesObservations(payload: unknown, fallbackKeyid = ""): ParsedSeries {
36
+ function parseSeriesBreaks(payload: unknown): Array<{ seriesBreak: string; startDate: string }> {
51
37
  const root = recordOrNull(payload);
52
38
  const pd = recordOrNull(root?.pd);
53
- const rows = Array.isArray(pd?.timeseries) ? pd.timeseries : Array.isArray(root?.timeseries) ? root.timeseries : [];
54
- const points: Array<SeriesPoint & { keyid: string }> = [];
39
+ const rows = Array.isArray(pd?.seriesbreaks) ? pd.seriesbreaks : [];
40
+ const breaks: Array<{ seriesBreak: string; startDate: string }> = [];
55
41
  for (const raw of rows) {
56
42
  const row = recordOrNull(raw);
57
- if (!row) continue;
58
- const asOf = isoDateOrNull(row.asofdate) ?? isoDateOrNull(row.asOfDate) ?? isoDateOrNull(row.date);
59
- const value = numericOrNull(row.value);
60
- if (!asOf || value == null) continue;
61
- points.push({
62
- asOf,
63
- value,
64
- keyid: textOrNull(row.keyid) ?? fallbackKeyid,
65
- });
43
+ const seriesBreak = textOrNull(row?.seriesbreak) ?? textOrNull(row?.seriesBreak);
44
+ const startDate = isoDateOrNull(row?.startdate) ?? isoDateOrNull(row?.startDate);
45
+ if (seriesBreak && startDate) breaks.push({ seriesBreak, startDate });
66
46
  }
67
- points.sort((a, b) => a.asOf < b.asOf ? -1 : a.asOf > b.asOf ? 1 : 0);
68
- const latest = points.length > 0 ? points[points.length - 1] ?? null : null;
69
- const previous = points.length >= 2 ? points[points.length - 2] ?? null : null;
70
- return {
71
- keyid: latest?.keyid || fallbackKeyid,
72
- latest: latest ? { asOf: latest.asOf, value: latest.value } : null,
73
- previous: previous ? { asOf: previous.asOf, value: previous.value } : null,
74
- };
47
+ return breaks;
48
+ }
49
+
50
+ /** The newest observation of every series that has a figure (the release writes `*` for a suppressed one). */
51
+ function parseLatest(payload: unknown): Map<string, SeriesPoint> {
52
+ const latest = new Map<string, SeriesPoint>();
53
+ for (const raw of timeseriesRows(payload)) {
54
+ const row = recordOrNull(raw);
55
+ const keyid = textOrNull(row?.keyid) ?? textOrNull(row?.keyId);
56
+ const asOf = isoDateOrNull(row?.asofdate) ?? isoDateOrNull(row?.asOfDate);
57
+ const value = numericOrNull(row?.value);
58
+ if (!keyid || !asOf || value == null) continue;
59
+ const known = latest.get(keyid);
60
+ if (!known || asOf > known.asOf) latest.set(keyid, { asOf, value });
61
+ }
62
+ return latest;
75
63
  }
76
64
 
77
- function seriesUrl(seriesBreak: string, keyid: string): string {
78
- return `https://markets.newyorkfed.org/api/pd/get/${encodeURIComponent(seriesBreak)}/timeseries/${encodeURIComponent(keyid)}.json`;
65
+ /** Every observation of one series, oldest first. */
66
+ function parseSeriesPoints(payload: unknown): SeriesPoint[] {
67
+ const points: SeriesPoint[] = [];
68
+ for (const raw of timeseriesRows(payload)) {
69
+ const row = recordOrNull(raw);
70
+ const asOf = isoDateOrNull(row?.asofdate) ?? isoDateOrNull(row?.asOfDate);
71
+ const value = numericOrNull(row?.value);
72
+ if (asOf && value != null) points.push({ asOf, value });
73
+ }
74
+ return points.sort((a, b) => (a.asOf < b.asOf ? -1 : a.asOf > b.asOf ? 1 : 0));
79
75
  }
80
76
 
81
77
  async function readJson(url: string): Promise<unknown> {
@@ -87,22 +83,101 @@ async function readJson(url: string): Promise<unknown> {
87
83
  return response.json() as Promise<unknown>;
88
84
  }
89
85
 
86
+ /** The series break changes about every two years; one read serves the session. */
87
+ let seriesBreakRead: Promise<string> | null = null;
88
+
89
+ function currentSeriesBreak(force: boolean): Promise<string> {
90
+ if (!seriesBreakRead || force) {
91
+ const read = readJson(`${API}/list/seriesbreaks.json`).then((payload) => {
92
+ const seriesBreak = newestSeriesBreak(parseSeriesBreaks(payload));
93
+ if (!seriesBreak) throw new Error("No series break was listed.");
94
+ return seriesBreak;
95
+ });
96
+ seriesBreakRead = read;
97
+ // A failed read is not kept, so the next one asks again.
98
+ read.catch(() => { if (seriesBreakRead === read) seriesBreakRead = null; });
99
+ }
100
+ return seriesBreakRead;
101
+ }
102
+
103
+ /**
104
+ * The board in two reads: the break the release is in now, and the latest
105
+ * figure of every series in it together with each one's change from the week
106
+ * before. History is not read here; see `loadDealerHistory`.
107
+ */
90
108
  export async function fetchPrimaryDealers(): Promise<DealerBoard> {
91
- const selected = selectSeries(parseCatalog(await readJson(LIST_URL)));
92
- const jobs: Array<{ entry: CatalogEntry; tab: DealerTab }> = [
93
- ...selected.positions.map((entry) => ({ entry, tab: "positions" as const })),
94
- ...selected.fails.map((entry) => ({ entry, tab: "fails" as const })),
95
- ];
96
- if (jobs.length === 0) throw new Error("No position series were listed.");
97
- const rows = (await Promise.all(jobs.map(async ({ entry, tab }) => {
98
- const parsed = parseSeriesObservations(await readJson(seriesUrl(entry.seriesBreak, entry.keyid)), entry.keyid);
99
- return toDealerRow(entry, parsed, tab);
100
- }))).filter((row) => row.latest != null);
109
+ const seriesBreak = await currentSeriesBreak(false);
110
+ const rows = buildDealerRows(parseLatest(await readJson(`${API}/latest/${encodeURIComponent(seriesBreak)}.json`)));
101
111
  if (rows.length === 0) throw new Error("No positions were recognized.");
102
112
  return {
103
- seriesBreak: selected.seriesBreak,
113
+ seriesBreak,
104
114
  positions: rows.filter((row) => row.tab === "positions"),
105
115
  fails: rows.filter((row) => row.tab === "fails"),
106
116
  asOf: boardAsOf(rows),
107
117
  };
108
118
  }
119
+
120
+ interface SeriesRead {
121
+ at: number;
122
+ read: Promise<SeriesPoint[]>;
123
+ points: SeriesPoint[] | null;
124
+ }
125
+
126
+ const seriesReads = new Map<string, SeriesRead>();
127
+
128
+ function seriesRead(seriesBreak: string, key: string, force: boolean): Promise<SeriesPoint[]> {
129
+ const id = `${seriesBreak}:${key}`;
130
+ const known = seriesReads.get(id);
131
+ if (known && !force && Date.now() - known.at < HISTORY_TTL_MS) return known.read;
132
+ const url = `${API}/get/${encodeURIComponent(seriesBreak)}/timeseries/${encodeURIComponent(key)}.json`;
133
+ const entry: SeriesRead = {
134
+ at: Date.now(),
135
+ points: null,
136
+ read: readJson(url).then((payload) => {
137
+ entry.points = parseSeriesPoints(payload);
138
+ return entry.points;
139
+ }),
140
+ };
141
+ seriesReads.set(id, entry);
142
+ // A failed read is not kept, so the next one asks again.
143
+ entry.read.catch(() => { if (seriesReads.get(id) === entry) seriesReads.delete(id); });
144
+ return entry.read;
145
+ }
146
+
147
+ /** A row's history, one read per series behind it, each kept for the session. */
148
+ export async function loadDealerHistory(
149
+ seriesBreak: string,
150
+ row: Pick<DealerRow, "id" | "keys">,
151
+ force = false,
152
+ ): Promise<DealerHistory> {
153
+ const series = await Promise.all(row.keys.map((key) => seriesRead(seriesBreak, key, force)));
154
+ return { rowId: row.id, points: sumSeries(series) };
155
+ }
156
+
157
+ /** The points of every series behind the row when they are all in hand and fresh. */
158
+ function heldSeries(seriesBreak: string, row: Pick<DealerRow, "keys">): SeriesPoint[][] | null {
159
+ const series: SeriesPoint[][] = [];
160
+ for (const key of row.keys) {
161
+ const known = seriesReads.get(`${seriesBreak}:${key}`);
162
+ if (!known?.points || Date.now() - known.at >= HISTORY_TTL_MS) return null;
163
+ series.push(known.points);
164
+ }
165
+ return series;
166
+ }
167
+
168
+ /** Whether the row's history is in hand, so a chart can follow the selection at once. */
169
+ export function hasDealerHistory(seriesBreak: string, row: Pick<DealerRow, "keys">): boolean {
170
+ return heldSeries(seriesBreak, row) != null;
171
+ }
172
+
173
+ /** The history when every series behind the row is already in hand, so a chart can draw before any read. */
174
+ export function cachedDealerHistory(seriesBreak: string, row: Pick<DealerRow, "id" | "keys">): DealerHistory | null {
175
+ const series = heldSeries(seriesBreak, row);
176
+ return series ? { rowId: row.id, points: sumSeries(series) } : null;
177
+ }
178
+
179
+ /** Forgets what was read, for a test or a refresh that must see the release again. */
180
+ export function resetPrimaryDealersCache(): void {
181
+ seriesBreakRead = null;
182
+ seriesReads.clear();
183
+ }
@@ -5,19 +5,26 @@ import { formatChange, formatLevel, type DealerRow } from "./model";
5
5
  const level = (value: unknown) => typeof value === "number" ? formatLevel(value) : "—";
6
6
  const change = (value: unknown) => typeof value === "number" ? formatChange(value) : "—";
7
7
  const text = (value: unknown) => typeof value === "string" && value ? value : "—";
8
+ const group = (value: unknown) => typeof value === "string" ? value : "";
8
9
 
9
10
  const COLUMNS: HeadlessPaneColumn[] = [
10
11
  { key: "position", header: "Position" },
12
+ { key: "group", header: "Group", format: group },
11
13
  { key: "asOf", header: "As of", format: text },
12
14
  { key: "latest", header: "Latest $mn", align: "right", format: level },
13
15
  { key: "previous", header: "Previous $mn", align: "right", format: level },
14
16
  { key: "change", header: "Change $mn", align: "right", format: change },
15
17
  ];
16
18
 
19
+ /** The report's table sets a breakdown in under its total, as the pane does; the export names the total in `group` instead. */
20
+ const TEXT_COLUMNS: HeadlessPaneColumn[] = COLUMNS.filter((column) => column.key !== "group");
21
+
17
22
  function reportRow(row: DealerRow) {
18
23
  return {
19
- id: row.keyid,
24
+ id: row.id,
20
25
  position: row.label,
26
+ group: row.parent,
27
+ series: row.keys.join("+"),
21
28
  asOf: row.asOf,
22
29
  latest: row.latest,
23
30
  previous: row.previous,
@@ -25,6 +32,24 @@ function reportRow(row: DealerRow) {
25
32
  };
26
33
  }
27
34
 
35
+ function textRow(row: DealerRow) {
36
+ return { ...reportRow(row), position: `${" ".repeat(row.depth)}${row.label}` };
37
+ }
38
+
39
+ function section(title: string, rows: readonly DealerRow[]) {
40
+ return {
41
+ title,
42
+ columns: COLUMNS,
43
+ rows: rows.map(reportRow),
44
+ textColumns: TEXT_COLUMNS,
45
+ textRows: rows.map(textRow),
46
+ unit: "USD millions",
47
+ rowSymbolKey: "series",
48
+ rowAsOfKey: "asOf",
49
+ interval: "weekly",
50
+ };
51
+ }
52
+
28
53
  export const primaryDealersHeadless: HeadlessPaneDefinition<"bundle"> = {
29
54
  shape: "bundle",
30
55
  argument: { kind: "none" },
@@ -35,8 +60,8 @@ export const primaryDealersHeadless: HeadlessPaneDefinition<"bundle"> = {
35
60
  aliases: ["PDLR"],
36
61
  dataRequirements: ["Weekly primary dealer positions and settlement fails"],
37
62
  limitations: [
38
- "Treasury and mortgage-backed aggregate totals, not tenor or CUSIP buckets",
39
- "Latest observation and the one before it",
63
+ "Net positions in Treasuries (bills, coupons by tenor, TIPS, FRN), agencies, MBS, corporates, munis and ABS; settlement fails in Treasuries, TIPS and MBS",
64
+ "Latest observation and the change from the week before; the weekly history is in the pane",
40
65
  ],
41
66
  },
42
67
  async load() {
@@ -44,8 +69,8 @@ export const primaryDealersHeadless: HeadlessPaneDefinition<"bundle"> = {
44
69
  return {
45
70
  complete: board.positions.length > 0,
46
71
  sections: [
47
- { title: "Positions", columns: COLUMNS, rows: board.positions.map(reportRow) },
48
- { title: "Fails", columns: COLUMNS, rows: board.fails.map(reportRow) },
72
+ section("Positions", board.positions),
73
+ section("Fails", board.fails),
49
74
  ],
50
75
  metadata: { asOf: board.asOf, seriesBreak: board.seriesBreak },
51
76
  };
@@ -8,8 +8,9 @@ export const primaryDealersModule: PluginModule = {
8
8
  id: "primary-dealers-pane",
9
9
  paneId: PRIMARY_DEALERS_PANE_ID,
10
10
  label: "Primary Dealer Positions",
11
- description: "Weekly net positions and settlement fails in Treasuries and mortgage-backed securities.",
12
- keywords: ["primary", "dealer", "dealers", "positions", "fails", "treasury", "tips", "mbs", "pdlr"],
11
+ description: "Weekly net positions with history, by sector and Treasury tenor, and settlement fails.",
12
+ // No "muni": a muni search lands on DVD's muni funds alone (known-gaps.test.ts), not on a table of two functions.
13
+ keywords: ["primary", "dealer", "dealers", "positions", "fails", "treasury", "bills", "coupons", "tips", "frn", "mbs", "pdlr"],
13
14
  shortcut: { prefix: "PDLR" },
14
15
  headless: primaryDealersHeadless,
15
16
  createInstance: () => ({ placement: "floating" }),