gloomberb 0.17.6 → 0.18.0
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/package.json +6 -1
- package/patches/electrobun@1.18.1.patch +74 -0
- package/src/account/auth-model.ts +3 -2
- package/src/account/device-signin-dialog.tsx +38 -13
- package/src/account/device-signin.ts +2 -1
- package/src/account/upgrade-dialog.tsx +6 -2
- package/src/api-client/askg.ts +5 -3
- package/src/api-client/bank-figures.ts +73 -0
- package/src/api-client/credit-boards.ts +21 -1
- package/src/api-client/crypto-markets.ts +88 -1
- package/src/api-client/data.ts +104 -4
- package/src/api-client/debt-maturities.ts +18 -0
- package/src/api-client/equity-screener.ts +3 -0
- package/src/api-client/errors.ts +13 -0
- package/src/api-client/fund-facts.ts +72 -0
- package/src/api-client/futures-curve.ts +26 -3
- package/src/api-client/geo.ts +11 -1
- package/src/api-client/index.ts +25 -2
- package/src/api-client/insider-signals.ts +95 -11
- package/src/api-client/normalizers.ts +1 -0
- package/src/api-client/paths.ts +20 -1
- package/src/api-client/request.ts +11 -3
- package/src/api-client/research-activity.ts +15 -8
- package/src/api-client/socket.ts +6 -3
- package/src/api-client/supply-chain-graph.ts +2 -2
- package/src/api-client/supply-chain.ts +8 -0
- package/src/api-client/tape.ts +12 -0
- package/src/api-client/types.ts +66 -2
- package/src/api-client/upgrade-experiment.ts +4 -3
- package/src/api-client/wall-experiments.ts +5 -3
- package/src/api-client/wall-summary.ts +3 -1
- package/src/api-client/web-experiments.ts +5 -2
- package/src/api-client/yield-curves.ts +17 -0
- package/src/app/command-bar-intro.ts +38 -0
- package/src/app/keybindings/actions.ts +1 -1
- package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
- package/src/app/pane-runtime/plugin-bindings.ts +4 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
- package/src/app/pane-runtime/ticker-open-target.ts +31 -0
- package/src/app/runtime/desktop-deeplink.ts +3 -4
- package/src/app/runtime/plugin-bindings.ts +3 -2
- package/src/app/runtime/startup.ts +3 -4
- package/src/app/runtime/ticker-refresh.ts +13 -12
- package/src/app/runtime/update.ts +3 -2
- package/src/architecture/core-builtin-allowlist.json +0 -3
- package/src/architecture/silent-catch-allowlist.json +3 -0
- package/src/architecture/ui-literals-allowlist.json +186 -0
- package/src/architecture/ui-tokens-allowlist.json +40 -0
- package/src/ask-gloom/pane-context.ts +3 -1
- package/src/brokers/signed-in/catalog.ts +2 -1
- package/src/brokers/signed-in/client.ts +7 -2
- package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
- package/src/brokers/signed-in/sign-in.ts +22 -20
- package/src/brokers/sync-broker-instance.ts +2 -1
- package/src/capabilities/chart-series.ts +2 -0
- package/src/capabilities/registry.ts +1 -3
- package/src/cli/bank-section.ts +147 -0
- package/src/cli/base-converter.ts +5 -2
- package/src/cli/board-coins.ts +70 -0
- package/src/cli/commands/fred-series.ts +90 -0
- package/src/cli/commands/market.ts +340 -69
- package/src/cli/commands/news-search.ts +35 -2
- package/src/cli/commands/options-chain.ts +7 -5
- package/src/cli/commands/options-leaps.ts +3 -3
- package/src/cli/commands/overview.ts +78 -29
- package/src/cli/commands/plugins.ts +1 -0
- package/src/cli/commands/search.ts +87 -24
- package/src/cli/commands/system.ts +5 -2
- package/src/cli/commands/ticker.ts +337 -60
- package/src/cli/context.ts +2 -1
- package/src/cli/desktop-pane-shot.ts +11 -3
- package/src/cli/earnings-calendar.ts +2 -1
- package/src/cli/errors.ts +10 -0
- package/src/cli/export-source.ts +18 -3
- package/src/cli/glossary.ts +196 -14
- package/src/cli/history-rows.ts +60 -9
- package/src/cli/index-roots.ts +26 -0
- package/src/cli/index.ts +34 -9
- package/src/cli/known-gaps.ts +156 -22
- package/src/cli/listing-arg.ts +156 -12
- package/src/cli/listing-suggestion.ts +4 -2
- package/src/cli/name-entries.ts +76 -0
- package/src/cli/not-a-ticker.ts +15 -6
- package/src/cli/pane-functions/access-gate.ts +22 -0
- package/src/cli/pane-functions/capabilities.ts +62 -4
- package/src/cli/pane-functions/catalog.ts +94 -39
- package/src/cli/pane-functions/data.ts +6 -5
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +3 -3
- package/src/cli/pane-functions/freshness.ts +49 -8
- package/src/cli/pane-functions/function-help.ts +13 -10
- package/src/cli/pane-functions/headless.ts +177 -42
- package/src/cli/pane-functions/index.ts +64 -8
- package/src/cli/pane-functions/options.ts +17 -3
- package/src/cli/pane-functions/report-notices.ts +17 -3
- package/src/cli/pane-functions/resolver.ts +48 -13
- package/src/cli/pane-functions/screenshot.ts +124 -23
- package/src/cli/pane-functions/shot-options.ts +68 -0
- package/src/cli/perpetual-contract.ts +3 -1
- package/src/cli/report-tables.ts +10 -2
- package/src/cli/staged-tickers.ts +5 -0
- package/src/cli/statement-filings.ts +261 -0
- package/src/cli/symbol-args.ts +47 -8
- package/src/cli/symbol-list.ts +9 -2
- package/src/cli/symbol-rows.ts +269 -29
- package/src/cli/ticker-resolution.ts +148 -34
- package/src/cli/treasury-yields.ts +86 -0
- package/src/cli/types.ts +3 -0
- package/src/cli/uncovered-instrument.ts +20 -0
- package/src/components/chart/composite/axis-overlays.tsx +16 -9
- package/src/components/chart/composite/composite-chart.tsx +85 -6
- package/src/components/chart/composite/downsample.ts +19 -3
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart/composite/legend.tsx +155 -76
- package/src/components/chart/composite/panel-bitmap.ts +8 -2
- package/src/components/chart/composite/panel-surface.tsx +3 -3
- package/src/components/chart/composite/toolbar.tsx +12 -7
- package/src/components/chart/composite/types.ts +6 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
- package/src/components/chart/curve/curve-surface.tsx +1 -1
- package/src/components/chart/static/series.ts +39 -0
- package/src/components/chart/static/trail-chart-surface.tsx +2 -1
- package/src/components/command-bar/commands/collection-list.ts +45 -15
- package/src/components/command-bar/commands/collection.ts +10 -3
- package/src/components/command-bar/commands/registry.ts +2 -2
- package/src/components/command-bar/list/badge.ts +4 -3
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/list/view.tsx +1 -1
- package/src/components/command-bar/panel/index.tsx +19 -8
- package/src/components/command-bar/panel/native-surface.ts +6 -4
- package/src/components/command-bar/panel/prompt-example.ts +22 -0
- package/src/components/command-bar/routes/root/provider-search.ts +3 -9
- package/src/components/command-bar/routes/root/results.ts +13 -0
- package/src/components/command-bar/routes/root/start-here.ts +90 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
- package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
- package/src/components/command-bar/theme-picker.tsx +3 -1
- package/src/components/command-bar/workflow/builtin.ts +5 -1
- package/src/components/command-bar/workflow/collection-actions.ts +21 -12
- package/src/components/command-bar/workflow/ops.ts +21 -1
- package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
- package/src/components/command-bar/workflow/tickers.ts +6 -4
- package/src/components/feedback-dialog.tsx +1 -1
- package/src/components/form-modal/broker-step.tsx +7 -63
- package/src/components/form-modal/content.tsx +7 -2
- package/src/components/form-modal/field-row.tsx +6 -8
- package/src/components/form-modal/host.tsx +3 -3
- package/src/components/form-modal/model.ts +3 -0
- package/src/components/form-modal/routes.ts +17 -2
- package/src/components/index.ts +6 -6
- package/src/components/input-search-bar.tsx +43 -6
- package/src/components/layout/detached-pane-shell.tsx +0 -1
- package/src/components/layout/floating-pane.tsx +0 -1
- package/src/components/layout/header.tsx +8 -7
- package/src/components/layout/pane/footer/index.tsx +7 -4
- package/src/components/layout/pane/footer/model.ts +28 -2
- package/src/components/layout/pane/header.tsx +9 -19
- package/src/components/layout/pane/index.tsx +0 -1
- package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
- package/src/components/layout/pane/pane-tabs.tsx +4 -3
- package/src/components/layout/pane/sizing.ts +9 -0
- package/src/components/layout/pane/ticker-fate-gate.tsx +8 -2
- package/src/components/layout/shell/pane/capture.ts +44 -20
- package/src/components/layout/status-bar.tsx +3 -6
- package/src/components/markdown-text.tsx +17 -2
- package/src/components/market-board/index.tsx +19 -8
- package/src/components/metric-treemap/heat-surface.tsx +2 -7
- package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
- package/src/components/onboarding/onboarding-frame.tsx +24 -21
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
- package/src/components/onboarding/wizard-positions.ts +40 -11
- package/src/components/pane-settings-dialog/desktop.tsx +12 -50
- package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
- package/src/components/price-performance.tsx +18 -2
- package/src/components/reader/article-reader.tsx +11 -10
- package/src/components/reader/detail-reader.tsx +9 -5
- package/src/components/reader/reader-text.tsx +51 -24
- package/src/components/sign-in-code-panel.tsx +129 -75
- package/src/components/ticker/badge/index.tsx +2 -1
- package/src/components/ui/action-row.tsx +5 -2
- package/src/components/ui/audio-player.tsx +70 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/data-table/index.tsx +4 -2
- package/src/components/ui/data-table/types.ts +11 -0
- package/src/components/ui/detail-scroll-body.tsx +1 -1
- package/src/components/ui/dialog-actions.tsx +1 -21
- package/src/components/ui/display.tsx +13 -5
- package/src/components/ui/external-link.tsx +4 -2
- package/src/components/ui/fields.tsx +12 -0
- package/src/components/ui/figure.tsx +5 -3
- package/src/components/ui/frame.tsx +40 -6
- package/src/components/ui/icon.tsx +4 -0
- package/src/components/ui/index.ts +4 -1
- package/src/components/ui/list-view.tsx +8 -0
- package/src/components/ui/prose.tsx +97 -25
- package/src/components/ui/query-bar-keys.tsx +3 -1
- package/src/components/ui/query-bar.tsx +6 -0
- package/src/components/ui/quote.ts +42 -0
- package/src/components/ui/range-track.tsx +15 -2
- package/src/components/ui/reveal-in-scroll-box.ts +21 -3
- package/src/components/ui/select-button.tsx +38 -3
- package/src/components/ui/status.tsx +73 -4
- package/src/components/ui/table-layout.ts +20 -0
- package/src/components/use-pinned-moment.ts +19 -0
- package/src/core/app-runtime.ts +2 -2
- package/src/core/state/app/instrument.ts +18 -1
- package/src/core/state/app/layout.ts +6 -0
- package/src/core/state/app/state.ts +14 -2
- package/src/data/cached-query.ts +1 -1
- package/src/data/config/chart-settings.ts +3 -1
- package/src/data/config/home.ts +4 -1
- package/src/data/config/store/node.ts +9 -4
- package/src/data/config/store/normalize.ts +9 -0
- package/src/data/json-storage.ts +6 -3
- package/src/data/json-ticker-repository.ts +3 -1
- package/src/data/plugin-cache.ts +5 -1
- package/src/data/sqlite/json.ts +3 -1
- package/src/data/ticker-repository.ts +5 -2
- package/src/feedback/diagnostics.ts +2 -6
- package/src/i18n/es.ts +86 -6
- package/src/i18n/index.ts +3 -9
- package/src/i18n/ja.ts +86 -6
- package/src/i18n/ko.ts +86 -6
- package/src/i18n/zh-cn.ts +86 -6
- package/src/i18n/zh-tw.ts +86 -6
- package/src/layout-marketplace/gallery-desktop.tsx +8 -4
- package/src/market-data/bars-per-year.ts +43 -0
- package/src/market-data/bond-funds.ts +85 -0
- package/src/market-data/coordinator/auxiliary.ts +5 -2
- package/src/market-data/coordinator/index.ts +6 -5
- package/src/market-data/futures-contract.ts +102 -5
- package/src/market-data/hooks.tsx +20 -11
- package/src/market-data/market/bar-period.ts +36 -0
- package/src/market-data/market/format.ts +5 -13
- package/src/market-data/market/freshness.ts +1 -3
- package/src/market-data/market/index-level.ts +51 -0
- package/src/market-data/model-dividend-yield.ts +57 -16
- package/src/market-data/quotes/live-valuation.ts +48 -2
- package/src/market-data/realized-volatility.ts +16 -7
- package/src/market-data/tax-equivalent.ts +344 -0
- package/src/news/aggregator.ts +4 -2
- package/src/notifications/notification-log.ts +5 -3
- package/src/plugins/auto-update-node.ts +1 -0
- package/src/plugins/builtin/account-management/footer.ts +1 -1
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +71 -19
- package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
- package/src/plugins/builtin/alerts/cli.ts +7 -18
- package/src/plugins/builtin/alerts/constants.ts +8 -0
- package/src/plugins/builtin/alerts/event-rows.ts +16 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
- package/src/plugins/builtin/alerts/headless.ts +182 -0
- package/src/plugins/builtin/alerts/index.tsx +3 -0
- package/src/plugins/builtin/alerts/pane.tsx +1 -7
- package/src/plugins/builtin/alerts/storage.ts +17 -1
- package/src/plugins/builtin/analytics/index.tsx +6 -1
- package/src/plugins/builtin/analytics/metrics.ts +47 -9
- package/src/plugins/builtin/analytics/risk-headless.ts +14 -2
- package/src/plugins/builtin/analytics/risk-math.ts +83 -0
- package/src/plugins/builtin/analytics/risk-model.ts +97 -4
- package/src/plugins/builtin/analytics/risk-pane.tsx +11 -6
- package/src/plugins/builtin/analytics/sector-model.ts +14 -3
- package/src/plugins/builtin/awards/headless.ts +6 -5
- package/src/plugins/builtin/awards/index.tsx +1 -1
- package/src/plugins/builtin/backtest/client.ts +38 -8
- package/src/plugins/builtin/backtest/engine.ts +25 -13
- package/src/plugins/builtin/backtest/export.ts +27 -0
- package/src/plugins/builtin/backtest/format.ts +19 -3
- package/src/plugins/builtin/backtest/headless.ts +42 -28
- package/src/plugins/builtin/backtest/index.tsx +3 -0
- package/src/plugins/builtin/backtest/pane.tsx +39 -10
- package/src/plugins/builtin/backtest/report.ts +160 -0
- package/src/plugins/builtin/backtest/rules.ts +2 -0
- package/src/plugins/builtin/backtest/window.ts +112 -0
- package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
- package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
- package/src/plugins/builtin/catalysts/headless.ts +24 -13
- package/src/plugins/builtin/catalysts/index.tsx +4 -2
- package/src/plugins/builtin/catalysts/model.ts +18 -1
- package/src/plugins/builtin/catalysts/pane.tsx +2 -2
- package/src/plugins/builtin/cds/client.ts +28 -5
- package/src/plugins/builtin/cds/headless.ts +183 -31
- package/src/plugins/builtin/cds/index.tsx +1 -1
- package/src/plugins/builtin/cds/model.ts +229 -53
- package/src/plugins/builtin/cds/pane.tsx +82 -26
- package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
- package/src/plugins/builtin/chart-composer/headless.ts +99 -21
- package/src/plugins/builtin/chart-composer/index.tsx +70 -13
- package/src/plugins/builtin/chart-composer/pane.tsx +65 -15
- package/src/plugins/builtin/chart-composer/presets.ts +79 -11
- package/src/plugins/builtin/chart-composer/series-catalog.ts +50 -14
- package/src/plugins/builtin/chart-composer/series-expression.ts +9 -7
- package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
- package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
- package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
- package/src/plugins/builtin/chat/content/composer.tsx +22 -1
- package/src/plugins/builtin/chat/content/index.tsx +35 -5
- package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
- package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
- package/src/plugins/builtin/chat/content/snapshot.ts +29 -6
- package/src/plugins/builtin/chat/ghost-user.ts +11 -3
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +13 -5
- package/src/plugins/builtin/chat/message/header.tsx +26 -4
- package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
- package/src/plugins/builtin/chat/message/render-state.ts +28 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
- package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
- package/src/plugins/builtin/chat/share/flow.tsx +9 -73
- package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
- package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
- package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/team/module.tsx +5 -1
- package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
- package/src/plugins/builtin/cloud/team/views.ts +11 -4
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
- package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
- package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
- package/src/plugins/builtin/commodities-energy/index.tsx +8 -5
- package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
- package/src/plugins/builtin/company-attention/headless.ts +15 -7
- package/src/plugins/builtin/company-attention/index.tsx +1 -1
- package/src/plugins/builtin/company-attention/model.ts +49 -4
- package/src/plugins/builtin/company-attention/pane.tsx +11 -9
- package/src/plugins/builtin/company-kpis/headless.ts +4 -3
- package/src/plugins/builtin/company-kpis/index.tsx +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
- package/src/plugins/builtin/congress-trades/headless.ts +23 -2
- package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
- package/src/plugins/builtin/cot/headless.ts +5 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
- package/src/plugins/builtin/credit-boards/client.ts +9 -2
- package/src/plugins/builtin/credit-boards/headless.ts +93 -27
- package/src/plugins/builtin/credit-boards/index.tsx +15 -4
- package/src/plugins/builtin/credit-boards/model.ts +105 -6
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
- package/src/plugins/builtin/credit-documents/index.tsx +3 -0
- package/src/plugins/builtin/credit-documents/model.ts +3 -10
- package/src/plugins/builtin/crypto-board/client.ts +152 -2
- package/src/plugins/builtin/crypto-board/headless.ts +138 -19
- package/src/plugins/builtin/crypto-board/index.tsx +5 -3
- package/src/plugins/builtin/crypto-board/model.ts +210 -12
- package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
- package/src/plugins/builtin/crypto-board/series.ts +52 -1
- package/src/plugins/builtin/crypto-board/supply.ts +123 -0
- package/src/plugins/builtin/debt-maturities/client.ts +54 -1
- package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/model.ts +58 -13
- package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
- package/src/plugins/builtin/dividend-yield/client.ts +42 -36
- package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
- package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
- package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
- package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
- package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
- package/src/plugins/builtin/dividend-yield/types.ts +6 -1
- package/src/plugins/builtin/dividend-yield/view.ts +1 -1
- package/src/plugins/builtin/docs/blocks.tsx +54 -8
- package/src/plugins/builtin/docs/pane.tsx +8 -13
- package/src/plugins/builtin/earnings/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/data.ts +51 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
- package/src/plugins/builtin/earnings-calls/model.ts +97 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
- package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
- package/src/plugins/builtin/econ/calendar-model.ts +22 -0
- package/src/plugins/builtin/econ/calendar-source.ts +35 -0
- package/src/plugins/builtin/econ/index.tsx +40 -8
- package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +8 -1
- package/src/plugins/builtin/econ-statistics/headless.ts +26 -5
- package/src/plugins/builtin/econ-statistics/pane.tsx +4 -2
- package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
- package/src/plugins/builtin/econ-statistics/view.ts +19 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +46 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
- package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
- package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
- package/src/plugins/builtin/executives/index.tsx +1 -0
- package/src/plugins/builtin/exposure/editor.tsx +3 -3
- package/src/plugins/builtin/fear-greed/index.tsx +9 -0
- package/src/plugins/builtin/filing-events/index.tsx +1 -0
- package/src/plugins/builtin/futures/index.tsx +2 -2
- package/src/plugins/builtin/futures/model.ts +34 -21
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
- package/src/plugins/builtin/futures-curve/headless.ts +4 -2
- package/src/plugins/builtin/futures-curve/model.ts +5 -7
- package/src/plugins/builtin/help/index.tsx +55 -8
- package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
- package/src/plugins/builtin/holders/headless.ts +12 -0
- package/src/plugins/builtin/insider/headless.ts +311 -41
- package/src/plugins/builtin/insider/index.tsx +280 -106
- package/src/plugins/builtin/insider/insiders.tsx +224 -0
- package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
- package/src/plugins/builtin/insider/signals-client.ts +135 -11
- package/src/plugins/builtin/insider/signals-model.ts +628 -56
- package/src/plugins/builtin/insider/signals.tsx +283 -83
- package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
- package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
- package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
- package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
- package/src/plugins/builtin/iv-history/headless.ts +2 -0
- package/src/plugins/builtin/jobs/headless.ts +1 -0
- package/src/plugins/builtin/jobs/index.tsx +1 -0
- package/src/plugins/builtin/jodi/headless.ts +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
- package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
- package/src/plugins/builtin/market-valuation/pane.tsx +1 -1
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
- package/src/plugins/builtin/mna/distress/headless.ts +2 -1
- package/src/plugins/builtin/mna/headless.ts +14 -2
- package/src/plugins/builtin/mna/index.tsx +1 -0
- package/src/plugins/builtin/news/headless.ts +4 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
- package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
- package/src/plugins/builtin/notes/cli.ts +8 -4
- package/src/plugins/builtin/notes/files.ts +66 -20
- package/src/plugins/builtin/notes/migration.ts +18 -6
- package/src/plugins/builtin/notes/owner.tsx +3 -2
- package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
- package/src/plugins/builtin/options-scenario/client.ts +7 -4
- package/src/plugins/builtin/options-scenario/headless.ts +5 -3
- package/src/plugins/builtin/pe-band/index.tsx +1 -1
- package/src/plugins/builtin/pe-band/model.ts +1 -15
- package/src/plugins/builtin/perps/headless.ts +30 -13
- package/src/plugins/builtin/perps/model.ts +45 -3
- package/src/plugins/builtin/perps/pane.tsx +7 -7
- package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +108 -46
- package/src/plugins/builtin/portfolio-list/cli/render.ts +368 -45
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +8 -2
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +24 -34
- package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +151 -29
- package/src/plugins/builtin/portfolio-list/income.ts +296 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
- package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +27 -10
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +10 -3
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +31 -3
- package/src/plugins/builtin/portfolio-list/table.tsx +25 -5
- package/src/plugins/builtin/power/prices-headless.ts +2 -1
- package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
- package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
- package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
- package/src/plugins/builtin/realized-vol/headless.ts +6 -4
- package/src/plugins/builtin/realized-vol/model.ts +5 -1
- package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
- package/src/plugins/builtin/realized-vol/settings.ts +1 -1
- package/src/plugins/builtin/research/analyst-headless.ts +4 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -0
- package/src/plugins/builtin/research/event-model.ts +22 -2
- package/src/plugins/builtin/research/headless-schema.ts +1 -0
- package/src/plugins/builtin/research/headless.ts +3 -0
- package/src/plugins/builtin/research/index.tsx +3 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
- package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +9 -6
- package/src/plugins/builtin/reverse-dcf/index.tsx +2 -2
- package/src/plugins/builtin/reverse-dcf/model.ts +51 -4
- package/src/plugins/builtin/reverse-dcf/pane.tsx +23 -10
- package/src/plugins/builtin/risk-factors/headless.ts +7 -0
- package/src/plugins/builtin/risk-factors/index.tsx +1 -0
- package/src/plugins/builtin/scanner/feed.ts +5 -3
- package/src/plugins/builtin/scanner/headless.ts +9 -57
- package/src/plugins/builtin/scanner/index.ts +1 -2
- package/src/plugins/builtin/sec/forms.ts +2 -6
- package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
- package/src/plugins/builtin/sec/fund-facts.ts +328 -0
- package/src/plugins/builtin/sec/headless.ts +140 -44
- package/src/plugins/builtin/sec/index.tsx +95 -30
- package/src/plugins/builtin/sec/model.ts +2 -44
- package/src/plugins/builtin/shared/headless-options.ts +10 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
- package/src/plugins/builtin/shared/move-rank.ts +108 -0
- package/src/plugins/builtin/shared/report-freshness.ts +25 -9
- package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
- package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +1 -0
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
- package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
- package/src/plugins/builtin/short-interest/headless.ts +3 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
- package/src/plugins/builtin/social-mentions/client.ts +8 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
- package/src/plugins/builtin/supply-chain/client.ts +12 -3
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
- package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
- package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
- package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
- package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
- package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
- package/src/plugins/builtin/supply-chain/headless.ts +69 -27
- package/src/plugins/builtin/supply-chain/index.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +76 -15
- package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
- package/src/plugins/builtin/themes/model.ts +2 -5
- package/src/plugins/builtin/thirteenf/api.ts +50 -2
- package/src/plugins/builtin/thirteenf/data.ts +250 -11
- package/src/plugins/builtin/thirteenf/headless.ts +311 -28
- package/src/plugins/builtin/thirteenf/index.tsx +4 -1
- package/src/plugins/builtin/thirteenf/model.ts +244 -19
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
- package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
- package/src/plugins/builtin/thirteenf/types.ts +49 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -8
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +17 -1
- package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +23 -10
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
- package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +36 -3
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
- package/src/plugins/builtin/time-sales/client.ts +7 -4
- package/src/plugins/builtin/time-sales/headless.ts +71 -22
- package/src/plugins/builtin/time-sales/model.ts +12 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -7
- package/src/plugins/builtin/time-sales/window.ts +128 -0
- package/src/plugins/builtin/trace-bonds/client.ts +351 -33
- package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
- package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
- package/src/plugins/builtin/trace-bonds/model.ts +27 -25
- package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
- package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
- package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
- package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
- package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
- package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
- package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
- package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
- package/src/plugins/builtin/vol-surface/headless.ts +3 -2
- package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
- package/src/plugins/builtin/vol-surface/stored.ts +4 -7
- package/src/plugins/builtin/volatility/chart-model.ts +16 -15
- package/src/plugins/builtin/volatility/charts.tsx +5 -5
- package/src/plugins/builtin/volatility/client.ts +73 -2
- package/src/plugins/builtin/volatility/evidence.ts +13 -14
- package/src/plugins/builtin/volatility/headless.ts +13 -7
- package/src/plugins/builtin/volatility/index.tsx +1 -1
- package/src/plugins/builtin/volatility/model.ts +104 -38
- package/src/plugins/builtin/volatility/pane.tsx +42 -24
- package/src/plugins/builtin/volatility/series.ts +22 -13
- package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
- package/src/plugins/builtin/world-venue-map/headless.ts +8 -5
- package/src/plugins/builtin/world-venue-map/index.ts +16 -2
- package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
- package/src/plugins/builtin/yield-curve/curves.ts +29 -5
- package/src/plugins/builtin/yield-curve/headless.ts +41 -8
- package/src/plugins/builtin/yield-curve/history.ts +5 -5
- package/src/plugins/builtin/yield-curve/index.tsx +30 -13
- package/src/plugins/compat.ts +3 -1
- package/src/plugins/dependencies.ts +8 -2
- package/src/plugins/host-link.ts +12 -5
- package/src/plugins/installer.ts +16 -7
- package/src/plugins/loader.ts +9 -2
- package/src/plugins/marketplace/activation.ts +10 -4
- package/src/plugins/registry/host-actions.ts +3 -0
- package/src/plugins/registry/index.ts +3 -1
- package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +74 -12
- package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +140 -53
- package/src/portfolio/futures-positions.ts +213 -0
- package/src/portfolio/futures-terms.ts +75 -0
- package/src/portfolio/manual-lots.ts +78 -0
- package/src/portfolio/mutations.ts +54 -10
- package/src/portfolio/position-metrics.ts +44 -10
- package/src/portfolio/valuation-basis.ts +31 -4
- package/src/public/__snapshots__/public-api.test.ts.snap +4 -0
- package/src/public/time-series.ts +2 -1
- package/src/remote/confirm-change.tsx +2 -1
- package/src/remote/server.ts +1 -1
- package/src/remote/watchlist.ts +9 -1
- package/src/renderers/dom/data-table/row.tsx +1 -1
- package/src/renderers/dom/desktop/audio-player.tsx +312 -0
- package/src/renderers/dom/desktop/control-styles.ts +17 -82
- package/src/renderers/dom/desktop/controls.tsx +39 -63
- package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
- package/src/renderers/dom/desktop/icons.tsx +8 -0
- package/src/renderers/dom/desktop/list-view.tsx +6 -6
- package/src/renderers/dom/desktop/popover.tsx +0 -3
- package/src/renderers/dom/desktop/query-bar.tsx +6 -4
- package/src/renderers/dom/desktop/select-field.tsx +9 -8
- package/src/renderers/dom/desktop/stack-header.ts +3 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -2
- package/src/renderers/dom/host/chart-surface.tsx +3 -3
- package/src/renderers/dom/host/size-observer.ts +33 -4
- package/src/renderers/dom/host/surface-3d.tsx +3 -2
- package/src/renderers/dom/host/surface-fit.ts +4 -1
- package/src/renderers/dom/host/tabs.tsx +22 -132
- package/src/renderers/dom/styles.css +358 -160
- package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
- package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
- package/src/series/capability.ts +1 -0
- package/src/series/files.ts +9 -3
- package/src/series/registry.ts +40 -14
- package/src/series/resolve.ts +121 -18
- package/src/series/standalone.ts +3 -2
- package/src/series/types.ts +55 -1
- package/src/series/validate.ts +39 -0
- package/src/shares/api.ts +26 -7
- package/src/shares/bug-report-dialog.tsx +2 -2
- package/src/shares/bug-report-flow.tsx +4 -7
- package/src/shares/capture-flow.ts +5 -4
- package/src/shares/capture-preview.tsx +2 -2
- package/src/shares/chat.ts +7 -27
- package/src/shares/flow.tsx +86 -40
- package/src/shares/location.ts +1 -3
- package/src/shares/pane-image.ts +11 -9
- package/src/shares/pane.ts +5 -2
- package/src/shares/payload.ts +1 -1
- package/src/shares/personal-pane.ts +3 -3
- package/src/shares/portable-layout.ts +9 -2
- package/src/shares/ready.ts +42 -0
- package/src/shares/test-harness.ts +62 -0
- package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
- package/src/sources/gloomberb-cloud/index.ts +64 -12
- package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
- package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
- package/src/sources/history-omitted.ts +34 -0
- package/src/sources/history-result.ts +3 -0
- package/src/sources/history-retention.ts +1 -1
- package/src/sources/provider-errors.ts +43 -1
- package/src/sources/provider-router/batches.ts +3 -2
- package/src/sources/provider-router/cache.ts +2 -1
- package/src/sources/provider-router/cached-routes.ts +9 -3
- package/src/sources/provider-router/history.ts +9 -8
- package/src/sources/provider-router/index.ts +18 -3
- package/src/sources/provider-router/primary.ts +5 -4
- package/src/sources/provider-router/quote-route.ts +3 -1
- package/src/sources/provider-router/routing.ts +2 -1
- package/src/sources/provider-router/search.ts +47 -13
- package/src/sources/provider-router/streaming.ts +7 -4
- package/src/sources/sec-edgar.ts +1 -3
- package/src/state/app/context/index.tsx +9 -13
- package/src/state/app/tab-cursor.ts +149 -12
- package/src/state/hooks/quote-streaming.ts +3 -2
- package/src/state/hooks/session-snapshot.ts +3 -2
- package/src/state/persist-scheduler.ts +12 -4
- package/src/sync/controller.ts +22 -1
- package/src/sync/core-contributors.ts +58 -15
- package/src/team/collections-sync.ts +14 -4
- package/src/team/collections.ts +40 -11
- package/src/team/store.ts +5 -4
- package/src/telemetry/attention-counts.ts +8 -2
- package/src/telemetry/crash-reports-dom.ts +3 -0
- package/src/telemetry/crash-reports-node.ts +7 -4
- package/src/telemetry/crash-reports.ts +11 -11
- package/src/telemetry/usage-counts.ts +16 -12
- package/src/theme/colors.ts +2 -0
- package/src/theme/font-scale.ts +2 -0
- package/src/theme/ui-tokens.ts +71 -0
- package/src/tickers/batched-lookups.ts +186 -0
- package/src/tickers/company-name-listing.ts +97 -0
- package/src/tickers/company-name.ts +13 -0
- package/src/tickers/instrument-coverage.ts +112 -0
- package/src/tickers/listing-fate.ts +49 -9
- package/src/tickers/listing-venues.ts +24 -5
- package/src/tickers/open-target.ts +13 -5
- package/src/tickers/search/index.ts +112 -35
- package/src/tickers/search/ranking.ts +49 -1
- package/src/tickers/terminal-codes.ts +79 -0
- package/src/tickers/ticker-changes.ts +74 -3
- package/src/tickers/us-listing.ts +2 -1
- package/src/time-series/chart-data.ts +79 -8
- package/src/time-series/fundamentals.ts +55 -7
- package/src/time-series/ice-credit.ts +17 -2
- package/src/time-series/live-bars.ts +21 -8
- package/src/time-series/live-quotes.ts +2 -3
- package/src/time-series/price-comparison.ts +6 -1
- package/src/time-series/resolve.ts +65 -10
- package/src/time-series/spec.ts +2 -1
- package/src/time-series/studies.ts +3 -3
- package/src/time-series/types.ts +22 -0
- package/src/time-series/use-chart-resolution.ts +2 -1
- package/src/types/config.ts +7 -0
- package/src/types/data-provider.ts +20 -0
- package/src/types/headless.ts +142 -1
- package/src/types/instrument.ts +6 -0
- package/src/types/plugin.ts +32 -9
- package/src/types/price-history.ts +5 -0
- package/src/types/ticker-changes.ts +19 -0
- package/src/types/ticker.ts +10 -2
- package/src/ui/context-menu.tsx +2 -0
- package/src/ui/host.tsx +40 -4
- package/src/ui/measured-cells.ts +13 -7
- package/src/ui/qr.ts +7 -5
- package/src/updater/index.ts +5 -5
- package/src/utils/calendar-date.ts +86 -27
- package/src/utils/currency-units.ts +17 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/dividend-cash.ts +48 -0
- package/src/utils/dom-screenshot-rasters.ts +3 -1
- package/src/utils/dom-screenshot.ts +14 -10
- package/src/utils/exchange-rate-snapshot.ts +9 -4
- package/src/utils/external-url.ts +1 -0
- package/src/utils/fiscal-period.ts +47 -0
- package/src/utils/format.ts +5 -2
- package/src/utils/forward-dividend.ts +160 -11
- package/src/utils/futures-generic.ts +34 -8
- package/src/utils/http-proxy-response.ts +2 -1
- package/src/utils/local-utc-offset.ts +2 -0
- package/src/utils/log-failure.ts +31 -0
- package/src/utils/option-expiry.ts +12 -12
- package/src/utils/price-history-integrity.ts +56 -0
- package/src/utils/price-history.ts +13 -1
- package/src/utils/sec-filings.ts +245 -0
- package/src/utils/selection-clipboard.ts +5 -2
- package/src/utils/throttled-fetch.ts +1 -0
- package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
- package/src/utils/url.ts +1 -0
- package/src/utils/utc-time.ts +11 -0
- package/src/plugins/builtin/chat/share/targets.ts +0 -27
- package/src/renderers/dom/host/top-surface.ts +0 -119
- package/src/shares/dialog.tsx +0 -221
- package/src/shares/live.ts +0 -19
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { useAsyncResource } from "../../../../react/async-resource";
|
|
3
|
+
import type { CorporateActionsData, TickerFinancials } from "../../../../types/financials";
|
|
4
|
+
import { measuresTrailingYield } from "../../../../market-data/quotes/live-valuation";
|
|
5
|
+
import {
|
|
6
|
+
distributionHistory,
|
|
7
|
+
forwardDividendMayHaveLapsed,
|
|
8
|
+
lapsedDividend,
|
|
9
|
+
type DividendLapse,
|
|
10
|
+
} from "../../../../utils/forward-dividend";
|
|
11
|
+
import { useAssetData } from "../../../runtime";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* What the overview's dividend yield reads beside the fundamentals: whether a
|
|
15
|
+
* forward yield stands, has stopped, or waits on the history that says, and
|
|
16
|
+
* the cash history a trailing yield is measured from.
|
|
17
|
+
*/
|
|
18
|
+
export interface OverviewDividendState {
|
|
19
|
+
/** The history is still being read; the yield is held back rather than shown and then withdrawn. */
|
|
20
|
+
pending: boolean;
|
|
21
|
+
lapse: DividendLapse | null;
|
|
22
|
+
/**
|
|
23
|
+
* The listing's cash history, for a trailing yield (listingDividendYield).
|
|
24
|
+
* Null while it is read and when it cannot be, which leaves Gloom Cloud's
|
|
25
|
+
* yield: a trailing yield is never withdrawn, so it is not held back.
|
|
26
|
+
*/
|
|
27
|
+
actions?: CorporateActionsData | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const CURRENT: OverviewDividendState = { pending: false, lapse: null };
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The listing's distribution history as the overview's dividend yield reads
|
|
34
|
+
* it. A trailing yield is measured from it as DVD measures it. A forward
|
|
35
|
+
* yield's stopped dividend (lapsedDividend) is read from it only once its
|
|
36
|
+
* ex-date is far enough past for the dividend to have stopped; without a
|
|
37
|
+
* history, Gloom Cloud's fifteen months decide on the ex-date alone.
|
|
38
|
+
*/
|
|
39
|
+
export function useOverviewDividend(
|
|
40
|
+
symbol: string | null,
|
|
41
|
+
exchange: string,
|
|
42
|
+
fundamentals: TickerFinancials["fundamentals"] | undefined,
|
|
43
|
+
): OverviewDividendState {
|
|
44
|
+
const dataProvider = useAssetData();
|
|
45
|
+
const forward = fundamentals?.dividendYieldBasis === "forward" && fundamentals.dividendYield != null;
|
|
46
|
+
const trailing = measuresTrailingYield(fundamentals);
|
|
47
|
+
const asOf = fundamentals?.exDividendDate;
|
|
48
|
+
const readsHistory = !!symbol && !!dataProvider?.getCorporateActions && (trailing || (forward && forwardDividendMayHaveLapsed(asOf)));
|
|
49
|
+
const loader = useCallback(() => dataProvider!.getCorporateActions!(symbol!, exchange), [dataProvider, exchange, symbol]);
|
|
50
|
+
const cached = useMemo(() => readsHistory
|
|
51
|
+
? dataProvider?.getCachedQuery?.("getCorporateActions", [symbol!, exchange]).getSnapshot().result?.value ?? null
|
|
52
|
+
: null, [dataProvider, exchange, readsHistory, symbol]);
|
|
53
|
+
const resource = useAsyncResource(readsHistory ? loader : null, { initialData: () => cached });
|
|
54
|
+
const actions = readsHistory ? resource.data ?? cached : null;
|
|
55
|
+
if (trailing) return { pending: false, lapse: null, actions };
|
|
56
|
+
if (!forward) return CURRENT;
|
|
57
|
+
if (!readsHistory) return { pending: false, lapse: lapsedDividend(asOf, null) };
|
|
58
|
+
if (!actions && resource.loading) return { pending: true, lapse: null };
|
|
59
|
+
return { pending: false, lapse: lapsedDividend(asOf, distributionHistory(actions)) };
|
|
60
|
+
}
|
|
@@ -18,14 +18,19 @@ import {
|
|
|
18
18
|
formatMarketQuantity,
|
|
19
19
|
withCurrencyMinorDigits,
|
|
20
20
|
} from "../../../../market-data/market/format";
|
|
21
|
+
import { reportedQuoteVolume } from "../../../../market-data/market/index-level";
|
|
21
22
|
import type { OverviewFunctionLink, PositionTableRow, StatField } from "./types";
|
|
23
|
+
import type { OverviewDividendState } from "./dividend";
|
|
24
|
+
import { NO_CURRENT_DIVIDEND } from "../../../../utils/forward-dividend";
|
|
22
25
|
import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue, resolvePortfolioPositionPnl, portfolioPnlPercent, signedPositionDirection } from "../../../../portfolio/position-metrics";
|
|
23
|
-
import {
|
|
26
|
+
import { listingDividendYield, liveForwardPE, liveMarketCapitalization, liveTrailingPE } from "../../../../market-data/quotes/live-valuation";
|
|
24
27
|
import { atRegularSessionPrice } from "../../../../market-data/quotes/regular-session-valuation";
|
|
25
28
|
import { formatReportedMoney } from "../../../../utils/reported-money";
|
|
26
29
|
import { enterpriseValueCheck, fundamentalsCurrency, fundamentalsEpsCurrency, reportedEnterpriseValue } from "../../../../utils/fundamentals";
|
|
27
30
|
import { formatShortDate } from "../../../../utils/datetime-format";
|
|
28
31
|
import { safeExternalUrl } from "../../../../utils/external-url";
|
|
32
|
+
import type { FundBondFacts } from "../../../../api-client/fund-facts";
|
|
33
|
+
import { bondFigures } from "../../../../market-data/bond-funds";
|
|
29
34
|
|
|
30
35
|
type CurrencyConverter = (value: number, fromCurrency: string) => number;
|
|
31
36
|
|
|
@@ -34,6 +39,10 @@ const HOLDERS: OverviewFunctionLink = { name: "Holders", tabId: "holders", templ
|
|
|
34
39
|
const SHORT_INTEREST: OverviewFunctionLink = { name: "Short Interest", tabId: "short-interest", templateId: "short-interest-pane" };
|
|
35
40
|
const EARNINGS: OverviewFunctionLink = { name: "Earnings", templateId: "earnings-calendar-pane" };
|
|
36
41
|
const DIVIDENDS: OverviewFunctionLink = { name: "Dividends", tabId: "dividend-yield", templateId: "dividend-yield-pane" };
|
|
42
|
+
const FUND_FACTS: OverviewFunctionLink = { name: "Fund Facts", templateId: "sec-etf-pane" };
|
|
43
|
+
|
|
44
|
+
/** A bond fund's sponsor figures in a narrow grid: `SEC Yield 4.04% 2026-10-15`. */
|
|
45
|
+
const BOND_STAT_LABELS = { secYield: "SEC Yield", yieldToWorst: "Yld to Worst" } as const;
|
|
37
46
|
|
|
38
47
|
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] as const;
|
|
39
48
|
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -77,6 +86,8 @@ export function buildOverviewStats({
|
|
|
77
86
|
nextEarnings,
|
|
78
87
|
depositaryReceipt = false,
|
|
79
88
|
today,
|
|
89
|
+
dividend,
|
|
90
|
+
bond = null,
|
|
80
91
|
}: {
|
|
81
92
|
quote: Quote | undefined;
|
|
82
93
|
fundamentals: TickerFinancials["fundamentals"] | undefined;
|
|
@@ -91,13 +102,22 @@ export function buildOverviewStats({
|
|
|
91
102
|
depositaryReceipt?: boolean;
|
|
92
103
|
/** The listing's calendar day, YYYY-MM-DD; dates before it are left out. */
|
|
93
104
|
today?: string;
|
|
105
|
+
/** The history the dividend yield reads (useOverviewDividend): a trailing one is measured from its cash, a forward one stops with it; without it the yield stands. */
|
|
106
|
+
dividend?: OverviewDividendState;
|
|
107
|
+
/**
|
|
108
|
+
* A bond fund's SEC yield, yield to worst and duration: its yield is then a
|
|
109
|
+
* distribution yield, not its SEC yield, which follows it with its date.
|
|
110
|
+
*/
|
|
111
|
+
bond?: FundBondFacts | null;
|
|
94
112
|
}): StatField[] {
|
|
95
113
|
const stats: StatField[] = [];
|
|
96
114
|
const money = (value: number, perShare = false) => formatReportedMoney(value, fundamentalsCurrency({ fundamentals, financialCurrency }), perShare);
|
|
97
115
|
|
|
98
|
-
|
|
116
|
+
// An index's 0 is volume nobody counts, not a quiet session: no Volume row.
|
|
117
|
+
const volume = reportedQuoteVolume(quote);
|
|
118
|
+
if (volume != null) {
|
|
99
119
|
// Live figures keep their decimals (12.30M, not 12.3M) so the digits hold still.
|
|
100
|
-
stats.push({ label: "Volume", value: formatCompact(
|
|
120
|
+
stats.push({ label: "Volume", value: formatCompact(volume, { fixedDecimals: true }) });
|
|
101
121
|
}
|
|
102
122
|
// Price-derived statistics follow the quote; see live-valuation for when a stored figure is kept.
|
|
103
123
|
const capitalization = liveMarketCapitalization(quote, fundamentals);
|
|
@@ -146,16 +166,33 @@ export function buildOverviewStats({
|
|
|
146
166
|
link: EARNINGS,
|
|
147
167
|
});
|
|
148
168
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
169
|
+
// A trailing yield as DVD measures it from the cash history, once that is read.
|
|
170
|
+
const dividendYield = listingDividendYield(quote, fundamentals, dividend?.actions);
|
|
171
|
+
// A dividend that stopped has no forward yield, and its last ex-date is when it was last paid.
|
|
172
|
+
const lapse = dividend?.lapse ?? null;
|
|
173
|
+
// A bond fund pays distributions, and its yield on them is not the SEC yield after it.
|
|
174
|
+
const kind = bond ? "Dist" : "Div";
|
|
175
|
+
const notSec = bond ? { detail: ["not SEC yield", "not SEC"] } : {};
|
|
176
|
+
if (fundamentals && lapse) {
|
|
177
|
+
stats.push({ label: `Fwd ${kind} Yld`, value: NO_CURRENT_DIVIDEND });
|
|
178
|
+
} else if (fundamentals && dividendYield != null && !dividend?.pending) {
|
|
179
|
+
const basis = fundamentals.dividendYieldBasis;
|
|
180
|
+
const label = basis === "forward" ? `Fwd ${kind} Yld` : basis === "trailing" ? `TTM ${kind} Yld` : `${kind} Yield`;
|
|
181
|
+
stats.push({ label, value: formatLevelPercent(dividendYield), ...notSec });
|
|
182
|
+
}
|
|
183
|
+
if (bond) {
|
|
184
|
+
// Each dated, as the sponsor publishes it; the narrowest column drops the year.
|
|
185
|
+
for (const figure of bondFigures(bond)) {
|
|
186
|
+
const label = figure.key === "duration" ? (bond.duration?.measure === "average" ? "Avg Duration" : "Eff Duration") : BOND_STAT_LABELS[figure.key];
|
|
187
|
+
const forms = figure.date ? [...new Set([figure.date, figure.date.replace(/\b\d{4}-(\d{2}-\d{2})\b/, "$1")])] : undefined;
|
|
188
|
+
stats.push({ label, value: figure.text, ...(forms ? { detail: forms } : {}), link: FUND_FACTS });
|
|
189
|
+
}
|
|
153
190
|
}
|
|
154
|
-
const exDividendDate = fundamentals?.exDividendDate;
|
|
191
|
+
const exDividendDate = lapse?.lastPaid ?? fundamentals?.exDividendDate;
|
|
155
192
|
if (exDividendDate && ISO_DATE.test(exDividendDate)) {
|
|
156
193
|
const days = today ? daysUntil(exDividendDate, today) : -1;
|
|
157
194
|
stats.push({
|
|
158
|
-
label: "Ex-Dividend",
|
|
195
|
+
label: lapse ? "Last Paid" : "Ex-Dividend",
|
|
159
196
|
value: calendarDate(exDividendDate, today ?? exDividendDate),
|
|
160
197
|
detail: days >= 0 ? daysAhead(days) : undefined,
|
|
161
198
|
link: DIVIDENDS,
|
|
@@ -12,6 +12,7 @@ import { getTickerResearchPaneSettings } from "../settings";
|
|
|
12
12
|
import { OverviewTab } from "../overview-tab";
|
|
13
13
|
import { useOpenResearchTab } from "../research-tab-navigation";
|
|
14
14
|
import type { OverviewFunctionLink } from "./types";
|
|
15
|
+
import { useOverviewDividend } from "./dividend";
|
|
15
16
|
|
|
16
17
|
/** Registered by the chart composer, in the same plugin as this tab. */
|
|
17
18
|
const CHART_TAB_ID = "chart";
|
|
@@ -23,6 +24,7 @@ export function OverviewResearchTab({ width, height, focused }: TickerResearchTa
|
|
|
23
24
|
const paneInstance = usePaneInstance();
|
|
24
25
|
const openResearchTab = useOpenResearchTab();
|
|
25
26
|
const { createPaneFromTemplate } = usePluginAppActions();
|
|
27
|
+
const dividend = useOverviewDividend(ticker?.metadata.ticker ?? null, ticker?.metadata.exchange ?? "", financials?.fundamentals);
|
|
26
28
|
// A pane locked to one tab has nowhere to go.
|
|
27
29
|
const lockedToOneTab = getTickerResearchPaneSettings(paneInstance?.settings).hideTabs;
|
|
28
30
|
const openChart = useCallback(() => {
|
|
@@ -43,6 +45,7 @@ export function OverviewResearchTab({ width, height, focused }: TickerResearchTa
|
|
|
43
45
|
financials={financials}
|
|
44
46
|
onOpenChart={paneId && !lockedToOneTab ? openChart : undefined}
|
|
45
47
|
onOpenFunction={openFunction}
|
|
48
|
+
dividend={dividend}
|
|
46
49
|
perpetuals={ticker ? <PerpEquityRow symbol={ticker.metadata.ticker} exchange={ticker.metadata.exchange} instrumentType={financials?.quote?.instrumentType ?? ticker.metadata.assetCategory} /> : undefined}
|
|
47
50
|
/>
|
|
48
51
|
);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { TrendingSummary } from "../attention/trending";
|
|
2
|
+
import { fundFactOverviewFields } from "../sec/fund-facts";
|
|
3
|
+
import { useFundFacts } from "../sec/fund-facts-view";
|
|
4
|
+
import { resolveTickerInstrumentKind } from "../../../tickers/instrument-kind";
|
|
5
|
+
import { mayBeUsEquityOrFundTicker } from "../../../utils/sec";
|
|
2
6
|
import type { ReactNode } from "react";
|
|
3
7
|
import { EmptyState, PaneLinkMenu, SectionHeading, usePaneNoticeFooter } from "../../../components";
|
|
4
8
|
import { CompositeChart, pricePointsToResolvedSeries } from "../../../components/chart/composite";
|
|
@@ -8,6 +12,7 @@ import { PriceReturnStrip } from "../../../components/price-performance";
|
|
|
8
12
|
import { t } from "../../../i18n";
|
|
9
13
|
import { useFxRatesMap } from "../../../market-data/hooks";
|
|
10
14
|
import { formatMarketPriceWithCurrency, formatSignedMarketPrice, liveQuoteFormatOptions } from "../../../market-data/market/format";
|
|
15
|
+
import { priceSeriesUnit } from "../../../market-data/market/index-level";
|
|
11
16
|
import {
|
|
12
17
|
exchangeShortName,
|
|
13
18
|
extendedSessionLineLabel,
|
|
@@ -41,6 +46,7 @@ import {
|
|
|
41
46
|
textGridColumns,
|
|
42
47
|
} from "./overview/components";
|
|
43
48
|
import { useFuturesContractTerms } from "./overview/futures-contract";
|
|
49
|
+
import type { OverviewDividendState } from "./overview/dividend";
|
|
44
50
|
import { buildOverviewStats, buildPositionRows, buildProfileFields } from "./overview/model";
|
|
45
51
|
import type { OverviewFunctionLink, StatField } from "./overview/types";
|
|
46
52
|
import { describeFundamentalMarketCap } from "../../../utils/market-capitalization";
|
|
@@ -80,6 +86,8 @@ interface OverviewTabProps {
|
|
|
80
86
|
onOpenChart?: () => void;
|
|
81
87
|
/** A click on a figure opens its research function (beta to GR, holders to HDS). */
|
|
82
88
|
onOpenFunction?: (link: OverviewFunctionLink) => void;
|
|
89
|
+
/** The history the dividend yield reads (useOverviewDividend): a trailing one's cash, a forward one's lapse; without it the yield stands. */
|
|
90
|
+
dividend?: OverviewDividendState;
|
|
83
91
|
}
|
|
84
92
|
|
|
85
93
|
export function OverviewTab(props: OverviewTabProps) {
|
|
@@ -87,7 +95,10 @@ export function OverviewTab(props: OverviewTabProps) {
|
|
|
87
95
|
return <ResolvedOverviewTab {...props} ticker={props.ticker} />;
|
|
88
96
|
}
|
|
89
97
|
|
|
90
|
-
|
|
98
|
+
/** Where a fund's facts open: the ETF function, its facts over its filings. */
|
|
99
|
+
const FUND_FACTS_LINK: OverviewFunctionLink = { name: "Fund Facts", templateId: "sec-etf-pane" };
|
|
100
|
+
|
|
101
|
+
function ResolvedOverviewTab({ width, height, focused = false, ticker, financials, onOpenChart, onOpenFunction, perpetuals, dividend }: OverviewTabProps & { ticker: TickerRecord }) {
|
|
91
102
|
const baseCurrency = useAppSelector((state) => state.config.baseCurrency);
|
|
92
103
|
const { width: termWidth } = useViewport();
|
|
93
104
|
const { fractionalViewport = false, nativePaneChrome, cellWidthPx = 8, cellHeightPx = 18, pixelRatio = 1 } = useUiCapabilities();
|
|
@@ -125,6 +136,14 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
125
136
|
routingVenue && routingVenue !== listingVenue ? `route ${routingVenue}` : "",
|
|
126
137
|
].filter((part) => part.length > 0);
|
|
127
138
|
|
|
139
|
+
// A US-listed fund's facts (fee, NAV, premium, net assets) stand where a company's fundamentals would; each opens ETF.
|
|
140
|
+
const fundFactsSymbol = resolveTickerInstrumentKind(ticker, financials) === "fund" && mayBeUsEquityOrFundTicker(ticker) ? ticker.metadata.ticker : null;
|
|
141
|
+
const fundFacts = useFundFacts(fundFactsSymbol).data?.payload;
|
|
142
|
+
// Each figure dated; one older than it looks takes the warning colour, as in ETF.
|
|
143
|
+
const fundFactFields: StatField[] = fundFacts?.covered
|
|
144
|
+
? fundFactOverviewFields(fundFacts).map(({ warn, ...field }) => ({ ...field, ...(warn ? { valueColor: colors.warning } : {}), link: FUND_FACTS_LINK }))
|
|
145
|
+
: [];
|
|
146
|
+
|
|
128
147
|
const contentWidth = Math.max((width || Math.floor(termWidth * 0.5)) - (fractionalViewport ? 2 : 4), 20);
|
|
129
148
|
const chartWidth = contentWidth;
|
|
130
149
|
const hasHistory = (financials?.priceHistory?.length ?? 0) > 2;
|
|
@@ -139,7 +158,8 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
139
158
|
id: `${ticker.metadata.ticker}:price`,
|
|
140
159
|
label: `${ticker.metadata.ticker} Price`,
|
|
141
160
|
color: priceColor(chartDelta),
|
|
142
|
-
|
|
161
|
+
// An index's axis reads in points: ^VIX at 20, never $20.
|
|
162
|
+
unit: unknownHistoryBasis ? "unknown" : priceSeriesUnit(quoteCurrency, instrumentType),
|
|
143
163
|
style: "area",
|
|
144
164
|
axis: "right",
|
|
145
165
|
panelId: "price",
|
|
@@ -236,6 +256,8 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
236
256
|
depositaryReceipt: sharesOutstandingInReceipts(quote, fundamentals, description),
|
|
237
257
|
// Report and ex-dividend dates are the listing's calendar days.
|
|
238
258
|
today: zonedDateKey(Date.now(), chartTimeZone ?? "America/New_York"),
|
|
259
|
+
dividend,
|
|
260
|
+
bond: fundFacts?.bond ?? null,
|
|
239
261
|
});
|
|
240
262
|
const profileFields = buildProfileFields({
|
|
241
263
|
instrumentType: instrumentTypeLabel(instrumentType, quote?.name || ticker.metadata.name),
|
|
@@ -355,7 +377,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
355
377
|
{perpetuals && <PaneLinkMenu>{perpetuals}</PaneLinkMenu>}
|
|
356
378
|
|
|
357
379
|
{(hasDayRange || yearRange) && quote && (
|
|
358
|
-
<Box flexDirection={rangeInline ? "row" : "column"} gap={rangeInline ? RANGE_PAIR_GAP : 0} width={contentWidth}>
|
|
380
|
+
<Box flexDirection={rangeInline ? "row" : "column"} gap={rangeInline ? RANGE_PAIR_GAP : 0} width={nativePaneChrome ? undefined : contentWidth}>
|
|
359
381
|
{hasDayRange && (
|
|
360
382
|
<CompactRangeBar
|
|
361
383
|
current={sessionQuote?.price ?? quote.price}
|
|
@@ -366,6 +388,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
366
388
|
labelWidth={rangeLabelWidth}
|
|
367
389
|
endpointWidth={rangeEndpointCells}
|
|
368
390
|
width={rangeWidth}
|
|
391
|
+
grow={rangeInline}
|
|
369
392
|
currency={quoteCurrency}
|
|
370
393
|
priceOptions={moneyOptions}
|
|
371
394
|
/>
|
|
@@ -380,6 +403,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
380
403
|
labelWidth={rangeLabelWidth}
|
|
381
404
|
endpointWidth={rangeEndpointCells}
|
|
382
405
|
width={rangeWidth}
|
|
406
|
+
grow={rangeInline}
|
|
383
407
|
currency={quoteCurrency}
|
|
384
408
|
priceOptions={moneyOptions}
|
|
385
409
|
/>
|
|
@@ -426,6 +450,15 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
426
450
|
</Box>
|
|
427
451
|
)}
|
|
428
452
|
|
|
453
|
+
{fundFactFields.length > 0 && (
|
|
454
|
+
<Box flexDirection="column">
|
|
455
|
+
<SectionHeading title={t("Fund Facts")} />
|
|
456
|
+
<PaneLinkMenu>
|
|
457
|
+
<FundamentalsGrid fields={fundFactFields} width={contentWidth} onOpenLink={onOpenFunction} />
|
|
458
|
+
</PaneLinkMenu>
|
|
459
|
+
</Box>
|
|
460
|
+
)}
|
|
461
|
+
|
|
429
462
|
{stats.length > 0 && (
|
|
430
463
|
<Box flexDirection="column">
|
|
431
464
|
<SectionHeading title={t("Fundamentals")} />
|
|
@@ -59,7 +59,7 @@ function extendedLineParts(
|
|
|
59
59
|
return candidates.find(({ label, figures }) => fits(`${label} ${figures}`)) ?? null;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
const DESKTOP_DETAIL_TEXT_STYLE = { fontSize: "
|
|
62
|
+
const DESKTOP_DETAIL_TEXT_STYLE = { fontSize: "var(--gloom-type-ui)", lineHeight: "14px", whiteSpace: "nowrap" };
|
|
63
63
|
|
|
64
64
|
function ExtendedSessionLine({
|
|
65
65
|
parts,
|
|
@@ -203,9 +203,10 @@ export function QuoteMonitorCard({
|
|
|
203
203
|
|| height >= (stacked ? 3 : 2) + statusRows + terminalSparklineHeight;
|
|
204
204
|
// Figures sit over the sparkline, so the desktop rings them in the card colour.
|
|
205
205
|
const figureHalo = { textShadow: `0 1px 2px ${colors.bg}` };
|
|
206
|
+
// The card's title: a heading over the app's 12px name, under the 20px price.
|
|
206
207
|
const desktopSymbolStyle = nativePaneChrome
|
|
207
208
|
? {
|
|
208
|
-
fontSize: "
|
|
209
|
+
fontSize: "var(--gloom-type-heading)",
|
|
209
210
|
lineHeight: "18px",
|
|
210
211
|
}
|
|
211
212
|
: undefined;
|
|
@@ -237,12 +238,10 @@ export function QuoteMonitorCard({
|
|
|
237
238
|
position: "relative",
|
|
238
239
|
width: "100%",
|
|
239
240
|
height: "100%",
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
borderRight: showRightDivider ? `1px solid ${colors.border}` : undefined,
|
|
245
|
-
borderBottom: showBottomDivider ? `1px solid ${colors.border}` : undefined,
|
|
241
|
+
padding: "var(--gloom-space-xs) var(--gloom-inset)",
|
|
242
|
+
// The rules between the board's cards are the app's hairline.
|
|
243
|
+
borderRight: showRightDivider ? "1px solid var(--gloom-hairline)" : undefined,
|
|
244
|
+
borderBottom: showBottomDivider ? "1px solid var(--gloom-hairline)" : undefined,
|
|
246
245
|
outline: selected ? `1px solid ${colors.borderFocused}` : undefined,
|
|
247
246
|
outlineOffset: selected ? -1 : undefined,
|
|
248
247
|
} : undefined}
|
|
@@ -267,7 +266,7 @@ export function QuoteMonitorCard({
|
|
|
267
266
|
// The name gives way first; the price keeps its natural width.
|
|
268
267
|
gridTemplateColumns: "minmax(0, 1fr) auto",
|
|
269
268
|
gridTemplateRows: "auto 1fr auto auto",
|
|
270
|
-
columnGap:
|
|
269
|
+
columnGap: "var(--gloom-space-md)",
|
|
271
270
|
width: "100%",
|
|
272
271
|
height: "100%",
|
|
273
272
|
}}
|
|
@@ -286,7 +285,7 @@ export function QuoteMonitorCard({
|
|
|
286
285
|
{symbol}
|
|
287
286
|
</Text>
|
|
288
287
|
{quoteFailed && (
|
|
289
|
-
<Text fg={colors.negative} style={{ fontSize: "
|
|
288
|
+
<Text fg={colors.negative} style={{ fontSize: "var(--gloom-type-ui)", lineHeight: "14px" }}>
|
|
290
289
|
{quoteStatus.text}
|
|
291
290
|
</Text>
|
|
292
291
|
)}
|
|
@@ -294,7 +293,7 @@ export function QuoteMonitorCard({
|
|
|
294
293
|
<Text
|
|
295
294
|
fg={colors.textDim}
|
|
296
295
|
style={{
|
|
297
|
-
fontSize: "
|
|
296
|
+
fontSize: "var(--gloom-type-ui)",
|
|
298
297
|
lineHeight: "14px",
|
|
299
298
|
maxWidth: "100%",
|
|
300
299
|
overflow: "hidden",
|
|
@@ -306,7 +305,7 @@ export function QuoteMonitorCard({
|
|
|
306
305
|
</Text>
|
|
307
306
|
)}
|
|
308
307
|
{navAsOf && (
|
|
309
|
-
<Text fg={colors.textDim} style={{ fontSize: "
|
|
308
|
+
<Text fg={colors.textDim} style={{ fontSize: "var(--gloom-type-ui)", lineHeight: "14px" }}>
|
|
310
309
|
{navAsOf}
|
|
311
310
|
</Text>
|
|
312
311
|
)}
|
|
@@ -320,9 +319,9 @@ export function QuoteMonitorCard({
|
|
|
320
319
|
gridRow: "1",
|
|
321
320
|
justifySelf: "end",
|
|
322
321
|
backgroundColor: colors.bg,
|
|
323
|
-
borderRadius:
|
|
322
|
+
borderRadius: "var(--gloom-radius-sm)",
|
|
324
323
|
boxShadow: `0 0 0 2px ${colors.bg}`,
|
|
325
|
-
paddingLeft:
|
|
324
|
+
paddingLeft: "var(--gloom-space-xs)",
|
|
326
325
|
paddingBottom: 1,
|
|
327
326
|
}}
|
|
328
327
|
>
|
|
@@ -361,10 +360,10 @@ export function QuoteMonitorCard({
|
|
|
361
360
|
gridRow: "3",
|
|
362
361
|
justifySelf: "end",
|
|
363
362
|
alignSelf: "end",
|
|
364
|
-
fontSize: "
|
|
365
|
-
lineHeight: "
|
|
363
|
+
fontSize: "var(--gloom-type-ui)",
|
|
364
|
+
lineHeight: "14px",
|
|
366
365
|
backgroundColor: colors.bg,
|
|
367
|
-
paddingLeft:
|
|
366
|
+
paddingLeft: "var(--gloom-space-xs)",
|
|
368
367
|
paddingRight: 2,
|
|
369
368
|
boxShadow: `0 0 0 2px ${colors.bg}`,
|
|
370
369
|
}}
|
|
@@ -410,7 +409,7 @@ export function QuoteMonitorCard({
|
|
|
410
409
|
<Text
|
|
411
410
|
fg={colors.textDim}
|
|
412
411
|
style={{
|
|
413
|
-
fontSize: "
|
|
412
|
+
fontSize: "var(--gloom-type-ui)",
|
|
414
413
|
lineHeight: "16px",
|
|
415
414
|
overflow: "hidden",
|
|
416
415
|
textOverflow: "ellipsis",
|
|
@@ -449,7 +448,7 @@ export function QuoteMonitorCard({
|
|
|
449
448
|
area={!nativePaneChrome}
|
|
450
449
|
/>
|
|
451
450
|
{nativePaneChrome && rangeLabel && (
|
|
452
|
-
<Text fg={colors.textDim} style={{ fontSize: "
|
|
451
|
+
<Text fg={colors.textDim} style={{ fontSize: "var(--gloom-type-ui)", lineHeight: "14px" }}>
|
|
453
452
|
{rangeLabel}
|
|
454
453
|
</Text>
|
|
455
454
|
)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
2
|
import { ApiRequestError } from "../../../api-client/errors";
|
|
3
|
-
import type { TapeSnapshot } from "../../../api-client/tape";
|
|
3
|
+
import type { TapeRange, TapeSnapshot } from "../../../api-client/tape";
|
|
4
4
|
import { canonicalExchange, normalizeSymbol } from "../../../utils/exchanges";
|
|
5
5
|
import { tapeTimeKey, tradeKey } from "./model";
|
|
6
6
|
|
|
@@ -25,7 +25,9 @@ export function validateTape(data: TapeSnapshot, symbol: string, exchange: strin
|
|
|
25
25
|
|| data.feed !== (data.access === "realtime" ? "sip" : "delayed_sip") || data.delaySeconds !== (data.access === "realtime" ? 0 : 900)
|
|
26
26
|
|| !data.capacity || !count(data.capacity.trades) || data.capacity.trades > 1000 || !count(data.capacity.quotes) || data.capacity.quotes > 500
|
|
27
27
|
|| !Array.isArray(data.trades) || data.trades.length > data.capacity.trades || !Array.isArray(data.quotes) || data.quotes.length > data.capacity.quotes
|
|
28
|
-
|| !data.dropped || !count(data.dropped.trades) || !count(data.dropped.quotes) || !count(data.corrections) || !count(data.cancels)
|
|
28
|
+
|| !data.dropped || !count(data.dropped.trades) || !count(data.dropped.quotes) || !count(data.corrections) || !count(data.cancels)
|
|
29
|
+
|| data.window !== undefined && (!data.window || !timestamp(data.window.from) || !timestamp(data.window.to) || !data.window.truncated
|
|
30
|
+
|| typeof data.window.truncated.trades !== "boolean" || typeof data.window.truncated.quotes !== "boolean")) {
|
|
29
31
|
throw new Error("The server returned an invalid tape snapshot");
|
|
30
32
|
}
|
|
31
33
|
const cutoffDate = new Date(Date.parse(data.generatedAt) - data.delaySeconds * 1000).toISOString();
|
|
@@ -56,9 +58,10 @@ export function validateTape(data: TapeSnapshot, symbol: string, exchange: strin
|
|
|
56
58
|
return { ...data, source: "Gloom Cloud", trades: [...data.trades].sort((a, b) => tapeTimeKey(a.timestamp).localeCompare(tapeTimeKey(b.timestamp))),
|
|
57
59
|
quotes: [...data.quotes].sort((a, b) => tapeTimeKey(a.timestamp).localeCompare(tapeTimeKey(b.timestamp))) };
|
|
58
60
|
}
|
|
61
|
+
/** The latest tape, or with a `range` that window of the session. */
|
|
59
62
|
export async function fetchTape(symbol: string, exchange: string, signal?: AbortSignal,
|
|
60
|
-
client: Pick<typeof apiClient, "getCloudTape"> = apiClient): Promise<TapeSnapshot> {
|
|
61
|
-
try { return validateTape(await client.getCloudTape(symbol, exchange, signal), symbol, exchange); }
|
|
63
|
+
client: Pick<typeof apiClient, "getCloudTape"> = apiClient, range?: TapeRange): Promise<TapeSnapshot> {
|
|
64
|
+
try { return validateTape(await (range ? client.getCloudTape(symbol, exchange, signal, range) : client.getCloudTape(symbol, exchange, signal)), symbol, exchange); }
|
|
62
65
|
catch (error) {
|
|
63
66
|
if (error instanceof ApiRequestError && [404, 503].includes(error.status ?? 0)) throw new Error("Time and sales is not available yet.");
|
|
64
67
|
// A rejected request answers with an unavailable snapshot; show its reason, not the JSON body.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/plugin";
|
|
2
|
-
import { subMinuteDataMessage } from "../../../cli/known-gaps";
|
|
3
|
-
import {
|
|
4
|
-
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
5
|
-
import { listingAbroadVenue } from "../../../utils/sec";
|
|
1
|
+
import type { HeadlessPaneColumn, HeadlessPaneContext, HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/plugin";
|
|
2
|
+
import { listedAbroad, subMinuteDataMessage } from "../../../cli/known-gaps";
|
|
3
|
+
import type { TapeRange, TapeSnapshot } from "../../../api-client/tape";
|
|
6
4
|
import { fetchTape } from "./client";
|
|
7
|
-
import { newestFirst, quoteSpread, tapePrice, tapePriceDigits, tapeStatistics } from "./model";
|
|
5
|
+
import { newestFirst, quoteSpread, tapePrice, tapePriceDigits, tapeStatistics, tapeTimeKey } from "./model";
|
|
6
|
+
import { REPORT_ROWS } from "../shared/headless-options";
|
|
7
|
+
import { inTapeRange, normalizeTapeClock, parseTapeClock, servedWindow, sessionDay, tapeMoment, tapeWindowRange } from "./window";
|
|
8
8
|
|
|
9
9
|
type Format = NonNullable<HeadlessPaneColumn["format"]>;
|
|
10
10
|
const fixedPrice = (digits: number): Format => (value) => tapePrice(typeof value === "number" ? value : null, digits);
|
|
11
|
+
const whole: Format = (value) => typeof value === "number" && Number.isFinite(value) ? value.toFixed(0) : "--";
|
|
11
12
|
const bps: Format = (value) => typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : "--";
|
|
12
13
|
|
|
13
14
|
/** Every row key in order, with the pane's rounding where the report would otherwise print raw floats. */
|
|
@@ -16,22 +17,62 @@ function formattedColumns(rows: readonly HeadlessPaneRow[], formats: Record<stri
|
|
|
16
17
|
.map((key) => ({ key, header: key, ...(formats[key] ? { format: formats[key] } : {}) }));
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
function oldestFirst<T extends { timestamp: string }>(rows: readonly T[]): T[] {
|
|
21
|
+
return [...rows].sort((a, b) => tapeTimeKey(a.timestamp).localeCompare(tapeTimeKey(b.timestamp)));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** When `--limit` cuts a window's trades: how many of them are listed. */
|
|
25
|
+
function listedNote(range: TapeRange, served: number, listed: number): string[] {
|
|
26
|
+
return listed < served ? [`Listing the first ${listed} of the ${served} trades from ${tapeMoment(Date.parse(range.from))}; --limit takes up to 1000.`] : [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const WINDOW_OPTION = { reportOnly: "lists a window of the session in the text report" };
|
|
31
|
+
|
|
19
32
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
33
|
+
* The window `--from`/`--to` ask for, read against the session the latest tape
|
|
34
|
+
* serves, then fetched as that window. A server that predates windows answers
|
|
35
|
+
* with its latest tape: its prints that fall in the window stand, and a note
|
|
36
|
+
* says from when that tape runs.
|
|
23
37
|
*/
|
|
24
|
-
function
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
38
|
+
async function loadWindow(symbol: string, exchange: string, from: string, to: string, ctx: HeadlessPaneContext) {
|
|
39
|
+
const latest = await fetchTape(symbol, exchange, ctx.signal, ctx.apiClient);
|
|
40
|
+
const served = servedWindow(latest);
|
|
41
|
+
if (!served) throw new Error(`TAS has no session for ${symbol} to read a window of; run it without --from and --to for the latest prints.`);
|
|
42
|
+
const range = tapeWindowRange(served, from ? parseTapeClock(from) : null, to ? parseTapeClock(to) : null);
|
|
43
|
+
const data = await fetchTape(symbol, exchange, ctx.signal, ctx.apiClient, range);
|
|
44
|
+
const notes = [`Window: ${sessionDay(served.date)}, ${tapeMoment(Date.parse(range.from))} to ${tapeMoment(Date.parse(range.to))}.`];
|
|
45
|
+
if (data.window) {
|
|
46
|
+
if (data.window.truncated.trades) {
|
|
47
|
+
const last = data.trades.at(-1)?.timestamp;
|
|
48
|
+
notes.push(`The window holds more than ${data.capacity.trades} trades: these are the first${last ? `, through ${tapeMoment(Date.parse(last))}` : ""}; start --from there to read on.`);
|
|
49
|
+
}
|
|
50
|
+
return { data, range, notes };
|
|
51
|
+
}
|
|
52
|
+
const trades = data.trades.filter((row) => inTapeRange(row.timestamp, range)), quotes = data.quotes.filter((row) => inTapeRange(row.timestamp, range));
|
|
53
|
+
// Dated by what it lists, as a served window is.
|
|
54
|
+
const times = oldestFirst([...trades, ...quotes]).map((row) => row.timestamp);
|
|
55
|
+
const kept = { ...data, trades, quotes, asOf: times.at(-1) ?? null, observedFrom: times[0] ?? null };
|
|
56
|
+
if (data.observedFrom && tapeTimeKey(data.observedFrom) > tapeTimeKey(range.from)) {
|
|
57
|
+
notes.push(`This server serves only its latest tape, from ${tapeMoment(Date.parse(data.observedFrom))}: prints before it are not listed.`);
|
|
58
|
+
}
|
|
59
|
+
return { data: kept, range, notes };
|
|
28
60
|
}
|
|
29
61
|
|
|
30
62
|
export const timeSalesHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
31
63
|
shape: "bundle", argument: { kind: "ticker", placeholder: "ticker", description: "US equity ticker." },
|
|
32
64
|
usListingsOnly: "Time and sales",
|
|
33
|
-
usListingsOnlyHint: subMinuteDataMessage,
|
|
34
|
-
options: [
|
|
65
|
+
usListingsOnlyHint: (symbol) => subMinuteDataMessage(symbol, "abroad"),
|
|
66
|
+
options: [
|
|
67
|
+
{ key: "limit", shot: REPORT_ROWS, type: "integer", minimum: 1, maximum: 1000, defaultValue: 100, description: "Maximum recent prints in the report." },
|
|
68
|
+
{ key: "from", shot: WINDOW_OPTION, type: "string", defaultValue: "", placeholder: "time", example: "--from 10:30 --to 10:35",
|
|
69
|
+
description: "Start of a window of the served session, as a time of day: 10:30 or 10:30:15 on New York's clock, 14:30Z on UTC's."
|
|
70
|
+
+ " The report then lists that window's prints oldest first; without --to it runs to the end of what is served.",
|
|
71
|
+
normalize: normalizeTapeClock },
|
|
72
|
+
{ key: "to", shot: WINDOW_OPTION, type: "string", defaultValue: "", placeholder: "time",
|
|
73
|
+
description: "End of the window, as --from takes it; without --from the window starts at 04:00 New York time.",
|
|
74
|
+
normalize: normalizeTapeClock },
|
|
75
|
+
],
|
|
35
76
|
discovery: { aliases: ["TAS", "QR"], dataRequirements: ["Gloom Cloud SIP trade and quote feed"],
|
|
36
77
|
limitations: ["Bounded observed window, not a complete session.", "SIP is delayed fifteen minutes without realtime entitlement.", "Quote sizes are round lots.", "No Level 2 or inferred aggressor side."] },
|
|
37
78
|
describe: (args) => `Time and sales ${args.argument ?? ""}`,
|
|
@@ -41,28 +82,36 @@ export const timeSalesHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
41
82
|
if (listedAbroad(symbol, instrument?.exchange)) {
|
|
42
83
|
// Say why the table is empty and where the finest bars for it are, rather than the tape's bare "unavailable".
|
|
43
84
|
return { complete: false, sections: [
|
|
44
|
-
{ title: ctx.settings?.tab === "quotes" ? "NBBO" : "Trades", rows: [], empty: subMinuteDataMessage(symbol) },
|
|
85
|
+
{ title: ctx.settings?.tab === "quotes" ? "NBBO" : "Trades", rows: [], empty: subMinuteDataMessage(symbol, "abroad") },
|
|
45
86
|
] };
|
|
46
87
|
}
|
|
47
|
-
const
|
|
88
|
+
const exchange = instrument?.exchange ?? "";
|
|
89
|
+
const from = String(args.options.from ?? "").trim(), to = String(args.options.to ?? "").trim();
|
|
90
|
+
const windowed = from || to ? await loadWindow(symbol, exchange, from, to, ctx) : null;
|
|
91
|
+
const data: TapeSnapshot = windowed?.data ?? await fetchTape(symbol, exchange, ctx.signal, ctx.apiClient);
|
|
92
|
+
const limit = Number(args.options.limit ?? 100);
|
|
48
93
|
const statistics = tapeStatistics(data);
|
|
49
94
|
const window = [{ from: statistics.from, asOf: statistics.asOf, prints: statistics.count, shares: statistics.volume,
|
|
50
95
|
vwap: statistics.vwap, low: statistics.low, high: statistics.high, pricePercentile: statistics.pricePercentile }];
|
|
51
|
-
|
|
96
|
+
// A window reads forward from its start; the latest tape newest first, as the pane lists it.
|
|
97
|
+
const ordered = <T extends { timestamp: string }>(rows: readonly T[]) => windowed ? oldestFirst(rows) : newestFirst(rows);
|
|
98
|
+
const quotes = ordered(data.quotes).slice(0, limit).map((row) => ({ ...row, ...quoteSpread(row), conditions: row.conditions.join(" ") }));
|
|
52
99
|
// One decimal count per column, as the pane shows it, so sub-penny prints align.
|
|
53
100
|
const price = fixedPrice(tapePriceDigits(data.trades.map((row) => row.price)));
|
|
54
|
-
const trades =
|
|
101
|
+
const trades = ordered(data.trades).slice(0, limit).map((row) => ({ ...row, conditions: row.conditions.join(" ") }));
|
|
102
|
+
const notes = windowed ? [...windowed.notes, ...listedNote(windowed.range, data.trades.length, trades.length)] : [];
|
|
55
103
|
// SIP prints, real-time or fifteen minutes delayed by entitlement, dated by the newest print.
|
|
56
104
|
const freshness = data.feed === "sip"
|
|
57
105
|
? { status: "live" as const, cadence: "daily" as const }
|
|
58
106
|
: { status: "delayed" as const, delayMinutes: Math.round(data.delaySeconds / 60), cadence: "daily" as const };
|
|
59
|
-
return { freshness, sections: [
|
|
60
|
-
|
|
107
|
+
return { freshness, ...(notes.length ? { notes } : {}), sections: [
|
|
108
|
+
// The last price's rank among the window's prints, whole, as the pane shows it.
|
|
109
|
+
{ title: windowed ? "Window" : "Observed window", columns: formattedColumns(window, { vwap: price, low: price, high: price, pricePercentile: whole }), rows: window },
|
|
61
110
|
{ title: "Regular session", columns: formattedColumns([data.session], { high: price, low: price }), rows: [data.session] },
|
|
62
111
|
{ title: "Trades", columns: formattedColumns(trades, { price }), rows: trades },
|
|
63
112
|
{ title: "NBBO", columns: formattedColumns(quotes, { bps }), rows: quotes },
|
|
64
113
|
], errors: data.gaps, metadata: { source: data.source, feed: data.feed, delaySeconds: data.delaySeconds,
|
|
65
|
-
asOf: data.asOf, observedFrom: data.observedFrom, generatedAt: data.generatedAt, status: data.status,
|
|
114
|
+
asOf: data.asOf, observedFrom: data.observedFrom, generatedAt: data.generatedAt, status: data.status, ...(windowed ? { window: windowed.range } : {}),
|
|
66
115
|
complete: data.status === "available", capacity: data.capacity, dropped: data.dropped, corrections: data.corrections, cancels: data.cancels, connected: data.connected } };
|
|
67
116
|
},
|
|
68
117
|
};
|
|
@@ -36,6 +36,18 @@ export const quoteKey = (row: TapeQuote) => JSON.stringify([tapeTimeKey(row.time
|
|
|
36
36
|
export function newestFirst<T extends { timestamp: string }>(rows: readonly T[]): T[] {
|
|
37
37
|
return [...rows].sort((a, b) => tapeTimeKey(b.timestamp).localeCompare(tapeTimeKey(a.timestamp)));
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A moment in milliseconds as a tape key, the one comparison both picking the
|
|
41
|
+
* row at a pinned moment and muting the rows after it use, so a print at
|
|
42
|
+
* 14:30:00.000000002 is after a moment of 14:30:00.000 for both.
|
|
43
|
+
*/
|
|
44
|
+
export const tapeMomentKey = (at: number) => tapeTimeKey(new Date(at).toISOString());
|
|
45
|
+
/** Whether a SIP stamp is after a moment's key (`tapeMomentKey`). */
|
|
46
|
+
export const tapeAfter = (timestamp: string, momentKey: string) => tapeTimeKey(timestamp) > momentKey;
|
|
47
|
+
/** Of rows newest first, the first printed at or before a moment's key: null when every row is later. */
|
|
48
|
+
export function tapeRowAtOrBefore<T>(rows: readonly T[], timestamp: (row: T) => string, momentKey: string): T | null {
|
|
49
|
+
return rows.find((row) => !tapeAfter(timestamp(row), momentKey)) ?? null;
|
|
50
|
+
}
|
|
39
51
|
export function tapeStatistics(data: Pick<TapeSnapshot, "trades">) {
|
|
40
52
|
const trades = newestFirst(data.trades);
|
|
41
53
|
const prices = trades.map((row) => row.price);
|