gloomberb 0.18.3 → 0.18.4

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 (886) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -0,0 +1,39 @@
1
+ import type { HeadlessPaneArgumentKind } from "../types/headless";
2
+ import type { PaneDef, PaneTemplateDef } from "../types/plugin";
3
+
4
+ type ShortcutArgKind = NonNullable<NonNullable<PaneTemplateDef["shortcut"]>["argKind"]>;
5
+
6
+ const SHORTCUT_ARG_KINDS: Record<Exclude<HeadlessPaneArgumentKind, "none">, ShortcutArgKind> = {
7
+ ticker: "ticker",
8
+ tickers: "ticker-list",
9
+ "symbol-list": "ticker-list",
10
+ "free-text": "text",
11
+ };
12
+
13
+ /**
14
+ * The template as the command bar reads it. A shortcut that declares no
15
+ * argument takes the one the function declares for `fn` and `catalog`
16
+ * (`headless.argument`), so `PF Book` opens Book in the bar as it does in
17
+ * `gloom fn PF Book`. A shortcut that declares its own argument keeps it.
18
+ */
19
+ export function withFunctionShortcutArgument(
20
+ template: PaneTemplateDef,
21
+ pane?: Pick<PaneDef, "headless">,
22
+ ): PaneTemplateDef {
23
+ const shortcut = template.shortcut;
24
+ if (!shortcut || shortcut.argKind || shortcut.argPlaceholder) return template;
25
+ const argument = (template.headless ?? pane?.headless)?.argument;
26
+ if (!argument || argument.kind === "none") return template;
27
+ const argKind = SHORTCUT_ARG_KINDS[argument.kind];
28
+ const argPlaceholder = argument.placeholder
29
+ ?? (argKind === "ticker" ? "ticker" : argKind === "ticker-list" ? "tickers" : undefined);
30
+ return {
31
+ ...template,
32
+ shortcut: {
33
+ ...shortcut,
34
+ argKind,
35
+ ...(argPlaceholder ? { argPlaceholder } : {}),
36
+ ...(argument.optional ? { argOptional: true } : {}),
37
+ },
38
+ };
39
+ }
@@ -6,7 +6,7 @@ import { parseTypedNumber } from "../utils/decimal-comma";
6
6
 
7
7
  /**
8
8
  * Weights, targets and the trades that would rebalance a portfolio. `PF`, its
9
- * `fn PF` report and `gloomberb portfolio show` all read these figures from
9
+ * `fn PF` report and `gloom portfolio show` all read these figures from
10
10
  * here, so a weight never differs between them.
11
11
  *
12
12
  * A weight is a share of the total value, cash included: the priced holdings
@@ -1,5 +1,9 @@
1
+ import type { ShortcutArgError } from "../types/plugin";
2
+
1
3
  /** Collections an unknown-name error lists, so the caller can retry with one. */
2
4
  const LISTED_COLLECTIONS = 12;
5
+ /** Names the command bar lists under an unknown one: they share one row of the bar. */
6
+ const LISTED_COLLECTION_NAMES = 6;
3
7
 
4
8
  /**
5
9
  * The portfolio or watchlist an argument names, by its id or its name,
@@ -21,6 +25,33 @@ export function findNamedCollection<T extends { id: string; name: string }>(
21
25
  )) ?? null;
22
26
  }
23
27
 
28
+ /**
29
+ * The command bar's row for a function typed with a portfolio or watchlist
30
+ * nothing has (`PF Nope`, `PORT Nope`), or null when one of `collections` has
31
+ * that name or id. `noun` says what was looked for; `form` writes each name
32
+ * the way the function takes it (`PORT:Book` for EXPO).
33
+ */
34
+ export function unknownCollectionArgError(
35
+ raw: string,
36
+ collections: readonly { id: string; name: string }[],
37
+ noun: "portfolio" | "watchlist" | "portfolio or watchlist",
38
+ form: (name: string) => string = (name) => name,
39
+ ): ShortcutArgError | null {
40
+ const wanted = raw.trim();
41
+ if (findNamedCollection(collections, wanted)) return null;
42
+ const names = collections.map((collection) => form(collection.name.trim() || collection.id));
43
+ const listed = names.slice(0, LISTED_COLLECTION_NAMES);
44
+ const more = names.length - listed.length;
45
+ const plural = noun === "portfolio or watchlist" ? "portfolios or watchlists" : `${noun}s`;
46
+ return {
47
+ message: `No ${noun} named "${wanted}"`,
48
+ detail: listed.length === 0 ? `No ${plural} are set up`
49
+ : more > 0 ? `Try ${listed.join(", ")} or ${more} more`
50
+ : listed.length === 1 ? `Try ${listed[0]}`
51
+ : `Try ${listed.slice(0, -1).join(", ")} or ${listed.at(-1)}`,
52
+ };
53
+ }
54
+
24
55
  /** "Main Portfolio (main), Family (family)": each collection's name and id, for an error naming what there is. */
