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.
- package/README.md +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +2 -2
- package/src/account/device-signin.ts +2 -2
- package/src/account/upgrade-dialog.tsx +1 -1
- package/src/api-client/askg.ts +2 -2
- package/src/api-client/auth.ts +2 -2
- package/src/api-client/central-bank-rates.ts +6 -0
- package/src/api-client/collections.ts +5 -1
- package/src/api-client/cot.ts +2 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +60 -0
- package/src/api-client/data.ts +84 -5
- package/src/api-client/doe.ts +15 -0
- package/src/api-client/equity-screener.ts +28 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/fund-facts.ts +39 -0
- package/src/api-client/futures-curve.ts +9 -1
- package/src/api-client/hiring.ts +301 -0
- package/src/api-client/index.ts +22 -5
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +6 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +21 -4
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/socket.ts +64 -4
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/desktop-deeplink.ts +5 -5
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +13 -0
- package/src/app/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- package/src/architecture/ui-literals-allowlist.json +3 -3
- package/src/architecture/ui-tokens-allowlist.json +1 -1
- package/src/brokers/broker-portfolio-sync.ts +37 -0
- package/src/brokers/directory.ts +11 -0
- package/src/brokers/logos.ts +3 -1
- package/src/brokers/signed-in/adapter.ts +304 -26
- package/src/brokers/signed-in/client.ts +14 -2
- package/src/brokers/signed-in/connect.ts +10 -2
- package/src/brokers/sync-broker-instance.ts +9 -1
- package/src/capabilities/chart-series.ts +22 -6
- package/src/cli/bank-section.ts +1 -1
- package/src/cli/base-converter.ts +23 -14
- package/src/cli/central-bank-events.ts +1 -1
- package/src/cli/command-options.ts +59 -4
- package/src/cli/commands/api.ts +9 -6
- package/src/cli/commands/broker.ts +2 -2
- package/src/cli/commands/command-utils.ts +2 -2
- package/src/cli/commands/crypto-hints.ts +7 -20
- package/src/cli/commands/fred-series.ts +4 -2
- package/src/cli/commands/keybindings.ts +2 -2
- package/src/cli/commands/market.ts +1117 -264
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +121 -48
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +64 -44
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +192 -47
- package/src/cli/context.ts +3 -2
- package/src/cli/desktop-pane-shot-view.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +1 -1
- package/src/cli/earnings-calendar.ts +4 -4
- package/src/cli/entry.ts +18 -1
- package/src/cli/errors.ts +20 -2
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +10 -4
- package/src/cli/freshness.ts +2 -8
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +10 -9
- package/src/cli/glossary.ts +64 -20
- package/src/cli/help.ts +19 -13
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +196 -43
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +24 -18
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +300 -41
- package/src/cli/name-entries.ts +30 -18
- package/src/cli/native-smoke.ts +2 -0
- package/src/cli/not-a-ticker.ts +19 -9
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +12 -0
- package/src/cli/pane-functions/capabilities.ts +75 -20
- package/src/cli/pane-functions/catalog.ts +24 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +10 -7
- package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
- package/src/cli/pane-functions/freshness.ts +174 -23
- package/src/cli/pane-functions/function-help.ts +66 -27
- package/src/cli/pane-functions/headless.ts +24 -11
- package/src/cli/pane-functions/index.ts +18 -16
- package/src/cli/pane-functions/options.ts +22 -2
- package/src/cli/pane-functions/report-notices.ts +12 -4
- package/src/cli/pane-functions/report.ts +5 -5
- package/src/cli/pane-functions/resolver.ts +47 -10
- package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +162 -0
- package/src/cli/pane-functions/shot-options.ts +8 -3
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +38 -3
- package/src/cli/report-tables.ts +61 -16
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +111 -22
- package/src/cli/series-search.ts +5 -5
- package/src/cli/statement-filings.ts +19 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +50 -15
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +198 -68
- package/src/cli/symbol-rows.ts +131 -25
- package/src/cli/ticker-resolution.ts +108 -11
- package/src/cli/treasury-yields.ts +5 -5
- package/src/cli/types.ts +2 -0
- package/src/cli/uncovered-instrument.ts +3 -3
- package/src/cli/unknown-command.ts +6 -6
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/composite-chart.tsx +373 -16
- package/src/components/chart/composite/interactions.ts +7 -2
- package/src/components/chart/composite/legend.tsx +75 -3
- package/src/components/chart/composite/levels.ts +5 -0
- package/src/components/chart/composite/panel-gestures.ts +41 -0
- package/src/components/chart/composite/panel-labels.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +305 -11
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +16 -4
- package/src/components/chart/composite/text-renderer.ts +48 -21
- package/src/components/chart/composite/toolbar.tsx +52 -5
- package/src/components/chart/composite/types.ts +13 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +12 -3
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +6 -3
- package/src/components/chart/core/pointer.ts +2 -0
- package/src/components/chart-table/index.ts +2 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/command-bar/commands/collection-list.ts +41 -19
- package/src/components/command-bar/commands/collection.ts +9 -3
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +3 -2
- package/src/components/command-bar/pane-templates/items.ts +59 -6
- package/src/components/command-bar/pane-templates/workflow.ts +10 -1
- package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
- package/src/components/command-bar/routes/root/provider-search.ts +42 -11
- package/src/components/command-bar/routes/root/results.ts +18 -4
- package/src/components/command-bar/routes/root/runtime.ts +15 -3
- package/src/components/command-bar/routes/root/search-providers.ts +25 -2
- package/src/components/command-bar/routes/root/selection.ts +6 -1
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
- package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
- package/src/components/command-bar/selection-runtime.ts +50 -10
- package/src/components/command-bar/surface/index.tsx +24 -2
- package/src/components/command-bar/view-model.ts +7 -0
- package/src/components/command-bar/workflow/broker.ts +2 -2
- package/src/components/command-bar/workflow/collection-actions.ts +46 -7
- package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
- package/src/components/command-bar/workflow/fields.ts +8 -2
- package/src/components/command-bar/workflow/ops.ts +18 -4
- package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
- package/src/components/command-bar/workflow/submit.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +5 -1
- package/src/components/company-logo.tsx +39 -0
- package/src/components/data-table/sections.ts +4 -2
- package/src/components/form-modal/confirm.tsx +2 -1
- package/src/components/form-modal/request.ts +5 -0
- package/src/components/form-modal/routes.ts +4 -1
- package/src/components/index.ts +12 -6
- package/src/components/layout/header.tsx +7 -6
- package/src/components/layout/market-summary.ts +9 -6
- package/src/components/layout/pane/capture-status.ts +1 -1
- package/src/components/layout/pane/footer/model.ts +11 -5
- package/src/components/layout/shell/chrome.ts +19 -6
- package/src/components/layout/shell/index.tsx +2 -1
- package/src/components/layout/shell/pane/layers.tsx +19 -1
- package/src/components/layout/status-bar.tsx +2 -2
- package/src/components/market-board/index.tsx +11 -3
- package/src/components/onboarding/onboarding-wizard.tsx +1 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
- package/src/components/onboarding/wizard-broker-sync.ts +4 -1
- package/src/components/pane-settings-dialog/desktop.tsx +20 -7
- package/src/components/pane-settings-dialog/tui.tsx +17 -9
- package/src/components/pane-settings-dialog/value.ts +8 -1
- package/src/components/pane-settings-dialog.tsx +44 -13
- package/src/components/sign-in-code-panel.tsx +27 -19
- package/src/components/ui/column-grid.tsx +48 -4
- package/src/components/ui/country-flag-art.tsx +180 -0
- package/src/components/ui/country-flag.tsx +118 -12
- package/src/components/ui/data-table/opentui/index.tsx +61 -2
- package/src/components/ui/data-table/opentui/model.ts +109 -2
- package/src/components/ui/data-table/types.ts +1 -1
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/logo-mark.tsx +11 -2
- package/src/components/ui/query-bar.tsx +1 -1
- package/src/components/ui/row-notes.ts +79 -0
- package/src/core/state/app/layout.ts +4 -4
- package/src/data/cached-query.ts +8 -1
- package/src/data/config/chart-settings.ts +3 -3
- package/src/data/config/home.ts +10 -2
- package/src/data/config/layout.ts +4 -1
- package/src/data/config/store/node.ts +4 -0
- package/src/data/config/store/normalize.ts +13 -3
- package/src/data/config/store/portfolio-as-of-column.ts +2 -2
- package/src/data/json-storage.ts +2 -0
- package/src/data/saved-copies.ts +84 -0
- package/src/docs/snapshot.json +65 -32
- package/src/i18n/es.ts +29 -12
- package/src/i18n/ja.ts +29 -12
- package/src/i18n/ko.ts +29 -12
- package/src/i18n/zh-cn.ts +29 -12
- package/src/i18n/zh-tw.ts +29 -12
- package/src/layout/desks.ts +1 -1
- package/src/layout/launch-layout.ts +1 -1
- package/src/layout/pane-manager/floating-actions.ts +5 -3
- package/src/layout/pane-manager/floating.ts +43 -1
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +124 -40
- package/src/market-data/fx-closes.ts +17 -2
- package/src/market-data/hooks.tsx +19 -11
- package/src/market-data/market/change-basis.ts +49 -7
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/trading-sessions.ts +13 -0
- package/src/market-data/market/year-to-date.ts +8 -13
- package/src/market-data/performance.ts +20 -14
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/published-apac-sessions.ts +2 -1
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/quotes/contributions.ts +5 -1
- package/src/market-data/quotes/freshness.ts +42 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/regular-session-valuation.ts +13 -8
- package/src/market-data/quotes/resolution.ts +4 -2
- package/src/market-data/tax-equivalent.ts +1 -1
- package/src/market-data/ticker-price-returns.ts +3 -1
- package/src/news/aggregator.ts +5 -1
- package/src/news/news-model.ts +4 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/app-notifier.ts +1 -1
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +9 -8
- package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
- package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
- package/src/plugins/builtin/alerts/cli.ts +485 -76
- package/src/plugins/builtin/alerts/command.ts +5 -10
- package/src/plugins/builtin/alerts/event-rows.ts +9 -5
- package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
- package/src/plugins/builtin/alerts/events.ts +107 -5
- package/src/plugins/builtin/alerts/fired-log.ts +78 -0
- package/src/plugins/builtin/alerts/format.ts +2 -1
- package/src/plugins/builtin/alerts/headless.ts +100 -39
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +89 -0
- package/src/plugins/builtin/alerts/index.tsx +45 -18
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/pane.tsx +97 -29
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +24 -0
- package/src/plugins/builtin/alerts/research-builder.ts +84 -11
- package/src/plugins/builtin/alerts/research-rules.ts +76 -3
- package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
- package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
- package/src/plugins/builtin/alerts/scheduled.ts +188 -29
- package/src/plugins/builtin/alerts/storage.ts +13 -10
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/analytics/index.tsx +8 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -1
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +13 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
- package/src/plugins/builtin/analytics/risk-math.ts +6 -3
- package/src/plugins/builtin/analytics/risk-model.ts +297 -104
- package/src/plugins/builtin/analytics/risk-options.ts +17 -2
- package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
- package/src/plugins/builtin/attention/pane.tsx +6 -2
- package/src/plugins/builtin/awards/headless.ts +7 -3
- package/src/plugins/builtin/awards/model.ts +18 -0
- package/src/plugins/builtin/awards/pane.tsx +6 -3
- package/src/plugins/builtin/backtest/headless.ts +3 -3
- package/src/plugins/builtin/backtest/rules.ts +41 -5
- package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
- package/src/plugins/builtin/broker-manager/footer.ts +27 -11
- package/src/plugins/builtin/broker-manager/index.tsx +21 -12
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +12 -2
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
- package/src/plugins/builtin/broker-manager/table.ts +28 -12
- package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
- package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
- package/src/plugins/builtin/broker-trading/controller.ts +45 -13
- package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
- package/src/plugins/builtin/broker-trading/headless.ts +2 -1
- package/src/plugins/builtin/broker-trading/index.tsx +7 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
- package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
- package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
- package/src/plugins/builtin/buildout/cells.tsx +4 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
- package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
- package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
- package/src/plugins/builtin/catalysts/detail.tsx +3 -2
- package/src/plugins/builtin/catalysts/index.tsx +1 -1
- package/src/plugins/builtin/catalysts/model.ts +49 -4
- package/src/plugins/builtin/catalysts/pane.tsx +6 -8
- package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
- package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
- package/src/plugins/builtin/changelog/index.tsx +1 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
- package/src/plugins/builtin/chart-composer/headless.ts +253 -35
- package/src/plugins/builtin/chart-composer/index.tsx +12 -2
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
- package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
- package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
- package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
- package/src/plugins/builtin/chart-composer/settings.ts +31 -19
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
- package/src/plugins/builtin/chat/attachments/model.ts +3 -1
- package/src/plugins/builtin/chat/content/index.tsx +1 -1
- package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/clinical-trials/client.ts +5 -2
- package/src/plugins/builtin/clinical-trials/model.ts +13 -4
- package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
- package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
- package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
- package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +172 -0
- package/src/plugins/builtin/company-attention/client.ts +117 -1
- package/src/plugins/builtin/company-attention/evidence.ts +16 -3
- package/src/plugins/builtin/company-attention/headless.ts +101 -5
- package/src/plugins/builtin/company-attention/index.tsx +6 -3
- package/src/plugins/builtin/company-attention/model.ts +33 -7
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +159 -0
- package/src/plugins/builtin/company-attention/pane.tsx +79 -23
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +160 -0
- package/src/plugins/builtin/company-kpis/headless.ts +3 -2
- package/src/plugins/builtin/company-kpis/model.ts +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/congress-trades/headless.ts +1 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +5 -3
- package/src/plugins/builtin/cot/headless.ts +28 -6
- package/src/plugins/builtin/cot/model.ts +12 -0
- package/src/plugins/builtin/cot/pane.tsx +3 -2
- package/src/plugins/builtin/cpi/headless.ts +5 -1
- package/src/plugins/builtin/cpi/pane.tsx +11 -9
- package/src/plugins/builtin/credit-boards/model.ts +2 -0
- package/src/plugins/builtin/credit-documents/headless.ts +1 -1
- package/src/plugins/builtin/crypto-board/client.ts +86 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +117 -6
- package/src/plugins/builtin/crypto-board/index.tsx +8 -3
- package/src/plugins/builtin/crypto-board/model.ts +40 -8
- package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
- package/src/plugins/builtin/crypto-board/series.ts +19 -1
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/debug/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/docs/index.tsx +1 -1
- package/src/plugins/builtin/docs/pane.tsx +2 -2
- package/src/plugins/builtin/docs/targets.ts +1 -1
- package/src/plugins/builtin/doe/client.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +12 -6
- package/src/plugins/builtin/doe/model.ts +43 -6
- package/src/plugins/builtin/doe/pane.tsx +18 -11
- package/src/plugins/builtin/earnings/board-model.ts +13 -6
- package/src/plugins/builtin/earnings/board-table.ts +4 -1
- package/src/plugins/builtin/earnings/board.tsx +253 -50
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +4 -3
- package/src/plugins/builtin/earnings/headless.ts +53 -15
- package/src/plugins/builtin/earnings/history-model.ts +53 -36
- package/src/plugins/builtin/earnings/index.tsx +15 -22
- package/src/plugins/builtin/earnings/model.ts +17 -12
- package/src/plugins/builtin/earnings/scope.ts +89 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +146 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
- package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
- package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +132 -20
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
- package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
- package/src/plugins/builtin/econ/index.tsx +112 -25
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +32 -1
- package/src/plugins/builtin/econ-statistics/client.ts +16 -3
- package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
- package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
- package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
- package/src/plugins/builtin/econ-statistics/view.ts +16 -1
- package/src/plugins/builtin/equity-screener/client.ts +21 -3
- package/src/plugins/builtin/equity-screener/headless.ts +68 -6
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +6 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
- package/src/plugins/builtin/exposure/headless.ts +51 -6
- package/src/plugins/builtin/exposure/index.tsx +28 -2
- package/src/plugins/builtin/exposure/model.ts +7 -0
- package/src/plugins/builtin/exposure/pane.tsx +6 -1
- package/src/plugins/builtin/fear-greed/index.tsx +1 -1
- package/src/plugins/builtin/filing-events/index.tsx +1 -1
- package/src/plugins/builtin/futures/model.ts +10 -9
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +40 -9
- package/src/plugins/builtin/futures-curve/headless.ts +39 -17
- package/src/plugins/builtin/futures-curve/model.ts +258 -69
- package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
- package/src/plugins/builtin/fx-matrix/client.ts +15 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
- package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
- package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
- package/src/plugins/builtin/gpu/headless.ts +9 -4
- package/src/plugins/builtin/help/index.tsx +13 -3
- package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
- package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +13 -1
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +46 -7
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/pane.tsx +79 -15
- package/src/plugins/builtin/holders/report-rows.ts +35 -7
- package/src/plugins/builtin/holders/table-model.ts +2 -1
- package/src/plugins/builtin/holders/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/headless.ts +1 -1
- package/src/plugins/builtin/insider/signals-client.ts +1 -1
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
- package/src/plugins/builtin/iv-history/client.ts +20 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -4
- package/src/plugins/builtin/iv-history/index.tsx +1 -1
- package/src/plugins/builtin/iv-history/model.ts +13 -4
- package/src/plugins/builtin/iv-history/series.ts +3 -1
- package/src/plugins/builtin/jobs/lock-note.ts +1 -1
- package/src/plugins/builtin/jobs/model.ts +4 -15
- package/src/plugins/builtin/jobs/pane.tsx +7 -5
- package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
- package/src/plugins/builtin/jodi/pane.tsx +10 -4
- package/src/plugins/builtin/layout-manager/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +20 -7
- package/src/plugins/builtin/macro-day/index.tsx +3 -2
- package/src/plugins/builtin/macro-day/model.ts +29 -8
- package/src/plugins/builtin/macro-day/pane.tsx +46 -10
- package/src/plugins/builtin/macro-day/releases.ts +55 -13
- package/src/plugins/builtin/market-heatmap/data.ts +9 -2
- package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
- package/src/plugins/builtin/market-heatmap/model.ts +10 -7
- package/src/plugins/builtin/market-movers/client.ts +33 -19
- package/src/plugins/builtin/market-movers/headless.ts +8 -2
- package/src/plugins/builtin/market-movers/index.tsx +16 -10
- package/src/plugins/builtin/market-movers/screener.ts +29 -8
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
- package/src/plugins/builtin/market-movers/session-table.ts +4 -2
- package/src/plugins/builtin/market-movers/table.ts +4 -1
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/view.ts +5 -0
- package/src/plugins/builtin/members/headless.ts +6 -5
- package/src/plugins/builtin/members/pane.tsx +11 -4
- package/src/plugins/builtin/mna/client.ts +1 -0
- package/src/plugins/builtin/mna/detail.tsx +20 -32
- package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
- package/src/plugins/builtin/mna/distress/model.ts +3 -2
- package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
- package/src/plugins/builtin/mna/headless.ts +6 -13
- package/src/plugins/builtin/mna/model.ts +93 -15
- package/src/plugins/builtin/mna/pane.tsx +5 -4
- package/src/plugins/builtin/news/headless.ts +12 -6
- package/src/plugins/builtin/news/index.tsx +10 -1
- package/src/plugins/builtin/news/wire/index.tsx +3 -2
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
- package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
- package/src/plugins/builtin/notes/cli.ts +49 -14
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/view.tsx +10 -10
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-scenario/client.ts +123 -28
- package/src/plugins/builtin/options-scenario/collar.ts +8 -6
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
- package/src/plugins/builtin/options-scenario/csp.ts +101 -21
- package/src/plugins/builtin/options-scenario/headless.ts +36 -15
- package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
- package/src/plugins/builtin/options-scenario/index.tsx +28 -3
- package/src/plugins/builtin/options-scenario/model.ts +13 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
- package/src/plugins/builtin/perps/equity-row.tsx +11 -3
- package/src/plugins/builtin/perps/freshness.ts +8 -5
- package/src/plugins/builtin/perps/headless.ts +3 -1
- package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
- package/src/plugins/builtin/perps/live-rows.ts +161 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +36 -17
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
- package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
- package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
- package/src/plugins/builtin/portfolio-list/income.ts +13 -2
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
- package/src/plugins/builtin/power/client.ts +4 -1
- package/src/plugins/builtin/power/evidence.ts +22 -7
- package/src/plugins/builtin/power/gas.tsx +5 -1
- package/src/plugins/builtin/power/pane.tsx +8 -1
- package/src/plugins/builtin/power/prices.tsx +10 -3
- package/src/plugins/builtin/research/analyst-headless.ts +25 -8
- package/src/plugins/builtin/research/analyst-model.ts +68 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
- package/src/plugins/builtin/research/event-model.ts +3 -1
- package/src/plugins/builtin/research/event-sources.ts +12 -4
- package/src/plugins/builtin/research/index.tsx +2 -1
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
- package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
- package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -1
- package/src/plugins/builtin/sec/forms.ts +13 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts.ts +68 -8
- package/src/plugins/builtin/sec/headless.ts +85 -8
- package/src/plugins/builtin/sec/index.tsx +58 -12
- package/src/plugins/builtin/sectors/client.ts +2 -1
- package/src/plugins/builtin/sectors/sector-model.ts +11 -15
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
- package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
- package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
- package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
- package/src/plugins/builtin/shared/earnings-report.ts +3 -3
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
- package/src/plugins/builtin/shared/job-posting.ts +129 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
- package/src/plugins/builtin/shared/report-freshness.ts +3 -0
- package/src/plugins/builtin/shared/research-trading.tsx +21 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
- package/src/plugins/builtin/short-interest/client.ts +2 -0
- package/src/plugins/builtin/short-interest/headless.ts +41 -12
- package/src/plugins/builtin/short-interest/index.tsx +23 -1
- package/src/plugins/builtin/short-interest/model.ts +70 -19
- package/src/plugins/builtin/short-interest/pane.tsx +19 -2
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +35 -2
- package/src/plugins/builtin/short-interest/surface.tsx +11 -4
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
- package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
- package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
- package/src/plugins/builtin/supply-chain/model.ts +51 -3
- package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -5
- package/src/plugins/builtin/themes/model.ts +14 -4
- package/src/plugins/builtin/themes/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/headless.ts +5 -5
- package/src/plugins/builtin/thirteenf/signals.ts +3 -1
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
- package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
- package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
- package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/client.ts +5 -4
- package/src/plugins/builtin/vol-surface/headless.ts +12 -3
- package/src/plugins/builtin/vol-surface/model.ts +4 -2
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
- package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
- package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -26
- package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
- package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +7 -4
- package/src/plugins/builtin/yield-curve/headless.ts +38 -4
- package/src/plugins/builtin/yield-curve/history.ts +4 -3
- package/src/plugins/builtin/yield-curve/index.tsx +12 -6
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/bundle.ts +1 -1
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/compat.ts +3 -3
- package/src/plugins/dependencies.ts +75 -14
- package/src/plugins/extracted-plugins.ts +132 -34
- package/src/plugins/host-link.ts +149 -34
- package/src/plugins/installer.ts +35 -10
- package/src/plugins/loader.ts +206 -38
- package/src/plugins/manager-node.ts +2 -2
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/model.ts +2 -2
- package/src/plugins/marketplace/packs.ts +1 -1
- package/src/plugins/ownership.ts +1 -1
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/registry/contributions.ts +3 -1
- package/src/plugins/registry/index.ts +4 -3
- package/src/plugins/registry/plugin-state.ts +3 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/portfolio/allocation.ts +1 -1
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/column-values.ts +10 -8
- package/src/portfolio/cost-fx.ts +296 -19
- package/src/portfolio/manual-lots.ts +75 -0
- package/src/portfolio/mutations.ts +60 -22
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/config-host.ts +3 -0
- package/src/renderers/browser/main.tsx +23 -3
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +46 -8
- package/src/renderers/dom/dom-ui-host.tsx +4 -29
- package/src/renderers/dom/host/ascii-text.ts +16 -2
- package/src/renderers/dom/host/box.tsx +43 -2
- package/src/renderers/dom/host/chart-surface.tsx +2 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/media-surface.tsx +24 -18
- package/src/renderers/dom/host/mouse.ts +30 -0
- package/src/renderers/dom/host/size-observer.ts +1 -1
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/styles.css +76 -0
- package/src/renderers/dom/test-utils.tsx +2 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
- package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +11 -1
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -1
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +6 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
- package/src/renderers/electrobun/view/main.tsx +30 -4
- package/src/renderers/electrobun/view/news-source.ts +5 -5
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/host.tsx +5 -14
- package/src/renderers/opentui/image/loader.ts +3 -2
- package/src/renderers/opentui/native-smoke.ts +10 -1
- package/src/renderers/opentui/pane-image/index.ts +2 -1
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +1 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/share/layout-view.tsx +2 -2
- package/src/renderers/share/main.tsx +4 -4
- package/src/renderers/share/view.tsx +1 -1
- package/src/series/resolve.ts +22 -6
- package/src/series/standalone.ts +30 -2
- package/src/series/types.ts +8 -1
- package/src/shares/api.ts +4 -1
- package/src/shares/pane.ts +1 -1
- package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
- package/src/sources/gloomberb-cloud/index.ts +32 -16
- package/src/sources/gloomberb-cloud/news.ts +39 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
- package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
- package/src/sources/listing-symbols.ts +37 -0
- package/src/sources/provider-errors.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +21 -3
- package/src/sources/provider-router/financials.ts +15 -2
- package/src/sources/provider-router/history.ts +30 -8
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/controller.ts +1 -1
- package/src/sync/core-contributors.ts +46 -14
- package/src/team/collections-sync.ts +2 -1
- package/src/team/collections.ts +178 -17
- package/src/telemetry/crash-reports.ts +17 -4
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +3 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +210 -33
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +76 -27
- package/src/tickers/search/ranking.ts +54 -4
- package/src/tickers/search/upsert.ts +29 -0
- package/src/tickers/terminal-codes.ts +416 -40
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/base-series.ts +307 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/fundamentals.ts +18 -3
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/market-resolution.ts +4 -2
- package/src/time-series/market.ts +2 -1
- package/src/time-series/price-comparison.ts +175 -30
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +157 -0
- package/src/time-series/resolve.ts +126 -1577
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +208 -0
- package/src/time-series/spec.ts +11 -6
- package/src/time-series/studies.ts +109 -12
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/types.ts +29 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +14 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +76 -6
- package/src/types/instrument.ts +3 -0
- package/src/types/plugin.ts +65 -8
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker.ts +13 -0
- package/src/types/trading.ts +59 -1
- package/src/ui/ascii-font.ts +13 -1
- package/src/ui/host.tsx +6 -1
- package/src/updater/command-names.ts +175 -0
- package/src/updater/index.ts +10 -5
- package/src/utils/calendar-date.ts +32 -15
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +42 -0
- package/src/utils/cli-output.ts +259 -80
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +61 -3
- package/src/utils/currency-units.ts +29 -0
- package/src/utils/datetime-format.ts +10 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/decimal-comma.ts +39 -6
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-rate-snapshot.ts +10 -0
- package/src/utils/financial-statements.ts +83 -4
- package/src/utils/format.ts +34 -16
- package/src/utils/fundamentals.ts +150 -34
- package/src/utils/fx-market-hours.ts +10 -1
- package/src/utils/latest-financial-period.ts +8 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/market-capitalization.ts +111 -15
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +83 -0
- package/src/utils/option-expiry.ts +139 -7
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +18 -8
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/sec.ts +3 -0
- package/src/utils/throttled-fetch.ts +11 -4
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/utc-time.ts +24 -1
- package/src/version.ts +6 -0
- 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 `
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
package/src/portfolio/cost-fx.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
|
26
|
-
*
|
|
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<
|
|
29
|
-
if (code === "USD") return { rate: 1, date };
|
|
30
|
-
const
|
|
31
|
-
if (!
|
|
32
|
-
|
|
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
|
|
39
|
-
*
|
|
40
|
-
*
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
}
|