25
56
  export function describeCollections(collections: readonly { id: string; name: string }[]): string {
26
57
  const listed = collections.slice(0, LISTED_COLLECTIONS).map(({ id, name }) => (
@@ -3,7 +3,7 @@
3
3
  import { comparablePriceEarnings, formatPriceEarnings } from "../utils/price-earnings";
4
4
  import { convertMarketCapitalization, listedMarketCapitalization } from "../utils/market-capitalization";
5
5
  import type { ColumnConfig } from "../types/config";
6
- import type { AnalystResearchData, CorporateActionsData, MarketState, TickerFinancials } from "../types/financials";
6
+ import type { AnalystResearchData, CorporateActionsData, MarketState, Quote, TickerFinancials } from "../types/financials";
7
7
  import type { EarningsEvent } from "../types/data-provider";
8
8
  import type { TickerRecord } from "../types/ticker";
9
9
  import { colors, priceColor } from "../theme/colors";
@@ -37,6 +37,7 @@ import { distributionHistory, forwardYieldMayHaveLapsed, lapsedDividend } from "
37
37
  import { classifyInstrumentType } from "../tickers/instrument-kind";
38
38
  import { isIndexLevel, reportedQuoteVolume } from "../market-data/market/index-level";
39
39
  import { perpetualDisplaySymbol, perpetualListing } from "../tickers/perpetual-listing";
40
+ import { premiumToUnderlying, type PerpetualPremiumRow } from "../market-data/perpetual-premium";
40
41
  import { currentListingName } from "../tickers/listing-name";
41
42
  import { PRICE_SPARKLINE_COLUMN_ID } from "../components/price-sparkline/view";
42
43
  import { followLiveSparklinePrice, resolveSparklineHistory, sparklineValues } from "../components/price-sparkline/model";
@@ -117,10 +118,8 @@ export interface ColumnContext {
117
118
  }
118
119
 
119
120
  /** A row's stock perpetual as PERP% and FUND 8H read it; rates are fractions. */
120
- interface ListedPerpetual {
121
+ interface ListedPerpetual extends PerpetualPremiumRow {
121
122
  marketId: string;
122
- /** The mark over the stock's last price; null without a comparable reference. */
123
- underlyingPremium: number | null;
124
123
  /** Funding per eight hours, whatever interval the venue pays on. */
125
124
  fundingRate8h: number | null;
126
125
  /** The venue's latest reading is old. */
@@ -136,9 +135,12 @@ export function formatPerpetualRate(value: number, decimals: number): string {
136
135
  return `${/[1-9]/.test(fixed) ? (value > 0 ? "+" : "-") : ""}${fixed}%`;
137
136
  }
138
137
 
139
- function perpetualValue(columnId: keyof typeof PERPETUAL_FIELDS, symbol: string, ctx: ColumnContext): { pending: boolean; data: ListedPerpetual | null; value: number | null } {
138
+ /** PERP% is against the row's own stock quote while that is live (premiumToUnderlying). */
139
+ function perpetualValue(columnId: keyof typeof PERPETUAL_FIELDS, symbol: string, ctx: ColumnContext, quote: Quote | undefined): { pending: boolean; data: ListedPerpetual | null; value: number | null } {
140
140
  const perp = mapData(ctx.perpetuals, symbol);
141
- return { ...perp, value: perp.data?.[PERPETUAL_FIELDS[columnId]] ?? null };
141
+ const value = !perp.data ? null
142
+ : columnId === "perp_premium" ? premiumToUnderlying(perp.data, quote) : perp.data[PERPETUAL_FIELDS[columnId]];
143
+ return { ...perp, value: value ?? null };
142
144
  }
143
145
 
144
146
  /** Position and calendar dates as "Jan 5", read in UTC. */
@@ -644,7 +646,7 @@ export function getColumnValue(
644
646
  }
645
647
  case "perp_premium":
646
648
  case "perp_funding": {
647
- const perp = perpetualValue(col.id, ticker.metadata.ticker, ctx);
649
+ const perp = perpetualValue(col.id, ticker.metadata.ticker, ctx, quote);
648
650
  if (perp.pending) return { text: "…" };
649
651
  if (perp.value == null) return { text: "—" };
650
652
  // An old venue reading keeps its figure, dimmed, as PERP shows a stale market.
@@ -823,7 +825,7 @@ export function getSortValue(
823
825
  }
824
826
  case "perp_premium":
825
827
  case "perp_funding":
826
- return perpetualValue(col.id, ticker.metadata.ticker, ctx).value;
828
+ return perpetualValue(col.id, ticker.metadata.ticker, ctx, quote).value;
827
829
  case "latency":
828
830
  return quote ? ctx.now - (resolveQuoteAgeTimestamp(quote, ctx.now) ?? ctx.now) : null;
829
831
  case "as_of":
@@ -1,6 +1,13 @@
1
- import { fxLegForCurrency } from "../market-data/coordinator/fx-legs";
2
- import { fxCloseOnDate, type FxHistoryClient } from "../market-data/fx-closes";
3
- import { heldCurrencyUnit } from "../utils/currency-units";
1
+ import type { FxLeg } from "../market-data/coordinator/fx-legs";
2
+ import { dailyFxLeg, readFxCloseOnDate, type FxHistoryClient } from "../market-data/fx-closes";
3
+ import { formatFxRate } from "../cli/fx-pair";
4
+ import { dateOptionError, readCalendarDate } from "../utils/calendar-date";
5
+ import { currencyUnitLabel, heldCurrencyUnit } from "../utils/currency-units";
6
+ import { readTypedNumber } from "../utils/decimal-comma";
7
+ import { logFailure } from "../utils/log-failure";
8
+ import { mapWithConcurrency } from "../utils/concurrency";
9
+ import type { TickerPosition, TickerRecord } from "../types/ticker";
10
+ import { lotCostFxRate, sameCurrency } from "./position-metrics";
4
11
 
5
12
  /** Reads a pair's daily bars; `apiClient` in the app, a fake in tests. */
6
13
  export type CostFxHistoryClient = FxHistoryClient;
@@ -20,24 +27,54 @@ export class CostFxError extends Error {
20
27
  }
21
28
  }
22
29
 
30
+ /** A dated close, and whether the service keeps daily bars of any pair it was looked for in. */
31
+ interface PairCloseRead {
32
+ reading: CostFxReading | null;
33
+ served: boolean;
34
+ }
35
+
36
+ /** The daily pair BASE/QUOTE as `history` asks for it (USD/THB is USDTHB=X), read as QUOTE per BASE, or inverted. */
37
+ const fxPair = (base: string, quote: string, invert: boolean): FxLeg =>
38
+ ({ currency: base, instrument: { symbol: `${base}${quote}=X`, exchange: "" }, invert });
39
+
40
+ /**
41
+ * The close in the week up to `date` of the first of the pairs, tried in
42
+ * order, that the service keeps daily bars of; the next is asked only when
43
+ * one is not served at all, as a served pair with no close that week is a
44
+ * gap the same pair the other way round has too.
45
+ */
46
+ async function firstPairClose(pairs: readonly FxLeg[], date: string, client: CostFxHistoryClient, now: Date): Promise<PairCloseRead> {
47
+ for (const pair of pairs) {
48
+ const read = await readFxCloseOnDate(pair, date, client, now);
49
+ if (read.reading || read.served) return read;
50
+ }
51
+ return { reading: null, served: false };
52
+ }
53
+
23
54
  /**
24
55
  * USD per unit of an ISO currency at its last completed daily close on or
25
- * before `date`, looking back a week at most (`fxCloseOnDate`, as `fx --date`
26
- * reads it). Null when the pair has no close in that week.
56
+ * before `date`, looking back a week at most, as `fx --date` reads it: its
57
+ * dollar pair as markets quote it (`dailyFxLeg`: EUR/USD, USD/THB), else the
58
+ * same pair the other way round, as `history` serves either.
27
59
  */
28
- async function usdPerUnitOn(code: string, date: string, client: CostFxHistoryClient, now: Date): Promise<CostFxReading | null> {
29
- if (code === "USD") return { rate: 1, date };
30
- const leg = fxLegForCurrency(code);
31
- if (!leg) throw new CostFxError(`No daily FX history is served for ${code}`);
32
- return fxCloseOnDate(leg, date, client, now);
60
+ async function usdPerUnitOn(code: string, date: string, client: CostFxHistoryClient, now: Date): Promise<PairCloseRead> {
61
+ if (code === "USD") return { reading: { rate: 1, date }, served: true };
62
+ const quoted = dailyFxLeg(code);
63
+ if (!quoted) return { reading: null, served: false };
64
+ const other = quoted.invert ? fxPair(code, "USD", false) : fxPair("USD", code, true);
65
+ return firstPairClose([quoted, other], date, client, now);
33
66
  }
34
67
 
35
68
  /**
36
69
  * What one unit of `from` (a lot's currency, GBp counting pence and USX cents) cost in
37
70
  * `into` (the portfolio's) at the daily FX closes of `date`, each pair's last
38
- * close on or before it, crossed through USD as every converted value is.
39
- * Throws `CostFxError` when a pair is not served or has no close in the week
40
- * before the date.
71
+ * close on or before it: crossed through USD as every converted value is,
72
+ * each dollar pair read either way round, and for two currencies neither of
73
+ * which is the dollar, their own pair (either way round) when a dollar pair
74
+ * is not served. No currency is left out for not being on a list: any pair
75
+ * `history` serves is read. Throws `CostFxError` when none of them has a
76
+ * close in the week before the date, saying when a currency has no daily
77
+ * history served at all.
41
78
  */
42
79
  export async function loadCostFxRate(
43
80
  from: string,
@@ -48,14 +85,254 @@ export async function loadCostFxRate(
48
85
  ): Promise<CostFxReading> {
49
86
  const fromUnit = heldCurrencyUnit(from);
50
87
  const intoUnit = heldCurrencyUnit(into);
88
+ // A minor unit (GBp, USX) is that fraction of its major currency's rate.
89
+ const units = intoUnit.divisor / fromUnit.divisor;
90
+ if (fromUnit.currency === intoUnit.currency) return { rate: units, date };
51
91
  const [fromUsd, intoUsd] = await Promise.all([
52
92
  usdPerUnitOn(fromUnit.currency, date, client, now),
53
93
  usdPerUnitOn(intoUnit.currency, date, client, now),
54
94
  ]);
55
- const missing = [fromUsd ? null : fromUnit.currency, intoUsd ? null : intoUnit.currency].filter(Boolean);
56
- if (!fromUsd || !intoUsd) throw new CostFxError(`No ${missing.join(" or ")} FX close in the week up to ${date}`);
57
- const rate = (fromUsd.rate / fromUnit.divisor) / (intoUsd.rate / intoUnit.divisor);
58
- if (!Number.isFinite(rate) || rate <= 0) throw new CostFxError(`No usable ${fromUnit.currency}/${intoUnit.currency} rate for ${date}`);
59
- // A pair that did not trade that day (its holiday) dates the cross by its own last close.
60
- return { rate, date: fromUsd.date < intoUsd.date ? fromUsd.date : intoUsd.date };
95
+ let reading: CostFxReading | null = null;
96
+ let crossServed = false;
97
+ if (fromUsd.reading && intoUsd.reading) {
98
+ // A pair that did not trade that day (its holiday) dates the cross by its own last close.
99
+ reading = {
100
+ rate: (fromUsd.reading.rate / intoUsd.reading.rate) * units,
101
+ date: fromUsd.reading.date < intoUsd.reading.date ? fromUsd.reading.date : intoUsd.reading.date,
102
+ };
103
+ } else if (fromUnit.currency !== "USD" && intoUnit.currency !== "USD" && (!fromUsd.served || !intoUsd.served)) {
104
+ const direct = await firstPairClose([
105
+ fxPair(fromUnit.currency, intoUnit.currency, false),
106
+ fxPair(intoUnit.currency, fromUnit.currency, true),
107
+ ], date, client, now);
108
+ if (direct.reading) reading = { rate: direct.reading.rate * units, date: direct.reading.date };
109
+ crossServed = direct.served;
110
+ }
111
+ if (!reading) {
112
+ const missing = [[fromUnit.currency, fromUsd], [intoUnit.currency, intoUsd]] as const;
113
+ const unserved = missing.filter(([, read]) => !read.reading && !read.served).map(([code]) => code);
114
+ if (unserved.length > 0 && !crossServed) throw new CostFxError(`No daily FX history is served for ${unserved.join(" or ")}`);
115
+ const late = missing.filter(([, read]) => !read.reading).map(([code]) => code);
116
+ throw new CostFxError(`No ${late.join(" or ")} FX close in the week up to ${date}`);
117
+ }
118
+ if (!Number.isFinite(reading.rate) || reading.rate <= 0) {
119
+ throw new CostFxError(`No usable ${fromUnit.currency}/${intoUnit.currency} rate for ${date}`);
120
+ }
121
+ return reading;
122
+ }
123
+
124
+ /** A lot's currency as a portfolio values it: its own, else its listing's. */
125
+ const lotCurrency = (ticker: TickerRecord, position: TickerPosition) => (position.currency || ticker.metadata.currency || "").trim();
126
+ const purchaseDateKey = (currency: string, date: string) => `${currency}|${date}`;
127
+
128
+ /**
129
+ * Whether a lot's cost converts at the FX close of its purchase date when it
130
+ * is valued in `into`: entered by hand, dated, held in another currency, and
131
+ * with no rate of its own into `into` (one given with --cost-fx, or read for
132
+ * its date when the portfolio was valued in `into` before). A broker's lot
133
+ * reads as it always has.
134
+ */
135
+ function convertsAtPurchaseDate(position: TickerPosition, currency: string, into: string, portfolioId?: string): boolean {
136
+ return position.broker === "manual" && position.shares !== 0 && (!portfolioId || position.portfolio === portfolioId)
137
+ && !!currency && !sameCurrency(currency, into) && /^\d{4}-\d{2}-\d{2}$/.test(position.dateAcquired ?? "")
138
+ && lotCostFxRate(position, currency, into) === null;
139
+ }
140
+
141
+ /** One unit of a lot's currency in the valuation's at the close of its purchase date, by currency and date. */
142
+ export type PurchaseDateCostFx = ReadonlyMap<string, CostFxReading>;
143
+
144
+ /** Purchase dates whose FX closes are read at once. */
145
+ const PURCHASE_DATE_READS = 4;
146
+
147
+ /** The lot currencies and purchase dates whose FX closes a portfolio valued in `into` converts costs at, by key. */
148
+ export function purchaseDatesToRead(tickers: readonly TickerRecord[], into: string, portfolioId?: string): Map<string, { currency: string; date: string }> {
149
+ const dates = new Map<string, { currency: string; date: string }>();
150
+ for (const ticker of tickers) {
151
+ for (const position of ticker.metadata.positions) {
152
+ const currency = lotCurrency(ticker, position);
153
+ if (!convertsAtPurchaseDate(position, currency, into, portfolioId)) continue;
154
+ dates.set(purchaseDateKey(currency, position.dateAcquired!), { currency, date: position.dateAcquired! });
155
+ }
156
+ }
157
+ return dates;
158
+ }
159
+
160
+ /**
161
+ * The FX closes of the purchase dates of a portfolio's lots that convert at
162
+ * them (convertsAtPurchaseDate) when valued in `into`, read as `position set
163
+ * --date` reads one: each pair's last close on or before the date, a week
164
+ * back at most. A date whose closes cannot be read is left out, so its lots'
165
+ * cost converts at today's rate.
166
+ */
167
+ export async function loadPurchaseDateCostFx(
168
+ tickers: readonly TickerRecord[],
169
+ { into, history, portfolioId, now = new Date() }: { into: string; history: CostFxHistoryClient; portfolioId?: string; now?: Date },
170
+ ): Promise<Map<string, CostFxReading>> {
171
+ const dates = purchaseDatesToRead(tickers, into, portfolioId);
172
+ const readings = await mapWithConcurrency([...dates], PURCHASE_DATE_READS, async ([key, { currency, date }]) =>
173
+ [key, await loadCostFxRate(currency, into, date, history, now)
174
+ .catch(logFailure("portfolio.cost-fx", "Reading a purchase date's FX close failed", null))] as const);
175
+ return new Map(readings.filter((entry): entry is readonly [string, CostFxReading] => entry[1] != null));
176
+ }
177
+
178
+ /**
179
+ * The tickers with each lot that converts at its purchase date's FX close
180
+ * given that rate into `into`, so its cost and P&L carry the currency's move
181
+ * since it was bought. For valuing only, never saved: the same array and
182
+ * records where nothing changes.
183
+ */
184
+ export function withPurchaseDateCostFx(tickers: readonly TickerRecord[], rates: PurchaseDateCostFx, into: string, portfolioId?: string): TickerRecord[] {
185
+ if (rates.size === 0) return tickers as TickerRecord[];
186
+ let changed = false;
187
+ const dated = tickers.map((ticker) => {
188
+ let touched = false;
189
+ const positions = ticker.metadata.positions.map((position) => {
190
+ const currency = lotCurrency(ticker, position);
191
+ const reading = convertsAtPurchaseDate(position, currency, into, portfolioId)
192
+ ? rates.get(purchaseDateKey(currency, position.dateAcquired!)) : undefined;
193
+ if (!reading) return position;
194
+ touched = true;
195
+ return { ...position, costFxRate: reading.rate, costFxCurrency: into.trim() };
196
+ });
197
+ if (!touched) return ticker;
198
+ changed = true;
199
+ return { ...ticker, metadata: { ...ticker.metadata, positions } };
200
+ });
201
+ return changed ? dated : tickers as TickerRecord[];
202
+ }
203
+
204
+ /** How the command line or the position form names the purchase date and the rate in what it says. */
205
+ export interface CostFxWording {
206
+ /** The rate's option or field: `--cost-fx`, Cost FX. */
207
+ rate: string;
208
+ /** The date's option or field as a date error names it: `--date`, or empty for a form field. */
209
+ dateOption: string;
210
+ /** The date's option or field as a sentence names it: `--date`, Purchase Date. */
211
+ date: string;
212
+ /** Where to give the rate instead, for a pair such as `TRY per USD`. */
213
+ rateInstead(pair: string): string;
214
+ /** That the purchase date gives the rate too. */
215
+ dateInstead: string;
216
+ }
217
+
218
+ export const CLI_COST_FX_WORDING: CostFxWording = {
219
+ rate: "--cost-fx",
220
+ dateOption: "--date",
221
+ date: "--date",
222
+ rateInstead: (pair) => `Give the rate it was bought at instead: --cost-fx <${pair}>.`,
223
+ dateInstead: "Or give the purchase --date, and its daily FX close is used.",
224
+ };
225
+
226
+ export const FORM_COST_FX_WORDING: CostFxWording = {
227
+ rate: "Cost FX",
228
+ dateOption: "",
229
+ date: "Purchase Date",
230
+ rateInstead: (pair) => `Give the rate it was bought at in Cost FX instead, in ${pair}.`,
231
+ dateInstead: "Or give the Purchase Date, and its daily FX close is used.",
232
+ };
233
+
234
+ /** A purchase date or cost FX rate that cannot be used, and what to give instead. */
235
+ export class PositionCostFxError extends Error {
236
+ constructor(message: string, readonly details?: string) {
237
+ super(message);
238
+ this.name = "PositionCostFxError";
239
+ }
240
+ }
241
+
242
+ /**
243
+ * A typed purchase date as an ISO day, no later than today (UTC); throws
244
+ * saying which formats it takes.
245
+ */
246
+ export function readPurchaseDate(value: string, wording: CostFxWording, now = new Date()): string {
247
+ const date = readCalendarDate(value);
248
+ if (!date) throw new PositionCostFxError(dateOptionError(value, wording.dateOption, { subject: "the purchase date", now }));
249
+ if (date > now.toISOString().slice(0, 10)) {
250
+ throw new PositionCostFxError(`${wording.date} ${value.trim()} is after today; it is the day the position was bought.`);
251
+ }
252
+ return date;
253
+ }
254
+
255
+ /** A typed cost FX rate: a positive number, 32,15 read as 32.15; throws otherwise. */
256
+ export function readCostFxRate(value: string, wording: CostFxWording): number {
257
+ const rate = readTypedNumber(value, wording.rate);
258
+ if (!Number.isFinite(rate) || rate <= 0) {
259
+ throw new PositionCostFxError(`${wording.rate} is the rate the cost was bought at, a positive number such as 32.15 (TRY per USD); got "${value.trim()}".`);
260
+ }
261
+ return rate;
262
+ }
263
+
264
+ /**
265
+ * The rate a position's cost was bought at, as typed or from the FX closes of
266
+ * its purchase date, for a position held in another currency than the
267
+ * portfolio's; undefined when neither was given or the position is in the
268
+ * portfolio's currency (a date alone then only dates the lot). A rate typed
269
+ * for a position in the portfolio's own currency, one that reads as a
270
+ * mistake against today's, and a date with no FX close throw
271
+ * PositionCostFxError saying what to give instead.
272
+ */
273
+ export async function resolvePositionCostFx({ date, costFx, history, currency, portfolioCurrency, listing, todaysRate, wording }: {
274
+ date?: string; costFx?: number; history: CostFxHistoryClient; currency: string; portfolioCurrency: string; listing: string;
275
+ /** Today's rate, one unit of `currency` in `portfolioCurrency`; NaN when it cannot be read. */
276
+ todaysRate: () => Promise<number>;
277
+ wording: CostFxWording;
278
+ }): Promise<{ costFx: { rate: number; currency: string }; date: string | null } | undefined> {
279
+ const foreign = !sameCurrency(currency, portfolioCurrency);
280
+ const insteadHint = wording.rateInstead(`${portfolioCurrency} per ${currency}`);
281
+ if (costFx != null) {
282
+ if (!foreign) {
283
+ const held = currency === portfolioCurrency ? currency : `${currency}, a unit of ${portfolioCurrency}`;
284
+ throw new PositionCostFxError(`${wording.rate} converts a cost in another currency; ${listing} is held in ${held}, the portfolio's currency.`);
285
+ }
286
+ const today = await todaysRate().catch(logFailure("portfolio.cost-fx", "Reading today's rate to check a typed cost FX rate failed", Number.NaN));
287
+ const problem = implausibleCostFx(costFx, today, { currency, portfolioCurrency, listing, wording });
288
+ if (problem) throw new PositionCostFxError(problem, wording.dateInstead);
289
+ return { costFx: { rate: costFx, currency: portfolioCurrency }, date: null };
290
+ }
291
+ if (!date || !foreign) return undefined;
292
+ try {
293
+ const reading = await loadCostFxRate(currency, portfolioCurrency, date, history);
294
+ return { costFx: { rate: reading.rate, currency: portfolioCurrency }, date: reading.date };
295
+ } catch (error) {
296
+ if (error instanceof CostFxError) {
297
+ throw new PositionCostFxError(`${error.message}, so the ${currency} cost cannot be converted at the rate of ${date}.`, insteadHint);
298
+ }
299
+ // Offline, signed out or a server error: say what was being read, and the way that needs no network.
300
+ const reason = error instanceof Error ? error.message : String(error);
301
+ throw new PositionCostFxError(`Could not read the ${currency} and ${portfolioCurrency} FX closes of ${date}: ${reason}.`, insteadHint);
302
+ }
303
+ }
304
+
305
+ /** How far from today's rate a typed one may be: a currency that lost 99% since purchase still fits. */
306
+ const COST_FX_MAX_RATIO = 100;
307
+
308
+ /**
309
+ * Why a typed cost FX rate reads as a mistake against today's rate, else null:
310
+ * a rate per pound for a lot held in pence, the inverse of the pair (0.0319
311
+ * for 31.37 TRY per USD), or more than a hundred times off either way. A
312
+ * rate is only called inverted when today's is far from 1 and the typed one
313
+ * lies on the other side of it, so EUR per USD near parity is never refused.
314
+ * Null when today's rate is not known.
315
+ */
316
+ function implausibleCostFx(
317
+ rate: number,
318
+ today: number,
319
+ { currency, portfolioCurrency, listing, wording }: { currency: string; portfolioCurrency: string; listing: string; wording: CostFxWording },
320
+ ): string | null {
321
+ if (!Number.isFinite(today) || today <= 0) return null;
322
+ const pair = `${portfolioCurrency} per ${currency}`;
323
+ const typed = formatFxRate(rate);
324
+ const todays = `today's is ${formatFxRate(today)} ${pair}`;
325
+ const unit = heldCurrencyUnit(currency);
326
+ if (unit.divisor !== 1 && rate / today >= unit.divisor / 5 && rate / today <= unit.divisor * 5) {
327
+ const name = currency.toUpperCase() === "USX" ? "US cents" : currencyUnitLabel(currency).replace(/^\S+ \((.+)\)$/, "$1");
328
+ return `${wording.rate} ${typed} looks like ${portfolioCurrency} per ${unit.currency}; ${listing} is held in ${currency} (${name}), `
329
+ + `so the rate is ${pair}, 1/${unit.divisor} of that: ${todays}.`;
330
+ }
331
+ if ((today >= 3 && rate < 1) || (today <= 1 / 3 && rate > 1)) {
332
+ return `${wording.rate} ${typed} looks inverted: it is the ${portfolioCurrency} paid for one ${currency}, and ${todays}.`;
333
+ }
334
+ if (rate / today > COST_FX_MAX_RATIO || today / rate > COST_FX_MAX_RATIO) {
335
+ return `${wording.rate} ${typed} is more than ${COST_FX_MAX_RATIO} times off today's rate (${formatFxRate(today)} ${pair}); check the rate.`;
336
+ }
337
+ return null;
61
338
  }
@@ -1,4 +1,6 @@
1
1
  import type { TickerPosition, TickerRecord } from "../types/ticker";
2
+ import { normalizeHeldCurrencyCode } from "../utils/currency-units";
3
+ import { parseDisplayDate } from "../utils/datetime-format";
2
4
  import { signedPositionDirection } from "./position-metrics";
3
5
 
4
6
  /** The ticker's manual lots in one portfolio, in saved order. */
@@ -19,6 +21,74 @@ export interface ManualPortfolioPositionPrefill {
19
21
  position: TickerPosition | null;
20
22
  /** The currencies of several lots that disagree. They are not totalled across currencies. */
21
23
  currencies?: string[];
24
+ /** Why several lots' cost FX rates do not become the position's one rate; the position then has none. */
25
+ costFxConflict?: LotCostFxConflict;
26
+ }
27
+
28
+ /**
29
+ * Why the cost FX rates of several lots cannot become one: some lots convert
30
+ * their cost at the rate they were bought at and some at today's, the lots are
31
+ * in different currencies, or their rates convert into different currencies.
32
+ */
33
+ export type LotCostFxConflict = "todays-rate" | "lot-currencies" | "rate-currencies";
34
+
35
+ export interface MergedLotCostTerms {
36
+ /** The earliest purchase date of the lots that have one. */
37
+ dateAcquired?: string;
38
+ /** One unit of `currency` in `costFx.currency`, weighted so the position's cost converts to what the lots' costs did. */
39
+ costFx?: { rate: number; currency: string };
40
+ /** The lots' common currency, the one `costFx` is a rate of. */
41
+ currency?: string;
42
+ costFxConflict?: LotCostFxConflict;
43
+ }
44
+
45
+ function lotCostFx(lot: TickerPosition): { rate: number; currency: string } | null {
46
+ const rate = lot.costFxRate;
47
+ const currency = lot.costFxCurrency?.trim().toUpperCase();
48
+ return typeof rate === "number" && Number.isFinite(rate) && rate > 0 && currency ? { rate, currency } : null;
49
+ }
50
+
51
+ /**
52
+ * The purchase date and cost FX rate several lots keep when they become one
53
+ * position (a set over several lots, or a team's new count collapsing them):
54
+ *
55
+ * - The date is the earliest of the lots': the PF ACQ and HELD columns read the
56
+ * earliest lot already, so it reads the same after the merge.
57
+ * - The rate is the lots' rates weighted by each lot's cost (shares times
58
+ * average cost), so the position's cost converts to exactly what the lots'
59
+ * costs did; by shares when a lot has no cost, there being no cost to keep.
60
+ *
61
+ * A rate is kept only when it is honest: every held lot has one, into one
62
+ * currency, and the lots share a currency. Otherwise `costFxConflict` says why
63
+ * and there is no rate. A lot of no shares weighs nothing and blocks nothing.
64
+ */
65
+ export function mergeLotCostTerms(lots: readonly TickerPosition[]): MergedLotCostTerms {
66
+ const dated = lots
67
+ .map((lot) => ({ value: lot.dateAcquired, time: parseDisplayDate(lot.dateAcquired)?.getTime() }))
68
+ .filter((entry): entry is { value: string; time: number } => !!entry.value && entry.time != null)
69
+ .sort((left, right) => left.time - right.time)[0];
70
+ const date = dated ? { dateAcquired: dated.value } : {};
71
+ const nonzero = lots.filter((lot) => lot.shares !== 0);
72
+ const held = nonzero.length > 0 ? nonzero : lots;
73
+ const currencies = new Set(held.map((lot) => normalizeHeldCurrencyCode(lot.currency)));
74
+ const currency = currencies.size === 1 ? { currency: [...currencies][0]! } : {};
75
+ const rates = held.map(lotCostFx);
76
+ const rated = rates.filter((rate): rate is { rate: number; currency: string } => rate !== null);
77
+ if (rated.length === 0) return { ...date, ...currency };
78
+ if (rated.length < held.length) return { ...date, ...currency, costFxConflict: "todays-rate" };
79
+ if (currencies.size > 1) return { ...date, costFxConflict: "lot-currencies" };
80
+ const into = new Set(rated.map((rate) => rate.currency));
81
+ if (into.size > 1) return { ...date, ...currency, costFxConflict: "rate-currencies" };
82
+ const costFxCurrency = rated[0]!.currency;
83
+ if (rated.every((rate) => rate.rate === rated[0]!.rate)) return { ...date, ...currency, costFx: { rate: rated[0]!.rate, currency: costFxCurrency } };
84
+ const costs = held.map((lot) => Math.abs(lot.shares) * (lot.avgCost ?? Number.NaN));
85
+ const costed = costs.every((cost) => Number.isFinite(cost) && cost >= 0) && costs.some((cost) => cost > 0);
86
+ const weights = costed ? costs : held.map((lot) => Math.abs(lot.shares));
87
+ const total = weights.reduce((sum, weight) => sum + weight, 0);
88
+ const rate = tidyNumber(weights.reduce((sum, weight, index) => sum + weight * rated[index]!.rate, 0) / total);
89
+ return Number.isFinite(rate) && rate > 0
90
+ ? { ...date, ...currency, costFx: { rate, currency: costFxCurrency } }
91
+ : { ...date, ...currency, costFxConflict: "todays-rate" };
22
92
  }
23
93
 
24
94
  /** Drops the float noise of a sum (0.1 + 0.2) without losing a digit anyone entered. */
@@ -63,6 +133,8 @@ export function getManualPortfolioPositionPrefill(ticker: TickerRecord, portfoli
63
133
  ? tidyNumber(lots.reduce((sum, lot) => sum + Math.abs(lot.shares) * lot.avgCost!, 0) / magnitude)
64
134
  : undefined;
65
135
  const multiplier = held[0]!.multiplier;
136
+ // The purchase date and the rate the cost was bought at carry over as mergeLotCostTerms keeps them.
137
+ const terms = mergeLotCostTerms(lots);
66
138
  return {
67
139
  lotCount,
68
140
  position: {
@@ -72,7 +144,10 @@ export function getManualPortfolioPositionPrefill(ticker: TickerRecord, portfoli
72
144
  // Lots that name no currency stay without one, as a single such lot does.
73
145
  ...(currencies[0] && lots.some((lot) => lot.currency?.trim()) ? { currency: currencies[0] } : {}),
74
146
  broker: "manual",
147
+ ...(terms.dateAcquired ? { dateAcquired: terms.dateAcquired } : {}),
148
+ ...(terms.costFx ? { costFxRate: terms.costFx.rate, costFxCurrency: terms.costFx.currency } : {}),
75
149
  ...(multiplier !== undefined ? { multiplier } : {}),
76
150
  },
151
+ ...(terms.costFxConflict ? { costFxConflict: terms.costFxConflict } : {}),
77
152
  };
78
153
  }